diff --git a/core/libs/prx/libkernel/CMakeLists.txt b/core/libs/prx/libkernel/CMakeLists.txt index 5e507ed890..e12d5a987f 100644 --- a/core/libs/prx/libkernel/CMakeLists.txt +++ b/core/libs/prx/libkernel/CMakeLists.txt @@ -2052,4 +2052,15 @@ if(BUILD_TESTING) endif() endforeach() endforeach() + foreach(sleepApi IN ITEMS sce_usleep sce_nanosleep sce_sleep) + set(sleepTest guest_sleep_cancel_${sleepApi}_deferred) + add_test(NAME ${sleepTest} COMMAND guest_sleep_cancel_tests deferred ${sleepApi}) + add_test(NAME ${sleepTest}_coarse COMMAND guest_sleep_cancel_tests deferred ${sleepApi}) + set_tests_properties(${sleepTest} ${sleepTest}_coarse PROPERTIES TIMEOUT 20) + set_tests_properties(${sleepTest}_coarse PROPERTIES ENVIRONMENT "APS5_COARSE_TIMED_WAITS=1") + if(WIN32) + set_tests_properties(${sleepTest} ${sleepTest}_coarse PROPERTIES + ENVIRONMENT_MODIFICATION "PATH=path_list_prepend:$") + endif() + endforeach() endif() diff --git a/core/libs/prx/libkernel/Time/TimedWait.cpp b/core/libs/prx/libkernel/Time/TimedWait.cpp index bb5ba2cdca..b60cfb995d 100644 --- a/core/libs/prx/libkernel/Time/TimedWait.cpp +++ b/core/libs/prx/libkernel/Time/TimedWait.cpp @@ -619,6 +619,11 @@ void SleepNanos(std::uint64_t nanos) { } SleepUntil(NowNanos() + nanos); #else + struct SleepRegistration { + std::atomic* state = WaitState().bound; + SleepRegistration() { state->fetch_add(1, std::memory_order_seq_cst); } + ~SleepRegistration() { state->fetch_sub(1, std::memory_order_seq_cst); } + } registration; struct timespec req{}; req.tv_sec = static_cast(nanos / 1000000000ULL); req.tv_nsec = static_cast(nanos % 1000000000ULL); diff --git a/core/libs/tests/GuestSleepCancel.cpp b/core/libs/tests/GuestSleepCancel.cpp index 3b3ea39e9f..11daf21aac 100644 --- a/core/libs/tests/GuestSleepCancel.cpp +++ b/core/libs/tests/GuestSleepCancel.cpp @@ -39,9 +39,11 @@ static bool pending = false; static unsigned seconds = LongSleep; static std::string_view api = "sleep"; static unsigned cleanupCalls = 0; +static std::chrono::steady_clock::time_point sleepStart{}; static std::chrono::steady_clock::duration elapsed{}; static void APS5_VABI Cleanup(void*) { + elapsed = std::chrono::steady_clock::now() - sleepStart; ++cleanupCalls; } @@ -64,9 +66,9 @@ static void* APS5_VABI Sleeper(void*) { ready.store(true); ready.notify_all(); if (pending) release.wait(false); - const auto start = std::chrono::steady_clock::now(); + sleepStart = std::chrono::steady_clock::now(); Require(Sleep() == 0); - elapsed = std::chrono::steady_clock::now() - start; + elapsed = std::chrono::steady_clock::now() - sleepStart; returned.store(true); if (disabled) { Require(scePthreadSetcancelstate(0, nullptr) == 0); @@ -86,13 +88,15 @@ static bool WaitForCompletion(Pthread thread) { return true; } -static bool WaitForSleep(Pthread thread) { +static bool WaitForSleep(Pthread thread, bool hostSleep) { const auto limit = std::chrono::steady_clock::now() + ObservationLimit; for (;;) { { std::lock_guard lock(thread->cancelLock); if (thread->cancelWait != nullptr) return true; } + if (hostSleep && thread->waitCount.load(std::memory_order_seq_cst) > 0) return true; + if (thread->_finished.load(std::memory_order_acquire)) return false; if (std::chrono::steady_clock::now() >= limit) return false; std::this_thread::yield(); } @@ -106,24 +110,33 @@ int main(int count, char** arguments) { const bool zero = mode == "pending"; pending = zero || disabled || mode == "pending_long"; const bool normal = mode == "normal"; - Require(mode == "blocked" || disabled || pending || normal); - if (disabled || normal) seconds = 1; + const bool deferred = mode == "deferred"; + Require(mode == "blocked" || disabled || pending || normal || deferred); + Require(!deferred || api == "sce_usleep" || api == "sce_nanosleep" || api == "sce_sleep"); + if (disabled || normal || deferred) seconds = 1; if (zero) seconds = 0; Pthread thread = nullptr; Require(scePthreadCreate(&thread, nullptr, Sleeper, nullptr, nullptr) == 0); - ready.wait(false); + const auto readyLimit = std::chrono::steady_clock::now() + ObservationLimit; + while (!ready.load()) { + Require(std::chrono::steady_clock::now() < readyLimit); + std::this_thread::yield(); + } bool wasSleeping = false; if (!normal) { - if (!pending) wasSleeping = WaitForSleep(thread); + if (!pending) { + wasSleeping = WaitForSleep(thread, deferred); + Require(wasSleeping); + } Require(scePthreadCancel(thread) == 0); release.store(true); release.notify_all(); } const bool completed = WaitForCompletion(thread); - void* result = nullptr; - Require(scePthreadJoin(thread, &result) == 0); if (!completed) std::fputs("sleep stayed blocked after cancellation\n", stderr); Require(completed); + void* result = nullptr; + Require(scePthreadJoin(thread, &result) == 0); if (normal) { Require(result == nullptr && returned.load()); Require(cleanupCalls == 0); @@ -133,5 +146,5 @@ int main(int count, char** arguments) { Require(returned.load() == disabled); if (!pending) Require(wasSleeping); } - if (normal || disabled) Require(elapsed >= std::chrono::seconds(1)); + if (normal || disabled || deferred) Require(elapsed >= std::chrono::seconds(1)); }