From 9b80ae2ee01a65c334d9937fb237b173cea36ff3 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 14 Apr 2013 21:00:02 -0700 Subject: [PATCH 1/3] Make event flags wait / do callbacks correctly. --- Core/HLE/sceKernelEventFlag.cpp | 108 ++++++++++++++++++++++++++++++++ 1 file changed, 108 insertions(+) diff --git a/Core/HLE/sceKernelEventFlag.cpp b/Core/HLE/sceKernelEventFlag.cpp index 1e8a41ed97..cd04d1e0ab 100644 --- a/Core/HLE/sceKernelEventFlag.cpp +++ b/Core/HLE/sceKernelEventFlag.cpp @@ -45,6 +45,7 @@ struct EventFlagTh u32 bits; u32 wait; u32 outAddr; + u64 pausedTimeout; }; class EventFlag : public KernelObject @@ -70,11 +71,14 @@ public: p.Do(nef); EventFlagTh eft = {0}; p.Do(waitingThreads, eft); + p.Do(pausedWaits); p.DoMarker("EventFlag"); } NativeEventFlag nef; std::vector waitingThreads; + // Key is the callback id it was for, or if no callback, the thread id. + std::map pausedWaits; }; @@ -102,9 +106,13 @@ enum PspEventFlagWaitTypes int eventFlagWaitTimer = -1; +void __KernelEventFlagBeginCallback(SceUID threadID, SceUID prevCallbackId); +void __KernelEventFlagEndCallback(SceUID threadID, SceUID prevCallbackId, u32 &returnValue); + void __KernelEventFlagInit() { eventFlagWaitTimer = CoreTiming::RegisterEvent("EventFlagTimeout", __KernelEventFlagTimeout); + __KernelRegisterWaitTypeFuncs(WAITTYPE_EVENTFLAG, __KernelEventFlagBeginCallback, __KernelEventFlagEndCallback); } void __KernelEventFlagDoState(PointerWrap &p) @@ -186,6 +194,106 @@ bool __KernelClearEventFlagThreads(EventFlag *e, int reason) return wokeThreads; } +void __KernelEventFlagBeginCallback(SceUID threadID, SceUID prevCallbackId) +{ + SceUID pauseKey = prevCallbackId == 0 ? threadID : prevCallbackId; + + u32 error; + SceUID flagID = __KernelGetWaitID(threadID, WAITTYPE_EVENTFLAG, error); + u32 timeoutPtr = __KernelGetWaitTimeoutPtr(threadID, error); + 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()) + return; + + EventFlagTh waitData = {0}; + for (size_t i = 0; i < flag->waitingThreads.size(); i++) + { + EventFlagTh *t = &flag->waitingThreads[i]; + if (t->tid == threadID) + { + waitData = *t; + // TODO: Hmm, what about priority/fifo order? Does it lose its place in line? + flag->waitingThreads.erase(flag->waitingThreads.begin() + i); + flag->nef.numWaitThreads--; + break; + } + } + + if (waitData.tid != threadID) + { + ERROR_LOG_REPORT(HLE, "sceKernelWaitEventFlagCB: wait not found to pause for callback"); + return; + } + + if (timeoutPtr != 0 && eventFlagWaitTimer != -1) + { + s64 cyclesLeft = CoreTiming::UnscheduleEvent(eventFlagWaitTimer, threadID); + waitData.pausedTimeout = CoreTiming::GetTicks() + cyclesLeft; + } + else + waitData.pausedTimeout = 0; + + flag->pausedWaits[pauseKey] = waitData; + DEBUG_LOG(HLE, "sceKernelWaitEventFlagCB: Suspending lock wait for callback"); + } + else + WARN_LOG_REPORT(HLE, "sceKernelWaitEventFlagCB: beginning callback with bad wait id?"); +} + +void __KernelEventFlagEndCallback(SceUID threadID, SceUID prevCallbackId, u32 &returnValue) +{ + SceUID pauseKey = prevCallbackId == 0 ? threadID : prevCallbackId; + + u32 error; + SceUID flagID = __KernelGetWaitID(threadID, WAITTYPE_EVENTFLAG, error); + u32 timeoutPtr = __KernelGetWaitTimeoutPtr(threadID, error); + EventFlag *flag = flagID == 0 ? NULL : kernelObjects.Get(flagID, error); + if (!flag || flag->pausedWaits.find(pauseKey) == flag->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 && eventFlagWaitTimer != -1) + Memory::Write_U32(0, timeoutPtr); + + __KernelResumeThreadFromWait(threadID, SCE_KERNEL_ERROR_WAIT_DELETE); + return; + } + + EventFlagTh waitData = flag->pausedWaits[pauseKey]; + u64 waitDeadline = waitData.pausedTimeout; + flag->pausedWaits.erase(pauseKey); + flag->nef.numWaitThreads++; + + // TODO: Don't wake up if __KernelCurHasReadyCallbacks()? + + bool wokeThreads; + // Attempt to unlock. + if (__KernelUnlockEventFlagForThread(flag, waitData, error, 0, wokeThreads)) + return; + + // We only check if it timed out if it couldn't unlock. + s64 cyclesLeft = waitDeadline - CoreTiming::GetTicks(); + if (cyclesLeft < 0 && waitDeadline != 0) + { + if (timeoutPtr != 0 && eventFlagWaitTimer != -1) + Memory::Write_U32(0, timeoutPtr); + + __KernelResumeThreadFromWait(threadID, SCE_KERNEL_ERROR_WAIT_TIMEOUT); + } + else + { + // TODO: Should this not go at the end? + flag->waitingThreads.push_back(waitData); + + DEBUG_LOG(HLE, "sceKernelWaitEventFlagCB: Resuming lock wait for callback"); + } +} + //SceUID sceKernelCreateEventFlag(const char *name, int attr, int bits, SceKernelEventFlagOptParam *opt); int sceKernelCreateEventFlag(const char *name, u32 flag_attr, u32 flag_initPattern, u32 optPtr) { From 200ec55c6148bbf23758ce2ecab566e79bc1a253 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 14 Apr 2013 21:31:41 -0700 Subject: [PATCH 2/3] sceKernelWaitEventFlagCB() calls callbacks first. --- Core/HLE/sceKernelEventFlag.cpp | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/Core/HLE/sceKernelEventFlag.cpp b/Core/HLE/sceKernelEventFlag.cpp index cd04d1e0ab..113c03af14 100644 --- a/Core/HLE/sceKernelEventFlag.cpp +++ b/Core/HLE/sceKernelEventFlag.cpp @@ -562,7 +562,15 @@ int sceKernelWaitEventFlagCB(SceUID id, u32 bits, u32 wait, u32 outBitsPtr, u32 if (e) { EventFlagTh th; - if (!__KernelEventFlagMatches(&e->nef.currentPattern, bits, wait, outBitsPtr)) + bool doWait = !__KernelEventFlagMatches(&e->nef.currentPattern, bits, wait, outBitsPtr); + bool doCallbackWait = false; + if (__KernelCurHasReadyCallbacks()) + { + doWait = true; + doCallbackWait = true; + } + + if (doWait) { // If this thread was left in waitingThreads after a timeout, remove it. // Otherwise we might write the outBitsPtr in the wrong place. @@ -586,7 +594,10 @@ int sceKernelWaitEventFlagCB(SceUID id, u32 bits, u32 wait, u32 outBitsPtr, u32 e->waitingThreads.push_back(th); __KernelSetEventFlagTimeout(e, timeoutPtr); - __KernelWaitCurThread(WAITTYPE_EVENTFLAG, id, 0, timeoutPtr, true, "event flag waited"); + if (doCallbackWait) + __KernelWaitCallbacksCurThread(WAITTYPE_EVENTFLAG, id, 0, timeoutPtr); + else + __KernelWaitCurThread(WAITTYPE_EVENTFLAG, id, 0, timeoutPtr, true, "event flag waited"); } else hleCheckCurrentCallbacks(); From 9ce8c07530cc6f70630922b115c1c90184a9cf3b Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 14 Apr 2013 21:34:33 -0700 Subject: [PATCH 3/3] Reschedule the timeout event on callback end. Darn, missed that 3 times before I caught it. --- Core/HLE/sceKernelEventFlag.cpp | 3 +++ Core/HLE/sceKernelMutex.cpp | 6 ++++++ Core/HLE/sceKernelSemaphore.cpp | 3 +++ 3 files changed, 12 insertions(+) diff --git a/Core/HLE/sceKernelEventFlag.cpp b/Core/HLE/sceKernelEventFlag.cpp index 113c03af14..34e44ec9bf 100644 --- a/Core/HLE/sceKernelEventFlag.cpp +++ b/Core/HLE/sceKernelEventFlag.cpp @@ -287,6 +287,9 @@ void __KernelEventFlagEndCallback(SceUID threadID, SceUID prevCallbackId, u32 &r } else { + if (timeoutPtr != 0 && eventFlagWaitTimer != -1) + CoreTiming::ScheduleEvent(cyclesLeft, eventFlagWaitTimer, __KernelGetCurThread()); + // TODO: Should this not go at the end? flag->waitingThreads.push_back(waitData); diff --git a/Core/HLE/sceKernelMutex.cpp b/Core/HLE/sceKernelMutex.cpp index d2a1557224..a08d7ee009 100644 --- a/Core/HLE/sceKernelMutex.cpp +++ b/Core/HLE/sceKernelMutex.cpp @@ -343,6 +343,9 @@ void __KernelMutexEndCallback(SceUID threadID, SceUID prevCallbackId, u32 &retur } else { + if (timeoutPtr != 0 && mutexWaitTimer != -1) + CoreTiming::ScheduleEvent(cyclesLeft, mutexWaitTimer, __KernelGetCurThread()); + // TODO: Should this not go at the end? mutex->waitingThreads.push_back(threadID); @@ -986,6 +989,9 @@ void __KernelLwMutexEndCallback(SceUID threadID, SceUID prevCallbackId, u32 &ret } else { + if (timeoutPtr != 0 && lwMutexWaitTimer != -1) + CoreTiming::ScheduleEvent(cyclesLeft, lwMutexWaitTimer, __KernelGetCurThread()); + // TODO: Should this not go at the end? mutex->waitingThreads.push_back(threadID); diff --git a/Core/HLE/sceKernelSemaphore.cpp b/Core/HLE/sceKernelSemaphore.cpp index 2842fab34c..f709874eca 100644 --- a/Core/HLE/sceKernelSemaphore.cpp +++ b/Core/HLE/sceKernelSemaphore.cpp @@ -209,6 +209,9 @@ void __KernelSemaEndCallback(SceUID threadID, SceUID prevCallbackId, u32 &return } else { + if (timeoutPtr != 0 && semaWaitTimer != -1) + CoreTiming::ScheduleEvent(cyclesLeft, semaWaitTimer, __KernelGetCurThread()); + // TODO: Should this not go at the end? s->waitingThreads.push_back(threadID);