The mutex attribute now keeps its protocol and priority ceiling the way
FreeBSD's libthr does: the ceiling can be set and read only under
PTHREAD_PRIO_PROTECT (EINVAL otherwise), and setting a protocol resets it
to the highest SCHED_RR priority. Mutexes still do not apply priority
protection; that is listed as a silent stub.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit d10c024f8fdbcdfced0187550f272333264f07df)
Since guest threads get exactly the stack size they request, host code
that a guest thread calls runs on that small stack too. sceAgcCreateShader
reaches Driver::RegisterShader, whose frame alone is about 50 KiB, so a
title whose job threads ask for 48 KiB overflows the stack (#2603).
Each guest thread now gets a fixed 1 MiB of host stack below the range it
asked for. The guest still sees the requested size and address range
through scePthreadAttrGet and sceKernelIsStack; only host calls made from
the thread use the extra room. Running the prepared-shader driver tests on
a limited main-thread stack needs between 128 and 192 KiB for shader
registration, Vulkan setup included, so 1 MiB leaves several times that.
Switching to a separate host stack at each HLE entry point would need a
trampoline on every export that may go deep, handling of stack-passed
arguments and unwinding across the switch, and a list of which exports
qualify. A margin below the guest range covers every host call with no
per-export list.
On Windows the committed size was the requested size, and the margin
below it depended on the executable's default stack reserve; requests at
or above that reserve had none. The commit now includes the same margin.
macOS does not run guest threads.
guest_pthread_stack now also calls host code that uses 256 KiB of stack
below each thread's guest range; without the margin it crashes on the
16 KiB thread.
Fixes#2603
scePthreadCancel and pthread_cancel threw, scePthreadTestcancel did
nothing and the cleanup handler exports did not exist.
A cancel request is now recorded on the thread and acted on at
scePthreadTestcancel, the condition variable waits, scePthreadSem and
sem waits, scePthreadJoin and the sleeps, and under their POSIX names.
A thread blocked in one of the waits is woken; a condition wait takes
its mutex back before the thread exits. The thread exits with
PTHREAD_CANCELED through scePthreadExit, which now runs the handlers
of __pthread_cleanup_push_imp and __pthread_cleanup_pop_imp.
scePthreadSetcancelstate and scePthreadSetcanceltype reject unknown
values, and pthread_setcanceltype is added.
Constants, the handler node layout and the order of events follow
FreeBSD libthr. A sleep is not interrupted, file and socket calls are
not cancellation points and the asynchronous type acts like the
deferred one except at the two set calls (TechnicalDebt). New test:
guest_pthread_cancel.
AI-assisted: yes. Match the firmware 13.60 probe in PR #1798: names of 32 bytes or more return guest ENAMETOOLONG=63 and preserve the prior name. The complete final change passed a full Windows build and eight focused runtime tests.
scePthreadCreate on Linux started guest threads with std::thread, so every
thread got the host default stack (RLIMIT_STACK, usually 8 MiB) whatever the
title asked for. A thread that asks for more than that and uses it crashes
with SIGSEGV. Once the thread ran, scePthreadAttrGet also reported the host
size instead of the requested one.
Create the thread with pthread_create and a stack of the requested size plus
what glibc keeps at its top (static TLS, the thread control block and the
start_thread frame). That reserve is measured once with a probe thread.
The thread reports [top - reserve - size, top - reserve) as its stack, as
Windows reports [top - size, top), and the creator waits until the range is
set, as on Windows.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
winpthreads initializes a PTHREAD_RWLOCK_INITIALIZER lock on its first
use and fails a concurrent first lock with EINVAL. std::shared_timed_mutex
ignores that result in release builds, so two guest threads could both
hold a fresh guest rwlock.
Lock and unlock the mutex once when the guest rwlock is created, before
another thread can reach it, and record the remaining host exposure.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Hades (PPSA03355) stops at load on main with "undefined symbol:
9HzfhdtESio", the NID of pthread_getname_np, which it calls on
pthread_self() with a 32-byte buffer. Measured on a PS5 (firmware 13.60)
through dlopen of libkernel: the function takes (thread, buffer), copies
the name and its terminator, returns an empty name for an unnamed
thread, ESRCH for a null or joined thread and EFAULT for a null buffer.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
scePthreadMutexDestroy threw when the mutex was locked. FreeBSD libthr,
which the console's libkernel follows, returns EBUSY and leaves the
mutex usable; PPSA28997 destroys a locked mutex during its intro and
was aborted.
A condition wait on a recursive mutex locked more than once unlocked the
native mutex only once, so the thread that should signal blocked forever
on it. Unlock the remaining levels before the wait and lock them again
after it, as FreeBSD libthr does in _mutex_cv_unlock and _mutex_cv_lock.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HeC9bA3n74TzJRYv9AgBE8
sceKernelGetCurrentCpu returned the raw host CPU index. Guest affinities
are bookkeeping only and sceKernelGetAvailableCpumask advertises CPUs 0
to 12, so on a host with more than 13 logical CPUs a title could get 13
or higher, and a thread pinned to one CPU could get another one.
The host index now selects a CPU of the calling thread's affinity
clipped to the available mask, so the result is always an advertised
CPU and a thread pinned to one CPU reports that CPU.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HeC9bA3n74TzJRYv9AgBE8
The APC delivery was allocated after SuspendThread, so a target holding
the allocator lock deadlocked the raising thread; it is now allocated
before the suspend. The wait count a raise checks lived in the target's
thread_local storage and dangled once the thread exited; it is now a
member of PthreadPrivate, which TimedWait counts into through a
thread-local pointer bound when the thread starts, and a raise on a
thread that already finished returns ESRCH. TechnicalDebt gets one line
per unverified choice: Windows only, the FreeBSD ucontext layout, the
signal mask, the YMM upper halves, and delivery to threads outside
TimedWait.
AI-assisted: yes
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
LEGO Horizon Adventures imports 19 functions that no library exports
and no open pull request adds, so it fails eager binding. They are
declared as throwing stubs. The two libSceAjmWVorbis imports have no
known name and are exported by NID from libSceAjm.native.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
20 of the 31 NIDs have names in shadPS4's aerolib list. They are declared
under those names; POSIX sleep is implemented. The NpPartner001 and Psml
MFSR2 exports stay unnamed.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
UFC 6 (PPSA23566) passes type 0 to pthread_mutexattr_settype during
startup. libkernel rejects it with EINVAL and leaves the attribute
unchanged, as fpPS4's decompiled body does, instead of aborting.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Returns the CPU mask threads get by default (DEFAULT_THREAD_AFFINITY,
0x1FFF), the value itself rather than an error code.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The PS5 kernel follows FreeBSD 11, whose libthr accepts process-shared
barriers. Guest code runs in a single process, so a process-shared
barrier synchronises the same threads a private one does; the attribute
is stored and reported back by getpshared.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Run the SIGUSR1 handler recorded by sceKernelInstallExceptionHandler on the
target thread with a PS5 ucontext (uc_mcontext at 0x40, FreeBSD amd64
layout) of its interrupted state:
- on the calling thread, directly;
- on a thread in a libkernel wait, from an APC: the TimedWait waits are
alertable and count themselves in a per-thread wait state, so the
handler runs inside the wait, which then continues;
- on a running thread, by suspending it and redirecting it to an entry
that calls the handler and restores the possibly modified context.
Other signals return EINVAL, as in shadPS4; raising without a handler
throws. Linux still throws.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
scePthreadAttrGet reported a null stack address and a zero size for an
adopted thread (the main thread), and for every guest thread on Linux,
where the stack range was never recorded. A garbage collector that scans
thread stacks through scePthreadAttrGetstackaddr and
scePthreadAttrGetstacksize (Unity IL2CPP in PPSA12544) then skipped the
main thread's stack and freed objects it still referenced. The range now
comes from the host thread: GetCurrentThreadStackLimits on Windows,
pthread_getattr_np on Linux.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Implement scePthreadGetschedparam and scePthreadSetschedparam on top of
the thread priority, POSIX utimes and fsync on top of sceKernelUtimes
and sceKernelFsync, and sceUserServiceGetAccessibilityZoomFollowFocus
like the other accessibility queries. Add the remaining imports of
PPSA12544 as unimplemented exports and record unknown NIDs and
signatures in the technical debt.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>