Skip to content

fix(libkernel): release every level of a recursive mutex during a cond wait - #1480

Merged
oneandonlydean merged 1 commit into
boykopovar:mainfrom
awake-devel:fix/cond-wait-recursive-mutex
Oct 9, 2026
Merged

oneandonlydean merged 1 commit into
boykopovar:mainfrom
awake-devel:fix/cond-wait-recursive-mutex

Conversation

@awake-devel

Copy link
Copy Markdown
Contributor

What

A condition wait on a recursive mutex that the waiter has locked more than once deadlocks. waitUntil in Pthread/src/Cond.cpp cleared _count and _owner, but the condition variable unlocks the native std::recursive_timed_mutex only once. Every scePthreadMutexLock on a recursive mutex locks the native mutex again, so after N locks the waiter still holds N-1 levels during the wait. The thread that should take the mutex and signal blocks in scePthreadMutexLock, and the waiter never wakes (an untimed wait hangs; a timed wait times out forever).

FreeBSD libthr, which the PS5 libkernel is based on, releases the mutex completely for the wait and restores the recursion count afterwards: _mutex_cv_unlock / _mutex_cv_detach save m_count, set it to 0 and unlock, and _mutex_cv_lock / _mutex_cv_attach put it back (thr_mutex.c, thr_cond.c cond_wait_kernel / cond_wait_user).

The fix unlocks the remaining N-1 native levels before the wait and locks them again after it. This covers scePthreadCondWait, scePthreadCondTimedwait, pthread_cond_wait and pthread_cond_timedwait, which all go through waitUntil. The FreeBSD behaviour is recorded in TechnicalDebt as unverified on hardware.

guest_cond_recursive_mutex locks a recursive mutex twice and waits with each of the four functions while another thread locks the mutex and signals. After the wake the waiter unlocks twice and a third unlock returns SCE_KERNEL_ERROR_EPERM.

This PR was investigated and written by Claude Code (AI), and posted by me after review.

Tested

Arch Linux, GCC, Release, -DBUILD_TESTING=ON, APS5_WRITE_WATCH_IMPORTS=watch (Intel UHD 630 + GTX 1050 Ti).

  • guest_cond_recursive_mutex: passes; with only the Cond.cpp change reverted it fails (ctest timeout, the signalling thread never gets the mutex).
  • Full ctest -j4: 424/424 passed, 0 skipped, 0 failed (main bc1fbe8: 423/423, 0 skipped; the new test is the only difference).
  • Full suite under VK_LAYER_KHRONOS_validation: 424/424 passed, 0 skipped; 27 Validation Error messages with the same VUIDs and counts as main (52 VUID-vkDestroyDevice-device-05137, 2 VUID-VkPipelineColorBlendStateCreateInfo-pAttachments-00605).
  • tools/check_conventions.py: passes.

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 in TechnicalDebt
  • 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
  • AI-assisted: yes

🤖 Generated with Claude Code

https://claude.ai/code/session_01HeC9bA3n74TzJRYv9AgBE8

…d wait

A condition wait on a recursive mutex locked more than once unlocked the
native mutex only once, so the thread that should signal blocked forever
on it. Unlock the remaining levels before the wait and lock them again
after it, as FreeBSD libthr does in _mutex_cv_unlock and _mutex_cv_lock.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HeC9bA3n74TzJRYv9AgBE8
@oneandonlydean

Copy link
Copy Markdown
Collaborator

Could you rebase onto current main? The full build checks out this branch with main's workflow, and it stops at tools/check_test_registration.py not being on the branch yet, so nothing gets compiled. After a rebase it can get a full build and go in.

@popcoder565

Copy link
Copy Markdown
Contributor

Native Windows run, since no Windows results were posted. Setup: Windows 10 (19045), MinGW-w64 GCC 15.2.0 (WinLibs r7), Release. Head 7406c975, merged onto main d70b899 in one build with 38 other PRs that had no Windows run.

No title was run.

🤖 Addressed by Claude Code

@oneandonlydean
oneandonlydean merged commit 0c8d535 into boykopovar:main Oct 9, 2026
4 checks passed
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.

3 participants