From 80938529a9e02f134f6db62a2b821b8538754015 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Tue, 29 Sep 2026 15:55:01 -0600 Subject: [PATCH] Kernel waits: Share waiter ordering and clearing between objects Priority-ordered waiting lists were sorted with a comparator wrapper per object (msgpipe, fpl, vpl), or searched with a copy of the same function (mutex, mbx). HLEKernel::SortWaitingThreadsByPriority() and FindBestPriorityWaiter() now do both for any waiting list, of thread ids or of structs with a threadID. HLEKernel::ClearWaitingThreads() replaces the identical cancel/delete loops in semaphores, event flags, fpl and vpl. Co-Authored-By: Claude Opus 5.5 (1M context) --- Core/HLE/KernelWaitHelpers.h | 48 +++++++++++++++++++++++++++++++++ Core/HLE/sceKernelEventFlag.cpp | 14 ++-------- Core/HLE/sceKernelMbx.cpp | 20 +------------- Core/HLE/sceKernelMemory.cpp | 46 +++++-------------------------- Core/HLE/sceKernelMsgPipe.cpp | 7 +---- Core/HLE/sceKernelMutex.cpp | 24 ++--------------- Core/HLE/sceKernelSemaphore.cpp | 17 +++--------- 7 files changed, 64 insertions(+), 112 deletions(-) diff --git a/Core/HLE/KernelWaitHelpers.h b/Core/HLE/KernelWaitHelpers.h index ae7db7815a..eaa7516fac 100644 --- a/Core/HLE/KernelWaitHelpers.h +++ b/Core/HLE/KernelWaitHelpers.h @@ -345,6 +345,54 @@ inline void CleanupWaitingThreads(WaitType waitType, SceUID uid, std::vector waitingThreads.resize(size); } +// Ends every wait on an object with the given result (for cancel and delete), through the object's +// function for releasing one waiter: +// bool Unlock(KO *ko, WaitInfoType &waitingThreadInfo, u32 &error, int result, bool &wokeThreads) +template +inline bool ClearWaitingThreads(KO *ko, int result, UnlockFunc Unlock) { + u32 error; + bool wokeThreads = false; + for (auto &waiting : ko->waitingThreads) { + Unlock(ko, waiting, error, result, wokeThreads); + } + ko->waitingThreads.clear(); + return wokeThreads; +} + +// A waiting list holds either thread ids or structs with a threadID. +inline SceUID WaitingThreadID(const SceUID &threadID) { + return threadID; +} +template +inline SceUID WaitingThreadID(const T &waitInfo) { + return waitInfo.threadID; +} + +// For objects created with the priority attribute: best priority first, and among equals the order +// they started waiting in. +template +inline void SortWaitingThreadsByPriority(std::vector &waitingThreads) { + std::stable_sort(waitingThreads.begin(), waitingThreads.end(), [](const T &a, const T &b) { + return __KernelThreadSortPriority(WaitingThreadID(a), WaitingThreadID(b)); + }); +} + +// The first waiter with the best priority, without reordering the list. +template +inline typename std::vector::iterator FindBestPriorityWaiter(std::vector &waitingThreads) { + _dbg_assert_msg_(!waitingThreads.empty(), "FindBestPriorityWaiter: no threads"); + auto best = waitingThreads.end(); + u32 bestPriority = 0xFFFFFFFF; + for (auto iter = waitingThreads.begin(); iter != waitingThreads.end(); ++iter) { + const u32 priority = __KernelGetThreadPrio(WaitingThreadID(*iter)); + if (priority < bestPriority) { + best = iter; + bestPriority = priority; + } + } + return best; +} + template inline void RemoveWaitingThread(std::vector &waitingThreads, const SceUID threadID) { waitingThreads.erase(std::remove(waitingThreads.begin(), waitingThreads.end(), threadID), waitingThreads.end()); diff --git a/Core/HLE/sceKernelEventFlag.cpp b/Core/HLE/sceKernelEventFlag.cpp index d318078385..2a16dbc1a6 100644 --- a/Core/HLE/sceKernelEventFlag.cpp +++ b/Core/HLE/sceKernelEventFlag.cpp @@ -135,16 +135,6 @@ static bool __KernelUnlockEventFlagForThread(EventFlag *e, EventFlagTh &th, u32 return true; } -static bool __KernelClearEventFlagThreads(EventFlag *e, int reason) { - u32 error; - bool wokeThreads = false; - for (auto &event : e->waitingThreads) - __KernelUnlockEventFlagForThread(e, event, error, reason, wokeThreads); - e->waitingThreads.clear(); - - return wokeThreads; -} - void __KernelEventFlagBeginCallback(SceUID threadID, SceUID prevCallbackId) { auto result = HLEKernel::WaitBeginCallback(threadID, prevCallbackId); if (result == HLEKernel::WAIT_CB_SUCCESS) @@ -206,7 +196,7 @@ u32 sceKernelCancelEventFlag(SceUID uid, u32 pattern, u32 numWaitThreadsPtr) { e->nef.currentPattern = pattern; - if (__KernelClearEventFlagThreads(e, SCE_KERNEL_ERROR_WAIT_CANCEL)) + if (HLEKernel::ClearWaitingThreads(e, SCE_KERNEL_ERROR_WAIT_CANCEL, __KernelUnlockEventFlagForThread)) hleReSchedule("event flag canceled"); hleEatCycles(580); @@ -233,7 +223,7 @@ u32 sceKernelDeleteEventFlag(SceUID uid) { u32 error; EventFlag *e = kernelObjects.Get(uid, error); if (e) { - bool wokeThreads = __KernelClearEventFlagThreads(e, SCE_KERNEL_ERROR_WAIT_DELETE); + bool wokeThreads = HLEKernel::ClearWaitingThreads(e, SCE_KERNEL_ERROR_WAIT_DELETE, __KernelUnlockEventFlagForThread); if (wokeThreads) hleReSchedule("event flag deleted"); diff --git a/Core/HLE/sceKernelMbx.cpp b/Core/HLE/sceKernelMbx.cpp index 469bc0813f..73e5e5809d 100644 --- a/Core/HLE/sceKernelMbx.cpp +++ b/Core/HLE/sceKernelMbx.cpp @@ -249,24 +249,6 @@ void __KernelMbxTimeout(u64 userdata, int cyclesLate) HLEKernel::WaitExecTimeout(threadID); } -static std::vector::iterator __KernelMbxFindPriority(std::vector &waiting) -{ - _dbg_assert_msg_(!waiting.empty(), "__KernelMutexFindPriority: Trying to find best of no threads."); - - std::vector::iterator iter, end, best = waiting.end(); - u32 best_prio = 0xFFFFFFFF; - for (iter = waiting.begin(), end = waiting.end(); iter != end; ++iter) { - u32 iter_prio = __KernelGetThreadPrio(iter->threadID); - if (iter_prio < best_prio) { - best = iter; - best_prio = iter_prio; - } - } - - _dbg_assert_msg_(best != waiting.end(), "__KernelMutexFindPriority: Returning invalid best thread."); - return best; -} - SceUID sceKernelCreateMbx(const char *name, u32 attr, u32 optAddr) { if (!name) @@ -353,7 +335,7 @@ int sceKernelSendMbx(SceUID id, u32 packetAddr) while (!wokeThreads && !m->waitingThreads.empty()) { if ((m->nmb.attr & SCE_KERNEL_MBA_THPRI) != 0) - iter = __KernelMbxFindPriority(m->waitingThreads); + iter = HLEKernel::FindBestPriorityWaiter(m->waitingThreads); else iter = m->waitingThreads.begin(); diff --git a/Core/HLE/sceKernelMemory.cpp b/Core/HLE/sceKernelMemory.cpp index c0d0bca000..84ca204ba1 100644 --- a/Core/HLE/sceKernelMemory.cpp +++ b/Core/HLE/sceKernelMemory.cpp @@ -509,22 +509,6 @@ void __KernelFplEndCallback(SceUID threadID, SceUID prevCallbackId) DEBUG_LOG(Log::sceKernel, "sceKernelAllocateFplCB: Resuming mbx wait from callback"); } -static bool __FplThreadSortPriority(FplWaitingThread thread1, FplWaitingThread thread2) -{ - return __KernelThreadSortPriority(thread1.threadID, thread2.threadID); -} - -static bool __KernelClearFplThreads(FPL *fpl, int reason) -{ - u32 error; - bool wokeThreads = false; - for (auto iter = fpl->waitingThreads.begin(), end = fpl->waitingThreads.end(); iter != end; ++iter) - __KernelUnlockFplForThread(fpl, *iter, error, reason, wokeThreads); - fpl->waitingThreads.clear(); - - return wokeThreads; -} - static void __KernelSortFplThreads(FPL *fpl) { // Remove any that are no longer waiting. @@ -532,7 +516,7 @@ static void __KernelSortFplThreads(FPL *fpl) HLEKernel::CleanupWaitingThreads(WAITTYPE_FPL, uid, fpl->waitingThreads); if ((fpl->nf.attr & PSP_FPL_ATTR_PRIORITY) != 0) - std::stable_sort(fpl->waitingThreads.begin(), fpl->waitingThreads.end(), __FplThreadSortPriority); + HLEKernel::SortWaitingThreadsByPriority(fpl->waitingThreads); } int sceKernelCreateFpl(const char *name, u32 mpid, u32 attr, u32 blockSize, u32 numBlocks, u32 optPtr) { @@ -616,7 +600,7 @@ int sceKernelDeleteFpl(SceUID uid) return hleLogDebug(Log::sceKernel, error, "invalid fpl"); } - bool wokeThreads = __KernelClearFplThreads(fpl, SCE_KERNEL_ERROR_WAIT_DELETE); + bool wokeThreads = HLEKernel::ClearWaitingThreads(fpl, SCE_KERNEL_ERROR_WAIT_DELETE, __KernelUnlockFplForThread); if (wokeThreads) hleReSchedule("fpl deleted"); @@ -758,7 +742,7 @@ int sceKernelCancelFpl(SceUID uid, u32 numWaitThreadsPtr) { if (Memory::IsValid4AlignedAddress(numWaitThreadsPtr)) { Memory::WriteUnchecked_U32(fpl->nf.numWaitThreads, numWaitThreadsPtr); } - bool wokeThreads = __KernelClearFplThreads(fpl, SCE_KERNEL_ERROR_WAIT_CANCEL); + bool wokeThreads = HLEKernel::ClearWaitingThreads(fpl, SCE_KERNEL_ERROR_WAIT_CANCEL, __KernelUnlockFplForThread); if (wokeThreads) hleReSchedule("fpl canceled"); return hleLogDebug(Log::sceKernel, 0); @@ -1286,22 +1270,6 @@ void __KernelVplEndCallback(SceUID threadID, SceUID prevCallbackId) DEBUG_LOG(Log::sceKernel, "sceKernelAllocateVplCB: Resuming mbx wait from callback"); } -static bool __VplThreadSortPriority(VplWaitingThread thread1, VplWaitingThread thread2) -{ - return __KernelThreadSortPriority(thread1.threadID, thread2.threadID); -} - -static bool __KernelClearVplThreads(VPL *vpl, int reason) -{ - u32 error; - bool wokeThreads = false; - for (auto iter = vpl->waitingThreads.begin(), end = vpl->waitingThreads.end(); iter != end; ++iter) - __KernelUnlockVplForThread(vpl, *iter, error, reason, wokeThreads); - vpl->waitingThreads.clear(); - - return wokeThreads; -} - static void __KernelSortVplThreads(VPL *vpl) { // Remove any that are no longer waiting. @@ -1309,7 +1277,7 @@ static void __KernelSortVplThreads(VPL *vpl) HLEKernel::CleanupWaitingThreads(WAITTYPE_VPL, uid, vpl->waitingThreads); if ((vpl->nv.attr & PSP_VPL_ATTR_PRIORITY) != 0) - std::stable_sort(vpl->waitingThreads.begin(), vpl->waitingThreads.end(), __VplThreadSortPriority); + HLEKernel::SortWaitingThreadsByPriority(vpl->waitingThreads); } SceUID sceKernelCreateVpl(const char *name, int partition, u32 attr, u32 vplSize, u32 optPtr) { @@ -1383,7 +1351,7 @@ int sceKernelDeleteVpl(SceUID uid) { return hleLogError(Log::sceKernel, error); } else { DEBUG_LOG(Log::sceKernel, "sceKernelDeleteVpl(%i)", uid); - bool wokeThreads = __KernelClearVplThreads(vpl, SCE_KERNEL_ERROR_WAIT_DELETE); + bool wokeThreads = HLEKernel::ClearWaitingThreads(vpl, SCE_KERNEL_ERROR_WAIT_DELETE, __KernelUnlockVplForThread); if (wokeThreads) hleReSchedule("vpl deleted"); @@ -1588,7 +1556,7 @@ int sceKernelCancelVpl(SceUID uid, u32 numWaitThreadsPtr) if (Memory::IsValid4AlignedAddress(numWaitThreadsPtr)) Memory::WriteUnchecked_U32(vpl->nv.numWaitThreads, numWaitThreadsPtr); - bool wokeThreads = __KernelClearVplThreads(vpl, SCE_KERNEL_ERROR_WAIT_CANCEL); + bool wokeThreads = HLEKernel::ClearWaitingThreads(vpl, SCE_KERNEL_ERROR_WAIT_CANCEL, __KernelUnlockVplForThread); if (wokeThreads) hleReSchedule("vpl canceled"); @@ -1750,7 +1718,7 @@ static void __KernelSortTlsplThreads(TLSPL *tls) HLEKernel::CleanupWaitingThreads(WAITTYPE_TLSPL, uid, tls->waitingThreads); if ((tls->ntls.attr & PSP_FPL_ATTR_PRIORITY) != 0) - std::stable_sort(tls->waitingThreads.begin(), tls->waitingThreads.end(), __KernelThreadSortPriority); + HLEKernel::SortWaitingThreadsByPriority(tls->waitingThreads); } int __KernelFreeTls(TLSPL *tls, SceUID threadID) diff --git a/Core/HLE/sceKernelMsgPipe.cpp b/Core/HLE/sceKernelMsgPipe.cpp index 375b404dfa..c5939b98db 100644 --- a/Core/HLE/sceKernelMsgPipe.cpp +++ b/Core/HLE/sceKernelMsgPipe.cpp @@ -50,11 +50,6 @@ static const u32 MSGPIPE_WAIT_VALUE_RECV = 1; // NativeMsgPipe/MsgPipeWaitingThread/MsgPipe itself now live in sceKernelMsgPipe.h - see the // comment on the class there for why. -static bool __KernelMsgPipeThreadSortPriority(const MsgPipeWaitingThread &thread1, const MsgPipeWaitingThread &thread2) -{ - return __KernelThreadSortPriority(thread1.threadID, thread2.threadID); -} - bool MsgPipeWaitingThread::IsStillWaiting(SceUID waitID) const { return HLEKernel::VerifyWait(threadID, WAITTYPE_MSGPIPE, waitID); @@ -213,7 +208,7 @@ void MsgPipe::SortThreads(std::vector &waitingThreads, boo HLEKernel::CleanupWaitingThreads(WAITTYPE_MSGPIPE, GetUID(), waitingThreads); if (usePrio) - std::stable_sort(waitingThreads.begin(), waitingThreads.end(), __KernelMsgPipeThreadSortPriority); + HLEKernel::SortWaitingThreadsByPriority(waitingThreads); } void MsgPipe::SortReceiveThreads() diff --git a/Core/HLE/sceKernelMutex.cpp b/Core/HLE/sceKernelMutex.cpp index 39ffc834b9..0907128605 100644 --- a/Core/HLE/sceKernelMutex.cpp +++ b/Core/HLE/sceKernelMutex.cpp @@ -199,26 +199,6 @@ static void __KernelMutexEraseLock(PSPMutex *mutex) { mutex->nm.lockThread = -1; } -static std::vector::iterator __KernelMutexFindPriority(std::vector &waiting) -{ - _dbg_assert_msg_(!waiting.empty(), "__KernelMutexFindPriority: Trying to find best of no threads."); - - std::vector::iterator iter, end, best = waiting.end(); - u32 best_prio = 0xFFFFFFFF; - for (iter = waiting.begin(), end = waiting.end(); iter != end; ++iter) - { - u32 iter_prio = __KernelGetThreadPrio(*iter); - if (iter_prio < best_prio) - { - best = iter; - best_prio = iter_prio; - } - } - - _dbg_assert_msg_(best != waiting.end(), "__KernelMutexFindPriority: Returning invalid best thread."); - return best; -} - static bool __KernelUnlockMutexForThread(PSPMutex *mutex, SceUID threadID, u32 &error, int result) { if (!HLEKernel::VerifyWait(threadID, WAITTYPE_MUTEX, mutex->GetUID())) return false; @@ -379,7 +359,7 @@ static bool __KernelUnlockMutex(PSPMutex *mutex, u32 &error) { while (!wokeThreads && !mutex->waitingThreads.empty()) { if ((mutex->nm.attr & PSP_MUTEX_ATTR_PRIORITY) != 0) - iter = __KernelMutexFindPriority(mutex->waitingThreads); + iter = HLEKernel::FindBestPriorityWaiter(mutex->waitingThreads); else iter = mutex->waitingThreads.begin(); @@ -770,7 +750,7 @@ bool __KernelUnlockLwMutex(T workarea, u32 &error) { while (!wokeThreads && !mutex->waitingThreads.empty()) { if ((mutex->nm.attr & PSP_MUTEX_ATTR_PRIORITY) != 0) - iter = __KernelMutexFindPriority(mutex->waitingThreads); + iter = HLEKernel::FindBestPriorityWaiter(mutex->waitingThreads); else iter = mutex->waitingThreads.begin(); diff --git a/Core/HLE/sceKernelSemaphore.cpp b/Core/HLE/sceKernelSemaphore.cpp index 3e814d03d0..78134a89f5 100644 --- a/Core/HLE/sceKernelSemaphore.cpp +++ b/Core/HLE/sceKernelSemaphore.cpp @@ -119,17 +119,6 @@ void __KernelSemaEndCallback(SceUID threadID, SceUID prevCallbackId) // Resume all waiting threads (for delete / cancel.) // Returns true if it woke any threads. -static bool __KernelClearSemaThreads(PSPSemaphore *s, int reason) { - u32 error; - bool wokeThreads = false; - std::vector::iterator iter, end; - for (iter = s->waitingThreads.begin(), end = s->waitingThreads.end(); iter != end; ++iter) - __KernelUnlockSemaForThread(s, *iter, error, reason, wokeThreads); - s->waitingThreads.clear(); - - return wokeThreads; -} - int sceKernelCancelSema(SceUID id, int newCount, u32 numWaitThreadsPtr) { u32 error; @@ -152,7 +141,7 @@ int sceKernelCancelSema(SceUID id, int newCount, u32 numWaitThreadsPtr) else s->ns.currentCount = newCount; - if (__KernelClearSemaThreads(s, SCE_KERNEL_ERROR_WAIT_CANCEL)) + if (HLEKernel::ClearWaitingThreads(s, SCE_KERNEL_ERROR_WAIT_CANCEL, __KernelUnlockSemaForThread)) hleReSchedule("semaphore canceled"); return hleNoLog(0); @@ -203,7 +192,7 @@ int sceKernelDeleteSema(SceUID id) { } else { DEBUG_LOG(Log::sceKernel, "sceKernelDeleteSema(%i)", id); - bool wokeThreads = __KernelClearSemaThreads(s, SCE_KERNEL_ERROR_WAIT_DELETE); + bool wokeThreads = HLEKernel::ClearWaitingThreads(s, SCE_KERNEL_ERROR_WAIT_DELETE, __KernelUnlockSemaForThread); if (wokeThreads) hleReSchedule("semaphore deleted"); @@ -252,7 +241,7 @@ int sceKernelSignalSema(SceUID id, int signal) { s->ns.currentCount += signal; if ((s->ns.attr & PSP_SEMA_ATTR_PRIORITY) != 0) - std::stable_sort(s->waitingThreads.begin(), s->waitingThreads.end(), __KernelThreadSortPriority); + HLEKernel::SortWaitingThreadsByPriority(s->waitingThreads); bool wokeThreads = false; retry: