#9272 keeps liveStacks and protectedStackPages alive through static
destruction so that a late mprotect from an atexit handler can still
look up guest thread stacks. The mutex guarding them is still a static
std::mutex. libstdc++'s std::mutex has a trivial destructor, so it keeps
working on Linux, but libc++ destroys the pthread mutex, and a later lock
throws "mutex lock failed: Invalid argument": on macOS the exit probe of
guest_memory_lifetime fails at its mapping protection stage. The lock is
now allocated and never destroyed, like the maps.
The same change is part of #7201 and #8658.
Co-Authored-By: marion909 <[email protected]>
Co-Authored-By: iamthehimansh <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
liveStacks, protectedStackPages and guestStackMappings were plain
globals, so libkernel's static destructors freed them at exit while
detached threads could still unregister their stacks or look one up.
ThreadSanitizer reports the race in guest_pthread_join_detach: the main
thread's static destruction of liveStacks against a detached thread's
UnregisterStack under stackLock. The maps are now created once and
never destroyed, as Apr.cpp does for its globals.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Both were missing from libkernel.prx, so a title importing either
(dYv-+If2GPk, RRnSj8h8VR4) failed to load. They take a relative timeout
in microseconds and are scePthreadRwlockTimedrdlock and
scePthreadRwlockTimedwrlock with POSIX error numbers; a zero lock is
initialized as by pthread_rwlock_rdlock. The signature comes from
shadPS4, which registers one function for each NID and its
scePthreadRwlockTimed* twin; FreeBSD 11.4 has no such calls. The throwing
libkernel_sys copies are removed, as an export may live in one library
only. Recorded in the libkernel debt file.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
_Call_once, _Tss_create, _Tss_delete, _Tss_get, _Tss_set, _Thrd_equal,
_Thrd_current and _Thrd_exit call scePthreadOnce, scePthreadKeyCreate,
scePthreadKeyDelete, scePthreadGetspecific, scePthreadSetspecific,
scePthreadEqual, scePthreadSelf and scePthreadExit and return their
results unchanged, as the console's libSceLibcInternal does.
_Mtx_current_owns returns 1 there. libkernel registers the calls with
libc at load, next to the pthread_create registration.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01RbLgECLQ2bpDM7oe2urGZK
Animal Well (PPSA22520) imports _Thrd_detach (L7f7zYwBvZA) from libc.prx and nothing exports it, so the Windows loader stops at startup. Register libkernel's pthread_detach with libc like pthread_create and pthread_join, and return _Thrd_error (4) when it fails.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01GtXavMkgCTCdmRpweviHcQ
scePthreadMutexUnlock resolved the slot with resolveMutex(mutex, false),
which throws "Mutex is not initialized" while the slot still holds the
static initializer (0) or the adaptive static initializer (1), so
unlocking a statically initialized mutex that was never locked ended
the process. FreeBSD 11.4's mutex_unlock_common returns EPERM for both
without initializing the slot, and EINVAL for the destroyed marker.
Load the slot once: the destroyed marker still returns 0x80020016, and
the two initializers now return 0x80020001 and stay in the slot, so a
later lock still creates the mutex.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
_pthread_cond_timedwait fails an abstime with a negative tv_sec with EINVAL
before it looks at the condition variable or the mutex. pthread_cond_timedwait
took it as a deadline in the past and answered ETIMEDOUT.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
_pthread_barrier_init fails a count above INT_MAX with EINVAL.
pthread_barrier_init accepted any count above zero.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
scePthreadAttrGet copied the attributes of any non-null handle, so a
thread that was joined or had finished still answered 0, and a thread
that ended detached was read after its object had been freed. FreeBSD
11.4's _pthread_attr_get_np returns ESRCH for a dead or reclaimed thread,
and Monster Hunter Wilds (PPSA24191) reuses a slot of its thread table
only when scePthreadAttrGet returns 0x80020003.
After the EINVAL checks it now returns ESRCH unless the handle is a live
guest thread, and copies the fields while holding the thread registry
lock (LockLiveGuestThread), so a detached thread exiting during the call
is not freed under the copy. GuestThreadIsLive uses the same lookup.
pthread_attr_get_np, which the title does not import, is left as it is;
the debt entry records the difference.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
PPSA34015 (EA SPORTS FC 27) stopped with "Joining guest thread: Access is
denied": scePthreadJoin waited on a thread that another call released
under it, and then waited on its closed handle. As FreeBSD 11.4's
thr_join.c and thr_detach.c do with the joiner field, scePthreadDetach
now returns EINVAL while a thread waits to join the target, a second
concurrent scePthreadJoin returns EOPNOTSUPP, a cancelled join backs out,
and a completed join leaves the thread detached.