diff --git a/Core/HLE/KernelWaitHelpers.h b/Core/HLE/KernelWaitHelpers.h index 9aed035147..196ee10060 100644 --- a/Core/HLE/KernelWaitHelpers.h +++ b/Core/HLE/KernelWaitHelpers.h @@ -150,8 +150,8 @@ template WaitBeginEndCallbackResult WaitBeginCallback(SceUID threadID, SceUID prevCallbackId, int waitTimer, std::vector &waitingThreads, std::map &pausedWaits, bool doTimeout = true) { SceUID pauseKey = prevCallbackId == 0 ? threadID : prevCallbackId; - // This means two callbacks in a row. PSP crashes if the same callback waits inside itself (may need more testing.) - // TODO: Handle this better? + // Shouldn't happen: each nesting level pauses under its own key, and on hardware a callback can + // nest only one level (a CB wait that would go deeper never returns.) if (pausedWaits.find(pauseKey) != pausedWaits.end()) { return WAIT_CB_SUCCESS; } @@ -222,6 +222,14 @@ WaitBeginEndCallbackResult WaitEndCallback(SceUID threadID, SceUID prevCallbackI // TODO: Don't wake up if __KernelCurHasReadyCallbacks()? + // The timeout kept running during the callback. Put the timer back first, so that an unlock + // reports the time that's left. + s64 cyclesLeft = waitDeadline - CoreTiming::GetTicks(currentMIPS); + const bool hasTimer = timeoutPtr != 0 && waitTimer != -1 && waitDeadline != 0; + if (hasTimer) { + CoreTiming::ScheduleEvent(cyclesLeft < 0 ? 0 : cyclesLeft, waitTimer, threadID); + } + bool wokeThreads; // Attempt to unlock. if (TryUnlock(ko, waitData, error, 0, wokeThreads)) { @@ -229,20 +237,18 @@ WaitBeginEndCallbackResult WaitEndCallback(SceUID threadID, SceUID prevCallbackI } // We only check if it timed out if it couldn't unlock. - s64 cyclesLeft = waitDeadline - CoreTiming::GetTicks(currentMIPS); if (cyclesLeft < 0 && waitDeadline != 0) { + if (hasTimer) { + CoreTiming::UnscheduleEvent(waitTimer, threadID); + } if (timeoutPtr != 0 && waitTimer != -1) { Memory::WriteOrException_U32(0, timeoutPtr); } __KernelResumeThreadFromWait(threadID, SCE_KERNEL_ERROR_WAIT_TIMEOUT); return WAIT_CB_TIMED_OUT; - } else { - if (timeoutPtr != 0 && waitTimer != -1) { - CoreTiming::ScheduleEvent(cyclesLeft, waitTimer, __KernelGetCurThread()); - } - return WAIT_CB_RESUMED_WAIT; } + return WAIT_CB_RESUMED_WAIT; } // Meant to be called in a registered end callback function for a wait type. diff --git a/Core/HLE/sceKernelMsgPipe.cpp b/Core/HLE/sceKernelMsgPipe.cpp index 47110b2752..82a2f2108d 100644 --- a/Core/HLE/sceKernelMsgPipe.cpp +++ b/Core/HLE/sceKernelMsgPipe.cpp @@ -566,7 +566,11 @@ static void __KernelMsgPipeEndCallback(SceUID threadID, SceUID prevCallbackId) { MsgPipe *ko = uid == 0 ? NULL : kernelObjects.Get(uid, error); if (ko == NULL) { - ERROR_LOG_REPORT(Log::sceKernel, "__KernelMsgPipeEndCallback: Invalid object"); + // Deleted during the callback. + u32 timeoutPtr = __KernelGetWaitTimeoutPtr(threadID, error); + if (timeoutPtr != 0 && waitTimer != -1) + Memory::WriteOrException_U32(0, timeoutPtr); + __KernelResumeThreadFromWait(threadID, SCE_KERNEL_ERROR_WAIT_DELETE); return; } diff --git a/Core/HLE/sceKernelThread.cpp b/Core/HLE/sceKernelThread.cpp index 0a7636494b..895774e174 100644 --- a/Core/HLE/sceKernelThread.cpp +++ b/Core/HLE/sceKernelThread.cpp @@ -782,7 +782,8 @@ void __KernelThreadingInit() { u32 blockSize = sizeof(idleThreadCode) + ARRAY_SIZE(threadHacks) * 2 * 4; // The thread code above plus 8 bytes per "hack" dispatchEnabled = true; - memset(waitTypeFuncs, 0, sizeof(waitTypeFuncs)); + // Don't clear waitTypeFuncs here: __KernelMemoryInit() registers VPL and FPL before this runs. + // Every entry is set again by its module's init anyway. __SetCurrentThread(NULL, 0, NULL); g_inCbCount = 0;