Merge pull request #735 from adriadam10/feat/libc-execute-once
feat(libc): implement std::_Execute_once
This commit is contained in:
commit
9c3ef3a6a9
|
|
@ -77,6 +77,16 @@ if(BUILD_TESTING)
|
|||
set_tests_properties(guest_math PROPERTIES ENVIRONMENT_MODIFICATION
|
||||
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libc>")
|
||||
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:$<TARGET_FILE_DIR:libc>")
|
||||
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)
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@
|
|||
#include <locale>
|
||||
#include <regex>
|
||||
#include <functional>
|
||||
#include <mutex>
|
||||
|
||||
#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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,70 @@
|
|||
#include "prx/libc/include/general/VabiMacros.hpp"
|
||||
#include <array>
|
||||
#include <atomic>
|
||||
#include <cstdlib>
|
||||
#include <thread>
|
||||
|
||||
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<int> 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<void**>(&control);
|
||||
|
||||
static int APS5_VABI Nested(void*, void* arg, void**) {
|
||||
nestedValue = *static_cast<int*>(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<std::thread, 16> 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);
|
||||
}
|
||||
Loading…
Reference in New Issue