Skip to content

fix(libc): keep the _Cnd_timedwait deadline in the host clock's unit - #6580

Merged
oneandonlydean merged 2 commits into
boykopovar:mainfrom
DavidSamir:fix/libc-cnd-timedwait-macos-clock
Oct 17, 2026
Merged

oneandonlydean merged 2 commits into
boykopovar:mainfrom
DavidSamir:fix/libc-cnd-timedwait-macos-clock

Conversation

@DavidSamir

@DavidSamir DavidSamir commented Oct 15, 2026 •

Copy link
Copy Markdown
Contributor

What

libc does not build on macOS: _Cnd_timedwait turns its deadline into a system_clock::time_point from seconds + nanoseconds, a nanosecond duration. On macOS system_clock counts microseconds, so the implicit conversion is lossy and AppleClang rejects it:

core/libs/prx/libc/src/Threads.cpp:248:35: error: no matching conversion for functional-style cast from 'typename common_type<duration<long long, std::ratio<1, 1>>, duration<long long, std::ratio<1, 1000000000>>>::type' ...

The seconds now stay in the clock's unit and only the nanoseconds are rounded up to it (std::chrono::ceil), so the wait never ends before the requested time. With a nanosecond system_clock (Linux, MinGW) the expression is unchanged.

Casting the nanosecond sum instead would compile but is wrong on macOS: the range check above allows deadlines up to the microsecond clock's limit (about 2.9e14 s), while the sum overflows above 2^63 ns (about 9.2e9 s). A deadline of 1e10 s then wraps into the past and the wait times out at once. The new test case waits on exactly that deadline: on a host whose clock holds it, the wait must block until a second thread wakes it; on a nanosecond clock it must throw the overflow error, as for any deadline past the clock's range.

Two commits: the test, then the fix. There are no further real steps for this change.

Tested

macOS 26.7.1, Apple M2, AppleClang 21.0.0, x86-64 build under Rosetta (as the full build selects), Release.

main does not build libc on macOS for other reasons as well, which #4627 addresses, so the test ran in a local tree of main ce991f1 with #4627, #4630 and #6124 merged. The branch itself is based on e16b2bc and merges with it cleanly. guest_libc_cnd_timed_wait_tests links only libc.

  • main's Threads.cpp: libc fails to compile with the error above.
  • Casting the nanosecond sum (time_point_cast of a sys_time<nanoseconds>): builds; the new case aborts, 3 runs of 3.
  • This branch: builds with no new warnings; guest_libc_cnd_timed_wait_tests passes 5 runs of 5, the existing cases included.
  • tools/check_conventions.py --base origin/main and git diff --check pass.

Linux and Windows, run by others on 2854da2 (thanks): jotitadev (Ubuntu 26.04, GCC 15.2) and coltken95 (Linux x86-64, GCC) and a WSL2 Ubuntu 24.04 run (GCC 13.3) pass guest_libc_cnd_timed_wait; johnmaia (Windows 11, WinLibs GCC 15.2.0) passes it and the full suite. With a nanosecond system_clock the new case takes the overflow branch, so it also passes with main's Threads.cpp there, as expected.

Overlap: yabanana's AOT pull requests (#1835 and #3891, #4156, #4396, #4397, #4422) carry the same ceil conversion inside their Apple ARM64 work. This lands it on its own with a test. @yabanana, if you'd rather keep it in your series, say so and I'll close this one. #6222 converts the nanosecond sum, which overflows as described above. #6044 moves GuestLibcCndTimedWait.cpp; whichever lands second rebases.

Checklist

  • Based on current main; no other open PR implements the same functions (see Overlap above)
  • 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 (every title on macOS needs libc to build)
  • GPUs tested: n/a
  • AI-assisted: yes

…ange

A deadline of 10^10 seconds does not fit a nanosecond count but fits the
host clock where system_clock counts microseconds, as on macOS. There the
wait must block until it is woken; where the clock cannot hold the
deadline, _Cnd_timedwait must throw the overflow error as for any other
deadline past the clock's range.
The deadline was built as seconds plus nanoseconds, a nanosecond duration,
and converted implicitly to a system_clock time point. Where system_clock
counts microseconds, as on macOS, that conversion is lossy and does not
compile, so libc did not build there.

