#include #include "pls/internal/base/spin_lock.h" #include "pls/internal/base/system_details.h" #include "pls/internal/base/alignment.h" #include "pls/internal/base/stack_allocator.h" #include "test_helpers.h" #include #include #include using namespace pls::internal::base; int base_tests_shared_counter; TEST_CASE("align helpers", "[internal/base/alignment.h") { system_details::pointer_t aligned_64 = 64; system_details::pointer_t aligned_32 = 32; system_details::pointer_t not_aligned_64 = 70; system_details::pointer_t not_aligned_32 = 60; REQUIRE(alignment::next_alignment(aligned_64, 64) == 64); REQUIRE(alignment::next_alignment(aligned_32, 32) == 32); REQUIRE(alignment::next_alignment(aligned_32, 64) == 64); REQUIRE(alignment::previous_alignment(not_aligned_64, 64) == 64); REQUIRE(alignment::next_alignment(not_aligned_64, 64) == 128); REQUIRE(alignment::previous_alignment(not_aligned_32, 32) == 32); REQUIRE(alignment::next_alignment(not_aligned_32, 32) == 64); } TEST_CASE("alignment wrapper", "[internal/base/alignment.h") { char filler1 = '\0'; alignment::alignment_wrapper int_256{256}; int filler2 = 0; alignment::alignment_wrapper int_1024{1024}; (void) filler1; (void) filler2; REQUIRE(int_256.object() == 256); REQUIRE((system_details::pointer_t) int_256.pointer() % 256 == 0); REQUIRE(int_1024.object() == 1024); REQUIRE((system_details::pointer_t) int_1024.pointer() % 1024 == 0); } TEST_CASE("mmap stack allocator", "[internal/base/stack_allocator.h") { mmap_stack_allocator stack_allocator; char *stack = stack_allocator.allocate_stack(4096); SECTION("stack passes in valid range") { stack[0] = 'a'; stack[4096 - 1] = 'a'; REQUIRE(true); } SECTION("guard page sigsev on overflow") { REQUIRE(CHECK_ABORT([&]() { stack[-10] = 'a'; })); } stack_allocator.free_stack(4096, stack); SECTION("stack unmaps after free") { REQUIRE(CHECK_ABORT([&]() { stack[0] = 'a'; })); } } TEST_CASE("spinlock protects concurrent counter", "[internal/data_structures/spinlock.h]") { constexpr int num_iterations = 10000; base_tests_shared_counter = 0; std::atomic barrier{2}; spin_lock lock{}; SECTION("lock can be used by itself") { std::thread t1{[&]() { barrier--; while (barrier != 0); for (int i = 0; i < num_iterations; i++) { lock.lock(); base_tests_shared_counter++; lock.unlock(); } }}; std::thread t2{[&]() { barrier--; while (barrier != 0); 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") { std::thread t1{[&]() { for (int i = 0; i < num_iterations; i++) { std::lock_guard my_lock{lock}; base_tests_shared_counter++; } }}; std::thread t2{[&]() { 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); } }