#include #include #include #include #include #include #include using namespace pls::internal::base; using namespace std; static bool base_tests_visited; static int base_tests_local_value_one; static vector base_tests_local_value_two; TEST_CASE( "thread creation and joining", "[internal/base/thread.h]") { base_tests_visited = false; auto t1 = start_thread([]() { base_tests_visited = true; }); t1.join(); REQUIRE(base_tests_visited); } TEST_CASE( "thread state", "[internal/base/thread.h]") { int state_one = 1; vector state_two{1, 2}; auto t1 = start_thread([]() { base_tests_local_value_one = *this_thread::state(); }, &state_one); auto t2 = start_thread([]() { base_tests_local_value_two = *this_thread::state>(); }, &state_two); t1.join(); t2.join(); REQUIRE(base_tests_local_value_one == 1); REQUIRE(base_tests_local_value_two == vector{1, 2}); } int base_tests_shared_counter; TEST_CASE( "spinlock protects concurrent counter", "[internal/base/spinlock.h]") { constexpr int num_iterations = 1000000; base_tests_shared_counter = 0; spin_lock lock{}; SECTION( "lock can be used by itself" ) { auto t1 = start_thread([&]() { for (int i = 0; i < num_iterations; i++) { lock.lock(); base_tests_shared_counter++; lock.unlock(); } }); auto t2 = start_thread([&]() { for (int i = 0; i < num_iterations; i++) { lock.lock(); base_tests_shared_counter--; lock.unlock(); } }); t1.join(); t2.join(); REQUIRE(base_tests_shared_counter == 0); } SECTION( "lock can be used with std::lock_guard" ) { auto t1 = start_thread([&]() { for (int i = 0; i < num_iterations; i++) { std::lock_guard my_lock{lock}; base_tests_shared_counter++; } }); auto t2 = start_thread([&]() { for (int i = 0; i < num_iterations; i++) { std::lock_guard my_lock{lock}; base_tests_shared_counter--; } }); t1.join(); t2.join(); REQUIRE(base_tests_shared_counter == 0); } } TEST_CASE( "aligned stack stores objects correctly", "[internal/base/aligned_stack.h]") { constexpr long data_size = 1024; char data[data_size]; aligned_stack stack{data, data_size}; SECTION( "stack correctly pushes sub linesize objects" ) { std::array small_data_one{'a', 'b', 'c', 'd', 'e'}; std::array small_data_two{}; std::array small_data_three{'A'}; auto pointer_one = stack.push(small_data_one); auto pointer_two = stack.push(small_data_two); auto pointer_three = stack.push(small_data_three); REQUIRE(reinterpret_cast(pointer_one) % CACHE_LINE_SIZE == 0); REQUIRE(reinterpret_cast(pointer_two) % CACHE_LINE_SIZE == 0); REQUIRE(reinterpret_cast(pointer_three) % CACHE_LINE_SIZE == 0); } SECTION( "stack correctly pushes above linesize objects" ) { std::array small_data_one{'a', 'b', 'c', 'd', 'e'}; std::array big_data_one{}; auto big_pointer_one = stack.push(big_data_one); auto small_pointer_one = stack.push(small_data_one); REQUIRE(reinterpret_cast(big_pointer_one) % CACHE_LINE_SIZE == 0); REQUIRE(reinterpret_cast(small_pointer_one) % CACHE_LINE_SIZE == 0); } SECTION( "stack correctly stores and retrieves objects" ) { std::array data_one{'a', 'b', 'c', 'd', 'e'}; stack.push(data_one); auto retrieved_data = stack.pop>(); REQUIRE(retrieved_data == std::array{'a', 'b', 'c', 'd', 'e'}); } SECTION( "stack can push and pop multiple times with correct alignment" ) { std::array small_data_one{'a', 'b', 'c', 'd', 'e'}; std::array small_data_two{}; std::array small_data_three{'A'}; auto pointer_one = stack.push(small_data_one); auto pointer_two = stack.push(small_data_two); auto pointer_three = stack.push(small_data_three); stack.pop(); stack.pop(); auto pointer_four = stack.push(small_data_two); auto pointer_five = stack.push(small_data_three); REQUIRE(reinterpret_cast(pointer_one) % CACHE_LINE_SIZE == 0); REQUIRE(reinterpret_cast(pointer_two) % CACHE_LINE_SIZE == 0); REQUIRE(reinterpret_cast(pointer_three) % CACHE_LINE_SIZE == 0); REQUIRE(reinterpret_cast(pointer_four) % CACHE_LINE_SIZE == 0); REQUIRE(reinterpret_cast(pointer_five) % CACHE_LINE_SIZE == 0); REQUIRE(pointer_four == pointer_two); REQUIRE(pointer_five == pointer_three); } }