diff --git a/core/libs/prx/libc/CMakeLists.txt b/core/libs/prx/libc/CMakeLists.txt index f5736578e..847b8ffc3 100644 --- a/core/libs/prx/libc/CMakeLists.txt +++ b/core/libs/prx/libc/CMakeLists.txt @@ -77,6 +77,16 @@ if(BUILD_TESTING) set_tests_properties(guest_math PROPERTIES ENVIRONMENT_MODIFICATION "PATH=path_list_prepend:$") endif() + add_test_executable(guest_execute_once_tests ${LIBS_INCLUDE_DIR}/tests/GuestExecuteOnce.cpp) + target_include_directories(guest_execute_once_tests PRIVATE ${LIBS_INCLUDE_DIR}) + target_link_libraries(guest_execute_once_tests PRIVATE libc) + configure_windows_unwind(guest_execute_once_tests) + add_test(NAME guest_execute_once COMMAND guest_execute_once_tests) + set_tests_properties(guest_execute_once PROPERTIES TIMEOUT 20) + if(WIN32) + set_tests_properties(guest_execute_once PROPERTIES ENVIRONMENT_MODIFICATION + "PATH=path_list_prepend:$") + endif() add_test_executable(guest_working_directory_tests ${LIBS_INCLUDE_DIR}/tests/GuestWorkingDirectory.cpp) target_include_directories(guest_working_directory_tests PRIVATE ${LIBS_INCLUDE_DIR}) target_link_libraries(guest_working_directory_tests PRIVATE libc) diff --git a/core/libs/prx/libc/Export.cpp b/core/libs/prx/libc/Export.cpp index 1d54d1084..bdb6ca001 100644 --- a/core/libs/prx/libc/Export.cpp +++ b/core/libs/prx/libc/Export.cpp @@ -55,12 +55,6 @@ int APS5_VABI libcCyberUnknown19(void) { } -APS5_EXPORT("DiGVep5yB5w", libcCyberUnknown01); -std::uint64_t APS5_VABI libcCyberUnknown01(void) { - NotImplemented_nid_no_patch("DiGVep5yB5w"); - return 0; -} - APS5_EXPORT("Ye20uNnlglA", libcCyberUnknown02); std::uint64_t APS5_VABI libcCyberUnknown02(void) { NotImplemented_nid_no_patch("Ye20uNnlglA"); diff --git a/core/libs/prx/libc/src/CxxAbiSupport.cpp b/core/libs/prx/libc/src/CxxAbiSupport.cpp index b3e467664..8f32a8572 100644 --- a/core/libs/prx/libc/src/CxxAbiSupport.cpp +++ b/core/libs/prx/libc/src/CxxAbiSupport.cpp @@ -16,6 +16,7 @@ #include #include #include +#include #include "prx/libc/include/General.hpp" @@ -40,4 +41,15 @@ int APS5_VABI LibcInternalExtCxaThreadAtexit_nid_postfix(void (*destructor)(void const std::error_category* _ZSt17iostream_categoryv_nid_postfix() { return &std::iostream_category(); } +int APS5_VABI _ZSt13_Execute_onceRSt9once_flagPFiPvS1_PS1_ES1__nid_postfix( + int* flag, int (APS5_VABI *callback)(void*, void*, void**), void* arg +) { + static std::recursive_mutex mutex; + std::lock_guard lock(mutex); + if (*flag != 0) return 1; + if (callback(nullptr, arg, nullptr) == 0) return 0; + *flag = 1; + return 1; +} + } diff --git a/core/libs/tests/GuestExecuteOnce.cpp b/core/libs/tests/GuestExecuteOnce.cpp new file mode 100644 index 000000000..327fbe8c3 --- /dev/null +++ b/core/libs/tests/GuestExecuteOnce.cpp @@ -0,0 +1,70 @@ +#include "prx/libc/include/general/VabiMacros.hpp" +#include +#include +#include +#include + +using Callback = int (APS5_VABI *)(void*, void*, void**); +extern "C" int APS5_VABI _ZSt13_Execute_onceRSt9once_flagPFiPvS1_PS1_ES1__nid_postfix(int*, Callback, void*); +static int ExecuteOnce(int* flag, Callback callback, void* arg) { + return _ZSt13_Execute_onceRSt9once_flagPFiPvS1_PS1_ES1__nid_postfix(flag, callback, arg); +} +static void Require(bool value) { if (!value) std::abort(); } + +static std::atomic calls{0}, arrived{0}; +static int control = 0, nested = 0, failing = 0, throwing = 0; +static int published = 0, nestedValue = 0, failures = 0, throws = 0; +static void* seenFirst = &control; +static void* seenArg = nullptr; +static void** seenThird = reinterpret_cast(&control); + +static int APS5_VABI Nested(void*, void* arg, void**) { + nestedValue = *static_cast(arg); + return 1; +} +static int APS5_VABI Initialize(void* first, void* arg, void** third) { + ++calls; + seenFirst = first; + seenArg = arg; + seenThird = third; + while (arrived.load() != 16) std::this_thread::yield(); + int value = 17; + Require(ExecuteOnce(&nested, Nested, &value) == 1); + published = 42; + return 5; +} +static int APS5_VABI Fail(void*, void*, void**) { + ++failures; + return 0; +} +static int APS5_VABI Throw(void*, void*, void**) { + if (++throws == 1) throw 7; + return 1; +} +static int APS5_VABI Unreachable(void*, void*, void**) { + std::abort(); +} + +int main() { + int token = 0; + std::array workers; + for (auto& worker : workers) worker = std::thread([&token] { + ++arrived; + Require(ExecuteOnce(&control, Initialize, &token) == 1); + Require(published == 42 && nestedValue == 17); + }); + for (auto& worker : workers) worker.join(); + Require(calls == 1 && control == 1 && nested == 1); + Require(seenFirst == nullptr && seenArg == &token && seenThird == nullptr); + Require(ExecuteOnce(&control, Unreachable, nullptr) == 1 && calls == 1); + + Require(ExecuteOnce(&failing, Fail, nullptr) == 0 && failing == 0 && failures == 1); + Require(ExecuteOnce(&failing, Fail, nullptr) == 0 && failing == 0 && failures == 2); + + try { ExecuteOnce(&throwing, Throw, nullptr); std::abort(); } + catch (int value) { Require(value == 7 && throwing == 0); } + Require(ExecuteOnce(&throwing, Throw, nullptr) == 1 && throwing == 1 && throws == 2); + + int preset = -3; + Require(ExecuteOnce(&preset, Unreachable, nullptr) == 1 && preset == -3); +}