Commit Graph
64 Commits
Author SHA1 Message Date
Adrià a0f3dc4014 Merge pull request #6091 from Ondra97/codex/fix-cond-canceled-signal
fix(libkernel): preserve condition signals across cancellation
2026-10-18 17:33:22 +02:00
Dean a3d2f6ef50 Merge pull request #7225 from nicolasfalesy/feat/libkernel-condattr-setpshared
feat(libkernel): implement pthread_condattr_setpshared
2026-10-18 10:09:16 +13:00
Dean b95768d1b0 Merge pull request #6540 from JJenkx/fix/kernel-cpuset-thread-id
feat(libkernel): support cpuset affinity by guest thread ID
2026-10-18 09:43:55 +13:00
Nicolas FalesyandClaude Opus 5.5 05b457d49a feat(libkernel): implement pthread_condattr_setpshared
PSFlyCast (PPSA99247) imports pthread_condattr_setpshared (3BpP850hBT4)
from libkernel and fails to load without it:
"./app.elf: symbol lookup error: ./app.elf: undefined symbol: 3BpP850hBT4".

The attribute stores PTHREAD_PROCESS_PRIVATE (0) or
PTHREAD_PROCESS_SHARED (1); a null or uninitialized attribute and any
other value return EINVAL without changing it, following FreeBSD 11
libthr thr_condattr.c and pthread_mutexattr_setpshared here. Sharing
across processes is not modeled, as for mutexes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Sovthwyu4H63RMbRVha2Sy
2026-10-17 14:06:11 -04:00
Dean aa819ac74a Merge pull request #6956 from jradic/fix/libkernel-thread-local-slot-first-use
fix(libkernel): allocate the Windows thread-local slot on first use
2026-10-18 00:31:01 +13:00
Dean 72a848ca52 Merge pull request #4533 from Roojool/contrib/g07-pthread-getname
fix(libkernel): answer pthread_getname_np for a joined thread with ESRCH
2026-10-17 13:05:38 +13:00
jradicandClaude Opus 5.5 75d29f5b3f fix(libkernel): allocate the Windows thread-local slot on first use
WindowsThreadLocal<T>::slot was a template static, so its dynamic
initialization was unordered against the timer self-test in Time.cpp.
With APS5_TRACE_TIMERS set, the self-test ran first, used slot 0 and
aborted every title with "Cannot bind native thread state". The slot is
now a function-local static. guest_nanosleep_timer_self_test runs
guest_nanosleep_tests with APS5_TRACE_TIMERS=1 on Windows.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KwsvKtVsZDLefysUM9m2QS
2026-10-16 16:03:14 -05:00
JJenkx 7777219ee1 feat(libkernel): support cpuset affinity by guest thread ID 2026-10-15 15:25:34 -04:00
Ondra97 241053ad9f fix(libkernel): preserve condition signals across cancellation 2026-10-14 23:34:45 +02:00
Rujul Talekar e41f7f292e fix(libkernel): answer a second scePthreadJoin with ESRCH 2026-10-14 10:57:05 +05:30
JJenkx bfa0de72cb fix(libkernel): retain shared mutex attributes 2026-10-13 23:13:51 -04:00
Dean 40047f46bd Merge pull request #4377 from MarcZX100/codex/carry-pthread-atfork-3628
feat(libkernel): implement pthread_atfork registration
2026-10-14 05:54:54 +13:00
Adrià 9c05bf25f5 Merge pull request #4585 from jagrat04/feat/libkernel-cpuset-affinity-errors-pr
feat(libkernel): return ESRCH and EINVAL for unsupported cpuset affinity targets
2026-10-13 17:23:01 +02:00
MarcZX100 9b6e7f66c4 fix(libkernel): protect host-only guest stack pages 2026-10-13 16:03:36 +02:00
Cesar RodriguezandClaude Opus 5.5 7637cac13e fix(libkernel): place guest thread stacks inside the application area
On Linux, glibc maps the stack of every guest thread near the top of the
host address space (around 0x7fff00000000). On the console, application
stacks lie inside the application area below 0xFC00000000, and guest code
relies on that: ChakraCore's garbage collector indexes a 4 MiB table with
address >> 18, so it only covers addresses below 1 TiB and reports
Js::OutOfMemoryException for a stack above it.

