Commit Graph

98 Commits

Author SHA1 Message Date
Dean a6ed539e70
Merge pull request #4659 from AaravMehta-07/codex/sce-pthread-sem-overflow
fix(libkernel): guard SCE pthread semaphore count overflow
2026-10-13 17:49:51 +13:00
Dean b7025517e1
Merge pull request #4656 from milescoviello/fix/rwlock-writer-preference
fix(libkernel): hold back new rwlock readers while a writer waits
2026-10-13 17:49:30 +13:00
VitzS7 28063f1951 feat(libkernel): wait for pending guest signals in the signal waits
Before: raise, kill and pthread_kill delivered a signal at once or failed
with EINVAL, nothing stayed pending, and pause, sigwait, sigwaitinfo and
sigtimedwait called NotImplemented_nid_no_patch.

Now: a process-wide pending set holds signals 1 to 128. A signal raised
while it is blocked stays pending and is delivered when _sigprocmask
unblocks it, taken by sigwait, sigwaitinfo or sigtimedwait, or delivered by
pause, which runs its handler on the calling thread and returns -1 with
EINTR. pthread_kill to another thread leaves the signal pending for
whichever thread waits for it and answers ESRCH for a handle that is not a
live guest thread. sigtimedwait follows FreeBSD's sigtimedwait(2): EAGAIN
on timeout, EINVAL for a tv_nsec outside 0 to 1000000000 while nothing is
pending, and an indefinite wait with a null timeout.
2026-10-12 17:47:33 -03:00
milescoviello 953ae58bc8 fix(libkernel): hold back new rwlock readers while a writer waits
Guest rwlocks were a host std::shared_timed_mutex. On Linux that is a
glibc rwlock, which prefers readers: a reader is let in while a writer
is blocked, so a writer waiting on a lock that readers keep taking in
overlapping turns never gets it (a probe with four readers each holding
the lock for 4 ms still had scePthreadRwlockWrlock waiting after 5 s,
with 4924 read acquisitions meanwhile), and tryrdlock succeeds while a
writer waits.

FreeBSD 11.4 libthr gives waiting writers precedence. A rwlock starts
without URWLOCK_PREFER_READER (rwlock_init() callocs it), and
lib/libthr/thread/thr_umtx.h, _thr_rwlock_tryrdlock(), refuses a reader
while a writer waits:

	if ((flags & URWLOCK_PREFER_READER) != 0 ||
	    (rwlock->rw_flags & URWLOCK_PREFER_READER) != 0)
		wrflags = URWLOCK_WRITE_OWNER;
	else
		wrflags = URWLOCK_WRITE_OWNER | URWLOCK_WRITE_WAITERS;

