From 81b28cef614d3c6e58086b510a37f13dccc24daf Mon Sep 17 00:00:00 2001 From: Th3EvilGod <117441689+Th3EvilGod@users.noreply.github.com> Date: Tue, 20 Oct 2026 03:21:28 +0400 Subject: [PATCH] fix(libc): use precise Windows thread sleep --- core/libs/prx/libc/src/Threads.cpp | 8 ++++++++ core/libs/tests/GuestAmprThreads.cpp | 18 ++++++++++++++++++ docs/dev/debt/libc.md | 2 +- 3 files changed, 27 insertions(+), 1 deletion(-) diff --git a/core/libs/prx/libc/src/Threads.cpp b/core/libs/prx/libc/src/Threads.cpp index 167360e72ca9..cb30fcea94fb 100644 --- a/core/libs/prx/libc/src/Threads.cpp +++ b/core/libs/prx/libc/src/Threads.cpp @@ -1,5 +1,8 @@ #include "prx/libc/include/general/VabiMacros.hpp" #include "prx/libc/include/General.hpp" +#ifdef _WIN32 +#include "prx/libc/include/PreciseWait.hpp" +#endif #include #include #include @@ -291,11 +294,16 @@ int APS5_VABI _Thrd_sleep_nid_postfix(const Xtime* target, Xtime*) { --remaining.tv_sec; } remaining.tv_sec = std::min(remaining.tv_sec, longestSleepSeconds); +#ifdef _WIN32 + PreciseSleepHundredNanos(static_cast(remaining.tv_sec) * 10000000ULL + + (static_cast(remaining.tv_nsec) + 99ULL) / 100ULL); +#else if (nanosleep(&remaining, nullptr) != 0) { if (errno == EINTR) continue; throw std::runtime_error("_Thrd_sleep: nanosleep failed"); } +#endif } } diff --git a/core/libs/tests/GuestAmprThreads.cpp b/core/libs/tests/GuestAmprThreads.cpp index 264ff62a6dff..0c669decd23b 100644 --- a/core/libs/tests/GuestAmprThreads.cpp +++ b/core/libs/tests/GuestAmprThreads.cpp @@ -1,6 +1,7 @@ #include "SceTypes.hpp" #include "prx/libc/include/exceptions/Runtime.hpp" #include "prx/libc/include/general/VabiMacros.hpp" +#include #include #include #include @@ -188,6 +189,23 @@ static void TestThreadSleep() { Require(_Thrd_sleep_nid_postfix(&negative, nullptr) == 0); Require(RealtimeNanos() >= borrowed.sec * NanosPerSecond + borrowed.nsec); +#ifdef _WIN32 + for (const auto duration : {100000LL, 2000000LL}) { + std::array samples{}; + for (auto& sample : samples) { + const auto start = std::chrono::steady_clock::now(); + const auto shortTarget = XtimeAfter(duration); + Require(_Thrd_sleep_nid_postfix(&shortTarget, nullptr) == ThrdSuccess); + sample = std::chrono::steady_clock::now() - start; + Require(RealtimeNanos() >= shortTarget.sec * NanosPerSecond + shortTarget.nsec); + } + std::sort(samples.begin(), samples.end()); + const auto median = samples[samples.size() / 2]; + std::fprintf(stderr, "_Thrd_sleep(%lld ns) median: %lld ns\n", duration, static_cast(median.count())); + Require(median < std::chrono::milliseconds(12)); + } +#endif + bool rejected = false; try { _Thrd_sleep_nid_postfix(nullptr, nullptr); diff --git a/docs/dev/debt/libc.md b/docs/dev/debt/libc.md index ebf631971afb..508d0aa65897 100644 --- a/docs/dev/debt/libc.md +++ b/docs/dev/debt/libc.md @@ -10,7 +10,7 @@ - [_Mtx_trylock](../../../core/libs/prx/libc/src/Threads.cpp) (libc, NID `k6pGNMwJB08`) - returns busy for a non-recursive mutex already owned by the caller, assumed to match `_Mtx_lock`'s ownership handling; exact console behavior is unverified. - [nearbyintf](../../../core/libs/prx/libc/src/Math.cpp) (libc, NID `c+4r-T-tEIc`) uses the host rounding mode, not the console's; console rounding mode state remains unverified. - [__std_find_trivial_unsized_N](../../../core/libs/prx/libc/src/VectorAlgorithms.cpp) (libc) - matches the `libc.prx` bundled with PPSA08427 (disassembly, and that binary run side by side on random inputs): when nothing matches between `first` and the end of its 32-byte-aligned block, the console rescans that whole block without masking and so can return a match up to 31 bytes before `first` -- [_Thrd_sleep](../../../core/libs/prx/libc/src/Threads.cpp) (libc, NID `jfRI3snge3o`) follows the Dinkumware threads library shipped with [Visual C++ 2013](https://github.com/adityaramesh/concurrency_runtime/tree/master/VS2013_SP2/crt/src/thr) (`xtime.c`) and C11 Annex K, without console verification. It reads a 16-byte xtime (64-bit seconds and nanoseconds) on the guest `CLOCK_REALTIME`, normalizes the nanoseconds and sleeps through `nanosleep`, retrying on `EINTR`, so it is a cancellation point; a null xtime or one whose normalization overflows throws. The console's `_Thrd_sleep` (disassembled from a title's `libc.prx`) also takes an optional `xtime*` of remaining time and returns `int`; here the sleep is never cut short, so it returns 0 and leaves the remaining time untouched. +- [_Thrd_sleep](../../../core/libs/prx/libc/src/Threads.cpp) (libc, NID `jfRI3snge3o`) follows the Dinkumware threads library shipped with [Visual C++ 2013](https://github.com/adityaramesh/concurrency_runtime/tree/master/VS2013_SP2/crt/src/thr) (`xtime.c`) and C11 Annex K, without console verification. It reads a 16-byte xtime (64-bit seconds and nanoseconds) on the guest `CLOCK_REALTIME`, normalizes the nanoseconds and sleeps through `nanosleep` on POSIX, retrying on `EINTR`, or high-resolution waitable timers on Windows; a null xtime or one whose normalization overflows throws. Guest thread cancellation is not integrated with this wait. The console's `_Thrd_sleep` (disassembled from a title's `libc.prx`) also takes an optional `xtime*` of remaining time and returns `int`; here the sleep is never cut short, so it returns 0 and leaves the remaining time untouched. - [_Mtx_init_with_name](../../../core/libs/prx/libc/src/Threads.cpp) (libc, NID `tgioGpKtmbE`) forwards to `_Mtx_init` and ignores the mutex name parameter. - [_Cnd_timedwait](../../../core/libs/prx/libc/src/Threads.cpp) (libc, NID `McaImWKXong`) follows the console's `_Cnd_timedwait` (disassembled from a title's `libc.prx`), where xtime is an absolute deadline converted against the current time: it returns `_Thrd_timedout` (2) on expiration and `_Thrd_success` (0) on notification. The console returns 4 for other internal pthread errors while this implementation throws. - [fegetround, fesetround, fetestexcept, feclearexcept, fegetexceptflag and fesetexceptflag](../../../core/libs/prx/libc/src/FloatingPoint.cpp) (libSceLibcInternal, implemented in libc) follow FreeBSD 11.4 amd64 [fenv.h](https://github.com/freebsd/freebsd-src/blob/4554bee35405b36ea19e9e60d6abe6ea534e36e0/lib/msun/x86/fenv.h): rounding modes use x87 control bits 10-11, exception flags include denormals (`0x3f`), and `fexcept_t` is 16 bits. They access the current host thread's x87 and MXCSR state. This console ABI is assumed, as for `fegetenv`, not measured on hardware. Null flag pointers throw instead of faulting.