Map each guest thread's stack with DoMapAnon, which allocates from the
guest arena, behind a PROT_NONE guard page, and pass it to pthread_attr_setstack.
The stack is freed after a join; the stack of a detached thread is freed once its
host thread is gone from /proc/self/task.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 16:03:36 +02:00
hugodearaujoandClaude Opus 5.5 5c6a9d22c4 fix(libkernel): check and keep the scheduling policy of a thread
On a PS5 (firmware 13.60) pthread_setschedparam and
scePthreadSetschedparam accept only SCHED_FIFO and SCHED_RR with a
priority of 256 to 767 or 896 to 959, return EINVAL otherwise, and
keep the policy for pthread_getschedparam. A created thread takes the
policy of explicit scheduling attributes, or its creator's when it
inherits. Here any policy and priority were accepted and SCHED_FIFO
was always reported.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 12:53:19 +02:00
jagrat Singh 9efabff1cb feat(libkernel): return ESRCH and EINVAL for unsupported cpuset affinity targets
Allow calling threads to query and set affinity using their own thread ID in addition to -1. Return ESRCH for non-matching thread or process IDs and EINVAL for invalid level/which arguments instead of throwing NotImplemented, matching FreeBSD cpuset semantics.
2026-10-13 15:58:11 +05:30
David e8fd542f03 feat(libkernel): implement pthread_atfork
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)
2026-10-13 07:38:13 +00: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
milescovielloandClaude Opus 5.5 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
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
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 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
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
pushkarreddyy 91bfd6a18c feat(libkernel): implement pthread_attr_getinheritsched, pthread_attr_getscope and pthread_attr_setscope 2026-10-11 17:45:17 -04:00
milescovielloandClaude Opus 5.5 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
Dani 21f4be11ad fix(libkernel): inherit process affinity in new guest threads 2026-10-11 12:44:26 +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 SchusterandClaude Opus 5.5 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 SchusterandClaude Opus 5.5 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
Zayd Zaari 718eb9cc3e fix(libkernel): return EBUSY when destroying locked rwlock 2026-10-11 01:37:26 +02:00
ps54all 08728f95b9 feat(libkernel): implement pthread_mutexattr_setprioceiling and getprioceiling
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 d10c024f8f)
2026-10-10 18:12:31 +00: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
EdouardSenceandClaude Opus 5.5 94409c9f32 fix(libkernel): keep the thread id 64-bit on every host
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 09:58:05 +02:00
EdouardSenceandClaude Opus 5.5 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
NevesHenryandClaude Opus 5.5 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
Dean ac57f46711 Merge pull request #2275 from mrk1987/fix/libkernel-rwlock-concurrent-first-lock
fix(libkernel): initialize guest rwlocks before they are shared
2026-10-10 17:46:25 +13:00
hamb3r 2d783b7962 fix(libkernel): keep Unity GC delivery out of MinGW TLS waits 2026-10-10 03:06:40 +03: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
AndreasandClaude Opus 5.5 69063dc1ef fix(libkernel): initialize guest rwlocks before they are shared
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>
2026-10-09 16:13:41 +02:00
Renan Pires 942a105c39 feat(libkernel): implement pthread_getcpuclockid and per-thread CPU clocks 2026-10-08 23:50:03 -03:00
clem-gbandClaude Opus 5.5 2ba8f75e15 fix(libkernel): report the current CPU within the thread's guest affinity
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
2026-10-08 10:47:33 +02:00
MerijnandClaude Opus 5.5 51b6079b5b fix(libkernel): no allocation while the target is suspended; own the wait count
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>
2026-10-07 21:10:25 +02:00
Merijn b92894c628 Merge branch 'main' of https://github.com/boykopovar/AnyPS5 into feat/kernel-raise-exception-windows
# Conflicts:
#	core/libs/prx/libkernel/CMakeLists.txt
#	core/libs/prx/libkernel/System/src/Exception.cpp
2026-10-07 07:45:49 +02:00
MerijnandClaude Opus 5.5 dd2890c1c5 feat(libkernel): deliver sceKernelRaiseException on Windows
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>
2026-10-05 23:19:38 +02:00
MerijnandClaude Opus 5.5 b530c9b784 feat(libkernel): implement sceKernelIsStack
Track the stack range of each running guest thread and answer
sceKernelIsStack from the calling thread's stack (the host limits for an
adopted thread) or from that registry. A mapped address outside every
stack succeeds with null bounds and an unmapped one returns EACCES; the
output pointers may be null.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 19:08:45 +02:00