FreeBSD's sigwait hands the error number back as its return value and leaves errno alone; the guest export returned -1 and set errno.
(cherry picked from commit b627f6c9443dd94a6392959f33e52d837ee4cae4)
Before: MarkPending set the bit and notified while the waits checked the set
under pending.lock, so a signal raised between the check and the wait was lost
and sigwait, sigwaitinfo, a null-timeout sigtimedwait and pause hung forever.
Now: the bit is set while holding that lock, so a wait either sees it or is
notified after it has blocked. GuestSigtimedwait raises with no delay 500 times
without the first sleep the other senders use; on the previous code that hung in
2 of 10 runs, and it passes on the new one.
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.
Exports pause (EDyhWelj7UY), sigwait (mrbHXqK8wkg) and sigwaitinfo
(Uq5BGthgbl4) from libkernel. NIDs computed with tools/nid_names.py and
matched in aerolib; ps5-payload-dev/sdk f5d40f02 sce_stubs/libkernel.c
places all three in libkernel.
They throw like the merged sigtimedwait (#3430), with the same message
pattern: "sigwait: waiting for a pending signal", "sigwaitinfo: waiting
for a pending signal" and "pause: waiting for a delivered signal". In
FreeBSD 11.4 (8c639945) sigwaitinfo is kern_sigtimedwait without a
timeout (sys/kern/kern_sig.c:1212), sigwait is the same wait returning
the error number (kern_sig.c:1150, lib/libthr/thread/thr_sig.c:676-728)
and pause is sigsuspend of the current mask (lib/libc/gen/pause.c:46-56).
AnyPS5 never leaves a guest signal pending (blocked signals are dropped
in Dispatch, raise delivers at once), so there is nothing to take, and
raise, kill and pthread_kill run a handler only on the calling thread,
so nothing can end a pause.
Titles (predicted stops, registry order; not observed runs):
- Ruffle PPSA68091, observed today
"FAIL: unresolved ELF import 1t1-JoZ0sZQ from libSceLibcInternal.prx"
(sinhf, #3519), predicted next
"FAIL: unresolved ELF import EDyhWelj7UY from libkernel.prx"
(pause, call site 0x8F7266).
- RetroArch PPSA99169: pause (0x30DB1, an address table entry).
- PS5MC PPSA99420: pause (0x3BD80F6, 0x3C9E220), sigwait (0x3C9E4C7),
sigwaitinfo (0x3C9E588).
- XPSemu PPSA97358: sigwait (0xE02AEC, 0x1101294, 0x1101368), QEMU's
dummy vCPU thread and sigwait_compat thread.
guest_sigtimedwait now also checks the three throws, with an empty set,
a null info, a SIGINT raised while blocked, and that neither the output
nor the blocked mask changes. TechnicalDebt records the call sites.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>