2 * Copyright 2015 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 unsigned 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 // This is written differently than you might expect so that
65 // it does not run afoul of -Wsign-compare, regardless of the
66 // signedness of this loop's upper bound.
67 for (auto i = traits_.limit(); i > 0; --i) {
68 while (!queue_.write(traits_.generate())) {
76 if (queue_.frontPtr()) {
89 std::atomic<bool> done_;
90 TestTraits<T> traits_;
93 template<class TestType> void doTest(const char* name) {
94 LOG(INFO) << " testing: " << name;
95 std::unique_ptr<TestType> const t(new TestType());
99 template<class T, bool Pop = false>
100 void perfTestType(const char* type) {
101 const size_t size = 0xfffe;
103 LOG(INFO) << "Type: " << type;
104 doTest<PerfTest<folly::ProducerConsumerQueue<T>,size,Pop> >(
105 "ProducerConsumerQueue");
108 template<class QueueType, size_t Size, bool Pop>
109 struct CorrectnessTest {
110 typedef typename QueueType::value_type T;
112 explicit CorrectnessTest()
116 const size_t testSize = traits_.limit();
117 testData_.reserve(testSize);
118 for (size_t i = 0; i < testSize; ++i) {
119 testData_.push_back(traits_.generate());
124 std::thread producer([this] { this->producer(); });
125 std::thread consumer([this] { this->consumer(); });
133 for (auto& data : testData_) {
134 while (!queue_.write(data)) {
148 for (auto expect : testData_) {
151 if (!(data = queue_.frontPtr())) {
153 // Try one more read; unless there's a bug in the queue class
154 // there should still be more data sitting in the queue even
155 // though the producer thread exited.
156 if (!(data = queue_.frontPtr())) {
157 EXPECT_TRUE(0 && "Finished too early ...");
167 EXPECT_EQ(*data, expect);
171 void consumerRead() {
172 for (auto expect : testData_) {
175 if (!queue_.read(data)) {
177 // Try one more read; unless there's a bug in the queue class
178 // there should still be more data sitting in the queue even
179 // though the producer thread exited.
180 if (!queue_.read(data)) {
181 EXPECT_TRUE(0 && "Finished too early ...");
188 EXPECT_EQ(data, expect);
192 std::vector<T> testData_;
194 TestTraits<T> traits_;
195 std::atomic<bool> done_;
198 template<class T, bool Pop = false>
199 void correctnessTestType(const std::string& type) {
200 LOG(INFO) << "Type: " << type;
201 doTest<CorrectnessTest<folly::ProducerConsumerQueue<T>,0xfffe,Pop> >(
202 "ProducerConsumerQueue");
206 static unsigned int numInstances;
207 DtorChecker() { ++numInstances; }
208 DtorChecker(const DtorChecker& o) { ++numInstances; }
209 ~DtorChecker() { --numInstances; }
212 unsigned int DtorChecker::numInstances = 0;
216 //////////////////////////////////////////////////////////////////////
218 TEST(PCQ, QueueCorrectness) {
219 correctnessTestType<std::string,true>("string (front+pop)");
220 correctnessTestType<std::string>("string");
221 correctnessTestType<int>("int");
222 correctnessTestType<unsigned long long>("unsigned long long");
225 TEST(PCQ, PerfTest) {
226 perfTestType<std::string,true>("string (front+pop)");
227 perfTestType<std::string>("string");
228 perfTestType<int>("int");
229 perfTestType<unsigned long long>("unsigned long long");
232 TEST(PCQ, Destructor) {
233 // Test that orphaned elements in a ProducerConsumerQueue are
236 folly::ProducerConsumerQueue<DtorChecker> queue(1024);
237 for (int i = 0; i < 10; ++i) {
238 EXPECT_TRUE(queue.write(DtorChecker()));
241 EXPECT_EQ(DtorChecker::numInstances, 10);
245 EXPECT_TRUE(queue.read(ignore));
246 EXPECT_TRUE(queue.read(ignore));
249 EXPECT_EQ(DtorChecker::numInstances, 8);
252 EXPECT_EQ(DtorChecker::numInstances, 0);
254 // Test the same thing in the case that the queue write pointer has
255 // wrapped, but the read one hasn't.
257 folly::ProducerConsumerQueue<DtorChecker> queue(4);
258 for (int i = 0; i < 3; ++i) {
259 EXPECT_TRUE(queue.write(DtorChecker()));
261 EXPECT_EQ(DtorChecker::numInstances, 3);
264 EXPECT_TRUE(queue.read(ignore));
266 EXPECT_EQ(DtorChecker::numInstances, 2);
267 EXPECT_TRUE(queue.write(DtorChecker()));
268 EXPECT_EQ(DtorChecker::numInstances, 3);
270 EXPECT_EQ(DtorChecker::numInstances, 0);
273 TEST(PCQ, EmptyFull) {
274 folly::ProducerConsumerQueue<int> queue(3);
275 EXPECT_TRUE(queue.isEmpty());
276 EXPECT_FALSE(queue.isFull());
278 EXPECT_TRUE(queue.write(1));
279 EXPECT_FALSE(queue.isEmpty());
280 EXPECT_FALSE(queue.isFull());
282 EXPECT_TRUE(queue.write(2));
283 EXPECT_FALSE(queue.isEmpty());
284 EXPECT_TRUE(queue.isFull()); // Tricky: full after 2 writes, not 3.
286 EXPECT_FALSE(queue.write(3));
287 EXPECT_EQ(queue.sizeGuess(), 2);