proxygen
ProducerConsumerQueueTest.cpp
Go to the documentation of this file.
1 /*
2  * Copyright 2012-present Facebook, Inc.
3  *
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
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16 
18 
19 #include <atomic>
20 #include <chrono>
21 #include <memory>
22 #include <thread>
23 #include <vector>
24 
25 #include <glog/logging.h>
26 
28 
30 
31 namespace {
32 
33 template <class T>
34 struct TestTraits {
35  T limit() const {
36  return 1 << 24;
37  }
38  T generate() const {
39  return rand() % 26;
40  }
41 };
42 
43 template <>
44 struct TestTraits<std::string> {
45  unsigned int limit() const {
46  return 1 << 22;
47  }
48  std::string generate() const {
49  return std::string(12, ' ');
50  }
51 };
52 
53 template <class QueueType, size_t Size, bool Pop = false>
54 struct PerfTest {
55  typedef typename QueueType::value_type T;
56 
57  explicit PerfTest() : queue_(Size), done_(false) {}
58 
59  void operator()() {
60  using namespace std::chrono;
61  auto const startTime = system_clock::now();
62 
63  std::thread producer([this] { this->producer(); });
64  std::thread consumer([this] { this->consumer(); });
65 
66  producer.join();
67  done_ = true;
68  consumer.join();
69 
70  auto duration =
71  duration_cast<milliseconds>(system_clock::now() - startTime);
72  LOG(INFO) << " done: " << duration.count() << "ms";
73  }
74 
75  void producer() {
76  // This is written differently than you might expect so that
77  // it does not run afoul of -Wsign-compare, regardless of the
78  // signedness of this loop's upper bound.
79  for (auto i = traits_.limit(); i > 0; --i) {
80  while (!queue_.write(traits_.generate())) {
81  }
82  }
83  }
84 
85  void consumer() {
86  if /* constexpr */ (Pop) {
87  while (!done_) {
88  if (queue_.frontPtr()) {
89  queue_.popFront();
90  }
91  }
92  } else {
93  while (!done_) {
94  T data;
95  queue_.read(data);
96  }
97  }
98  }
99 
100  QueueType queue_;
101  std::atomic<bool> done_;
102  TestTraits<T> traits_;
103 };
104 
105 template <class TestType>
106 void doTest(const char* name) {
107  LOG(INFO) << " testing: " << name;
108  std::unique_ptr<TestType> const t(new TestType());
109  (*t)();
110 }
111 
112 template <class T, bool Pop = false>
113 void perfTestType(const char* type) {
114  const size_t size = 0xfffe;
115 
116  LOG(INFO) << "Type: " << type;
117  doTest<PerfTest<folly::ProducerConsumerQueue<T>, size, Pop>>(
118  "ProducerConsumerQueue");
119 }
120 
121 template <class QueueType, size_t Size, bool Pop>
122 struct CorrectnessTest {
123  typedef typename QueueType::value_type T;
124 
125  explicit CorrectnessTest() : queue_(Size), done_(false) {
126  const size_t testSize = traits_.limit();
127  testData_.reserve(testSize);
128  for (size_t i = 0; i < testSize; ++i) {
129  testData_.push_back(traits_.generate());
130  }
131  }
132 
133  void operator()() {
134  std::thread producer([this] { this->producer(); });
135  std::thread consumer([this] { this->consumer(); });
136 
137  producer.join();
138  done_ = true;
139  consumer.join();
140  }
141 
142  void producer() {
143  for (auto& data : testData_) {
144  while (!queue_.write(data)) {
145  }
146  }
147  }
148 
149  void consumer() {
150  if (Pop) {
151  consumerPop();
152  } else {
153  consumerRead();
154  }
155  }
156 
157  void consumerPop() {
158  for (auto expect : testData_) {
159  again:
160  T* data;
161  if (!(data = queue_.frontPtr())) {
162  if (done_) {
163  // Try one more read; unless there's a bug in the queue class
164  // there should still be more data sitting in the queue even
165  // though the producer thread exited.
166  if (!(data = queue_.frontPtr())) {
167  EXPECT_TRUE(0 && "Finished too early ...");
168  return;
169  }
170  } else {
171  goto again;
172  }
173  EXPECT_EQ(*data, expect);
174  } else {
175  EXPECT_EQ(*data, expect);
176  queue_.popFront();
177  }
178  }
179  }
180 
181  void consumerRead() {
182  for (auto expect : testData_) {
183  again:
184  T data;
185  if (!queue_.read(data)) {
186  if (done_) {
187  // Try one more read; unless there's a bug in the queue class
188  // there should still be more data sitting in the queue even
189  // though the producer thread exited.
190  if (!queue_.read(data)) {
191  EXPECT_TRUE(0 && "Finished too early ...");
192  return;
193  }
194  } else {
195  goto again;
196  }
197  }
198  EXPECT_EQ(data, expect);
199  }
200  }
201 
202  std::vector<T> testData_;
203  QueueType queue_;
204  TestTraits<T> traits_;
205  std::atomic<bool> done_;
206 };
207 
208 template <class T, bool Pop = false>
209 void correctnessTestType(const std::string& type) {
210  LOG(INFO) << "Type: " << type;
211  doTest<CorrectnessTest<folly::ProducerConsumerQueue<T>, 0xfffe, Pop>>(
212  "ProducerConsumerQueue");
213 }
214 
215 struct DtorChecker {
216  static unsigned int numInstances;
217  DtorChecker() {
218  ++numInstances;
219  }
220  DtorChecker(const DtorChecker& /* o */) {
221  ++numInstances;
222  }
223  ~DtorChecker() {
224  --numInstances;
225  }
226 };
227 
228 unsigned int DtorChecker::numInstances = 0;
229 
230 } // namespace
231 
233 
234 TEST(PCQ, QueueCorrectness) {
235  correctnessTestType<std::string, true>("string (front+pop)");
236  correctnessTestType<std::string>("string");
237  correctnessTestType<int>("int");
238  correctnessTestType<unsigned long long>("unsigned long long");
239 }
240 
241 TEST(PCQ, PerfTest) {
242  perfTestType<std::string, true>("string (front+pop)");
243  perfTestType<std::string>("string");
244  perfTestType<int>("int");
245  perfTestType<unsigned long long>("unsigned long long");
246 }
247 
248 TEST(PCQ, Destructor) {
249  // Test that orphaned elements in a ProducerConsumerQueue are
250  // destroyed.
251  {
253  for (int i = 0; i < 10; ++i) {
254  EXPECT_TRUE(queue.write(DtorChecker()));
255  }
256 
257  EXPECT_EQ(DtorChecker::numInstances, 10);
258 
259  {
260  DtorChecker ignore;
261  EXPECT_TRUE(queue.read(ignore));
262  EXPECT_TRUE(queue.read(ignore));
263  }
264 
265  EXPECT_EQ(DtorChecker::numInstances, 8);
266  }
267 
268  EXPECT_EQ(DtorChecker::numInstances, 0);
269 
270  // Test the same thing in the case that the queue write pointer has
271  // wrapped, but the read one hasn't.
272  {
274  for (int i = 0; i < 3; ++i) {
275  EXPECT_TRUE(queue.write(DtorChecker()));
276  }
277  EXPECT_EQ(DtorChecker::numInstances, 3);
278  {
279  DtorChecker ignore;
280  EXPECT_TRUE(queue.read(ignore));
281  }
282  EXPECT_EQ(DtorChecker::numInstances, 2);
283  EXPECT_TRUE(queue.write(DtorChecker()));
284  EXPECT_EQ(DtorChecker::numInstances, 3);
285  }
286  EXPECT_EQ(DtorChecker::numInstances, 0);
287 }
288 
289 TEST(PCQ, EmptyFull) {
291  EXPECT_TRUE(queue.isEmpty());
292  EXPECT_FALSE(queue.isFull());
293 
294  EXPECT_TRUE(queue.write(1));
295  EXPECT_FALSE(queue.isEmpty());
296  EXPECT_FALSE(queue.isFull());
297 
298  EXPECT_TRUE(queue.write(2));
299  EXPECT_FALSE(queue.isEmpty());
300  EXPECT_TRUE(queue.isFull()); // Tricky: full after 2 writes, not 3.
301 
302  EXPECT_FALSE(queue.write(3));
303  EXPECT_EQ(queue.sizeGuess(), 2);
304 }
305 
306 TEST(PCQ, Capacity) {
308  EXPECT_EQ(queue.capacity(), 2); // PCQ max size is buffer size - 1.
309 }
#define T(v)
Definition: http_parser.c:233
static std::atomic< int > testSize(1000)
PskType type
#define EXPECT_EQ(val1, val2)
Definition: gtest.h:1922
std::chrono::steady_clock::time_point now()
STL namespace.
TEST(PCQ, QueueCorrectness)
const char * name
Definition: http_parser.c:437
constexpr auto size(C const &c) -> decltype(c.size())
Definition: Access.h:45
void expect(LineReader &lr, const char *expected)
#define EXPECT_TRUE(condition)
Definition: gtest.h:1859
bool write(Args &&...recordArgs)
const char * string
Definition: Conv.cpp:212
#define EXPECT_FALSE(condition)
Definition: gtest.h:1862
static constexpr uint64_t data[1]
Definition: Fingerprint.cpp:43