danicoin/tests/UnitTests/BlockingQueue.cpp
2016-01-18 15:33:29 +00:00

198 lines
3.6 KiB
C++

// Copyright (c) 2011-2016 The Cryptonote developers
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <gtest/gtest.h>
#include "Common/BlockingQueue.h"
#include <future>
#include <vector>
#include <numeric>
#include <memory>
class ParallelProcessor {
public:
ParallelProcessor(size_t threads)
: m_threads(threads) {}
template <typename F>
void spawn(F f) {
for (auto& t : m_threads) {
t = std::thread(f);
}
}
void join() {
for (auto& t : m_threads) {
t.join();
}
}
private:
std::vector<std::thread> m_threads;
};
// single producer, many consumers
void TestQueue_SPMC(unsigned iterations, unsigned threadCount, unsigned queueSize) {
BlockingQueue<int> bq(queueSize);
ParallelProcessor processor(threadCount);
std::atomic<int64_t> result(0);
processor.spawn([&bq, &result]{
int v = 0;
int64_t sum = 0;
while (bq.pop(v)) {
sum += v;
}
result += sum;
// std::cout << "Sum: " << sum << std::endl;
});
int64_t expectedSum = 0;
for (unsigned i = 0; i < iterations; ++i) {
expectedSum += i;
ASSERT_TRUE(bq.push(i));
}
bq.close();
processor.join();
ASSERT_EQ(expectedSum, result.load());
}
void TestQueue_MPSC(unsigned iterations, unsigned threadCount, unsigned queueSize) {
BlockingQueue<int> bq(queueSize);
ParallelProcessor processor(threadCount);
std::atomic<unsigned> counter(0);
std::atomic<int64_t> pushed(0);
processor.spawn([&]{
int64_t sum = 0;
for(;;) {
unsigned value = counter.fetch_add(1);
if (value >= iterations)
break;
bq.push(value);
sum += value;
}
pushed += sum;
// std::cout << "Sum: " << sum << std::endl;
});
int64_t expectedSum = 0;
for (unsigned i = 0; i < iterations; ++i) {
int value;
ASSERT_TRUE(bq.pop(value));
expectedSum += i;
}
ASSERT_EQ(0, bq.size());
processor.join();
ASSERT_EQ(expectedSum, pushed);
}
TEST(BlockingQueue, SPMC)
{
TestQueue_SPMC(10000, 1, 1);
TestQueue_SPMC(10000, 4, 1);
TestQueue_SPMC(10000, 16, 16);
TestQueue_SPMC(10000, 16, 100);
}
TEST(BlockingQueue, MPSC)
{
TestQueue_MPSC(10000, 1, 1);
TestQueue_MPSC(10000, 4, 1);
TestQueue_MPSC(10000, 16, 16);
TestQueue_MPSC(10000, 16, 100);
}
TEST(BlockingQueue, PerfTest)
{
// TestQueue_SPMC(1000000, 32, 1);
}
TEST(BlockingQueue, Close)
{
BlockingQueue<int> bq(4);
ParallelProcessor p(4);
p.spawn([&bq] {
int v;
while (bq.pop(v))
;
});
bq.push(10); // enqueue 1 item
bq.close(); // all threads should unblock and finish
p.join();
}
TEST(BlockingQueue, CloseAndWait)
{
size_t queueSize = 100;
BlockingQueue<int> bq(queueSize);
ParallelProcessor p(4);
std::atomic<size_t> itemsPopped(0);
// fill the queue
for (int i = 0; i < queueSize; ++i)
bq.push(i);
p.spawn([&bq, &itemsPopped] {
int v;
while (bq.pop(v)) {
itemsPopped += 1;
// some delay to make close() really wait
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
});
// check with multiple closing
auto f1 = std::async(std::launch::async, [&] { bq.close(true); });
auto f2 = std::async(std::launch::async, [&] { bq.close(true); });
bq.close(true);
f1.get();
f2.get();
p.join();
ASSERT_EQ(queueSize, itemsPopped.load());
}
TEST(BlockingQueue, AllowsMoveOnly)
{
BlockingQueue<std::unique_ptr<int>> bq(1);
std::unique_ptr<int> v(new int(100));
ASSERT_TRUE(bq.push(std::move(v)));
std::unique_ptr<int> popval;
bq.pop(popval);
ASSERT_EQ(*popval, 100);
}