/*
- * Copyright 2012 Facebook, Inc.
+ * Copyright 2015 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* limitations under the License.
*/
-#include "folly/ProducerConsumerQueue.h"
+#include <folly/ProducerConsumerQueue.h>
#include <gtest/gtest.h>
#include <vector>
};
template<> struct TestTraits<std::string> {
- int limit() const { return 1 << 21; }
+ unsigned int limit() const { return 1 << 22; }
std::string generate() const { return std::string(12, ' '); }
};
-template<class QueueType, size_t Size>
+template<class QueueType, size_t Size, bool Pop = false>
struct PerfTest {
typedef typename QueueType::value_type T;
}
void producer() {
- for (int i = 0; i < traits_.limit(); ++i) {
+ // This is written differently than you might expect so that
+ // it does not run afoul of -Wsign-compare, regardless of the
+ // signedness of this loop's upper bound.
+ for (auto i = traits_.limit(); i > 0; --i) {
while (!queue_.write(traits_.generate())) {
}
}
}
void consumer() {
- while (!done_) {
- T data;
- queue_.read(data);
+ /*static*/ if (Pop) {
+ while (!done_) {
+ if (queue_.frontPtr()) {
+ queue_.popFront();
+ }
+ }
+ } else {
+ while (!done_) {
+ T data;
+ queue_.read(data);
+ }
}
}
(*t)();
}
-template<class T> void perfTestType(const char* type) {
+template<class T, bool Pop = false>
+void perfTestType(const char* type) {
const size_t size = 0xfffe;
LOG(INFO) << "Type: " << type;
- doTest<PerfTest<folly::ProducerConsumerQueue<T>,size> >(
+ doTest<PerfTest<folly::ProducerConsumerQueue<T>,size,Pop> >(
"ProducerConsumerQueue");
}
-template<class QueueType, size_t Size>
+template<class QueueType, size_t Size, bool Pop>
struct CorrectnessTest {
typedef typename QueueType::value_type T;
{
const size_t testSize = traits_.limit();
testData_.reserve(testSize);
- for (int i = 0; i < testSize; ++i) {
+ for (size_t i = 0; i < testSize; ++i) {
testData_.push_back(traits_.generate());
}
}
}
void consumer() {
- for (auto& expect : testData_) {
+ if (Pop) {
+ consumerPop();
+ } else {
+ consumerRead();
+ }
+ }
+
+ void consumerPop() {
+ for (auto expect : testData_) {
+ again:
+ T* data;
+ if (!(data = queue_.frontPtr())) {
+ if (done_) {
+ // Try one more read; unless there's a bug in the queue class
+ // there should still be more data sitting in the queue even
+ // though the producer thread exited.
+ if (!(data = queue_.frontPtr())) {
+ EXPECT_TRUE(0 && "Finished too early ...");
+ return;
+ }
+ } else {
+ goto again;
+ }
+ } else {
+ queue_.popFront();
+ }
+
+ EXPECT_EQ(*data, expect);
+ }
+ }
+
+ void consumerRead() {
+ for (auto expect : testData_) {
again:
T data;
if (!queue_.read(data)) {
std::atomic<bool> done_;
};
-template<class T> void correctnessTestType(const std::string& type) {
+template<class T, bool Pop = false>
+void correctnessTestType(const std::string& type) {
LOG(INFO) << "Type: " << type;
- doTest<CorrectnessTest<folly::ProducerConsumerQueue<T>,0xfffe> >(
+ doTest<CorrectnessTest<folly::ProducerConsumerQueue<T>,0xfffe,Pop> >(
"ProducerConsumerQueue");
}
struct DtorChecker {
- static int numInstances;
+ static unsigned int numInstances;
DtorChecker() { ++numInstances; }
DtorChecker(const DtorChecker& o) { ++numInstances; }
~DtorChecker() { --numInstances; }
};
-int DtorChecker::numInstances = 0;
+unsigned int DtorChecker::numInstances = 0;
}
//////////////////////////////////////////////////////////////////////
TEST(PCQ, QueueCorrectness) {
+ correctnessTestType<std::string,true>("string (front+pop)");
correctnessTestType<std::string>("string");
correctnessTestType<int>("int");
correctnessTestType<unsigned long long>("unsigned long long");
}
TEST(PCQ, PerfTest) {
+ perfTestType<std::string,true>("string (front+pop)");
perfTestType<std::string>("string");
perfTestType<int>("int");
perfTestType<unsigned long long>("unsigned long long");
}
EXPECT_EQ(DtorChecker::numInstances, 0);
}
+
+TEST(PCQ, EmptyFull) {
+ folly::ProducerConsumerQueue<int> queue(3);
+ EXPECT_TRUE(queue.isEmpty());
+ EXPECT_FALSE(queue.isFull());
+
+ EXPECT_TRUE(queue.write(1));
+ EXPECT_FALSE(queue.isEmpty());
+ EXPECT_FALSE(queue.isFull());
+
+ EXPECT_TRUE(queue.write(2));
+ EXPECT_FALSE(queue.isEmpty());
+ EXPECT_TRUE(queue.isFull()); // Tricky: full after 2 writes, not 3.
+
+ EXPECT_FALSE(queue.write(3));
+ EXPECT_EQ(queue.sizeGuess(), 2);
+}