From acd2738b7a2599a3d9d5a77f4f0dab95b98b0c96 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Mon, 28 Sep 2026 14:52:45 -0600 Subject: [PATCH] Callbacks: Deliver to other threads by priority, fix sceKernelCancelCallback Verified against pspautotests threads/callbacks/delivery: - Notifying the callback of a better priority thread in a CB wait runs it right away. Callbacks of other waiting threads stay pending until those threads would get to run, rather than being taken at any reschedule, so they can still be counted or canceled. - sceKernelCancelCallback clears the notify count, not just the arg. threads/callbacks/cancel, count and umd/wait/wait now pass. Co-Authored-By: Claude Opus 5.5 (1M context) --- Core/HLE/sceKernelThread.cpp | 18 ++++++++++++++++++ pspautotests | 2 +- test.py | 7 ++++--- 3 files changed, 23 insertions(+), 4 deletions(-) diff --git a/Core/HLE/sceKernelThread.cpp b/Core/HLE/sceKernelThread.cpp index 6a4f885295..0a7636494b 100644 --- a/Core/HLE/sceKernelThread.cpp +++ b/Core/HLE/sceKernelThread.cpp @@ -2774,6 +2774,11 @@ int sceKernelNotifyCallback(SceUID cbId, int notifyArg) { PSPCallback *cb = kernelObjects.Get(cbId, error); if (cb) { __KernelNotifyCallback(cbId, notifyArg); + // A better priority thread in a CB wait runs the callback right away. + PSPThread *t = kernelObjects.Get(cb->nc.threadId, error); + if (t && t->GetUID() != currentThread && t->isWaiting() && t->isProcessingCallbacks) { + hleReSchedule("callback notified"); + } return hleLogDebug(Log::sceKernel, 0); } else { return hleLogError(Log::sceKernel, error, "bad cbId"); @@ -2785,6 +2790,10 @@ int sceKernelCancelCallback(SceUID cbId) { PSPCallback *cb = kernelObjects.Get(cbId, error); if (cb) { // This just resets the notify count. + if (cb->nc.notifyCount != 0) { + readyCallbacksCount--; + } + cb->nc.notifyCount = 0; cb->nc.notifyArg = 0; return hleLogDebug(Log::sceKernel, 0); } else { @@ -3439,9 +3448,18 @@ bool __KernelCheckCallbacks() { bool processed = false; + // A waiting thread takes its callbacks when it would get to run. While the current thread + // keeps running, that's only threads of better priority; the rest stay pending (and can + // still be counted or canceled.) + PSPThread *cur = __GetCurrentThread(); + const bool curRunning = cur && cur->isRunning(); + u32 error; for (auto iter = threadqueue.begin(); iter != threadqueue.end(); ++iter) { PSPThread *thread = kernelObjects.Get(*iter, error); + if (thread && curRunning && thread != cur && thread->nt.currentPriority >= cur->nt.currentPriority) { + continue; + } if (thread && __KernelCheckThreadCallbacks(thread, false)) { processed = true; } diff --git a/pspautotests b/pspautotests index 3f41a30b6c..2a58a15610 160000 --- a/pspautotests +++ b/pspautotests @@ -1 +1 @@ -Subproject commit 3f41a30b6c4ac52c9357b0a27de2b9ce6666c37f +Subproject commit 2a58a15610739ee7d0cf95044f9888c399d79d5e diff --git a/test.py b/test.py index 6917c04b5e..b0f26916f4 100755 --- a/test.py +++ b/test.py @@ -311,9 +311,12 @@ tests_good = [ "threads/alarm/set/set", "threads/callbacks/afterwait", "threads/callbacks/callbacks", + "threads/callbacks/cancel", "threads/callbacks/check", + "threads/callbacks/count", "threads/callbacks/create", "threads/callbacks/delete", + "threads/callbacks/delivery", "threads/callbacks/exit", "threads/callbacks/nested", "threads/callbacks/notify", @@ -441,6 +444,7 @@ tests_good = [ "utility/savedata/makedata", "utility/systemparam/systemparam", "umd/callbacks/umd", + "umd/wait/wait", "umd/register", "video/mpeg/ringbuffer/avail", "video/mpeg/ringbuffer/construct", @@ -581,8 +585,6 @@ tests_next = [ "net/primary/ether", "power/freq", "sysmem/partition", - "threads/callbacks/cancel", - "threads/callbacks/count", # These two mbx tests only appeared to work because they papered over bugs @@ -594,7 +596,6 @@ tests_next = [ "threads/vpl/create", "umd/io/umd_io", "umd/raw_access/raw_access", - "umd/wait/wait", "utility/msgdialog/dialog", "utility/savedata/idlist", # These tests appear to be broken and just hang.