#include "base/include/concurrent_queue.h"
#include <algorithm>
#include <cstddef>
#include <cstdint>
#include <thread>
#include "third_party/googletest/googletest/include/gtest/gtest.h"
namespace lynx {
namespace base {
namespace {
static void ToStep(std::atomic<int32_t>& s, int32_t next) {
int32_t pre = next - 1;
while (!s.compare_exchange_weak(pre, next)) {
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
}
static void Wait(std::atomic<int32_t>& s, int32_t next) {
while (s.load() != next) {
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
}
template <typename PopFunction>
static void TestEnqueue(ConcurrentQueue<int32_t>& queue, PopFunction pop_fn) {
constexpr int32_t thread_num = 8;
constexpr int32_t push_num = 100;
std::thread threads[thread_num];
std::atomic<int32_t> start(0);
for (auto i = 0; i < thread_num; ++i) {
threads[i] = std::thread([&start, &queue, i] {
Wait(start, 1);
for (auto j = i * push_num; j < (i + 1) * push_num; ++j) {
queue.Push(j);
std::this_thread::yield();
}
});
}
std::this_thread::sleep_for(std::chrono::milliseconds(thread_num));
ToStep(start, 1);
for (auto i = 0; i < thread_num; ++i) {
threads[i].join();
}
auto list = (queue.*pop_fn)();
for (auto i = 0; i < thread_num * push_num; ++i) {
EXPECT_TRUE(std::find(list.begin(), list.end(), i) != list.end());
}
for (auto i = 0; i < thread_num; ++i) {
for (auto j = 0; j < push_num; ++j) {
auto j_it = std::find(list.begin(), list.end(), i * push_num + j);
for (auto j_after = i * push_num + j + 1; j_after < (i + 1) * push_num;
++j_after) {
auto j_after_it = std::find(list.begin(), list.end(), j_after);
EXPECT_GT(std::distance(list.begin(), j_after_it),
std::distance(list.begin(), j_it));
}
}
}
}
TEST(ConcurrentQueueTest, ConcurrentlyEnqueueVector) {
ConcurrentQueue<int32_t> queue;
TestEnqueue(queue, &ConcurrentQueue<int32_t>::PopAll);
}
template <typename PopFunction>
static void TestEnqueueWithReverseDequeue(ConcurrentQueue<int32_t>& queue,
PopFunction pop_fn) {
constexpr int32_t thread_num = 8;
constexpr int32_t push_num = 100;
std::thread threads[thread_num];
std::atomic<int32_t> start(0);
for (auto i = 0; i < thread_num; ++i) {
threads[i] = std::thread([&start, &queue, i] {
Wait(start, 1);
for (auto j = i * push_num; j < (i + 1) * push_num; ++j) {
queue.Push(j);
std::this_thread::yield();
}
});
}
std::this_thread::sleep_for(std::chrono::milliseconds(thread_num));
ToStep(start, 1);
for (auto i = 0; i < thread_num; ++i) {
threads[i].join();
}
auto list = (queue.*pop_fn)();
for (auto i = 0; i < thread_num * push_num; ++i) {
EXPECT_TRUE(std::find(list.begin(), list.end(), i) != list.end());
}
for (auto i = 0; i < thread_num; ++i) {
for (auto j = 0; j < push_num; ++j) {
auto j_it = std::find(list.begin(), list.end(), i * push_num + j);
for (auto j_after = i * push_num + j + 1; j_after < (i + 1) * push_num;
++j_after) {
auto j_after_it = std::find(list.begin(), list.end(), j_after);
EXPECT_GT(std::distance(list.begin(), j_it),
std::distance(list.begin(), j_after_it));
}
}
}
}
TEST(ConcurrentQueueTest, ConcurrentlyEnqueueVectorWithReverseDequeue) {
ConcurrentQueue<int32_t> queue;
TestEnqueueWithReverseDequeue(queue,
&ConcurrentQueue<int32_t>::ReversePopAll);
}
template <typename PopFunction>
static void TestAppendOrder(ConcurrentQueue<int32_t>& super_queue,
PopFunction pop_fn) {
super_queue.Push(0);
super_queue.Push(1);
ConcurrentQueue<int32_t> sub_queue;
sub_queue.Push(2);
sub_queue.Push(3);
super_queue.Push(sub_queue);
super_queue.Push(4);
auto result = (super_queue.*pop_fn)();
EXPECT_EQ(result.size(), size_t(5));
size_t i = 0;
for (auto it = result.begin(); it != result.end(); ++it, ++i) {
EXPECT_EQ(size_t(*it), i);
}
EXPECT_TRUE(sub_queue.PopAll().empty());
EXPECT_TRUE(super_queue.PopAll().empty());
}
TEST(ConcurrentQueueTest, AppendOrderVector) {
ConcurrentQueue<int32_t> super_queue;
TestAppendOrder(super_queue, &ConcurrentQueue<int32_t>::PopAll);
}
template <typename PopFunction>
static void TestAppendEmpty(ConcurrentQueue<int32_t>& super_queue,
PopFunction pop_fn) {
super_queue.Push(0);
ConcurrentQueue<int32_t> sub_queue;
super_queue.Push(sub_queue);
auto result = (super_queue.*pop_fn)();
EXPECT_EQ(result.size(), size_t(1));
EXPECT_EQ(result.front(), 0);
EXPECT_TRUE(sub_queue.PopAll().empty());
EXPECT_TRUE(super_queue.PopAll().empty());
sub_queue.Push(0);
super_queue.Push(sub_queue);
result = (super_queue.*pop_fn)();
EXPECT_EQ(result.size(), size_t(1));
EXPECT_EQ(result.front(), 0);
EXPECT_TRUE(sub_queue.PopAll().empty());
EXPECT_TRUE(super_queue.PopAll().empty());
}
TEST(ConcurrentQueueTest, AppendEmptyVector) {
ConcurrentQueue<int32_t> super_queue;
TestAppendEmpty(super_queue, &ConcurrentQueue<int32_t>::PopAll);
}
template <typename PopFunction>
static void TestMove(ConcurrentQueue<int32_t>& src_queue, PopFunction pop_fn) {
src_queue.Push(0);
ConcurrentQueue<int32_t> dst_queue(std::move(src_queue));
auto result = (dst_queue.*pop_fn)();
EXPECT_EQ(result.size(), size_t(1));
EXPECT_EQ(result.front(), 0);
EXPECT_TRUE(src_queue.PopAll().empty());
EXPECT_TRUE(dst_queue.PopAll().empty());
src_queue.Push(0);
dst_queue = std::move(src_queue);
auto result2 = (dst_queue.*pop_fn)();
EXPECT_EQ(result2.size(), size_t(1));
EXPECT_EQ(result2.front(), 0);
EXPECT_TRUE(src_queue.PopAll().empty());
EXPECT_TRUE(dst_queue.PopAll().empty());
}
TEST(ConcurrentQueueTest, MoveVector) {
ConcurrentQueue<int32_t> src_queue;
TestMove(src_queue, &ConcurrentQueue<int32_t>::PopAll);
}
}
}
}