mirror of
https://github.com/boykopovar/AnyPS5.git
synced 2026-10-22 16:43:05 -05:00
pthread_atfork (U9t5kJAWPnA, aerolib) appends the prepare, parent and
child handlers to a process-wide list under a lock and returns 0, or
ENOMEM when the entry cannot be allocated, as FreeBSD 11.4's
_pthread_atfork does (lib/libthr/thread/thr_fork.c:78-100, freebsd-src
8c639945a44588f6cfdddfb057bb5aad88735da0). Null handlers are accepted;
FreeBSD's fork skips them. The handlers never run: fork
(libkernel_sys) throws and vfork is absent, so no child process is
created. The list is kept, in registration order, for a later fork
(PthreadAtforkHandlers_nid_no_patch); recorded in TechnicalDebt.
XPSemu (PPSA97358) calls it from a constructor, before main
(ZiZc3/XPSemu 8eec07b4, util/rcu.c:488-495):
static void __attribute__((__constructor__)) rcu_init(void)
{
smp_mb_global_init();
#ifdef CONFIG_POSIX
pthread_atfork(rcu_init_lock, rcu_init_unlock, rcu_init_child);
#endif
rcu_init_complete();
}
so a throwing export would stop the title before main. Predicted stop
removed (registry index 255, not run):
FAIL: unresolved ELF import U9t5kJAWPnA from libkernel.prx
guest_pthread_atfork checks registration order, null handlers,
concurrent registration from four threads and that no handler runs.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
(cherry picked from commit f37bccf6efd76edcde5d582800c89b83fdd30472)
49 lines
2.0 KiB
C++
49 lines
2.0 KiB
C++
#include "prx/libkernel/Pthread/include/Atfork.hpp"
|
|
#include <atomic>
|
|
#include <cstddef>
|
|
#include <cstdlib>
|
|
#include <thread>
|
|
#include <vector>
|
|
|
|
static void Require(bool value) { if (!value) std::abort(); }
|
|
|
|
static std::atomic<int> calls{0};
|
|
|
|
static void APS5_VABI Prepare() { ++calls; }
|
|
static void APS5_VABI Parent() { ++calls; }
|
|
static void APS5_VABI Child() { ++calls; }
|
|
|
|
static bool Same(const PthreadAtforkHandlers& entry, PthreadAtforkHandler prepare, PthreadAtforkHandler parent, PthreadAtforkHandler child) {
|
|
return entry.prepare == prepare && entry.parent == parent && entry.child == child;
|
|
}
|
|
|
|
int main() {
|
|
Require(PthreadAtforkHandlers_nid_no_patch().empty());
|
|
Require(pthread_atfork_nid_postfix(Prepare, Parent, Child) == 0);
|
|
Require(pthread_atfork_nid_postfix(nullptr, Parent, nullptr) == 0);
|
|
Require(pthread_atfork_nid_postfix(nullptr, nullptr, nullptr) == 0);
|
|
Require(pthread_atfork_nid_postfix(Prepare, Parent, Child) == 0);
|
|
auto handlers = PthreadAtforkHandlers_nid_no_patch();
|
|
Require(handlers.size() == 4);
|
|
Require(Same(handlers[0], Prepare, Parent, Child));
|
|
Require(Same(handlers[1], nullptr, Parent, nullptr));
|
|
Require(Same(handlers[2], nullptr, nullptr, nullptr));
|
|
Require(Same(handlers[3], Prepare, Parent, Child));
|
|
|
|
constexpr std::size_t threadCount = 4;
|
|
constexpr std::size_t perThread = 64;
|
|
std::vector<std::thread> threads;
|
|
for (std::size_t index = 0; index < threadCount; ++index) {
|
|
threads.emplace_back([] {
|
|
for (std::size_t count = 0; count < perThread; ++count) Require(pthread_atfork_nid_postfix(nullptr, nullptr, Child) == 0);
|
|
});
|
|
}
|
|
for (auto& thread : threads) thread.join();
|
|
handlers = PthreadAtforkHandlers_nid_no_patch();
|
|
Require(handlers.size() == 4 + threadCount * perThread);
|
|
Require(Same(handlers[3], Prepare, Parent, Child));
|
|
for (std::size_t index = 4; index < handlers.size(); ++index) Require(Same(handlers[index], nullptr, nullptr, Child));
|
|
Require(calls == 0);
|
|
return 0;
|
|
}
|