The seconds now stay in the clock's unit and only the nanoseconds are
rounded up to it, so the wait never ends before the requested time. With
a nanosecond system_clock nothing changes. Converting the nanosecond sum
instead would compile but overflow above 2^63 nanoseconds, which the
clock range check above allows on macOS: a 10^10 second deadline then
wraps into the past and the wait times out at once.
@github-actions

github-actions Bot commented Oct 15, 2026 •

Copy link
Copy Markdown

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 1835 core/libs/prx/libc/src/Threads.cpp
PR 5021 core/libs/prx/libc/src/Threads.cpp
PR 6044 core/libs/tests/GuestLibcCndTimedWait.cpp
PR 6222 core/libs/prx/libc/src/Threads.cpp

@coltken95

Copy link
Copy Markdown

Tested on Linux x86-64 (Release build, GCC/libstdc++), with 2854da2 merged into main 31cbb55:

  • With the PR: guest_libc_cnd_timed_wait passes.
  • With main's libc/src/Threads.cpp it also passes here, as expected: libstdc++'s std::chrono::system_clock::duration is nanoseconds, so the added ceil<system_clock::duration> changes nothing on Linux. The bug needs a coarser system_clock (microseconds on macOS's libc++), so this run only shows the change is harmless on Linux.

AI-assisted: yes (Claude Code)

@jotitadev jotitadev left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Linux run, which the description says is still to come.

Ubuntu 26.04, Intel i7-8700, GCC 15.2 (libstdc++, nanosecond system_clock), Ninja, Release, BUILD_TESTING=ON, full build. main 31cbb55 with head 2854da2 merged on top.

  • With the PR: libc builds with no new warnings; guest_libc_cnd_timed_wait passes 3 runs of 3. With a nanosecond clock, 1e10 s is above the clock's limit (about 9.2e9 s), so the new case takes the std::overflow_error branch, as described.
  • Threads.cpp reverted to main, test kept: it also passes. That is expected here: ceil to nanoseconds is the identity, so the change cannot show up on Linux; the red case is the macOS one in the description.
  • Non-GPU suite (ctest -E "^agc|gpu|vulkan|video_out" -j8): 648/650, same as main on this machine. The 2 failures (file_writer_binary, file_writer_text) also fail on main here.

Written with AI assistance (Claude Code). I ran the builds and tests above on my machine.

@johnmaia

Copy link
Copy Markdown
Contributor

Windows run, since Windows is untested. Windows 11, WinLibs GCC 15.2.0 r7 (MinGW), Release, BUILD_TESTING=ON, RTX 4080, ctest -j4 --timeout 300. Head 2854da2 merged into main 5d9dcc4, plus #6452 so the MinGW test build works.

Not tested: a negative control on Windows.

AI-assisted: yes (Claude Code)

@DavidSamir

Copy link
Copy Markdown
Contributor Author

Linux and Windows results for 2854da2. The merge base is e16b2bc.

Host Toolchain system_clock With the fix Threads.cpp as on e16b2bc, new test kept
Ubuntu 24.04 (WSL2) GCC 13.3.0, Release 1 ns, max 9223372036 s guest_libc_cnd_timed_wait passes 3/3 passes 3/3
Windows 11 WinLibs GCC 15.2.0 r7 (UCRT), Release 1 ns, max 9223372036 s passes 3/3 passes 3/3

On both hosts the new case takes the overflow branch. The 1e10 s deadline is beyond the clock's 9223372036 s, so the test requires std::overflow_error, and _Cnd_timedwait throws it. I printed the clock values with a small program built by the same compilers, using the test's own duration_cast<seconds>(system_clock::duration::max()).

As expected, the test does not tell the fix from the old code on a nanosecond clock. There the deadline expression is the same before and after, so the with/without difference only shows on macOS. After restoring the branch's Threads.cpp, the test passes again (1/1 on Linux).

AI-assisted: yes

@github-actions github-actions Bot added the libc label Oct 16, 2026
SteelCrab added a commit to SteelCrab/AnyPS5 that referenced this pull request Oct 16, 2026
SteelCrab added a commit to SteelCrab/AnyPS5 that referenced this pull request Oct 17, 2026
@oneandonlydean oneandonlydean self-assigned this Oct 17, 2026
@oneandonlydean
oneandonlydean merged commit e09dbc2 into boykopovar:main Oct 17, 2026
16 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants