2 * Copyright 2014 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>
19 #include <gtest/gtest.h>
25 #include <glog/logging.h>
27 //////////////////////////////////////////////////////////////////////
31 template<class T> struct TestTraits {
32 T limit() const { return 1 << 24; }
33 T generate() const { return rand() % 26; }
36 template<> struct TestTraits<std::string> {
37 int limit() const { return 1 << 22; }
38 std::string generate() const { return std::string(12, ' '); }
41 template<class QueueType, size_t Size, bool Pop = false>
43 typedef typename QueueType::value_type T;
45 explicit PerfTest() : queue_(Size), done_(false) {}
48 using namespace std::chrono;
49 auto const startTime = system_clock::now();
51 std::thread producer([this] { this->producer(); });
52 std::thread consumer([this] { this->consumer(); });
58 auto duration = duration_cast<milliseconds>(
59 system_clock::now() - startTime);
60 LOG(INFO) << " done: " << duration.count() << "ms";
64 for (int i = 0; i < traits_.limit(); ++i) {
65 while (!queue_.write(traits_.generate())) {
73 if (queue_.frontPtr()) {
86 std::atomic<bool> done_;
87 TestTraits<T> traits_;
90 template<class TestType> void doTest(const char* name) {
91 LOG(INFO) << " testing: " << name;
92 std::unique_ptr<TestType> const t(new TestType());
96 template<class T, bool Pop = false>
97 void perfTestType(const char* type) {
98 const size_t size = 0xfffe;
100 LOG(INFO) << "Type: " << type;
101 doTest<PerfTest<folly::ProducerConsumerQueue<T>,size,Pop> >(
102 "ProducerConsumerQueue");
105 template<class QueueType, size_t Size, bool Pop>
106 struct CorrectnessTest {
107 typedef typename QueueType::value_type T;
109 explicit CorrectnessTest()
113 const size_t testSize = traits_.limit();
114 testData_.reserve(testSize);
115 for (int i = 0; i < testSize; ++i) {
116 testData_.push_back(traits_.generate());
121 std::thread producer([this] { this->producer(); });
122 std::thread consumer([this] { this->consumer(); });
130 for (auto& data : testData_) {
131 while (!queue_.write(data)) {
145 for (auto expect : testData_) {
148 if (!(data = queue_.frontPtr())) {
150 // Try one more read; unless there's a bug in the queue class
151 // there should still be more data sitting in the queue even
152 // though the producer thread exited.
153 if (!(data = queue_.frontPtr())) {
154 EXPECT_TRUE(0 && "Finished too early ...");
164 EXPECT_EQ(*data, expect);
168 void consumerRead() {
169 for (auto expect : testData_) {
172 if (!queue_.read(data)) {
174 // Try one more read; unless there's a bug in the queue class
175 // there should still be more data sitting in the queue even
176 // though the producer thread exited.
177 if (!queue_.read(data)) {
178 EXPECT_TRUE(0 && "Finished too early ...");
185 EXPECT_EQ(data, expect);
189 std::vector<T> testData_;
191 TestTraits<T> traits_;
192 std::atomic<bool> done_;
195 template<class T, bool Pop = false>
196 void correctnessTestType(const std::string& type) {
197 LOG(INFO) << "Type: " << type;
198 doTest<CorrectnessTest<folly::ProducerConsumerQueue<T>,0xfffe,Pop> >(
199 "ProducerConsumerQueue");
203 static int numInstances;
204 DtorChecker() { ++numInstances; }
205 DtorChecker(const DtorChecker& o) { ++numInstances; }
206 ~DtorChecker() { --numInstances; }
209 int DtorChecker::numInstances = 0;
213 //////////////////////////////////////////////////////////////////////
215 TEST(PCQ, QueueCorrectness) {
216 correctnessTestType<std::string,true>("string (front+pop)");
217 correctnessTestType<std::string>("string");
218 correctnessTestType<int>("int");
219 correctnessTestType<unsigned long long>("unsigned long long");
222 TEST(PCQ, PerfTest) {
223 perfTestType<std::string,true>("string (front+pop)");
224 perfTestType<std::string>("string");
225 perfTestType<int>("int");
226 perfTestType<unsigned long long>("unsigned long long");
229 TEST(PCQ, Destructor) {
230 // Test that orphaned elements in a ProducerConsumerQueue are
233 folly::ProducerConsumerQueue<DtorChecker> queue(1024);
234 for (int i = 0; i < 10; ++i) {
235 EXPECT_TRUE(queue.write(DtorChecker()));
238 EXPECT_EQ(DtorChecker::numInstances, 10);
242 EXPECT_TRUE(queue.read(ignore));
243 EXPECT_TRUE(queue.read(ignore));
246 EXPECT_EQ(DtorChecker::numInstances, 8);
249 EXPECT_EQ(DtorChecker::numInstances, 0);
251 // Test the same thing in the case that the queue write pointer has
252 // wrapped, but the read one hasn't.
254 folly::ProducerConsumerQueue<DtorChecker> queue(4);
255 for (int i = 0; i < 3; ++i) {
256 EXPECT_TRUE(queue.write(DtorChecker()));
258 EXPECT_EQ(DtorChecker::numInstances, 3);
261 EXPECT_TRUE(queue.read(ignore));
263 EXPECT_EQ(DtorChecker::numInstances, 2);
264 EXPECT_TRUE(queue.write(DtorChecker()));
265 EXPECT_EQ(DtorChecker::numInstances, 3);
267 EXPECT_EQ(DtorChecker::numInstances, 0);
270 TEST(PCQ, EmptyFull) {
271 folly::ProducerConsumerQueue<int> queue(3);
272 EXPECT_TRUE(queue.isEmpty());
273 EXPECT_FALSE(queue.isFull());
275 EXPECT_TRUE(queue.write(1));
276 EXPECT_FALSE(queue.isEmpty());
277 EXPECT_FALSE(queue.isFull());
279 EXPECT_TRUE(queue.write(2));
280 EXPECT_FALSE(queue.isEmpty());
281 EXPECT_TRUE(queue.isFull()); // Tricky: full after 2 writes, not 3.
283 EXPECT_FALSE(queue.write(3));
284 EXPECT_EQ(queue.sizeGuess(), 2);