Skip to content

fix(libkernel): preserve condition signals across cancellation - #6091

Merged
adriadam10 merged 1 commit into
boykopovar:mainfrom
Ondra97:codex/fix-cond-canceled-signal
Oct 18, 2026
Merged

adriadam10 merged 1 commit into
boykopovar:mainfrom
Ondra97:codex/fix-cond-canceled-signal

Conversation

@Ondra97

@Ondra97 Ondra97 commented Oct 14, 2026

Copy link
Copy Markdown
Contributor

What

When a condition signal selects a waiter that is canceled while reacquiring its mutex, that waiter exits without returning the signal. A second waiter can remain blocked indefinitely. On main, the deterministic two-waiter reproduction sends one signal, cancels the selected waiter while the signaler still holds the mutex, and the survivor times out.

Condition wait completion now restores mutex ownership and recursive depth, makes one cancellation decision, and forwards a notification before cancellation cleanup. The host condition state is held separately across the wait: a caller can destroy and free its condition after notification while the awakened waiter is still reacquiring the mutex. The retained owner is explicitly released before guest thread exit, which does not unwind C++ stack objects.

The behavior follows the POSIX condition wait and cancellation contract. Cancellation may produce a spurious wake, which the contract permits. Console cancellation ordering and signal replacement remain unmeasured and are recorded in technical debt.

Tested

Windows 11 Pro build 26300, WinLibs MinGW-w64 GCC 15.2.0 UCRT POSIX SEH r7, Release, BUILD_TESTING=ON, full build-runtime configuration.

  • Main fa539dd9, unchanged production: existing cancellation recovery tests pass. The public-API scratch reproduction leaves the survivor at ETIMEDOUT; the new registered recovery case fails in 2.22 s after the selected waiter exits canceled.
  • Expanded recovery cases cover SCE/POSIX timed and untimed calls with error-checking and three-level recursive mutexes, signal replacement, full-depth cleanup, cancellation disabled, and ordinary completion.
  • A separately mapped guest condition slot is destroyed and unmapped after notification while the waiter is gated on its mutex. Cancellation, normal return and disabled-cancellation paths complete without accessing the freed slot. A weak host reference expires after each join, checking that thread exit did not abandon an owner.
  • The predicate-based setup permits spurious or multiple wakes. Fine and coarse recovery variants each pass five repetitions locally (0.35 s total), without increasing timeouts.
  • Full Windows build passed before and after rebase onto f59d91ffc. All 19 targeted synchronization/runtime and patched-library tests passed without skips; the final run completed in 0.58 s. Conventions, whitespace, test registration, the repository overlap checker and two independent source/test reviews passed.
  • I manually compared the exact shared source and tests, including older conflicting PRs: 19 large AOT patches only preserve errno around sleep; fix(shader): accept TYPE=0 images during materialization #5473 inherits identical relevant blobs; test: organize library tests and label CTest suites #6044 only relocates unchanged tests; fix(libkernel): answer a second scePthreadJoin with ESRCH #4481/fix(libkernel): keep a finished Windows thread's stack until it is joined or detached #4563 concern join and stack lifetime. PthreadCondPrivate is unchanged in all 23 shared header PRs. No other open PR implements this fix.
  • Retail games, Linux, ASan and PS5 hardware were not tested. No title unblock or measured console timing is claimed.

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
  • GPUs tested: n/a (kernel condition variables only)
  • AI-assisted: yes (Codex); I reviewed the change and ran the local validation.

@github-actions

github-actions Bot commented Oct 15, 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
PR 6244 core/libs/prx/libkernel/Pthread/src/Cond.cpp L43-48, L76-77, L118, L168, L173, L179

Some comparisons could not be completed because a pull request conflicts with the base branch. Review these shared files manually before concluding that there is no overlap.

Pull request Shared files not compared
PR 5021 core/libs/prx/libkernel/Pthread/include/Pthread.hpp
docs/dev/debt/libkernel.md
PR 5999 docs/dev/debt/libkernel.md
PR 6044 core/libs/tests/GuestPthreadCancelRecovery.cpp
PR 6222 core/libs/prx/libkernel/Pthread/include/Pthread.hpp
docs/dev/debt/libkernel.md
PR 6510 docs/dev/debt/libkernel.md
PR 6519 docs/dev/debt/libkernel.md
PR 6534 core/libs/prx/libkernel/Pthread/include/Pthread.hpp
docs/dev/debt/libkernel.md
PR 6658 docs/dev/debt/libkernel.md
PR 6908 docs/dev/debt/libkernel.md
PR 6936 docs/dev/debt/libkernel.md
PR 6977 docs/dev/debt/libkernel.md
PR 7049 docs/dev/debt/libkernel.md
PR 7088 docs/dev/debt/libkernel.md
PR 7105 docs/dev/debt/libkernel.md
PR 7149 core/libs/prx/libkernel/Pthread/include/Pthread.hpp
docs/dev/debt/libkernel.md
PR 7192 docs/dev/debt/libkernel.md
PR 7299 docs/dev/debt/libkernel.md
PR 7447 docs/dev/debt/libkernel.md
PR 7464 docs/dev/debt/libkernel.md
PR 7509 docs/dev/debt/libkernel.md
PR 7556 docs/dev/debt/libkernel.md

This was referenced Oct 15, 2026
@adriadam10

Copy link
Copy Markdown
Collaborator

Checked the cancellation path in Cond.cpp (the condition is held by shared_ptr across the wait, the notification is forwarded before scePthreadExit, mutex and recursive depth restored first) and the new recovery cases; guest_pthread_cancel_recovery and the coarse variant pass on Linux with the PR merged onto current main. Merging once the Windows build is green.

@adriadam10
adriadam10 merged commit a0f3dc4 into boykopovar:main Oct 18, 2026
9 checks passed
@Ondra97
Ondra97 deleted the codex/fix-cond-canceled-signal branch October 19, 2026 04:24
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.

2 participants