2 * Copyright 2017 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>
27 #include <folly/portability/GTest.h>
29 //////////////////////////////////////////////////////////////////////
33 template<class T> struct TestTraits {
34 T limit() const { return 1 << 24; }
35 T generate() const { return rand() % 26; }
38 template<> struct TestTraits<std::string> {
39 unsigned int limit() const { return 1 << 22; }
40 std::string generate() const { return std::string(12, ' '); }
43 template<class QueueType, size_t Size, bool Pop = false>
45 typedef typename QueueType::value_type T;
47 explicit PerfTest() : queue_(Size), done_(false) {}
50 using namespace std::chrono;
51 auto const startTime = system_clock::now();
53 std::thread producer([this] { this->producer(); });
54 std::thread consumer([this] { this->consumer(); });
60 auto duration = duration_cast<milliseconds>(
61 system_clock::now() - startTime);
62 LOG(INFO) << " done: " << duration.count() << "ms";
66 // This is written differently than you might expect so that
67 // it does not run afoul of -Wsign-compare, regardless of the
68 // signedness of this loop's upper bound.
69 for (auto i = traits_.limit(); i > 0; --i) {
70 while (!queue_.write(traits_.generate())) {
78 if (queue_.frontPtr()) {
91 std::atomic<bool> done_;
92 TestTraits<T> traits_;
95 template<class TestType> void doTest(const char* name) {
96 LOG(INFO) << " testing: " << name;
97 std::unique_ptr<TestType> const t(new TestType());
101 template<class T, bool Pop = false>
102 void perfTestType(const char* type) {
103 const size_t size = 0xfffe;
105 LOG(INFO) << "Type: " << type;
106 doTest<PerfTest<folly::ProducerConsumerQueue<T>,size,Pop> >(
107 "ProducerConsumerQueue");
110 template<class QueueType, size_t Size, bool Pop>
111 struct CorrectnessTest {
112 typedef typename QueueType::value_type T;
114 explicit CorrectnessTest()
118 const size_t testSize = traits_.limit();
119 testData_.reserve(testSize);
120 for (size_t i = 0; i < testSize; ++i) {
121 testData_.push_back(traits_.generate());
126 std::thread producer([this] { this->producer(); });
127 std::thread consumer([this] { this->consumer(); });
135 for (auto& data : testData_) {
136 while (!queue_.write(data)) {
150 for (auto expect : testData_) {
153 if (!(data = queue_.frontPtr())) {
155 // Try one more read; unless there's a bug in the queue class
156 // there should still be more data sitting in the queue even
157 // though the producer thread exited.
158 if (!(data = queue_.frontPtr())) {
159 EXPECT_TRUE(0 && "Finished too early ...");
165 EXPECT_EQ(*data, expect);
167 EXPECT_EQ(*data, expect);
173 void consumerRead() {
174 for (auto expect : testData_) {
177 if (!queue_.read(data)) {
179 // Try one more read; unless there's a bug in the queue class
180 // there should still be more data sitting in the queue even
181 // though the producer thread exited.
182 if (!queue_.read(data)) {
183 EXPECT_TRUE(0 && "Finished too early ...");
190 EXPECT_EQ(data, expect);
194 std::vector<T> testData_;
196 TestTraits<T> traits_;
197 std::atomic<bool> done_;
200 template<class T, bool Pop = false>
201 void correctnessTestType(const std::string& type) {
202 LOG(INFO) << "Type: " << type;
203 doTest<CorrectnessTest<folly::ProducerConsumerQueue<T>,0xfffe,Pop> >(
204 "ProducerConsumerQueue");
208 static unsigned int numInstances;
209 DtorChecker() { ++numInstances; }
210 DtorChecker(const DtorChecker& /* o */) { ++numInstances; }
211 ~DtorChecker() { --numInstances; }
214 unsigned int DtorChecker::numInstances = 0;
218 //////////////////////////////////////////////////////////////////////
220 TEST(PCQ, QueueCorrectness) {
221 correctnessTestType<std::string,true>("string (front+pop)");
222 correctnessTestType<std::string>("string");
223 correctnessTestType<int>("int");
224 correctnessTestType<unsigned long long>("unsigned long long");
227 TEST(PCQ, PerfTest) {
228 perfTestType<std::string,true>("string (front+pop)");
229 perfTestType<std::string>("string");
230 perfTestType<int>("int");
231 perfTestType<unsigned long long>("unsigned long long");
234 TEST(PCQ, Destructor) {
235 // Test that orphaned elements in a ProducerConsumerQueue are
238 folly::ProducerConsumerQueue<DtorChecker> queue(1024);
239 for (int i = 0; i < 10; ++i) {
240 EXPECT_TRUE(queue.write(DtorChecker()));
243 EXPECT_EQ(DtorChecker::numInstances, 10);
247 EXPECT_TRUE(queue.read(ignore));
248 EXPECT_TRUE(queue.read(ignore));
251 EXPECT_EQ(DtorChecker::numInstances, 8);
254 EXPECT_EQ(DtorChecker::numInstances, 0);
256 // Test the same thing in the case that the queue write pointer has
257 // wrapped, but the read one hasn't.
259 folly::ProducerConsumerQueue<DtorChecker> queue(4);
260 for (int i = 0; i < 3; ++i) {
261 EXPECT_TRUE(queue.write(DtorChecker()));
263 EXPECT_EQ(DtorChecker::numInstances, 3);
266 EXPECT_TRUE(queue.read(ignore));
268 EXPECT_EQ(DtorChecker::numInstances, 2);
269 EXPECT_TRUE(queue.write(DtorChecker()));
270 EXPECT_EQ(DtorChecker::numInstances, 3);
272 EXPECT_EQ(DtorChecker::numInstances, 0);
275 TEST(PCQ, EmptyFull) {
276 folly::ProducerConsumerQueue<int> queue(3);
277 EXPECT_TRUE(queue.isEmpty());
278 EXPECT_FALSE(queue.isFull());
280 EXPECT_TRUE(queue.write(1));
281 EXPECT_FALSE(queue.isEmpty());
282 EXPECT_FALSE(queue.isFull());
284 EXPECT_TRUE(queue.write(2));
285 EXPECT_FALSE(queue.isEmpty());
286 EXPECT_TRUE(queue.isFull()); // Tricky: full after 2 writes, not 3.
288 EXPECT_FALSE(queue.write(3));
289 EXPECT_EQ(queue.sizeGuess(), 2);