2 * Copyright 2016 Facebook, Inc.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 #include <folly/ProducerConsumerQueue.h>
25 #include <glog/logging.h>
26 #include <gtest/gtest.h>
28 //////////////////////////////////////////////////////////////////////
32 template<class T> struct TestTraits {
33 T limit() const { return 1 << 24; }
34 T generate() const { return rand() % 26; }
37 template<> struct TestTraits<std::string> {
38 unsigned int limit() const { return 1 << 22; }
39 std::string generate() const { return std::string(12, ' '); }
42 template<class QueueType, size_t Size, bool Pop = false>
44 typedef typename QueueType::value_type T;
46 explicit PerfTest() : queue_(Size), done_(false) {}
49 using namespace std::chrono;
50 auto const startTime = system_clock::now();
52 std::thread producer([this] { this->producer(); });
53 std::thread consumer([this] { this->consumer(); });
59 auto duration = duration_cast<milliseconds>(
60 system_clock::now() - startTime);
61 LOG(INFO) << " done: " << duration.count() << "ms";
65 // This is written differently than you might expect so that
66 // it does not run afoul of -Wsign-compare, regardless of the
67 // signedness of this loop's upper bound.
68 for (auto i = traits_.limit(); i > 0; --i) {
69 while (!queue_.write(traits_.generate())) {
77 if (queue_.frontPtr()) {
90 std::atomic<bool> done_;
91 TestTraits<T> traits_;
94 template<class TestType> void doTest(const char* name) {
95 LOG(INFO) << " testing: " << name;
96 std::unique_ptr<TestType> const t(new TestType());
100 template<class T, bool Pop = false>
101 void perfTestType(const char* type) {
102 const size_t size = 0xfffe;
104 LOG(INFO) << "Type: " << type;
105 doTest<PerfTest<folly::ProducerConsumerQueue<T>,size,Pop> >(
106 "ProducerConsumerQueue");
109 template<class QueueType, size_t Size, bool Pop>
110 struct CorrectnessTest {
111 typedef typename QueueType::value_type T;
113 explicit CorrectnessTest()
117 const size_t testSize = traits_.limit();
118 testData_.reserve(testSize);
119 for (size_t i = 0; i < testSize; ++i) {
120 testData_.push_back(traits_.generate());
125 std::thread producer([this] { this->producer(); });
126 std::thread consumer([this] { this->consumer(); });
134 for (auto& data : testData_) {
135 while (!queue_.write(data)) {
149 for (auto expect : testData_) {
152 if (!(data = queue_.frontPtr())) {
154 // Try one more read; unless there's a bug in the queue class
155 // there should still be more data sitting in the queue even
156 // though the producer thread exited.
157 if (!(data = queue_.frontPtr())) {
158 EXPECT_TRUE(0 && "Finished too early ...");
164 EXPECT_EQ(*data, expect);
166 EXPECT_EQ(*data, expect);
172 void consumerRead() {
173 for (auto expect : testData_) {
176 if (!queue_.read(data)) {
178 // Try one more read; unless there's a bug in the queue class
179 // there should still be more data sitting in the queue even
180 // though the producer thread exited.
181 if (!queue_.read(data)) {
182 EXPECT_TRUE(0 && "Finished too early ...");
189 EXPECT_EQ(data, expect);
193 std::vector<T> testData_;
195 TestTraits<T> traits_;
196 std::atomic<bool> done_;
199 template<class T, bool Pop = false>
200 void correctnessTestType(const std::string& type) {
201 LOG(INFO) << "Type: " << type;
202 doTest<CorrectnessTest<folly::ProducerConsumerQueue<T>,0xfffe,Pop> >(
203 "ProducerConsumerQueue");
207 static unsigned int numInstances;
208 DtorChecker() { ++numInstances; }
209 DtorChecker(const DtorChecker& /* o */) { ++numInstances; }
210 ~DtorChecker() { --numInstances; }
213 unsigned int DtorChecker::numInstances = 0;
217 //////////////////////////////////////////////////////////////////////
219 TEST(PCQ, QueueCorrectness) {
220 correctnessTestType<std::string,true>("string (front+pop)");
221 correctnessTestType<std::string>("string");
222 correctnessTestType<int>("int");
223 correctnessTestType<unsigned long long>("unsigned long long");
226 TEST(PCQ, PerfTest) {
227 perfTestType<std::string,true>("string (front+pop)");
228 perfTestType<std::string>("string");
229 perfTestType<int>("int");
230 perfTestType<unsigned long long>("unsigned long long");
233 TEST(PCQ, Destructor) {
234 // Test that orphaned elements in a ProducerConsumerQueue are
237 folly::ProducerConsumerQueue<DtorChecker> queue(1024);
238 for (int i = 0; i < 10; ++i) {
239 EXPECT_TRUE(queue.write(DtorChecker()));
242 EXPECT_EQ(DtorChecker::numInstances, 10);
246 EXPECT_TRUE(queue.read(ignore));
247 EXPECT_TRUE(queue.read(ignore));
250 EXPECT_EQ(DtorChecker::numInstances, 8);
253 EXPECT_EQ(DtorChecker::numInstances, 0);
255 // Test the same thing in the case that the queue write pointer has
256 // wrapped, but the read one hasn't.
258 folly::ProducerConsumerQueue<DtorChecker> queue(4);
259 for (int i = 0; i < 3; ++i) {
260 EXPECT_TRUE(queue.write(DtorChecker()));
262 EXPECT_EQ(DtorChecker::numInstances, 3);
265 EXPECT_TRUE(queue.read(ignore));
267 EXPECT_EQ(DtorChecker::numInstances, 2);
268 EXPECT_TRUE(queue.write(DtorChecker()));
269 EXPECT_EQ(DtorChecker::numInstances, 3);
271 EXPECT_EQ(DtorChecker::numInstances, 0);
274 TEST(PCQ, EmptyFull) {
275 folly::ProducerConsumerQueue<int> queue(3);
276 EXPECT_TRUE(queue.isEmpty());
277 EXPECT_FALSE(queue.isFull());
279 EXPECT_TRUE(queue.write(1));
280 EXPECT_FALSE(queue.isEmpty());
281 EXPECT_FALSE(queue.isFull());
283 EXPECT_TRUE(queue.write(2));
284 EXPECT_FALSE(queue.isEmpty());
285 EXPECT_TRUE(queue.isFull()); // Tricky: full after 2 writes, not 3.
287 EXPECT_FALSE(queue.write(3));
288 EXPECT_EQ(queue.sizeGuess(), 2);