Skip to content

fix(libkernel): defer raises while a thread holds the address-wait lock on Windows - #5756

Merged
oneandonlydean merged 1 commit into
boykopovar:mainfrom
oneandonlydean:fix/raise-address-lock-1488
Oct 14, 2026
Merged

oneandonlydean merged 1 commit into
boykopovar:mainfrom
oneandonlydean:fix/raise-address-lock-1488

Conversation

@oneandonlydean

@oneandonlydean oneandonlydean commented Oct 14, 2026 •

Copy link
Copy Markdown
Collaborator

What

Fixes #1488. On Windows, sceKernelRaiseException redirects a thread wherever it was stopped. A thread stopped inside sceKernelSyncOnAddressWake, sceKernelSyncOnAddressWait or _umtx_op while it held the address-wait lock ran the handler with the lock held, so a handler that waits, as a stop-the-world GC handler does, blocked every other thread that waits on or wakes an address. After #1562, LeandroLP23's reproducer in #1488 still blocked 35 of 300 rounds, all inside sceKernelSyncOnAddressWake.

The address-wait lock is now taken under the DeliveryDeferral that the guest mutex park queues already use (#5429): while it is held the thread's wait count is raised, so a raise queues an APC instead of redirecting the thread. The APC runs in the condition wait, where the lock is released, or after the lock is released on the way out of the call, before it returns to the guest. Nothing is delivered from inside the unlock or between the condition's enqueue and its wait (the points raised in the #4888 review). DeliveryDeferral and DeliverDeferred move from TimedWait.cpp into TimedWait.hpp for this.

Linux is unchanged: delivery there runs in the target's SIGUSR1 handler and the debt entry for it stays. Other emulator locks can take the same deferral when a case shows up. The Windows sceKernelRaiseException entry in docs/dev/debt/libkernel.md now names the deferred locks.

Tested

  • New Windows phases in guest_raise_exception: a guest thread loops on sceKernelSyncOnAddressWake, or on a 20 µs sceKernelSyncOnAddressWait; for 100 rounds each the test raises at it, holds the handler, and requires a wake on another address from another thread to return within 500 ms.
  • Windows: CI run 38547032212 (green, guest_raise_exception passes on Windows). Test without the fix: guest_raise_exception fails on Windows in run 38543474956 (completed failure).
  • Linux (CachyOS, GCC 16, Release): builds; full ctest except agc_driver*: 724 of 724 (6 VideoOut tests skipped, no display). The new phases are Windows only.
  • tools/check_conventions.py and tools/check_test_registration.py pass.
  • Not tested: titles.

Checklist

  • Based on current main; no other open PR implements the same functions
  • One topic per PR; follow-ups go in a new PR
  • No comments in code except technical debt
  • Unimplemented paths throw (NotImplemented_nid_no_patch); silent stubs are listed under Silent stubs in their library's technical debt file
  • No notes, investigation .md files or images added to the repository (attach them to this PR)
  • New third-party code is a submodule built from source
  • Generic behaviour, not specific to one title
  • Depends on: none
  • Unblocks: none known
  • GPUs tested: n/a
  • AI-assisted: yes

@github-actions

github-actions Bot commented Oct 14, 2026 •

Copy link
Copy Markdown

Other open pull requests touch the same code. Merging one will break the other, or both implement the same thing. If this one builds on them, list them in Depends on.

Pull request Conflicting files (lines on main) Same exports Same new files
#4630 core/libs/tests/GuestRaiseException.cpp L662

@oneandonlydean
oneandonlydean force-pushed the fix/raise-address-lock-1488 branch from cb270d5 to ba231e9 Compare October 14, 2026 09:43
@oneandonlydean
oneandonlydean force-pushed the fix/raise-address-lock-1488 branch from ba231e9 to 5281378 Compare October 14, 2026 10:03
@oneandonlydean
oneandonlydean marked this pull request as ready for review October 14, 2026 10:38
@oneandonlydean
oneandonlydean merged commit ed835aa into boykopovar:main Oct 14, 2026
9 checks passed
@mugurc mugurc mentioned this pull request Oct 14, 2026
imellag added a commit to imellag/AnyPS5 that referenced this pull request Oct 24, 2026
…ck on Linux and macOS

On Linux and macOS, sceKernelRaiseException runs the guest handler in
the target's SIGUSR1 handler wherever the thread was interrupted. A
thread interrupted while it held the address-wait lock of
sceKernelSyncOnAddressWait, sceKernelSyncOnAddressWake or _umtx_op ran
the handler with the lock held, so a handler that waits, as a
stop-the-world collector's does, blocked every other thread that waits
on or wakes an address. boykopovar#5756 fixed this on Windows.

The address-wait lock is now taken under DeliveryDeferral on every
host. On POSIX the deferral is a count per thread: while it is raised
the SIGUSR1 handler only records the signal, and DeliverDeferred sends
it to the thread again once the lock is released. An address waiter
waits on its own semaphore (sem_t, a dispatch semaphore on macOS) with
the lock released and the deferral lifted, so a raise reaches a thread
that waits for an address without a timeout.

The address-lock phases of guest_raise_exception now run on every
host, with a new phase that raises at a thread in an unbounded address
wait.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

sceKernelRaiseException on Windows can stop a thread inside emulator code that holds a host lock

1 participant