From 638da935ea4612698b0ed4f778071e0af55bfbd8 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] Threads: sceKernelStartThread hands the CPU straight to a better thread When the new thread outranks the caller, the firmware switches to it directly, even if a thread of still better priority is ready but hasn't been dispatched (one that a sceKernelTerminateThread woke, say). Verified against the new pspautotests threads/threads/termsuspended. Co-Authored-By: Claude Opus 5.5 (1M context) --- Core/HLE/sceKernelThread.cpp | 23 ++++++++++++++++++++++- pspautotests | 2 +- 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/Core/HLE/sceKernelThread.cpp b/Core/HLE/sceKernelThread.cpp index b4132b0f9a..8cd668313b 100644 --- a/Core/HLE/sceKernelThread.cpp +++ b/Core/HLE/sceKernelThread.cpp @@ -1629,8 +1629,17 @@ static PSPThread *__KernelNextThread() { return 0; } +// Set by sceKernelStartThread when the new thread outranks the caller, for the reschedule right +// after it. The firmware hands the CPU straight to the new thread, even when a thread of better +// priority is ready but hasn't been dispatched (say, one that a sceKernelTerminateThread woke.) +// See tests/threads/threads/termsuspended. +static SceUID g_startThreadHandoff = 0; + void __KernelReSchedule(const char *reason) { + const SceUID handoff = g_startThreadHandoff; + g_startThreadHandoff = 0; + // First, let's check if there are any pending callbacks to trigger. // TODO: Could probably take this out of __KernelReSchedule() which is a bit hot. __KernelCheckCallbacks(); @@ -1643,6 +1652,16 @@ void __KernelReSchedule(const char *reason) return; } + if (handoff != 0) { + u32 error; + PSPThread *started = kernelObjects.Get(handoff, error); + if (started && started->nt.status == THREADSTATUS_READY) { + threadReadyQueue.remove(started->nt.currentPriority, handoff); + __KernelSwitchContext(started, reason); + return; + } + } + PSPThread *nextThread = __KernelNextThread(); if (nextThread) { __KernelSwitchContext(nextThread, reason); @@ -1971,8 +1990,10 @@ int __KernelStartThread(SceUID threadToStartID, int argSize, u32 argBlockPtr, bo if (cur && cur->nt.currentPriority > startThread->nt.currentPriority) { KernelValidateThreadTarget(startThread->context.pc); __KernelChangeReadyState(cur, currentThread, true); - if (__InterruptsEnabled()) + if (__InterruptsEnabled()) { + g_startThreadHandoff = threadToStartID; hleReSchedule("thread started"); + } } // Starting a thread automatically resumes the dispatch thread if the new thread has worse priority. diff --git a/pspautotests b/pspautotests index fa87488fb7..c67239046d 160000 --- a/pspautotests +++ b/pspautotests @@ -1 +1 @@ -Subproject commit fa87488fb7f034ce006490285297ca290d8d23b7 +Subproject commit c67239046db47d83351ced09e4e66d0cf3382f9c