2016-01-18 15:33:29 +00:00
|
|
|
// Copyright (c) 2011-2016 The Cryptonote developers
|
2015-04-23 16:07:22 +00:00
|
|
|
// Distributed under the MIT/X11 software license, see the accompanying
|
|
|
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
2014-08-13 10:51:37 +00:00
|
|
|
|
|
|
|
#include <gtest/gtest.h>
|
2015-05-27 12:08:46 +00:00
|
|
|
#include "Common/BlockingQueue.h"
|
2014-08-13 10:51:37 +00:00
|
|
|
|
|
|
|
#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();
|
2015-05-27 12:08:46 +00:00
|
|
|
|
|
|
|
ASSERT_EQ(queueSize, itemsPopped.load());
|
2014-08-13 10:51:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
|
|
|
}
|