diff --git a/Core/HLE/KernelWaitHelpers.h b/Core/HLE/KernelWaitHelpers.h index d7bf3ddb41..db7257110f 100644 --- a/Core/HLE/KernelWaitHelpers.h +++ b/Core/HLE/KernelWaitHelpers.h @@ -259,7 +259,17 @@ WaitBeginEndCallbackResult WaitEndCallback(SceUID threadID, SceUID prevCallbackI } // Verify that a thread has not been released from waiting, e.g. by sceKernelReleaseWaitThread(). -inline bool VerifyWait(SceUID threadID, WaitType waitType, SceUID uid) { +// For a waiting thread info struct. +template +inline bool VerifyWait(const T &waitInfo, WaitType waitType, SceUID uid) { + u32 error; + SceUID waitID = __KernelGetWaitID(waitInfo.threadID, waitType, error); + return waitID == uid && error == 0; +} + +// Verify that a thread has not been released from waiting, e.g. by sceKernelReleaseWaitThread(). +template <> +inline bool VerifyWait(const SceUID &threadID, WaitType waitType, SceUID uid) { u32 error; SceUID waitID = __KernelGetWaitID(threadID, waitType, error); return waitID == uid && error == 0; @@ -275,4 +285,26 @@ inline bool ResumeFromWait(SceUID threadID, WaitType waitType, SceUID uid, T res return false; } +// Removes threads that are not waiting anymore from a waitingThreads list. +template +inline void CleanupWaitingThreads(WaitType waitType, SceUID uid, std::vector &waitingThreads) { + size_t size = waitingThreads.size(); + for (size_t i = 0; i < size; ++i) { + if (!VerifyWait(waitingThreads[i], waitType, uid)) { + // Decrement size and swap what was there with i. + if (--size != i) { + std::swap(waitingThreads[i], waitingThreads[size]); + } + // Now we haven't checked the new i, so go back and do i again. + --i; + } + } + waitingThreads.resize(size); +} + +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/sceIo.cpp b/Core/HLE/sceIo.cpp index 12fe6ac838..e253a2e924 100644 --- a/Core/HLE/sceIo.cpp +++ b/Core/HLE/sceIo.cpp @@ -16,6 +16,7 @@ // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. #include +#include #include "native/thread/thread.h" #include "native/thread/threadutil.h" #include "Core/Config.h" @@ -108,6 +109,8 @@ const int PSP_MIN_FD = 4; static int asyncNotifyEvent = -1; static int syncNotifyEvent = -1; static SceUID fds[PSP_COUNT_FDS]; +static std::set memStickCallbacks; +static std::set memStickFatCallbacks; static AsyncIOManager ioManager; static bool ioManagerThreadEnabled = false; static std::thread *ioManagerThread; @@ -466,6 +469,8 @@ void __IoDoState(PointerWrap &p) { CoreTiming::RestoreRegisterEvent(asyncNotifyEvent, "IoAsyncNotify", __IoAsyncNotify); p.Do(syncNotifyEvent); CoreTiming::RestoreRegisterEvent(syncNotifyEvent, "IoSyncNotify", __IoSyncNotify); + p.Do(memStickCallbacks); + p.Do(memStickFatCallbacks); p.DoMarker("sceIo"); } @@ -487,6 +492,9 @@ void __IoShutdown() { memstickSystem = NULL; delete flash0System; flash0System = NULL; + + memStickCallbacks.clear(); + memStickFatCallbacks.clear(); } u32 __IoGetFileHandleFromId(u32 id, u32 &outError) @@ -552,7 +560,7 @@ void __IoCompleteAsyncIO(int fd) { // It's okay, not all operations are deferred. } if (f->callbackID) { - __KernelNotifyCallback(THREAD_CALLBACK_IO, f->callbackID, f->callbackArg); + __KernelNotifyCallback(f->callbackID, f->callbackArg); } f->pendingAsyncResult = false; f->hasAsyncResult = true; @@ -1262,10 +1270,12 @@ u32 sceIoDevctl(const char *name, int cmd, u32 argAddr, int argLen, u32 outPtr, case 0x02015804: // Register MemoryStick's insert/eject callback (mscmhc0) if (Memory::IsValidAddress(argAddr) && argLen == 4) { + // TODO: Verify how duplicates work / how many are allowed. u32 cbId = Memory::Read_U32(argAddr); - if (0 == __KernelRegisterCallback(THREAD_CALLBACK_MEMORYSTICK, cbId)) { + if (memStickCallbacks.find(cbId) == memStickCallbacks.end()) { + memStickCallbacks.insert(cbId); DEBUG_LOG(SCEIO, "sceIoDevCtl: Memstick callback %i registered, notifying immediately.", cbId); - __KernelNotifyCallbackType(THREAD_CALLBACK_MEMORYSTICK, cbId, MemoryStick_State()); + __KernelNotifyCallback(cbId, MemoryStick_State()); return 0; } else { return ERROR_MEMSTICK_DEVCTL_TOO_MANY_CALLBACKS; @@ -1277,8 +1287,10 @@ u32 sceIoDevctl(const char *name, int cmd, u32 argAddr, int argLen, u32 outPtr, case 0x02025805: // Unregister MemoryStick's insert/eject callback (mscmhc0) if (Memory::IsValidAddress(argAddr) && argLen == 4) { + // TODO: Verify how duplicates work / how many are allowed. u32 cbId = Memory::Read_U32(argAddr); - if (0 == __KernelUnregisterCallback(THREAD_CALLBACK_MEMORYSTICK, cbId)) { + if (memStickCallbacks.find(cbId) != memStickCallbacks.end()) { + memStickCallbacks.erase(cbId); DEBUG_LOG(SCEIO, "sceIoDevCtl: Unregistered memstick callback %i", cbId); return 0; } else { @@ -1337,10 +1349,12 @@ u32 sceIoDevctl(const char *name, int cmd, u32 argAddr, int argLen, u32 outPtr, switch (cmd) { case 0x02415821: // MScmRegisterMSInsertEjectCallback { + // TODO: Verify how duplicates work / how many are allowed. u32 cbId = Memory::Read_U32(argAddr); - if (0 == __KernelRegisterCallback(THREAD_CALLBACK_MEMORYSTICK_FAT, cbId)) { + if (memStickFatCallbacks.find(cbId) == memStickFatCallbacks.end()) { + memStickFatCallbacks.insert(cbId); DEBUG_LOG(SCEIO, "sceIoDevCtl: Memstick FAT callback %i registered, notifying immediately.", cbId); - __KernelNotifyCallbackType(THREAD_CALLBACK_MEMORYSTICK_FAT, cbId, MemoryStick_FatState()); + __KernelNotifyCallback(cbId, MemoryStick_FatState()); return 0; } else { return -1; @@ -1350,8 +1364,10 @@ u32 sceIoDevctl(const char *name, int cmd, u32 argAddr, int argLen, u32 outPtr, case 0x02415822: { // MScmUnregisterMSInsertEjectCallback + // TODO: Verify how duplicates work / how many are allowed. u32 cbId = Memory::Read_U32(argAddr); - if (0 == __KernelUnregisterCallback(THREAD_CALLBACK_MEMORYSTICK_FAT, cbId)) { + if (memStickFatCallbacks.find(cbId) != memStickFatCallbacks.end()) { + memStickFatCallbacks.erase(cbId); DEBUG_LOG(SCEIO, "sceIoDevCtl: Unregistered memstick FAT callback %i", cbId); return 0; } else { diff --git a/Core/HLE/sceKernel.cpp b/Core/HLE/sceKernel.cpp index 8696cb5229..6038c70725 100644 --- a/Core/HLE/sceKernel.cpp +++ b/Core/HLE/sceKernel.cpp @@ -86,6 +86,7 @@ static bool kernelRunning = false; KernelObjectPool kernelObjects; KernelStats kernelStats; +// TODO: Savestate this? u32 registeredExitCbId; void __KernelInit() @@ -261,10 +262,8 @@ int LoadExecForUser_362A956B() u32 sceKernelRegisterExitCallback(u32 cbId) { - DEBUG_LOG(SCEKERNEL, "sceKernelRegisterExitCallback(%i)", cbId); - if (__KernelRegisterCallback(THREAD_CALLBACK_EXIT, cbId) == 0) { - registeredExitCbId = cbId; - } + DEBUG_LOG(SCEKERNEL,"sceKernelRegisterExitCallback(%i)", cbId); + registeredExitCbId = cbId; return 0; } diff --git a/Core/HLE/sceKernelEventFlag.cpp b/Core/HLE/sceKernelEventFlag.cpp index 32072cc8f5..b48f7f2acb 100644 --- a/Core/HLE/sceKernelEventFlag.cpp +++ b/Core/HLE/sceKernelEventFlag.cpp @@ -47,6 +47,11 @@ struct EventFlagTh u32 wait; u32 outAddr; u64 pausedTimeout; + + bool operator ==(const SceUID &otherThreadID) const + { + return threadID == otherThreadID; + } }; class EventFlag : public KernelObject @@ -397,19 +402,6 @@ void __KernelSetEventFlagTimeout(EventFlag *e, u32 timeoutPtr) CoreTiming::ScheduleEvent(usToCycles(micro), eventFlagWaitTimer, __KernelGetCurThread()); } -void __KernelEventFlagRemoveThread(EventFlag *e, SceUID threadID) -{ - for (size_t i = 0; i < e->waitingThreads.size(); i++) - { - EventFlagTh *t = &e->waitingThreads[i]; - if (t->threadID == threadID) - { - e->waitingThreads.erase(e->waitingThreads.begin() + i); - break; - } - } -} - int sceKernelWaitEventFlag(SceUID id, u32 bits, u32 wait, u32 outBitsPtr, u32 timeoutPtr) { if ((wait & ~PSP_EVENT_WAITKNOWN) != 0) @@ -439,7 +431,7 @@ int sceKernelWaitEventFlag(SceUID id, u32 bits, u32 wait, u32 outBitsPtr, u32 ti { // If this thread was left in waitingThreads after a timeout, remove it. // Otherwise we might write the outBitsPtr in the wrong place. - __KernelEventFlagRemoveThread(e, __KernelGetCurThread()); + HLEKernel::RemoveWaitingThread(e->waitingThreads, __KernelGetCurThread()); u32 timeout = 0xFFFFFFFF; if (Memory::IsValidAddress(timeoutPtr)) @@ -514,7 +506,7 @@ int sceKernelWaitEventFlagCB(SceUID id, u32 bits, u32 wait, u32 outBitsPtr, u32 { // If this thread was left in waitingThreads after a timeout, remove it. // Otherwise we might write the outBitsPtr in the wrong place. - __KernelEventFlagRemoveThread(e, __KernelGetCurThread()); + HLEKernel::RemoveWaitingThread(e->waitingThreads, __KernelGetCurThread()); u32 timeout = 0xFFFFFFFF; if (Memory::IsValidAddress(timeoutPtr)) @@ -622,12 +614,7 @@ u32 sceKernelReferEventFlagStatus(SceUID id, u32 statusPtr) if (!Memory::IsValidAddress(statusPtr)) return -1; - for (auto iter = e->waitingThreads.begin(); iter != e->waitingThreads.end(); ++iter) - { - // The thread is no longer waiting for this, clean it up. - if (!HLEKernel::VerifyWait(iter->threadID, WAITTYPE_EVENTFLAG, id)) - e->waitingThreads.erase(iter--); - } + HLEKernel::CleanupWaitingThreads(WAITTYPE_EVENTFLAG, id, e->waitingThreads); e->nef.numWaitThreads = (int) e->waitingThreads.size(); if (Memory::Read_U32(statusPtr) != 0) diff --git a/Core/HLE/sceKernelMbx.cpp b/Core/HLE/sceKernelMbx.cpp index 5716803e5f..e8e3e57cc8 100644 --- a/Core/HLE/sceKernelMbx.cpp +++ b/Core/HLE/sceKernelMbx.cpp @@ -37,6 +37,11 @@ struct MbxWaitingThread SceUID threadID; u32 packetAddr; u64 pausedTimeout; + + bool operator ==(const SceUID &otherThreadID) const + { + return threadID == otherThreadID; + } }; void __KernelMbxTimeout(u64 userdata, int cyclesLate); @@ -274,19 +279,6 @@ void __KernelWaitMbx(Mbx *m, u32 timeoutPtr) CoreTiming::ScheduleEvent(usToCycles(micro), mbxWaitTimer, __KernelGetCurThread()); } -void __KernelMbxRemoveThread(Mbx *m, SceUID threadID) -{ - for (size_t i = 0; i < m->waitingThreads.size(); i++) - { - MbxWaitingThread *t = &m->waitingThreads[i]; - if (t->threadID == threadID) - { - m->waitingThreads.erase(m->waitingThreads.begin() + i); - break; - } - } -} - std::vector::iterator __KernelMbxFindPriority(std::vector &waiting) { _dbg_assert_msg_(SCEKERNEL, !waiting.empty(), "__KernelMutexFindPriority: Trying to find best of no threads."); @@ -480,7 +472,7 @@ int sceKernelReceiveMbx(SceUID id, u32 packetAddrPtr, u32 timeoutPtr) else { DEBUG_LOG(SCEKERNEL, "sceKernelReceiveMbx(%i, %08x, %08x): no message in queue, waiting", id, packetAddrPtr, timeoutPtr); - __KernelMbxRemoveThread(m, __KernelGetCurThread()); + HLEKernel::RemoveWaitingThread(m->waitingThreads, __KernelGetCurThread()); m->AddWaitingThread(__KernelGetCurThread(), packetAddrPtr); __KernelWaitMbx(m, timeoutPtr); __KernelWaitCurThread(WAITTYPE_MBX, id, 0, timeoutPtr, false, "mbx waited"); @@ -508,7 +500,7 @@ int sceKernelReceiveMbxCB(SceUID id, u32 packetAddrPtr, u32 timeoutPtr) else { DEBUG_LOG(SCEKERNEL, "sceKernelReceiveMbxCB(%i, %08x, %08x): no message in queue, waiting", id, packetAddrPtr, timeoutPtr); - __KernelMbxRemoveThread(m, __KernelGetCurThread()); + HLEKernel::RemoveWaitingThread(m->waitingThreads, __KernelGetCurThread()); m->AddWaitingThread(__KernelGetCurThread(), packetAddrPtr); __KernelWaitMbx(m, timeoutPtr); __KernelWaitCurThread(WAITTYPE_MBX, id, 0, timeoutPtr, true, "mbx waited"); @@ -583,12 +575,7 @@ int sceKernelReferMbxStatus(SceUID id, u32 infoAddr) for (int i = 0, n = m->nmb.numMessages; i < n; ++i) m->nmb.packetListHead = Memory::Read_U32(m->nmb.packetListHead); - for (auto iter = m->waitingThreads.begin(); iter != m->waitingThreads.end(); ++iter) - { - // The thread is no longer waiting for this, clean it up. - if (!HLEKernel::VerifyWait(iter->threadID, WAITTYPE_MBX, id)) - m->waitingThreads.erase(iter--); - } + HLEKernel::CleanupWaitingThreads(WAITTYPE_MBX, id, m->waitingThreads); // For whatever reason, it won't write if the size (first member) is 0. if (Memory::Read_U32(infoAddr) != 0) diff --git a/Core/HLE/sceKernelMemory.cpp b/Core/HLE/sceKernelMemory.cpp index bcabba4273..e08e126598 100644 --- a/Core/HLE/sceKernelMemory.cpp +++ b/Core/HLE/sceKernelMemory.cpp @@ -57,6 +57,11 @@ struct FplWaitingThread SceUID threadID; u32 addrPtr; u64 pausedTimeout; + + bool operator ==(const SceUID &otherThreadID) const + { + return threadID == otherThreadID; + } }; struct NativeFPL @@ -141,6 +146,11 @@ struct VplWaitingThread SceUID threadID; u32 addrPtr; u64 pausedTimeout; + + bool operator ==(const SceUID &otherThreadID) const + { + return threadID == otherThreadID; + } }; struct SceKernelVplInfo @@ -292,19 +302,6 @@ void __KernelFplEndCallback(SceUID threadID, SceUID prevCallbackId) DEBUG_LOG(SCEKERNEL, "sceKernelReceiveMbxCB: Resuming mbx wait from callback"); } -void __KernelFplRemoveThread(FPL *fpl, SceUID threadID) -{ - for (size_t i = 0; i < fpl->waitingThreads.size(); i++) - { - FplWaitingThread *t = &fpl->waitingThreads[i]; - if (t->threadID == threadID) - { - fpl->waitingThreads.erase(fpl->waitingThreads.begin() + i); - break; - } - } -} - bool __FplThreadSortPriority(FplWaitingThread thread1, FplWaitingThread thread2) { return __KernelThreadSortPriority(thread1.threadID, thread2.threadID); @@ -325,14 +322,7 @@ void __KernelSortFplThreads(FPL *fpl) { // Remove any that are no longer waiting. SceUID uid = fpl->GetUID(); - for (size_t i = 0; i < fpl->waitingThreads.size(); i++) - { - if (!HLEKernel::VerifyWait(fpl->waitingThreads[i].threadID, WAITTYPE_FPL, uid)) - { - fpl->waitingThreads.erase(fpl->waitingThreads.begin() + i); - --i; - } - } + 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); @@ -489,7 +479,7 @@ int sceKernelAllocateFpl(SceUID uid, u32 blockPtrAddr, u32 timeoutPtr) Memory::Write_U32(blockPtr, blockPtrAddr); } else { SceUID threadID = __KernelGetCurThread(); - __KernelFplRemoveThread(fpl, threadID); + HLEKernel::RemoveWaitingThread(fpl->waitingThreads, threadID); FplWaitingThread waiting = {threadID, blockPtrAddr}; fpl->waitingThreads.push_back(waiting); @@ -520,7 +510,7 @@ int sceKernelAllocateFplCB(SceUID uid, u32 blockPtrAddr, u32 timeoutPtr) Memory::Write_U32(blockPtr, blockPtrAddr); } else { SceUID threadID = __KernelGetCurThread(); - __KernelFplRemoveThread(fpl, threadID); + HLEKernel::RemoveWaitingThread(fpl->waitingThreads, threadID); FplWaitingThread waiting = {threadID, blockPtrAddr}; fpl->waitingThreads.push_back(waiting); @@ -1179,19 +1169,6 @@ void __KernelVplEndCallback(SceUID threadID, SceUID prevCallbackId) DEBUG_LOG(SCEKERNEL, "sceKernelReceiveMbxCB: Resuming mbx wait from callback"); } -void __KernelVplRemoveThread(VPL *vpl, SceUID threadID) -{ - for (size_t i = 0; i < vpl->waitingThreads.size(); i++) - { - VplWaitingThread *t = &vpl->waitingThreads[i]; - if (t->threadID == threadID) - { - vpl->waitingThreads.erase(vpl->waitingThreads.begin() + i); - break; - } - } -} - bool __VplThreadSortPriority(VplWaitingThread thread1, VplWaitingThread thread2) { return __KernelThreadSortPriority(thread1.threadID, thread2.threadID); @@ -1212,14 +1189,7 @@ void __KernelSortFplThreads(VPL *vpl) { // Remove any that are no longer waiting. SceUID uid = vpl->GetUID(); - for (size_t i = 0; i < vpl->waitingThreads.size(); i++) - { - if (!HLEKernel::VerifyWait(vpl->waitingThreads[i].threadID, WAITTYPE_VPL, uid)) - { - vpl->waitingThreads.erase(vpl->waitingThreads.begin() + i); - --i; - } - } + 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); @@ -1391,7 +1361,7 @@ int sceKernelAllocateVpl(SceUID uid, u32 size, u32 addrPtr, u32 timeoutPtr) if (vpl) { SceUID threadID = __KernelGetCurThread(); - __KernelVplRemoveThread(vpl, threadID); + HLEKernel::RemoveWaitingThread(vpl->waitingThreads, threadID); VplWaitingThread waiting = {threadID, addrPtr}; vpl->waitingThreads.push_back(waiting); } @@ -1416,7 +1386,7 @@ int sceKernelAllocateVplCB(SceUID uid, u32 size, u32 addrPtr, u32 timeoutPtr) if (vpl) { SceUID threadID = __KernelGetCurThread(); - __KernelVplRemoveThread(vpl, threadID); + HLEKernel::RemoveWaitingThread(vpl->waitingThreads, threadID); VplWaitingThread waiting = {threadID, addrPtr}; vpl->waitingThreads.push_back(waiting); } diff --git a/Core/HLE/sceKernelMsgPipe.cpp b/Core/HLE/sceKernelMsgPipe.cpp index eec9cc7176..8eff9b277f 100644 --- a/Core/HLE/sceKernelMsgPipe.cpp +++ b/Core/HLE/sceKernelMsgPipe.cpp @@ -241,18 +241,7 @@ struct MsgPipe : public KernelObject void SortThreads(std::vector &waitingThreads, bool usePrio) { // Clean up any not waiting at the same time. - size_t size = waitingThreads.size(); - for (size_t i = 0; i < size; ++i) - { - if (!waitingThreads[i].IsStillWaiting(GetUID())) - { - // Decrement size and swap what was there with i. - std::swap(waitingThreads[i], waitingThreads[--size]); - // Now we haven't checked the new i, so go back and do i again. - --i; - } - } - waitingThreads.resize(size); + HLEKernel::CleanupWaitingThreads(WAITTYPE_MSGPIPE, GetUID(), waitingThreads); if (usePrio) std::stable_sort(waitingThreads.begin(), waitingThreads.end(), __KernelMsgPipeThreadSortPriority); @@ -270,19 +259,14 @@ struct MsgPipe : public KernelObject SortThreads(sendWaitingThreads, usePrio); } - void RemoveWaitingThread(std::vector &waitingThreads, SceUID threadID) - { - waitingThreads.erase(std::remove(waitingThreads.begin(), waitingThreads.end(), threadID), waitingThreads.end()); - } - void RemoveReceiveWaitingThread(SceUID threadID) { - RemoveWaitingThread(receiveWaitingThreads, threadID); + HLEKernel::RemoveWaitingThread(receiveWaitingThreads, threadID); } void RemoveSendWaitingThread(SceUID threadID) { - RemoveWaitingThread(sendWaitingThreads, threadID); + HLEKernel::RemoveWaitingThread(sendWaitingThreads, threadID); } virtual void DoState(PointerWrap &p) diff --git a/Core/HLE/sceKernelMutex.cpp b/Core/HLE/sceKernelMutex.cpp index 7275c793cc..b90efe2c56 100644 --- a/Core/HLE/sceKernelMutex.cpp +++ b/Core/HLE/sceKernelMutex.cpp @@ -461,7 +461,7 @@ void __KernelMutexThreadEnd(SceUID threadID) { Mutex *mutex = kernelObjects.Get(waitingMutexID, error); if (mutex) - mutex->waitingThreads.erase(std::remove(mutex->waitingThreads.begin(), mutex->waitingThreads.end(), threadID), mutex->waitingThreads.end()); + HLEKernel::RemoveWaitingThread(mutex->waitingThreads, threadID); } // Unlock all mutexes the thread had locked. @@ -517,12 +517,7 @@ int sceKernelCancelMutex(SceUID uid, int count, u32 numWaitThreadsPtr) DEBUG_LOG(SCEKERNEL, "sceKernelCancelMutex(%i, %d, %08x)", uid, count, numWaitThreadsPtr); // Remove threads no longer waiting on this first (so the numWaitThreads value is correct.) - for (auto iter = mutex->waitingThreads.begin(); iter != mutex->waitingThreads.end(); ++iter) - { - // The thread is no longer waiting for this, clean it up. - if (!HLEKernel::VerifyWait(*iter, WAITTYPE_MUTEX, uid)) - mutex->waitingThreads.erase(iter--); - } + HLEKernel::CleanupWaitingThreads(WAITTYPE_MUTEX, uid, mutex->waitingThreads); if (Memory::IsValidAddress(numWaitThreadsPtr)) Memory::Write_U32((u32)mutex->waitingThreads.size(), numWaitThreadsPtr); @@ -682,12 +677,7 @@ int sceKernelReferMutexStatus(SceUID id, u32 infoAddr) // Don't write if the size is 0. Anything else is A-OK, though, apparently. if (Memory::Read_U32(infoAddr) != 0) { - for (auto iter = m->waitingThreads.begin(); iter != m->waitingThreads.end(); ++iter) - { - // The thread is no longer waiting for this, clean it up. - if (!HLEKernel::VerifyWait(*iter, WAITTYPE_MUTEX, id)) - m->waitingThreads.erase(iter--); - } + HLEKernel::CleanupWaitingThreads(WAITTYPE_MUTEX, id, m->waitingThreads); m->nm.numWaitThreads = (int) m->waitingThreads.size(); Memory::WriteStruct(infoAddr, &m->nm); @@ -1065,12 +1055,7 @@ int __KernelReferLwMutexStatus(SceUID uid, u32 infoPtr) { auto workarea = m->nm.workarea; - for (auto iter = m->waitingThreads.begin(); iter != m->waitingThreads.end(); ++iter) - { - // The thread is no longer waiting for this, clean it up. - if (!HLEKernel::VerifyWait(*iter, WAITTYPE_LWMUTEX, uid)) - m->waitingThreads.erase(iter--); - } + HLEKernel::CleanupWaitingThreads(WAITTYPE_LWMUTEX, uid, m->waitingThreads); // Refresh and write m->nm.currentCount = workarea->lockLevel; diff --git a/Core/HLE/sceKernelSemaphore.cpp b/Core/HLE/sceKernelSemaphore.cpp index 5884aa9cee..152de6a44e 100644 --- a/Core/HLE/sceKernelSemaphore.cpp +++ b/Core/HLE/sceKernelSemaphore.cpp @@ -266,12 +266,7 @@ int sceKernelReferSemaStatus(SceUID id, u32 infoPtr) if (!Memory::IsValidAddress(infoPtr)) return -1; - for (auto iter = s->waitingThreads.begin(); iter != s->waitingThreads.end(); ++iter) - { - // The thread is no longer waiting for this, clean it up. - if (!HLEKernel::VerifyWait(*iter, WAITTYPE_SEMA, id)) - s->waitingThreads.erase(iter--); - } + HLEKernel::CleanupWaitingThreads(WAITTYPE_SEMA, id, s->waitingThreads); s->ns.numWaitThreads = (int) s->waitingThreads.size(); if (Memory::Read_U32(infoPtr) != 0) diff --git a/Core/HLE/sceKernelThread.cpp b/Core/HLE/sceKernelThread.cpp index 2a26309494..c6ab762bcc 100644 --- a/Core/HLE/sceKernelThread.cpp +++ b/Core/HLE/sceKernelThread.cpp @@ -464,6 +464,10 @@ public: ~Thread() { + // Callbacks are automatically deleted when their owning thread is deleted. + for (auto it = callbacks.begin(), end = callbacks.end(); it != end; ++it) + kernelObjects.Destroy(*it); + if (pushedStacks.size() != 0) { WARN_LOG_REPORT(SCEKERNEL, "Thread ended within an extended stack"); @@ -498,20 +502,7 @@ public: p.Do(currentCallbackId); p.Do(context); - u32 numCallbacks = THREAD_CALLBACK_NUM_TYPES; - p.Do(numCallbacks); - if (numCallbacks != THREAD_CALLBACK_NUM_TYPES) - { - p.SetError(p.ERROR_FAILURE); - ERROR_LOG(SCEKERNEL, "Unable to load state: different thread callback storage."); - return; - } - - for (size_t i = 0; i < THREAD_CALLBACK_NUM_TYPES; ++i) - { - p.Do(registeredCallbacks[i]); - p.Do(readyCallbacks[i]); - } + p.Do(callbacks); p.Do(pendingMipsCalls); p.Do(pushedStacks); @@ -531,8 +522,7 @@ public: ThreadContext context; - std::set registeredCallbacks[THREAD_CALLBACK_NUM_TYPES]; - std::list readyCallbacks[THREAD_CALLBACK_NUM_TYPES]; + std::vector callbacks; std::list pendingMipsCalls; @@ -1765,9 +1755,12 @@ u32 __KernelDeleteThread(SceUID threadID, int exitStatus, const char *reason) Thread *t = kernelObjects.Get(threadID, error); if (t) { - // TODO: Unless they should be run before deletion? - for (int i = 0; i < THREAD_CALLBACK_NUM_TYPES; i++) - readyCallbacksCount -= (int)t->readyCallbacks[i].size(); + for (auto it = t->callbacks.begin(), end = t->callbacks.end(); it != end; ++it) + { + Callback *callback = kernelObjects.Get(*it, error); + if (callback && callback->nc.notifyCount != 0) + readyCallbacksCount--; + } } return kernelObjects.Destroy(threadID); @@ -2828,6 +2821,10 @@ SceUID sceKernelCreateCallback(const char *name, u32 entrypoint, u32 signalArg) cb->nc.notifyCount = 0; cb->nc.notifyArg = 0; + Thread *thread = __GetCurrentThread(); + if (thread) + thread->callbacks.push_back(id); + DEBUG_LOG(SCEKERNEL, "%i=sceKernelCreateCallback(name=%s, entry=%08x, callbackArg=%08x)", id, name, entrypoint, signalArg); return id; @@ -2837,9 +2834,19 @@ int sceKernelDeleteCallback(SceUID cbId) { DEBUG_LOG(SCEKERNEL, "sceKernelDeleteCallback(%i)", cbId); - // TODO: Make sure it's gone from all threads first! + u32 error; + Callback *cb = kernelObjects.Get(cbId, error); + if (cb) + { + Thread *thread = kernelObjects.Get(cb->nc.threadId, error); + if (thread) + thread->callbacks.erase(std::find(thread->callbacks.begin(), thread->callbacks.end(), cbId), thread->callbacks.end()); + if (cb->nc.notifyCount != 0) + readyCallbacksCount--; - return kernelObjects.Destroy(cbId); + return kernelObjects.Destroy(cbId); + } + return error; } // Generally very rarely used, but Numblast uses it like candy. @@ -2849,8 +2856,7 @@ int sceKernelNotifyCallback(SceUID cbId, int notifyArg) u32 error; Callback *cb = kernelObjects.Get(cbId, error); if (cb) { - // TODO: Should this notify other existing callbacks too? - __KernelNotifyCallback(THREAD_CALLBACK_USER_DEFINED, cbId, notifyArg); + __KernelNotifyCallback(cbId, notifyArg); return 0; } else { ERROR_LOG(SCEKERNEL, "sceKernelCancelCallback(%i) - bad cbId", cbId); @@ -2988,7 +2994,7 @@ void ActionAfterMipsCall::run(MipsCall &call) { ActionAfterMipsCall *Thread::getRunningCallbackAction() { - if (this->GetUID() == currentThread && g_inCbCount > 0) { + if (this->GetUID() == currentThread && g_inCbCount > 0) { MipsCall *call = mipsCalls.get(this->currentMipscallId); ActionAfterMipsCall *action = 0; if (call) @@ -3440,42 +3446,37 @@ void ActionAfterCallback::run(MipsCall &call) { } bool __KernelCurHasReadyCallbacks() { - if (readyCallbacksCount == 0) + if (readyCallbacksCount == 0) { return false; + } Thread *thread = __GetCurrentThread(); - for (int i = 0; i < THREAD_CALLBACK_NUM_TYPES; i++) { - if (thread->readyCallbacks[i].size()) { + u32 error; + for (auto it = thread->callbacks.begin(), end = thread->callbacks.end(); it != end; ++it) { + Callback *callback = kernelObjects.Get(*it, error); + if (callback && callback->nc.notifyCount != 0) { return true; } } - return false; } // Check callbacks on the current thread only. // Returns true if any callbacks were processed on the current thread. -bool __KernelCheckThreadCallbacks(Thread *thread, bool force) -{ - if (!thread || (!thread->isProcessingCallbacks && !force)) +bool __KernelCheckThreadCallbacks(Thread *thread, bool force) { + if (!thread || (!thread->isProcessingCallbacks && !force)) { return false; + } - for (int i = 0; i < THREAD_CALLBACK_NUM_TYPES; i++) { - if (thread->readyCallbacks[i].size()) { - SceUID readyCallback = thread->readyCallbacks[i].front(); - thread->readyCallbacks[i].pop_front(); - readyCallbacksCount--; - - // If the callback was deleted, we're good. Just skip it. - if (kernelObjects.IsValid(readyCallback)) - { - __KernelRunCallbackOnThread(readyCallback, thread, !force); // makes pending + if (!thread->callbacks.empty()) { + u32 error; + for (auto it = thread->callbacks.begin(), end = thread->callbacks.end(); it != end; ++it) { + Callback *callback = kernelObjects.Get(*it, error); + if (callback && callback->nc.notifyCount != 0) { + __KernelRunCallbackOnThread(callback->GetUID(), thread, !force); + readyCallbacksCount--; return true; } - else - { - WARN_LOG(SCEKERNEL, "Ignoring deleted callback %08x", readyCallback); - } } } return false; @@ -3491,23 +3492,20 @@ bool __KernelCheckCallbacks() { ERROR_LOG_REPORT(SCEKERNEL, "readyCallbacksCount became negative: %i", readyCallbacksCount); } - // SceUID currentThread = __KernelGetCurThread(); - // __GetCurrentThread()->isProcessingCallbacks = true; - // do { - bool processed = false; + bool processed = false; - u32 error; - for (std::vector::iterator iter = threadqueue.begin(); iter != threadqueue.end(); iter++) { - Thread *thread = kernelObjects.Get(*iter, error); - if (thread && __KernelCheckThreadCallbacks(thread, false)) { - processed = true; - } + u32 error; + for (std::vector::iterator iter = threadqueue.begin(); iter != threadqueue.end(); iter++) { + Thread *thread = kernelObjects.Get(*iter, error); + if (thread && __KernelCheckThreadCallbacks(thread, false)) { + processed = true; } - // } while (processed && currentThread == __KernelGetCurThread()); + } - if (processed) + if (processed) { return __KernelExecutePendingMipsCalls(__GetCurrentThread(), true); - return processed; + } + return false; } bool __KernelForceCallbacks() @@ -3549,30 +3547,7 @@ bool __KernelInCallback() return (g_inCbCount != 0); } - -u32 __KernelRegisterCallback(RegisteredCallbackType type, SceUID cbId) -{ - Thread *t = __GetCurrentThread(); - if (cbId > 0 && t->registeredCallbacks[type].find(cbId) == t->registeredCallbacks[type].end()) { - t->registeredCallbacks[type].insert(cbId); - return 0; - } else { - return SCE_KERNEL_ERROR_INVAL; - } -} - -u32 __KernelUnregisterCallback(RegisteredCallbackType type, SceUID cbId) -{ - Thread *t = __GetCurrentThread(); - if (t->registeredCallbacks[type].find(cbId) != t->registeredCallbacks[type].end()) { - t->registeredCallbacks[type].erase(cbId); - return 0; - } else { - return 0x80010016; - } -} - -void __KernelNotifyCallback(RegisteredCallbackType type, SceUID cbId, int notifyArg) +void __KernelNotifyCallback(SceUID cbId, int notifyArg) { u32 error; @@ -3582,37 +3557,11 @@ void __KernelNotifyCallback(RegisteredCallbackType type, SceUID cbId, int notify ERROR_LOG(SCEKERNEL, "__KernelNotifyCallback - invalid callback %08x", cbId); return; } - cb->nc.notifyCount++; - cb->nc.notifyArg = notifyArg; - - Thread *t = kernelObjects.Get(cb->nc.threadId, error); - std::list &readyCallbacks = t->readyCallbacks[type]; - auto iter = std::find(readyCallbacks.begin(), readyCallbacks.end(), cbId); - if (iter == readyCallbacks.end()) - { - t->readyCallbacks[type].push_back(cbId); + if (cb->nc.notifyCount == 0) { readyCallbacksCount++; } -} - -// TODO: If cbId == -1, notify the callback ID on all threads that have it. -u32 __KernelNotifyCallbackType(RegisteredCallbackType type, SceUID cbId, int notifyArg) -{ - u32 error; - for (std::vector::iterator iter = threadqueue.begin(); iter != threadqueue.end(); iter++) { - Thread *t = kernelObjects.Get(*iter, error); - if (!t) - continue; - - for (std::set::iterator citer = t->registeredCallbacks[type].begin(); citer != t->registeredCallbacks[type].end(); citer++) { - if (cbId == -1 || cbId == *citer) { - __KernelNotifyCallback(type, *citer, notifyArg); - } - } - } - - // checkCallbacks on other threads? - return 0; + cb->nc.notifyCount++; + cb->nc.notifyArg = notifyArg; } void __KernelRegisterWaitTypeFuncs(WaitType type, WaitBeginCallbackFunc beginFunc, WaitEndCallbackFunc endFunc) diff --git a/Core/HLE/sceKernelThread.h b/Core/HLE/sceKernelThread.h index ce452d9fd5..e409dc9cab 100644 --- a/Core/HLE/sceKernelThread.h +++ b/Core/HLE/sceKernelThread.h @@ -173,27 +173,6 @@ void __KernelWaitCallbacksCurThread(WaitType type, SceUID waitID, u32 waitValue, void __KernelReSchedule(const char *reason = "no reason"); void __KernelReSchedule(bool doCallbacks, const char *reason); -// Registered callback types -enum RegisteredCallbackType { - THREAD_CALLBACK_UMD = 0, - THREAD_CALLBACK_IO = 1, - THREAD_CALLBACK_MEMORYSTICK = 2, - THREAD_CALLBACK_MEMORYSTICK_FAT = 3, - THREAD_CALLBACK_POWER = 4, - THREAD_CALLBACK_EXIT = 5, - THREAD_CALLBACK_USER_DEFINED = 6, - THREAD_CALLBACK_SIZE = 7, - THREAD_CALLBACK_NUM_TYPES = 8, -}; - -// These operate on the current thread -u32 __KernelRegisterCallback(RegisteredCallbackType type, SceUID cbId); -u32 __KernelUnregisterCallback(RegisteredCallbackType type, SceUID cbId); - -// If cbId == -1, all the callbacks of the type on all the threads get notified. -// If not, only this particular callback gets notified. -u32 __KernelNotifyCallbackType(RegisteredCallbackType type, SceUID cbId, int notifyArg); - SceUID __KernelGetCurThread(); SceUID __KernelGetCurThreadModuleId(); SceUID __KernelSetupRootThread(SceUID moduleId, int args, const char *argp, int prio, int stacksize, int attr); //represents the real PSP elf loader, run before execution @@ -232,7 +211,7 @@ bool __KernelCurHasReadyCallbacks(); class Thread; void __KernelSwitchContext(Thread *target, const char *reason); bool __KernelExecutePendingMipsCalls(Thread *currentThread, bool reschedAfter); -void __KernelNotifyCallback(RegisteredCallbackType type, SceUID cbId, int notifyArg); +void __KernelNotifyCallback(SceUID cbId, int notifyArg); // Switch to an idle / non-user thread, if not already on one. // Returns whether a switch occurred. diff --git a/Core/HLE/scePower.cpp b/Core/HLE/scePower.cpp index c619574b23..7465b7b4ef 100644 --- a/Core/HLE/scePower.cpp +++ b/Core/HLE/scePower.cpp @@ -154,10 +154,8 @@ int scePowerRegisterCallback(int slot, int cbId) { } } if (retval >= 0) { - __KernelRegisterCallback(THREAD_CALLBACK_POWER, cbId); - int arg = PSP_POWER_CB_AC_POWER | PSP_POWER_CB_BATTERY_EXIST | PSP_POWER_CB_BATTERY_FULL; - __KernelNotifyCallbackType(THREAD_CALLBACK_POWER, cbId, arg); + __KernelNotifyCallback(cbId, arg); } return retval; } @@ -175,7 +173,6 @@ int scePowerUnregisterCallback(int slotId) { if (powerCbSlots[slotId] != 0) { int cbId = powerCbSlots[slotId]; DEBUG_LOG(HLE, "0=scePowerUnregisterCallback(%i) (cbid = %i)", slotId, cbId); - __KernelUnregisterCallback(THREAD_CALLBACK_POWER, cbId); powerCbSlots[slotId] = 0; } else { return PSP_POWER_ERROR_EMPTY_SLOT; diff --git a/Core/HLE/sceUmd.cpp b/Core/HLE/sceUmd.cpp index 804bfcbb8c..2dd8bbe634 100644 --- a/Core/HLE/sceUmd.cpp +++ b/Core/HLE/sceUmd.cpp @@ -114,7 +114,8 @@ void __UmdStatChange(u64 userdata, int cyclesLate) void __KernelUmdActivate() { u32 notifyArg = PSP_UMD_PRESENT | PSP_UMD_READABLE; - __KernelNotifyCallbackType(THREAD_CALLBACK_UMD, -1, notifyArg); + if (driveCBId != -1) + __KernelNotifyCallback(driveCBId, notifyArg); // Don't activate immediately, take time to "spin up." CoreTiming::RemoveAllEvents(umdStatChangeEvent); @@ -124,7 +125,8 @@ void __KernelUmdActivate() void __KernelUmdDeactivate() { u32 notifyArg = PSP_UMD_PRESENT | PSP_UMD_READY; - __KernelNotifyCallbackType(THREAD_CALLBACK_UMD, -1, notifyArg); + if (driveCBId != -1) + __KernelNotifyCallback(driveCBId, notifyArg); CoreTiming::RemoveAllEvents(umdStatChangeEvent); __UmdStatChange(0, 0); @@ -148,11 +150,7 @@ void __UmdBeginCallback(SceUID threadID, SceUID prevCallbackId) else umdPausedWaits[pauseKey] = 0; - for (auto it = umdWaitingThreads.begin(); it < umdWaitingThreads.end(); ++it) - { - if (*it == threadID) - umdWaitingThreads.erase(it--); - } + HLEKernel::RemoveWaitingThread(umdWaitingThreads, threadID); DEBUG_LOG(SCEIO, "sceUmdWaitDriveStatCB: Suspending lock wait for callback"); } @@ -256,17 +254,13 @@ int sceUmdDeactivate(u32 mode, const char *name) u32 sceUmdRegisterUMDCallBack(u32 cbId) { - int retVal; + int retVal = 0; // TODO: If the callback is invalid, return PSP_ERROR_UMD_INVALID_PARAM. - if (cbId == 0) + if (!kernelObjects.IsValid(cbId)) { retVal = PSP_ERROR_UMD_INVALID_PARAM; - else { - // Remove the old one, we're replacing. - if (driveCBId != -1) - __KernelUnregisterCallback(THREAD_CALLBACK_UMD, driveCBId); - - retVal = __KernelRegisterCallback(THREAD_CALLBACK_UMD, cbId); + } else { + // There's only ever one. driveCBId = cbId; } @@ -283,7 +277,6 @@ int sceUmdUnRegisterUMDCallBack(int cbId) else { retVal = cbId; driveCBId = -1; - __KernelUnregisterCallback(THREAD_CALLBACK_UMD, cbId); } DEBUG_LOG(SCEIO, "%08x=sceUmdUnRegisterUMDCallBack(id=%08x)", retVal, cbId); @@ -308,10 +301,7 @@ void __UmdStatTimeout(u64 userdata, int cyclesLate) if (waitID == 1) __KernelResumeThreadFromWait(threadID, SCE_KERNEL_ERROR_WAIT_TIMEOUT); - for (size_t i = 0; i < umdWaitingThreads.size(); ++i) { - if (umdWaitingThreads[i] == threadID) - umdWaitingThreads.erase(umdWaitingThreads.begin() + i--); - } + HLEKernel::RemoveWaitingThread(umdWaitingThreads, threadID); } void __UmdWaitStat(u32 timeout)