Merge pull request #735 from adriadam10/feat/libc-execute-once

feat(libc): implement std::_Execute_once
This commit is contained in:
Dean 2026-10-06 12:38:07 +13:00 committed by GitHub
commit 9c3ef3a6a9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 92 additions and 6 deletions

View File

@ -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)

View File

@ -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");

View File

@ -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;
}
}

View File

@ -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);
}