mirror of
https://github.com/boykopovar/AnyPS5.git
synced 2026-10-28 12:05:06 +00:00
__std_init_once_begin_initialize_clr, __std_init_once_complete_clr and __std_init_once_link_alternate_names_and_abort were not exported. As in the PS5 12.00 module, begin moves an idle once object to running and reports pending, reports done without pending, and waits on the object while another caller runs; complete marks it done, or idle again for flag 4, wakes every waiter and returns 1; the third aborts. Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_01RbLgECLQ2bpDM7oe2urGZK
54 lines
2.0 KiB
C++
54 lines
2.0 KiB
C++
#include "prx/libc/include/general/VabiMacros.hpp"
|
|
#include <atomic>
|
|
#include <chrono>
|
|
#include <cstdio>
|
|
#include <cstdlib>
|
|
#include <thread>
|
|
|
|
extern "C" {
|
|
int APS5_VABI __std_init_once_begin_initialize_clr_nid_postfix(void** once, unsigned long flags, int* pending, void** context);
|
|
int APS5_VABI __std_init_once_complete_clr_nid_postfix(void** once, unsigned long flags, void* context);
|
|
}
|
|
|
|
namespace {
|
|
|
|
constexpr unsigned long InitFailed = 4;
|
|
|
|
void Require(bool condition, const char* message) {
|
|
if (condition) return;
|
|
std::fprintf(stderr, "init once: %s\n", message);
|
|
std::abort();
|
|
}
|
|
|
|
int Begin(void** once) {
|
|
int pending = -1;
|
|
Require(__std_init_once_begin_initialize_clr_nid_postfix(once, 0, &pending, nullptr) == 1, "begin returns 1");
|
|
return pending;
|
|
}
|
|
|
|
}
|
|
|
|
int main() {
|
|
alignas(8) void* once = nullptr;
|
|
Require(Begin(&once) == 1, "the first caller runs the initialization");
|
|
Require(reinterpret_cast<std::uintptr_t>(once) == 1, "a running initialization marks the object");
|
|
Require(__std_init_once_complete_clr_nid_postfix(&once, InitFailed, nullptr) == 1, "complete a failed initialization");
|
|
Require(once == nullptr, "a failed initialization resets the object");
|
|
Require(Begin(&once) == 1, "a caller after a failure runs the initialization again");
|
|
|
|
std::atomic<bool> waiterDone{false};
|
|
std::atomic<int> waiterPending{-1};
|
|
std::thread waiter([&] {
|
|
waiterPending = Begin(&once);
|
|
waiterDone = true;
|
|
});
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
|
Require(!waiterDone, "a second caller waits while the initialization runs");
|
|
Require(__std_init_once_complete_clr_nid_postfix(&once, 0, nullptr) == 1, "complete the initialization");
|
|
waiter.join();
|
|
Require(waiterPending == 0, "the waiting caller sees the initialization done");
|
|
Require(reinterpret_cast<std::uintptr_t>(once) == 2, "a completed initialization marks the object");
|
|
Require(Begin(&once) == 0, "a later caller does not run the initialization");
|
|
return 0;
|
|
}
|