From 3903f27dfd52329a4c10743df6fe4a8d08afa3ab Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Mon, 2 Sep 2013 15:11:49 -0700 Subject: [PATCH] Add handlers for callbacks during mbx waits. --- Core/HLE/sceKernelEventFlag.cpp | 1 - Core/HLE/sceKernelMbx.cpp | 143 +++++++++++++++++++++++++++++--- 2 files changed, 132 insertions(+), 12 deletions(-) diff --git a/Core/HLE/sceKernelEventFlag.cpp b/Core/HLE/sceKernelEventFlag.cpp index a281879d9a..49c88fc1f3 100644 --- a/Core/HLE/sceKernelEventFlag.cpp +++ b/Core/HLE/sceKernelEventFlag.cpp @@ -203,7 +203,6 @@ void __KernelEventFlagBeginCallback(SceUID threadID, SceUID prevCallbackId) EventFlag *flag = flagID == 0 ? NULL : kernelObjects.Get(flagID, error); if (flag) { - // This means two callbacks in a row. PSP crashes if the same callback runs inside itself. // TODO: Handle this better? if (flag->pausedWaits.find(pauseKey) != flag->pausedWaits.end()) diff --git a/Core/HLE/sceKernelMbx.cpp b/Core/HLE/sceKernelMbx.cpp index 86bc38fe7c..451f87bb44 100644 --- a/Core/HLE/sceKernelMbx.cpp +++ b/Core/HLE/sceKernelMbx.cpp @@ -15,6 +15,8 @@ // Official git repository and contact information can be found at // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. +#include +#include #include "sceKernel.h" #include "sceKernelThread.h" #include "sceKernelMbx.h" @@ -31,8 +33,9 @@ const int PSP_MBX_ERROR_DUPLICATE_MSG = 0x800201C9; struct MbxWaitingThread { - SceUID first; - u32 second; + SceUID threadID; + u32 packetAddr; + u64 pausedTimeout; }; void __KernelMbxTimeout(u64 userdata, int cyclesLate); @@ -63,7 +66,7 @@ struct Mbx : public KernelObject { for (std::vector::iterator it = waitingThreads.begin(); it != waitingThreads.end(); it++) { - if (__KernelGetThreadPrio(id) < __KernelGetThreadPrio((*it).first)) + if (__KernelGetThreadPrio(id) < __KernelGetThreadPrio(it->threadID)) { MbxWaitingThread waiting = {id, addr}; waitingThreads.insert(it, waiting); @@ -169,17 +172,24 @@ struct Mbx : public KernelObject p.Do(nmb); MbxWaitingThread mwt = {0}; p.Do(waitingThreads, mwt); + p.Do(pausedWaits); p.DoMarker("Mbx"); } NativeMbx nmb; std::vector waitingThreads; + // Key is the callback id it was for, or if no callback, the thread id. + std::map pausedWaits; }; +void __KernelMbxBeginCallback(SceUID threadID, SceUID prevCallbackId); +void __KernelMbxEndCallback(SceUID threadID, SceUID prevCallbackId, u32 &returnValue); + void __KernelMbxInit() { mbxWaitTimer = CoreTiming::RegisterEvent("MbxTimeout", __KernelMbxTimeout); + __KernelRegisterWaitTypeFuncs(WAITTYPE_MBX, __KernelMbxBeginCallback, __KernelMbxEndCallback); } void __KernelMbxDoState(PointerWrap &p) @@ -196,8 +206,8 @@ KernelObject *__KernelMbxObject() bool __KernelUnlockMbxForThread(Mbx *m, MbxWaitingThread &th, u32 &error, int result, bool &wokeThreads) { - SceUID waitID = __KernelGetWaitID(th.first, WAITTYPE_MBX, error); - u32 timeoutPtr = __KernelGetWaitTimeoutPtr(th.first, error); + SceUID waitID = __KernelGetWaitID(th.threadID, WAITTYPE_MBX, error); + u32 timeoutPtr = __KernelGetWaitTimeoutPtr(th.threadID, error); // The waitID may be different after a timeout. if (waitID != m->GetUID()) @@ -206,15 +216,118 @@ bool __KernelUnlockMbxForThread(Mbx *m, MbxWaitingThread &th, u32 &error, int re if (timeoutPtr != 0 && mbxWaitTimer != -1) { // Remove any event for this thread. - s64 cyclesLeft = CoreTiming::UnscheduleEvent(mbxWaitTimer, th.first); + s64 cyclesLeft = CoreTiming::UnscheduleEvent(mbxWaitTimer, th.threadID); Memory::Write_U32((u32) cyclesToUs(cyclesLeft), timeoutPtr); } - __KernelResumeThreadFromWait(th.first, result); + __KernelResumeThreadFromWait(th.threadID, result); wokeThreads = true; return true; } +void __KernelMbxBeginCallback(SceUID threadID, SceUID prevCallbackId) +{ + SceUID pauseKey = prevCallbackId == 0 ? threadID : prevCallbackId; + + u32 error; + SceUID mbxID = __KernelGetWaitID(threadID, WAITTYPE_MBX, error); + u32 timeoutPtr = __KernelGetWaitTimeoutPtr(threadID, error); + Mbx *m = mbxID == 0 ? NULL : kernelObjects.Get(mbxID, error); + if (m) + { + // This means two callbacks in a row. PSP crashes if the same callback runs inside itself. + // TODO: Handle this better? + if (m->pausedWaits.find(pauseKey) != m->pausedWaits.end()) + return; + + MbxWaitingThread waitData = {0}; + for (size_t i = 0; i < m->waitingThreads.size(); i++) + { + MbxWaitingThread *t = &m->waitingThreads[i]; + if (t->threadID == threadID) + { + waitData = *t; + // TODO: Hmm, what about priority/fifo order? Does it lose its place in line? + m->waitingThreads.erase(m->waitingThreads.begin() + i); + break; + } + } + + if (waitData.threadID != threadID) + { + ERROR_LOG_REPORT(HLE, "sceKernelReceiveMbxCB: wait not found to pause for callback"); + return; + } + + if (timeoutPtr != 0 && mbxWaitTimer != -1) + { + s64 cyclesLeft = CoreTiming::UnscheduleEvent(mbxWaitTimer, threadID); + waitData.pausedTimeout = CoreTiming::GetTicks() + cyclesLeft; + } + else + waitData.pausedTimeout = 0; + + m->pausedWaits[pauseKey] = waitData; + DEBUG_LOG(HLE, "sceKernelReceiveMbxCB: Suspending mbx wait for callback"); + } + else + WARN_LOG_REPORT(HLE, "sceKernelReceiveMbxCB: beginning callback with bad wait id?"); +} + +void __KernelMbxEndCallback(SceUID threadID, SceUID prevCallbackId, u32 &returnValue) +{ + SceUID pauseKey = prevCallbackId == 0 ? threadID : prevCallbackId; + + u32 error; + SceUID mbxID = __KernelGetWaitID(threadID, WAITTYPE_MBX, error); + u32 timeoutPtr = __KernelGetWaitTimeoutPtr(threadID, error); + Mbx *m = mbxID == 0 ? NULL : kernelObjects.Get(mbxID, error); + if (!m || m->pausedWaits.find(pauseKey) == m->pausedWaits.end()) + { + // TODO: Since it was deleted, we don't know how long was actually left. + // For now, we just say the full time was taken. + if (timeoutPtr != 0 && mbxWaitTimer != -1) + Memory::Write_U32(0, timeoutPtr); + + __KernelResumeThreadFromWait(threadID, SCE_KERNEL_ERROR_WAIT_DELETE); + return; + } + + MbxWaitingThread waitData = m->pausedWaits[pauseKey]; + u64 waitDeadline = waitData.pausedTimeout; + m->pausedWaits.erase(pauseKey); + + // TODO: Don't wake up if __KernelCurHasReadyCallbacks()? + + bool wokeThreads; + // Attempt to unlock. + if (m->nmb.numMessages > 0 && __KernelUnlockMbxForThread(m, waitData, error, 0, wokeThreads)) + { + m->ReceiveMessage(waitData.packetAddr); + return; + } + + // We only check if it timed out if it couldn't receive. + s64 cyclesLeft = waitDeadline - CoreTiming::GetTicks(); + if (cyclesLeft < 0 && waitDeadline != 0) + { + if (timeoutPtr != 0 && mbxWaitTimer != -1) + Memory::Write_U32(0, timeoutPtr); + + __KernelResumeThreadFromWait(threadID, SCE_KERNEL_ERROR_WAIT_TIMEOUT); + } + else + { + if (timeoutPtr != 0 && mbxWaitTimer != -1) + CoreTiming::ScheduleEvent(cyclesLeft, mbxWaitTimer, __KernelGetCurThread()); + + // TODO: Should this not go at the end? + m->waitingThreads.push_back(waitData); + + DEBUG_LOG(HLE, "sceKernelReceiveMbxCB: Resuming mbx wait from callback"); + } +} + void __KernelMbxTimeout(u64 userdata, int cyclesLate) { SceUID threadID = (SceUID)userdata; @@ -260,7 +373,7 @@ void __KernelMbxRemoveThread(Mbx *m, SceUID threadID) for (size_t i = 0; i < m->waitingThreads.size(); i++) { MbxWaitingThread *t = &m->waitingThreads[i]; - if (t->first == threadID) + if (t->threadID == threadID) { m->waitingThreads.erase(m->waitingThreads.begin() + i); break; @@ -276,7 +389,7 @@ std::vector::iterator __KernelMbxFindPriority(std::vectorfirst); + u32 iter_prio = __KernelGetThreadPrio(iter->threadID); if (iter_prio < best_prio) { best = iter; @@ -386,8 +499,8 @@ int sceKernelSendMbx(SceUID id, u32 packetAddr) if (wokeThreads) { - DEBUG_LOG(HLE, "sceKernelSendMbx(%i, %08x): threads waiting, resuming %d", id, packetAddr, t.first); - Memory::Write_U32(packetAddr, t.second); + DEBUG_LOG(HLE, "sceKernelSendMbx(%i, %08x): threads waiting, resuming %d", id, packetAddr, t.threadID); + Memory::Write_U32(packetAddr, t.packetAddr); hleReSchedule("mbx sent"); // We don't need to do anything else, finish here. @@ -564,6 +677,14 @@ 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) + { + SceUID waitID = __KernelGetWaitID(iter->threadID, WAITTYPE_MBX, error); + // The thread is no longer waiting for this, clean it up. + if (waitID != id) + m->waitingThreads.erase(iter--); + } + // For whatever reason, it won't write if the size (first member) is 0. if (Memory::Read_U32(infoAddr) != 0) {