From 436db85cf51f72fbe18f098ac28897bca4e83b38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Mon, 28 Sep 2026 17:16:36 -0600 Subject: [PATCH] Reschedule on IO completion, drop the reschedule in time queries The time queries rescheduled since 2013, so that a game spinning on the clock would let a thread that a timing event had woken run (it fixed audio in Crimson Gem Saga and Where Is My Heart?). In 2014 audio and delay wakeups started rescheduling themselves, but IO completion never did, and a movie reader thread in Driver 76 was only getting in through the time queries. Now IO completion dispatches like any other wakeup. The PSP doesn't dispatch in a time query, and doing so let a thread that a terminate woke run too early. threads/threads/terminate now passes. Checked by booting 75 games against master: the same in all of them, with Asphalt Urban GT2 getting further in the same time. Co-Authored-By: Claude Opus 5.5 (1M context) --- Core/HLE/sceIo.cpp | 4 ++++ Core/HLE/sceKernelTime.cpp | 6 ------ test.py | 3 ++- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/Core/HLE/sceIo.cpp b/Core/HLE/sceIo.cpp index f9058bfcae..86e878da39 100644 --- a/Core/HLE/sceIo.cpp +++ b/Core/HLE/sceIo.cpp @@ -474,6 +474,9 @@ static void __IoAsyncNotify(u64 userdata, int cyclesLate) { if (f->closePending) { __IoFreeFd(fd, error); } + // Like any interrupt that wakes a thread, this dispatches right away, so a better + // priority waiter doesn't sit ready until something else happens to reschedule. + __KernelReSchedule("async io completed"); } } @@ -522,6 +525,7 @@ static void __IoSyncNotify(u64 userdata, int cyclesLate) { HLEKernel::ResumeFromWait(threadID, WAITTYPE_IO, fd, result); f->waitingSyncThreads.erase(std::remove(f->waitingSyncThreads.begin(), f->waitingSyncThreads.end(), threadID), f->waitingSyncThreads.end()); + __KernelReSchedule("io completed"); } static void __IoAsyncBeginCallback(SceUID threadID, SceUID prevCallbackId) { diff --git a/Core/HLE/sceKernelTime.cpp b/Core/HLE/sceKernelTime.cpp index b0f8c6e4eb..e6576dd261 100644 --- a/Core/HLE/sceKernelTime.cpp +++ b/Core/HLE/sceKernelTime.cpp @@ -74,7 +74,6 @@ int sceKernelGetSystemTime(u32 sysclockPtr) } VERBOSE_LOG(Log::sceKernel, "sceKernelGetSystemTime(out:%16llx)", t); hleEatCycles(265); - hleReSchedule("system time"); return hleNoLog(0); } @@ -87,7 +86,6 @@ u32 sceKernelGetSystemTimeLow() if (PSP_CoreParameter().compat.flags().KernelGetSystemTimeLowEatMoreCycles) { hleEatCycles(70000); } - hleReSchedule("system time"); return hleNoLog((u32)t); } @@ -96,7 +94,6 @@ u64 sceKernelGetSystemTimeWide() u64 t = CoreTiming::GetGlobalTimeUsScaled(); VERBOSE_LOG(Log::sceKernel,"%i=sceKernelGetSystemTimeWide()",(u32)t); hleEatCycles(250); - hleReSchedule("system time"); return hleNoLog(t); } @@ -153,7 +150,6 @@ u32 sceKernelLibcClock() { u32 retVal = (u32) CoreTiming::GetGlobalTimeUs(); hleEatCycles(330); - hleReSchedule("libc clock"); return hleLogDebug(Log::sceKernel, retVal); } @@ -169,7 +165,6 @@ u32 sceKernelLibcTime(u32 outPtr) else if (outPtr != 0) return 0; - hleReSchedule("libc time"); return hleLogDebug(Log::sceKernel, t); } @@ -185,7 +180,6 @@ u32 sceKernelLibcGettimeofday(u32 timeAddr, u32 tzAddr) DEBUG_LOG(Log::sceKernel,"sceKernelLibcGettimeofday(%08x, %08x)", timeAddr, tzAddr); hleEatCycles(1885); - hleReSchedule("libc timeofday"); return hleNoLog(0); } diff --git a/test.py b/test.py index a839f672a5..0553541591 100755 --- a/test.py +++ b/test.py @@ -405,6 +405,8 @@ tests_good = [ "threads/threads/stackfree", "threads/threads/start", "threads/threads/suspend", + "threads/threads/terminate", + "threads/threads/termsuspended", "threads/threads/threadend", "threads/threads/threadmanidlist", "threads/threads/threadmanidtype", @@ -599,7 +601,6 @@ tests_next = [ "threads/scheduling/dispatch", "threads/scheduling/scheduling", "threads/threads/create", - "threads/threads/terminate", "threads/tls/memory", "threads/vpl/create", "umd/io/umd_io",