From bd74a53e491ac0b8ec88bc29e61a29f568f0563c Mon Sep 17 00:00:00 2001 From: Sai Kishor Kothakota Date: Thu, 9 Jan 2025 19:04:21 +0100 Subject: [PATCH] reuse tests to test all constructable types of LockFreeQueues --- test/lock_free_queue_tests.cpp | 611 +++++++++++++++++---------------- 1 file changed, 306 insertions(+), 305 deletions(-) diff --git a/test/lock_free_queue_tests.cpp b/test/lock_free_queue_tests.cpp index 909c14fc..a4c26dcd 100644 --- a/test/lock_free_queue_tests.cpp +++ b/test/lock_free_queue_tests.cpp @@ -29,113 +29,126 @@ class DefaultConstructable TEST(LockFreeSPSCQueue, default_construct) { - { - LockFreeSPSCQueue buffer(10); + const auto default_construct_test_lambda = [](auto & queue) { DefaultConstructable obj1; - ASSERT_EQ(10, buffer.capacity()); - ASSERT_TRUE(buffer.empty()) << "Buffer should be empty"; - ASSERT_FALSE(buffer.pop(obj1)) << "Buffer should be empty"; - ASSERT_TRUE(buffer.push(DefaultConstructable())) << "Buffer should have space for one element"; - ASSERT_EQ(10, buffer.capacity()); - ASSERT_EQ(1, buffer.size()) << "Buffer should have one element"; - ASSERT_TRUE(buffer.pop(obj1)); - ASSERT_TRUE(buffer.is_lock_free()); - ASSERT_EQ(10, buffer.capacity()); - ASSERT_EQ(42, obj1.number_); - } - { - LockFreeSPSCQueue buffer; - DefaultConstructable obj1; - ASSERT_EQ(10, buffer.capacity()); - ASSERT_TRUE(buffer.empty()) << "Buffer should be empty"; - ASSERT_FALSE(buffer.pop(obj1)) << "Buffer should be empty"; - ASSERT_TRUE(buffer.push(DefaultConstructable())) << "Buffer should have space for one element"; - ASSERT_EQ(10, buffer.capacity()); - ASSERT_EQ(1, buffer.size()) << "Buffer should have one element"; - ASSERT_TRUE(buffer.pop(obj1)); - ASSERT_TRUE(buffer.is_lock_free()); - ASSERT_EQ(10, buffer.capacity()); + ASSERT_EQ(10, queue.capacity()); + ASSERT_TRUE(queue.empty()) << "Buffer should be empty"; + ASSERT_FALSE(queue.pop(obj1)) << "Buffer should be empty"; + ASSERT_TRUE(queue.push(DefaultConstructable())) << "Buffer should have space for one element"; + ASSERT_EQ(10, queue.capacity()); + ASSERT_EQ(1, queue.size()) << "Buffer should have one element"; + ASSERT_TRUE(queue.pop(obj1)); + ASSERT_TRUE(queue.is_lock_free()); + ASSERT_EQ(10, queue.capacity()); ASSERT_EQ(42, obj1.number_); - } + }; + + LockFreeSPSCQueue queue_1(10); + LockFreeSPSCQueue queue_2; + default_construct_test_lambda(queue_1); + default_construct_test_lambda(queue_2); } TEST(LockFreeSPSCQueue, initialize_value) { - LockFreeSPSCQueue buffer; - ASSERT_TRUE(buffer.empty()) << "Buffer should be empty"; - ASSERT_TRUE(buffer.push(3.14)) << "Buffer should have space for one element"; - ASSERT_EQ(1, buffer.size()) << "Buffer should have one element"; - ASSERT_TRUE(buffer.is_lock_free()); - double obj1; - ASSERT_TRUE(buffer.pop(obj1)); - ASSERT_DOUBLE_EQ(3.14, obj1); - ASSERT_EQ(0, buffer.size()) << "Buffer should be empty"; - ASSERT_TRUE(buffer.empty()); - ASSERT_TRUE(buffer.push(2.71)) << "Buffer should have space for one element"; - ASSERT_EQ(1, buffer.size()) << "Buffer should have one element"; - int obj2; - ASSERT_TRUE(buffer.pop(obj2)); - ASSERT_EQ(2, obj2); - ASSERT_EQ(0, buffer.size()) << "Buffer should be empty"; - ASSERT_TRUE(buffer.empty()); - ASSERT_TRUE(buffer.push(6)); - ASSERT_TRUE(buffer.pop(obj1)); - ASSERT_EQ(6, obj1); + const auto initialize_value_test = [](auto & queue) { + ASSERT_TRUE(queue.empty()) << "Buffer should be empty"; + ASSERT_TRUE(queue.push(3.14)) << "Buffer should have space for one element"; + ASSERT_EQ(1, queue.size()) << "Buffer should have one element"; + ASSERT_TRUE(queue.is_lock_free()); + double obj1; + ASSERT_TRUE(queue.pop(obj1)); + ASSERT_DOUBLE_EQ(3.14, obj1); + ASSERT_EQ(0, queue.size()) << "Buffer should be empty"; + ASSERT_TRUE(queue.empty()); + ASSERT_TRUE(queue.push(2.71)) << "Buffer should have space for one element"; + ASSERT_EQ(1, queue.size()) << "Buffer should have one element"; + int obj2; + ASSERT_TRUE(queue.pop(obj2)); + ASSERT_EQ(2, obj2); + ASSERT_EQ(0, queue.size()) << "Buffer should be empty"; + ASSERT_TRUE(queue.empty()); + ASSERT_TRUE(queue.push(6)); + ASSERT_TRUE(queue.pop(obj1)); + ASSERT_EQ(6, obj1); + }; + + LockFreeSPSCQueue queue_1; + LockFreeSPSCQueue queue_2(10); + initialize_value_test(queue_1); + initialize_value_test(queue_2); } TEST(LockFreeSPSCQueue, test_push) { - LockFreeSPSCQueue buffer; - ASSERT_TRUE(buffer.empty()) << "Buffer should be empty"; - for (auto i = 1; i <= 10; i++) { - ASSERT_TRUE(buffer.push(i)) << "Buffer should have space for element as size is 10"; - ASSERT_EQ(i, buffer.size()); - ASSERT_EQ(10, buffer.capacity()); - } - ASSERT_FALSE(buffer.push(11)) << "Buffer should not have space for element as size is 10"; - ASSERT_EQ(10, buffer.size()); - ASSERT_FALSE(buffer.empty()); + const auto push_test_lambda = [](auto & queue) { + ASSERT_TRUE(queue.empty()) << "Buffer should be empty"; + for (auto i = 1; i <= 10; i++) { + ASSERT_TRUE(queue.push(i)) << "Buffer should have space for element as size is 10"; + ASSERT_EQ(i, queue.size()); + ASSERT_EQ(10, queue.capacity()); + } + ASSERT_FALSE(queue.push(11)) << "Buffer should not have space for element as size is 10"; + ASSERT_EQ(10, queue.size()); + ASSERT_FALSE(queue.empty()); + }; + + LockFreeSPSCQueue queue_1; + LockFreeSPSCQueue queue_2(10); + push_test_lambda(queue_1); + push_test_lambda(queue_2); } TEST(LockFreeSPSCQueue, test_pop) { - LockFreeSPSCQueue buffer; - ASSERT_TRUE(buffer.empty()) << "Buffer should be empty"; - for (auto i = 1; i <= 10; i++) { - ASSERT_TRUE(buffer.push(i)) << "Buffer should have space for element as size is 10"; - ASSERT_EQ(i, buffer.size()); - } - for (auto i = 1; i <= 10; i++) { + const auto pop_test_lambda = [](auto & queue) { + ASSERT_TRUE(queue.empty()) << "Buffer should be empty"; + for (auto i = 1; i <= 10; i++) { + ASSERT_TRUE(queue.push(i)) << "Buffer should have space for element as size is 10"; + ASSERT_EQ(i, queue.size()); + } + for (auto i = 1; i <= 10; i++) { + double obj1 = -10; + ASSERT_TRUE(queue.pop(obj1)); + ASSERT_EQ(i, obj1); + ASSERT_EQ(10 - i, queue.size()); + } double obj1; - ASSERT_TRUE(buffer.pop(obj1)); - ASSERT_EQ(i, obj1); - ASSERT_EQ(10 - i, buffer.size()); - } - ASSERT_TRUE(buffer.empty()) << "Buffer should be empty"; - double obj1; - ASSERT_FALSE(buffer.pop(obj1)) << "Buffer should be empty"; + ASSERT_TRUE(queue.empty()) << "Buffer should be empty"; + ASSERT_FALSE(queue.pop(obj1)) << "Buffer should be empty"; + }; + + LockFreeSPSCQueue queue_1; + LockFreeSPSCQueue queue_2(10); + pop_test_lambda(queue_1); + pop_test_lambda(queue_2); } TEST(LockFreeSPSCQueue, test_get_latest) { - LockFreeSPSCQueue buffer; - ASSERT_TRUE(buffer.empty()) << "Buffer should be empty"; - for (auto i = 1; i <= 10; i++) { - ASSERT_TRUE(buffer.push(i)) << "Buffer should have space for element as size is 10"; - ASSERT_EQ(i, buffer.size()); - } - double obj1; - ASSERT_TRUE(buffer.get_latest(obj1)); - ASSERT_EQ(10, obj1); - ASSERT_TRUE(buffer.empty()) << "Buffer should be empty"; - ASSERT_EQ(0, buffer.size()); - ASSERT_FALSE(buffer.get_latest(obj1)); + const auto get_latest_test = [](auto & queue) { + ASSERT_TRUE(queue.empty()) << "Buffer should be empty"; + for (auto i = 1; i <= 10; i++) { + ASSERT_TRUE(queue.push(i)) << "Buffer should have space for element as size is 10"; + ASSERT_EQ(i, queue.size()); + } + double obj1 = -10; + ASSERT_TRUE(queue.get_latest(obj1)); + ASSERT_EQ(10, obj1); + ASSERT_TRUE(queue.empty()) << "Buffer should be empty"; + ASSERT_EQ(0, queue.size()); + ASSERT_FALSE(queue.get_latest(obj1)); + }; + + LockFreeSPSCQueue queue_1; + LockFreeSPSCQueue queue_2(10); + get_latest_test(queue_1); + get_latest_test(queue_2); } TEST(LockFreeSPSCQueue, test_bounded_push) { - { - LockFreeSPSCQueue queue; + const auto bounded_push_test = [](auto & queue) { ASSERT_TRUE(queue.empty()) << "Buffer should be empty"; for (auto i = 1; i <= 25; i++) { ASSERT_TRUE(queue.bounded_push(i)) << "Buffer should have space for element as size is 10"; @@ -155,181 +168,171 @@ TEST(LockFreeSPSCQueue, test_bounded_push) double obj1; ASSERT_FALSE(queue.pop(obj1)); ASSERT_TRUE(queue.empty()) << "Buffer should be empty"; - } - { - LockFreeSPSCQueue queue(10); - ASSERT_TRUE(queue.empty()) << "Buffer should be empty"; - for (auto i = 1; i <= 25; i++) { - ASSERT_TRUE(queue.bounded_push(i)) << "Buffer should have space for element as size is 10"; - ASSERT_EQ(std::min(i, 10), queue.size()); - ASSERT_EQ(10, queue.capacity()); - } - ASSERT_EQ(10, queue.size()); - ASSERT_EQ(10, queue.capacity()); + }; - // when we start popping, the pop-ed elements should start from 16 - for (auto i = 1u; i <= queue.capacity(); i++) { - double obj1; - ASSERT_TRUE(queue.pop(obj1)); - ASSERT_EQ(i + 15, obj1); - ASSERT_EQ(queue.capacity() - i, queue.size()); - } - double obj1; - ASSERT_FALSE(queue.pop(obj1)); - ASSERT_TRUE(queue.empty()) << "Buffer should be empty"; - } + LockFreeSPSCQueue queue_1; + LockFreeSPSCQueue queue_2(10); + bounded_push_test(queue_1); + bounded_push_test(queue_2); } TEST(LockFreeSPSCQueue, test_lockfree_queue_push) { - LockFreeSPSCQueue queue(100); - int producer_count = 0; - std::atomic_int consumer_count(0); - std::atomic_bool done(false); - - const int iterations = 1000000; - - std::thread producer([&]() { - for (int i = 0; i < iterations; ++i) { - while (!queue.push(i)) { - std::this_thread::yield(); + const auto lockfree_queue_push_test = [](auto & queue) { + int producer_count = 0; + std::atomic_int consumer_count(0); + std::atomic_bool done(false); + + const int iterations = 1000000; + + std::thread producer([&]() { + for (int i = 0; i < iterations; ++i) { + while (!queue.push(i)) { + std::this_thread::yield(); + } + ++producer_count; + } + }); + + std::thread consumer([&]() { + int value; + while (!done) { + while (queue.pop(value)) { + ++consumer_count; + } } - ++producer_count; - } - }); - - std::thread consumer([&]() { - int value; - while (!done) { while (queue.pop(value)) { ++consumer_count; } - } - while (queue.pop(value)) { - ++consumer_count; - } - }); + }); + + producer.join(); + done = true; + consumer.join(); - producer.join(); - done = true; - consumer.join(); + ASSERT_EQ(producer_count, consumer_count); + ASSERT_EQ(producer_count, iterations); + ASSERT_EQ(consumer_count, iterations); + }; - ASSERT_EQ(producer_count, consumer_count); - ASSERT_EQ(producer_count, iterations); - ASSERT_EQ(consumer_count, iterations); + LockFreeSPSCQueue queue_1(100); + LockFreeSPSCQueue queue_2; + lockfree_queue_push_test(queue_1); + lockfree_queue_push_test(queue_2); } TEST(LockFreeMPMCQueue, default_construct) { - { - LockFreeMPMCQueue buffer(10); - DefaultConstructable obj1; - ASSERT_EQ(10, buffer.capacity()); - ASSERT_TRUE(buffer.empty()) << "Buffer should be empty"; - ASSERT_FALSE(buffer.pop(obj1)) << "Buffer should be empty"; - ASSERT_TRUE(buffer.push(DefaultConstructable())) << "Buffer should have space for one element"; - ASSERT_EQ(10, buffer.capacity()); - ASSERT_TRUE(buffer.is_lock_free()); - ASSERT_TRUE(buffer.pop(obj1)); - ASSERT_EQ(10, buffer.capacity()); - ASSERT_EQ(42, obj1.number_); - } - { - LockFreeMPMCQueue buffer; + const auto default_construct_test = [](auto & queue) { DefaultConstructable obj1; - ASSERT_EQ(10, buffer.capacity()); - ASSERT_TRUE(buffer.empty()) << "Buffer should be empty"; - ASSERT_FALSE(buffer.pop(obj1)) << "Buffer should be empty"; - ASSERT_TRUE(buffer.push(DefaultConstructable())) << "Buffer should have space for one element"; - ASSERT_EQ(10, buffer.capacity()); - ASSERT_TRUE(buffer.pop(obj1)); - ASSERT_TRUE(buffer.is_lock_free()); - ASSERT_EQ(10, buffer.capacity()); + ASSERT_EQ(10, queue.capacity()); + ASSERT_TRUE(queue.empty()) << "Buffer should be empty"; + ASSERT_FALSE(queue.pop(obj1)) << "Buffer should be empty"; + ASSERT_TRUE(queue.push(DefaultConstructable())) << "Buffer should have space for one element"; + ASSERT_EQ(10, queue.capacity()); + ASSERT_TRUE(queue.is_lock_free()); + ASSERT_TRUE(queue.pop(obj1)); + ASSERT_EQ(10, queue.capacity()); ASSERT_EQ(42, obj1.number_); - } + }; + + LockFreeMPMCQueue queue_1; + LockFreeMPMCQueue queue_2(10); + default_construct_test(queue_1); + default_construct_test(queue_2); } TEST(LockFreeMPMCQueue, initialize_value) { - LockFreeMPMCQueue buffer; - ASSERT_TRUE(buffer.empty()) << "Buffer should be empty"; - ASSERT_TRUE(buffer.push(3.14)) << "Buffer should have space for one element"; - ASSERT_TRUE(buffer.is_lock_free()); - double obj1; - ASSERT_TRUE(buffer.pop(obj1)); - ASSERT_DOUBLE_EQ(3.14, obj1); - ASSERT_TRUE(buffer.empty()) << "Buffer should be empty"; - ASSERT_TRUE(buffer.push(2.71)) << "Buffer should have space for one element"; - int obj2; - ASSERT_TRUE(buffer.pop(obj2)); - ASSERT_EQ(2, obj2); - ASSERT_TRUE(buffer.empty()); - ASSERT_TRUE(buffer.push(6)); - ASSERT_TRUE(buffer.pop(obj1)); - ASSERT_EQ(6, obj1); + const auto initialize_value_test = [](auto & queue) { + ASSERT_TRUE(queue.empty()) << "Buffer should be empty"; + ASSERT_TRUE(queue.push(3.14)) << "Buffer should have space for one element"; + ASSERT_TRUE(queue.is_lock_free()); + double obj1; + ASSERT_TRUE(queue.pop(obj1)); + ASSERT_DOUBLE_EQ(3.14, obj1); + ASSERT_TRUE(queue.empty()) << "Buffer should be empty"; + ASSERT_TRUE(queue.push(2.71)) << "Buffer should have space for one element"; + int obj2; + ASSERT_TRUE(queue.pop(obj2)); + ASSERT_EQ(2, obj2); + ASSERT_TRUE(queue.empty()); + ASSERT_TRUE(queue.push(6)); + ASSERT_TRUE(queue.pop(obj1)); + ASSERT_EQ(6, obj1); + }; + + LockFreeMPMCQueue queue_1; + LockFreeMPMCQueue queue_2(10); + initialize_value_test(queue_1); + initialize_value_test(queue_2); } TEST(LockFreeMPMCQueue, test_push) { - { - LockFreeMPMCQueue buffer; - ASSERT_TRUE(buffer.empty()) << "Buffer should be empty"; - for (auto i = 1; i <= 10; i++) { - ASSERT_TRUE(buffer.push(i)) << "Buffer should have space for element as size is 10"; - ASSERT_EQ(10, buffer.capacity()); - } - ASSERT_FALSE(buffer.push(11)) << "Buffer should not have space for element as size is 10"; - ASSERT_FALSE(buffer.empty()); - } - { - LockFreeMPMCQueue buffer(10); - ASSERT_TRUE(buffer.empty()) << "Buffer should be empty"; + const auto push_test_lambda = [](auto & queue) { + ASSERT_TRUE(queue.empty()) << "Buffer should be empty"; for (auto i = 1; i <= 10; i++) { - ASSERT_TRUE(buffer.push(i)) << "Buffer should have space for element as size is 10"; - ASSERT_EQ(10, buffer.capacity()); + ASSERT_TRUE(queue.push(i)) << "Buffer should have space for element as size is 10"; + ASSERT_EQ(10, queue.capacity()); } - ASSERT_FALSE(buffer.push(11)) << "Buffer should not have space for element as size is 10"; - ASSERT_FALSE(buffer.push(12)) << "Buffer should not have space for element as size is 10"; - ASSERT_FALSE(buffer.empty()); - } + ASSERT_FALSE(queue.push(11)) << "Buffer should not have space for element as size is 10"; + ASSERT_FALSE(queue.empty()); + }; + + LockFreeMPMCQueue queue_1; + LockFreeMPMCQueue queue_2(10); + push_test_lambda(queue_1); + push_test_lambda(queue_2); } TEST(LockFreeMPMCQueue, test_pop) { - LockFreeMPMCQueue buffer; - ASSERT_TRUE(buffer.empty()) << "Buffer should be empty"; - for (auto i = 1; i <= 10; i++) { - ASSERT_TRUE(buffer.push(i)) << "Buffer should have space for element as size is 10"; - } - for (auto i = 1; i <= 10; i++) { + const auto pop_test_lambda = [](auto & queue) { + ASSERT_TRUE(queue.empty()) << "Buffer should be empty"; + for (auto i = 1; i <= 10; i++) { + ASSERT_TRUE(queue.push(i)) << "Buffer should have space for element as size is 10"; + } + for (auto i = 1; i <= 10; i++) { + double obj1; + ASSERT_TRUE(queue.pop(obj1)); + ASSERT_EQ(i, obj1); + } + ASSERT_TRUE(queue.empty()) << "Buffer should be empty"; double obj1; - ASSERT_TRUE(buffer.pop(obj1)); - ASSERT_EQ(i, obj1); - } - ASSERT_TRUE(buffer.empty()) << "Buffer should be empty"; - double obj1; - ASSERT_FALSE(buffer.pop(obj1)) << "Buffer should be empty"; + ASSERT_FALSE(queue.pop(obj1)) << "Buffer should be empty"; + }; + + LockFreeMPMCQueue queue_1; + LockFreeMPMCQueue queue_2(10); + pop_test_lambda(queue_1); + pop_test_lambda(queue_2); } TEST(LockFreeMPMCQueue, test_get_latest) { - LockFreeMPMCQueue buffer; - ASSERT_TRUE(buffer.empty()) << "Buffer should be empty"; - for (auto i = 1; i <= 10; i++) { - ASSERT_TRUE(buffer.push(i)) << "Buffer should have space for element as size is 10"; - } - ASSERT_FALSE(buffer.empty()) << "Buffer should not be empty"; - double obj1; - ASSERT_TRUE(buffer.get_latest(obj1)); - ASSERT_EQ(10, obj1); - ASSERT_TRUE(buffer.empty()) << "Buffer should be empty"; - ASSERT_FALSE(buffer.get_latest(obj1)); + const auto get_latest_test_lambda = [](auto & queue) { + ASSERT_TRUE(queue.empty()) << "Buffer should be empty"; + for (auto i = 1; i <= 10; i++) { + ASSERT_TRUE(queue.push(i)) << "Buffer should have space for element as size is 10"; + } + ASSERT_FALSE(queue.empty()) << "Buffer should not be empty"; + double obj1; + ASSERT_TRUE(queue.get_latest(obj1)); + ASSERT_EQ(10, obj1); + ASSERT_TRUE(queue.empty()) << "Buffer should be empty"; + ASSERT_FALSE(queue.get_latest(obj1)); + }; + + LockFreeMPMCQueue queue_1; + LockFreeMPMCQueue queue_2(10); + get_latest_test_lambda(queue_1); + get_latest_test_lambda(queue_2); } TEST(LockFreeMPMCQueue, test_bounded_push) { - { - LockFreeMPMCQueue queue; + const auto bounded_push_test = [](auto & queue) { ASSERT_TRUE(queue.empty()) << "Buffer should be empty"; for (auto i = 1; i <= 25; i++) { ASSERT_TRUE(queue.bounded_push(i)) << "Buffer should have space for element as size is 10"; @@ -339,114 +342,112 @@ TEST(LockFreeMPMCQueue, test_bounded_push) // when we start popping, the pop-ed elements should start from 16 for (auto i = 1u; i <= queue.capacity(); i++) { - double obj1; + double obj1 = -10.0; ASSERT_TRUE(queue.pop(obj1)); ASSERT_EQ(i + 15, obj1); } double obj1; ASSERT_FALSE(queue.pop(obj1)); ASSERT_TRUE(queue.empty()) << "Buffer should be empty"; - } - { - LockFreeMPMCQueue queue(10); - ASSERT_TRUE(queue.empty()) << "Buffer should be empty"; - for (auto i = 1; i <= 25; i++) { - ASSERT_TRUE(queue.bounded_push(i)) << "Buffer should have space for element as size is 10"; - ASSERT_EQ(10, queue.capacity()); - } - ASSERT_EQ(10, queue.capacity()); + }; - // when we start popping, the pop-ed elements should start from 16 - for (auto i = 1u; i <= queue.capacity(); i++) { - double obj1; - ASSERT_TRUE(queue.pop(obj1)); - ASSERT_EQ(i + 15, obj1); - } - double obj1; - ASSERT_FALSE(queue.pop(obj1)); - ASSERT_TRUE(queue.empty()) << "Buffer should be empty"; - } + LockFreeMPMCQueue queue_1; + LockFreeMPMCQueue queue_2(10); + bounded_push_test(queue_1); + bounded_push_test(queue_2); } TEST(LockFreeMPMCQueue, test_lockfree_queue_push) { - LockFreeMPMCQueue queue; - int producer_count = 0; - std::atomic_int consumer_count(0); - std::atomic_bool done(false); - - const int iterations = 1000000; - - std::thread producer([&]() { - for (int i = 0; i < iterations; ++i) { - while (!queue.push(i)) { - std::this_thread::yield(); + const auto test_queue = [](auto & queue) { + int producer_count = 0; + std::atomic_int consumer_count(0); + std::atomic_bool done(false); + + const int iterations = 1000000; + + std::thread producer([&]() { + for (int i = 0; i < iterations; ++i) { + while (!queue.push(i)) { + std::this_thread::yield(); + } + ++producer_count; + } + }); + + std::thread consumer([&]() { + int value; + while (!done) { + while (queue.pop(value)) { + ++consumer_count; + } } - ++producer_count; - } - }); - - std::thread consumer([&]() { - int value; - while (!done) { while (queue.pop(value)) { ++consumer_count; } - } - while (queue.pop(value)) { - ++consumer_count; - } - }); + }); + + producer.join(); + done = true; + consumer.join(); - producer.join(); - done = true; - consumer.join(); + ASSERT_EQ(producer_count, consumer_count); + ASSERT_EQ(producer_count, iterations); + ASSERT_EQ(consumer_count, iterations); + }; - ASSERT_EQ(producer_count, consumer_count); - ASSERT_EQ(producer_count, iterations); - ASSERT_EQ(consumer_count, iterations); + LockFreeMPMCQueue queue_1; + LockFreeMPMCQueue queue_2(100); + test_queue(queue_1); + test_queue(queue_2); } TEST(LockFreeMPMCQueue, test_lockfree_queue_bounded_push) { - LockFreeMPMCQueue queue; - std::atomic_int producer_count = 0; - std::atomic_int consumer_count(0); - std::atomic_bool done(false); - - const int iterations = 1000000; - - std::thread producer([&]() { - for (auto j = 0; j < iterations; ++j) { - ASSERT_TRUE(queue.bounded_push(j)); - ASSERT_EQ(100, queue.capacity()); - ++producer_count; - } - }); + const auto bounded_push_test = [](auto & queue) { + std::atomic_int producer_count = 0; + std::atomic_int consumer_count(0); + std::atomic_bool done(false); + + const int iterations = 1000000; + + std::thread producer([&]() { + for (auto j = 0; j < iterations; ++j) { + ASSERT_TRUE(queue.bounded_push(j)); + ASSERT_EQ(100, queue.capacity()); + ++producer_count; + } + }); - std::this_thread::sleep_for(std::chrono::milliseconds(100)); + std::this_thread::sleep_for(std::chrono::milliseconds(100)); - std::cerr << "producer_count: " << producer_count << std::endl; + std::cerr << "producer_count: " << producer_count << std::endl; - std::thread consumer([&]() { - int value; - while (!done && queue.pop(value)) { - ++consumer_count; - std::this_thread::yield(); - } - while (queue.pop(value)) { - ++consumer_count; - } - }); - - producer.join(); - done = true; - consumer.join(); - - std::cerr << "producer_count: " << producer_count << std::endl; - std::cerr << "consumer_count: " << consumer_count << std::endl; - std::cerr << "iterations: " << iterations << std::endl; - ASSERT_GT(producer_count, consumer_count); - ASSERT_EQ(producer_count, iterations); - ASSERT_GT(iterations, consumer_count); + std::thread consumer([&]() { + int value; + while (!done && queue.pop(value)) { + ++consumer_count; + std::this_thread::yield(); + } + while (queue.pop(value)) { + ++consumer_count; + } + }); + + producer.join(); + done = true; + consumer.join(); + + std::cerr << "producer_count: " << producer_count << std::endl; + std::cerr << "consumer_count: " << consumer_count << std::endl; + std::cerr << "iterations: " << iterations << std::endl; + ASSERT_GT(producer_count, consumer_count); + ASSERT_EQ(producer_count, iterations); + ASSERT_GT(iterations, consumer_count); + }; + + LockFreeMPMCQueue queue_1; + LockFreeMPMCQueue queue_2(100); + bounded_push_test(queue_1); + bounded_push_test(queue_2); }