as does do_rw_rdlock() in sys/kern/kern_umtx.c for the blocking wait.
The only exception is a thread that already holds a read lock, so that a
recursive read cannot deadlock (lib/libthr/thread/thr_rwlock.c,
rwlock_rdlock_common()):

	if (curthread->rdlock_count) {
	...
		flags = URWLOCK_PREFER_READER;

The rwlock is now a mutex and condition with a count of waiting writers:
a reader waits while a writer holds the lock or, unless the thread
already holds a read lock, while a writer waits; a timed writer that
gives up wakes the readers it held back. The TechnicalDebt note about
locking guest rwlocks once for winpthreads no longer applies to them.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 18:43:43 +00:00
Aarav f11798413e fix(libkernel): guard SCE pthread semaphore count overflow
Reject invalid initial counts without replacing the semaphore and preserve a saturated count on post, matching the existing guest POSIX limits with encoded SCE errors. Add boundary and unchanged-value guest regressions.
2026-10-12 23:26:25 +05:30
Dean 2a5eca11cd
Merge pull request #4350 from milescoviello/fix/once-reset-on-exit
fix(libkernel): reset a once control when its initializer exits or is cancelled
2026-10-13 04:53:46 +13:00
Dean fb23e85f66
Merge pull request #4063 from MarcZX100/codex/carry-pthread-join-self-4052
fix(libkernel): return EDEADLK for self-join and EINVAL for null threads
2026-10-13 01:25:36 +13:00
Dean 9a1922145d
Merge pull request #4080 from hugodearaujo/fix/rwlock-destroy-held
fix(libkernel): destroy rwlocks the way the console does
2026-10-13 01:21:08 +13:00
Dean 9fdddc8747
Merge pull request #4160 from hugodearaujo/fix/join-self-create-attr
fix(libkernel): join the calling thread and create from an unset attribute as the console does
2026-10-12 21:42:14 +13:00
Dean 82fc98f5e5
Merge pull request #4158 from hugodearaujo/fix/mutex-errors
fix(libkernel): return the console's errors from the SCE mutex calls
2026-10-12 21:41:52 +13:00
Dean 5ccf78e4e3
Merge pull request #4153 from hugodearaujo/fix/pthread-attr-errors
fix(libkernel): return the console's errors from the scePthreadAttr calls
2026-10-12 21:41:31 +13:00
Dean 69f7411ec9
Merge pull request #4096 from hugodearaujo/fix/solosched-values
fix(libkernel): accept only the solo scheduling values the console accepts
2026-10-12 21:40:24 +13:00
milescoviello 083c98a067 fix(libkernel): leave a once initializer's own cleanup handlers alone when it throws
pthread_once and scePthreadOnce removed their reset handler with a pop
that runs whatever handler is on top of the cleanup stack. An
initializer that pushed a handler of its own and then threw left that
handler on top, so the pop ran it with dangling state. Both now record
the stack top before pushing the reset handler; on a throw they restore
the stack to it and reset the control directly, and a normal return
restores it as well.

Reported in review by Hussein Kadhim, who fixed it the same way.

Co-authored-by: Hussein Kadhim <husseinkad.dev@gmail.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 07:49:09 +00:00
Dean 46f5b72e8e
Merge pull request #3623 from milescoviello/fix/pthread-key-numbering
fix(libkernel): number pthread keys from 1 as FreeBSD libthr does
2026-10-12 20:24:15 +13:00
Dean d1758a91c8
Merge pull request #3494 from babyhuey/carry/pthread-attr-scope-3377
feat(libkernel): implement pthread_attr_getscope, setscope, getinheritsched and getstackaddr
2026-10-12 15:33:56 +13:00
hugodearaujo 0b7e4ddf9f fix(libkernel): join the calling thread and create from an unset attribute as the console does
On a PS5 (firmware 13.60) scePthreadJoin on the calling thread returns
EDEADLK and scePthreadCreate with a zeroed attribute creates a thread
with the default attributes. Here both threw.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 21:09:08 -03:00
hugodearaujo b9b5bcc190 fix(libkernel): return the console's errors from the SCE mutex calls
On a PS5 (firmware 13.60) the SCE mutex attribute calls return EINVAL
for a null or uninitialized attribute, scePthreadMutexattrSetprotocol
accepts 0 to 2, scePthreadMutexInit takes an uninitialized attribute as
the default one, and destroying a destroyed mutex returns EINVAL. Here
these threw, including for the priority protection protocol.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 21:06:55 -03:00
hugodearaujo 2446a2648a fix(libkernel): return the console's errors from the scePthreadAttr calls
On a PS5 (firmware 13.60) the scePthreadAttr setters and getters return
EINVAL for a null or uninitialized attribute and for a null output,
ENOTSUP for a policy other than SCHED_FIFO and SCHED_RR, an inheritance
other than explicit and inherit, and a priority outside 256 to 767, and
EINVAL for an invalid detach state. Here they threw, or stored any
policy, inheritance and priority.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 20:59:17 -03:00
hugodearaujo 65b6a4729b fix(libkernel): accept only the solo scheduling values the console accepts
On a PS5 (firmware 13.60) scePthreadAttrSetsolosched and
pthread_attr_setsolosched_np accept only 0 and 16; every other value
returns ENOTSUP and keeps the stored one. The SCE set and get calls
return EINVAL for a null or uninitialized attribute instead of throwing.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 20:01:44 -03:00
hugodearaujo 7b5ac83a2a fix(libkernel): destroy rwlocks the way the console does
On a PS5 (firmware 13.60) destroying a rwlock returns 0 even while it
is held, and leaves 1 in the handle; every later call on that handle
returns EINVAL until it is initialized again. A zero handle is
initialized on first use by the scePthreadRwlock calls too. Here a held
rwlock returned EBUSY, a destroyed handle became null and was silently
re-created by the next POSIX call, and the scePthreadRwlock calls threw
on a zero handle.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 19:48:33 -03:00
Hussein Kadhim 351ca3bb12 fix(libkernel): return EDEADLK for a thread joining itself and EINVAL for a null thread
scePthreadJoin threw when a joinable thread joined itself and
scePthreadJoin and scePthreadDetach threw on a null thread, so
pthread_join and pthread_detach ended the process where FreeBSD libthr
returns EDEADLK and EINVAL.
2026-10-11 22:23:16 +00:00
milescoviello 972caa4f2a fix(libkernel): number pthread keys from 1 as FreeBSD libthr does
scePthreadKeyCreate and pthread_key_create handed out the slot index,
so the first key in a process was 0. FreeBSD libthr returns slot + 1
(_pthread_key_create: "*key = i + 1") and subtracts 1 again in
pthread_key_delete, pthread_getspecific and pthread_setspecific, so 0 is
never a valid key. Code that keeps 0 as "key not created yet" therefore
created a new key on every call and lost the values it stored under the
previous one.

Keys now start at 1; 0 and other out-of-range keys are rejected with
EINVAL by delete and set and read as NULL, as before.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XYhZu5HokHMo9mfi8QU83m
2026-10-11 22:16:59 +00:00
Adrià 5130c66b88
Merge pull request #3794 from semirkabir/feat/cpuset-getaffinity-process
feat(libkernel): report the process affinity from cpuset_getaffinity
2026-10-12 00:04:42 +02:00
Adrià d4938e1534
Merge pull request #3753 from jweidenh/codex/pthread-destructor-lifetime
fix(libkernel): run pthread key destructors before releasing thread state
2026-10-11 23:58:33 +02:00
pushkarreddyy 91bfd6a18c feat(libkernel): implement pthread_attr_getinheritsched, pthread_attr_getscope and pthread_attr_setscope 2026-10-11 17:45:17 -04:00
milescoviello 3ba4532352 fix(libkernel): reset a once control when its initializer exits or is cancelled
pthread_once and scePthreadOnce marked the control as running, called
the initializer and marked the control done only after it returned. A
thread that left the initializer through pthread_exit or a
cancellation point never came back, so the control stayed in progress
and every later call on it waited forever.

FreeBSD libthr runs the initializer under a cleanup handler
(lib/libthr/thread/thr_once.c, _pthread_once:
"THR_CLEANUP_PUSH(curthread, once_cancel_handler, once_control);
init_routine(); THR_CLEANUP_POP(curthread, 0);"). once_cancel_handler
stores ONCE_NEVER_DONE and wakes the waiters, and thread exit and
cancellation run the cleanup handlers. POSIX requires this for
cancellation: "if init_routine is a cancellation point and is canceled,
the effect on once_control shall be as if pthread_once() was never
called."

Both functions now push such a handler around the initializer, so the
control returns to its initial state and the next caller, or one
already waiting, runs the initializer. scePthreadOnce waiters now retry
when the control goes back to its initial state, and an exception from
its initializer resets the control as it already did for pthread_once.
CleanupHandler moves to Cancel.hpp so that Once.cpp can use it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 21:30:23 +00:00
Semir Kabir 2124cfba5f feat(libkernel): report the process affinity from cpuset_getaffinity 2026-10-11 16:43:36 +00:00
jweidenh 503043ca00 fix(libkernel): run pthread key destructors before thread release 2026-10-11 12:21:40 -04:00
Hussein Kadhim b02881cf0e fix(libkernel): fail pthread_rwlock_unlock with EPERM when the rwlock is not held
scePthreadRwlockUnlock called unlock_shared on the host mutex whenever the
caller was not the writer, which is undefined behaviour for an unlocked rwlock
or one write-locked by another thread. Track the reader count and return
EPERM like FreeBSD libthr when the caller does not own the write lock and no
read lock is held.
2026-10-11 21:25:20 +05:30
Dani 21f4be11ad fix(libkernel): inherit process affinity in new guest threads 2026-10-11 12:44:26 +02:00
Adrià e608d40265
Merge pull request #2051 from esatturan/fix/libkernel-umtx-interruptible-waits
fix(libkernel): let exception delivery interrupt mutex and rwlock waits
2026-10-11 11:31:40 +02:00
esatturan be4ab94642 fix(libkernel): let exception delivery interrupt timed mutex and rwlock waits on Windows 2026-10-11 11:20:40 +02:00
Claude c356112f32 fix(libkernel): keep raises off unregistered threads and clamp the handler's MXCSR
Two defects found in review of the Linux delivery:

A raise right after scePthreadCreate reached the new thread before
RunThread set currentThread, so scePthreadSelf() in the handler adopted
a second handle, and could deadlock on glibc's thread lock when the
signal interrupted pthread_getattr_np. The creator now blocks all
signals around the std::thread construction, so the child starts with
them blocked, and RunThread restores the creator's mask once the thread
is registered, as libthr does. A raise in that window stays pending and
is delivered just before the entry runs.

A handler that set reserved MXCSR bits made rt_sigreturn fail with
SIGSEGV. The MXCSR the handler leaves is now masked with the frame's
MXCSR_MASK (0xffbf when it reads 0), as FreeBSD's fpusetregs does, on
both platforms; Windows also keeps CONTEXT.MxCsr equal to
FltSave.MxCsr.

guest_raise_exception raises right after each of 50 thread creations and
checks scePthreadSelf() in the handler, and its context round trip sets
a reserved MXCSR bit and FZ and checks that only FZ is kept.

AI-assisted: yes

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LwJx7AHrSUaMGSpEGhL1Kb
2026-10-11 11:12:02 +02:00
Claude dca5a88ef2 feat(libkernel): deliver sceKernelRaiseException on Linux
sceKernelRaiseException threw on Linux, so a title whose garbage
collector stops threads with it (Unity's PS5Util raises SIGUSR1 on each
thread and reads mc_rsp from the handler's context) could not run there.

The raise now sends the host SIGUSR1 to the target thread with
pthread_kill. The host handler, installed on first use with SA_SIGINFO
and SA_RESTART, builds the FreeBSD-layout ucontext from the interrupted
registers and the FXSAVE area, calls the guest handler on the target's
own stack and writes the registers back, so the handler's changes reach
the interrupted code, as they do on Windows. errno is left as the
handler leaves it, as libthr does.

A raise on another thread takes the target's join lock with the host
signal blocked and returns ESRCH once the thread has finished, so a
detached thread's pthread_t is never signalled after it exits. The
pthread_t is recorded when the thread is created or adopted. Handlers
are now lock-free atomics, as the host handler reads them.

guest_raise_exception now runs on every platform. It also checks that
r13 and xmm8 reach the handler and that r12 and xmm8 written by the
handler reach the interrupted code.

AI-assisted: yes

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LwJx7AHrSUaMGSpEGhL1Kb
2026-10-11 11:11:23 +02:00
Adrià 5460611fed
Merge pull request #3323 from TheAggie/feat/libkernel-cpuset-setaffinity
feat(libkernel): implement cpuset_setaffinity for the calling thread or process
2026-10-11 09:57:15 +02:00
Scott Schuster bb4b938e34 feat(libkernel): implement cpuset_setaffinity for the calling thread or process
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 02:31:57 -05:00
Scott Schuster f38408ef5c fix(libkernel): keep stack protection inside the stack and never throw while restoring it
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 02:28:03 -05:00
Scott Schuster 24ae0a98d0 fix(libkernel): check the requested stack range and restore guard pages at thread exit
A stack base need not be page aligned (it is not on Linux), so rounding the
range down first could leave the stack and miss the stack path. The range the
guest asked for is now checked against the guest stack, and the rounded
pages are protected, as FreeBSD rounds the address down.

A thread that exits with part of its stack protected left those pages to the
next thread that reused the stack. Protected stack pages are recorded, and a
thread's stack is made readable and writable again when it is unregistered at
exit.

AI-assisted (Claude Code).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 02:28:03 -05:00
Hussein Kadhim 709c12e133 fix(libkernel): return EPERM or EINVAL when a condition wait gets a mutex the caller does not hold
pthread_cond_wait and pthread_cond_timedwait, and their sce counterparts, threw a
host exception when the mutex was unlocked, locked by another thread, or not yet
initialized, and dereferenced the adaptive static initializer. FreeBSD returns
EPERM for these (EINVAL for a destroyed mutex) before touching the condition.

(cherry picked from commit a78bb8a855ca3b277f128c6a9e52ff3949b9cb80)
2026-10-11 01:53:12 +02:00
Zayd Zaari 718eb9cc3e fix(libkernel): return EBUSY when destroying locked rwlock 2026-10-11 01:37:26 +02:00
Dean Galvin 84d90152b4 fix(libkernel): keep host stack room below guest thread stacks
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
2026-10-10 22:41:03 +13:00
Nicolas Abosch 3acca27f27 feat(libkernel): implement thread cancellation
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.
2026-10-10 11:32:09 +02:00
EdouardSence 9ba1b06a4e fix(libkernel): start the pthread object with the thread id
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 09:58:05 +02:00
NevesHenry faf983ae01 fix(libkernel): give Linux guest threads the stack size they request
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>
2026-10-10 08:57:00 +02:00
hamb3r 2d783b7962 fix(libkernel): keep Unity GC delivery out of MinGW TLS waits 2026-10-10 03:06:40 +03:00
Adrià a291ae0f88
Merge pull request #1483 from Aryansh1520/fix/libkernel-mutex-destroy-busy
fix(libkernel): return EBUSY when destroying a locked mutex
2026-10-10 01:32:32 +02:00
Adrià c2ac5dd93f
Merge pull request #1676 from Recapi/feat/libkernel-thread-cpu-clock
feat(libkernel): implement pthread_getcpuclockid and per-thread CPU clocks
2026-10-10 00:01:18 +02:00
Dean 0c8d5356d7
Merge pull request #1480 from awake-devel/fix/cond-wait-recursive-mutex
fix(libkernel): release every level of a recursive mutex during a cond wait
2026-10-10 08:05:19 +13:00
Renan Pires 942a105c39 feat(libkernel): implement pthread_getcpuclockid and per-thread CPU clocks 2026-10-08 23:50:03 -03:00
Aryan Sharma d77bc7c363 fix(libkernel): return EBUSY when destroying a locked mutex
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.
2026-10-08 14:25:16 +05:30