diff --git a/core/libs/prx/libc/CMakeLists.txt b/core/libs/prx/libc/CMakeLists.txt index d8f9c928da..a3b839e5b8 100644 --- a/core/libs/prx/libc/CMakeLists.txt +++ b/core/libs/prx/libc/CMakeLists.txt @@ -743,6 +743,16 @@ if(BUILD_TESTING) set_tests_properties(guest_qsort PROPERTIES ENVIRONMENT_MODIFICATION "PATH=path_list_prepend:$") endif() + add_test_executable(guest_cxa_guard_tests ${LIBS_INCLUDE_DIR}/tests/GuestCxaGuard.cpp) + target_include_directories(guest_cxa_guard_tests PRIVATE ${LIBS_INCLUDE_DIR}) + target_link_libraries(guest_cxa_guard_tests PRIVATE libc) + configure_windows_unwind(guest_cxa_guard_tests) + add_test(NAME guest_cxa_guard COMMAND guest_cxa_guard_tests) + set_tests_properties(guest_cxa_guard PROPERTIES TIMEOUT 20) + if(WIN32) + set_tests_properties(guest_cxa_guard PROPERTIES ENVIRONMENT_MODIFICATION + "PATH=path_list_prepend:$") + endif() add_test_executable(guest_strftime_tests ${LIBS_INCLUDE_DIR}/tests/GuestStrftime.cpp) target_include_directories(guest_strftime_tests PRIVATE ${LIBS_INCLUDE_DIR}) target_link_libraries(guest_strftime_tests PRIVATE libc) diff --git a/core/libs/tests/GuestCxaGuard.cpp b/core/libs/tests/GuestCxaGuard.cpp new file mode 100644 index 0000000000..5846411155 --- /dev/null +++ b/core/libs/tests/GuestCxaGuard.cpp @@ -0,0 +1,104 @@ +#include "prx/libc/include/Api.hpp" + +#include +#include +#include +#include +#include +#include + +static void RequireAt(bool value, int line) { + if (!value) { + std::fprintf(stderr, "cxa guard check failed at line %d\n", line); + std::abort(); + } +} +#define Require(value) RequireAt((value), __LINE__) + +static bool Initialized(const std::uint64_t& guard) { + return (guard & 1) != 0; +} + +static void WaitForStartAt(const std::atomic& started, int line) { + const auto deadline = std::chrono::steady_clock::now() + std::chrono::seconds(5); + while (!started && std::chrono::steady_clock::now() < deadline) std::this_thread::sleep_for(std::chrono::milliseconds(1)); + RequireAt(started, line); +} +#define WaitForStart(started) WaitForStartAt((started), __LINE__) + +int main() { + { + std::uint64_t guard = 0; + Require(__cxa_guard_acquire_nid_postfix(&guard) != 0); + Require(!Initialized(guard)); + __cxa_guard_release_nid_postfix(&guard); + Require(Initialized(guard)); + Require(__cxa_guard_acquire_nid_postfix(&guard) == 0); + Require(Initialized(guard)); + } + + { + std::uint64_t guard = 0; + Require(__cxa_guard_acquire_nid_postfix(&guard) != 0); + __cxa_guard_abort_nid_postfix(&guard); + Require(!Initialized(guard)); + Require(__cxa_guard_acquire_nid_postfix(&guard) != 0); + __cxa_guard_release_nid_postfix(&guard); + Require(Initialized(guard)); + Require(__cxa_guard_acquire_nid_postfix(&guard) == 0); + } + + { + std::uint64_t outer = 0; + std::uint64_t inner = 0; + Require(__cxa_guard_acquire_nid_postfix(&outer) != 0); + Require(__cxa_guard_acquire_nid_postfix(&inner) != 0); + __cxa_guard_release_nid_postfix(&inner); + Require(Initialized(inner) && !Initialized(outer)); + __cxa_guard_release_nid_postfix(&outer); + Require(Initialized(outer)); + } + + { + std::uint64_t guard = 0; + Require(__cxa_guard_acquire_nid_postfix(&guard) != 0); + std::atomic started{false}; + std::atomic returned{false}; + std::atomic result{-1}; + std::thread waiter([&] { + started = true; + result = __cxa_guard_acquire_nid_postfix(&guard); + returned = true; + }); + WaitForStart(started); + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + Require(!returned); + __cxa_guard_release_nid_postfix(&guard); + waiter.join(); + Require(returned && result == 0); + Require(Initialized(guard)); + } + + { + std::uint64_t guard = 0; + Require(__cxa_guard_acquire_nid_postfix(&guard) != 0); + std::atomic started{false}; + std::atomic returned{false}; + std::atomic result{-1}; + std::thread waiter([&] { + started = true; + result = __cxa_guard_acquire_nid_postfix(&guard); + returned = true; + if (result != 0) __cxa_guard_release_nid_postfix(&guard); + }); + WaitForStart(started); + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + Require(!returned); + __cxa_guard_abort_nid_postfix(&guard); + waiter.join(); + Require(returned && result != 0); + Require(Initialized(guard)); + } + + return 0; +}