From b20bb3b60966374618a943100ee75d7dafd65797 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Mon, 2 Sep 2013 17:59:47 -0700 Subject: [PATCH] Refactor down the begin/end callback stuff. --- CMakeLists.txt | 1 + Core/Core.vcxproj | 1 + Core/Core.vcxproj.filters | 3 + Core/HLE/KernelWaitHelpers.h | 188 ++++++++++++++++++++++++++++++ Core/HLE/sceKernelEventFlag.cpp | 117 +++---------------- Core/HLE/sceKernelMbx.cpp | 120 ++++--------------- Core/HLE/sceKernelMemory.cpp | 197 +++----------------------------- Core/HLE/sceKernelMutex.cpp | 185 ++++++------------------------ Core/HLE/sceKernelSemaphore.cpp | 83 ++------------ Core/HLE/sceKernelThread.cpp | 36 +++--- Core/HLE/sceKernelThread.h | 2 +- 11 files changed, 311 insertions(+), 622 deletions(-) create mode 100644 Core/HLE/KernelWaitHelpers.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 5a954889ae..c5c5b4b80c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -801,6 +801,7 @@ add_library(${CoreLibName} ${CoreLinkType} Core/HLE/HLE.h Core/HLE/HLETables.cpp Core/HLE/HLETables.h + Core/HLE/KernelWaitHelpers.h Core/HLE/__sceAudio.cpp Core/HLE/__sceAudio.h Core/HLE/sceAtrac.cpp diff --git a/Core/Core.vcxproj b/Core/Core.vcxproj index 850a8af2d5..0c3cb71a0a 100644 --- a/Core/Core.vcxproj +++ b/Core/Core.vcxproj @@ -429,6 +429,7 @@ + diff --git a/Core/Core.vcxproj.filters b/Core/Core.vcxproj.filters index 9cbfcc44f9..a4f1c75666 100644 --- a/Core/Core.vcxproj.filters +++ b/Core/Core.vcxproj.filters @@ -891,6 +891,9 @@ MIPS\PPC + + HLE\Kernel + diff --git a/Core/HLE/KernelWaitHelpers.h b/Core/HLE/KernelWaitHelpers.h new file mode 100644 index 0000000000..e5edb005df --- /dev/null +++ b/Core/HLE/KernelWaitHelpers.h @@ -0,0 +1,188 @@ +// Copyright (c) 2012- PPSSPP Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0 or later versions. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official git repository and contact information can be found at +// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. + +#pragma once + +#include +#include +#include +#include "Core/HLE/sceKernelThread.h" + +namespace HLEKernel +{ + +template +inline bool WaitPauseHelperUpdate(SceUID pauseKey, SceUID threadID, std::vector &waitingThreads, std::map &pausedWaits, u64 pauseTimeout) { + WaitInfoType waitData = {0}; + for (size_t i = 0; i < waitingThreads.size(); i++) { + WaitInfoType *t = &waitingThreads[i]; + if (t->threadID == threadID) + { + waitData = *t; + // TODO: Hmm, what about priority/fifo order? Does it lose its place in line? + waitingThreads.erase(waitingThreads.begin() + i); + break; + } + } + + if (waitData.threadID != threadID) + return false; + + waitData.pausedTimeout = pauseTimeout; + pausedWaits[pauseKey] = waitData; + return true; +} + +template <> +inline bool WaitPauseHelperUpdate(SceUID pauseKey, SceUID threadID, std::vector &waitingThreads, std::map &pausedWaits, u64 pauseTimeout) { + // TODO: Hmm, what about priority/fifo order? Does it lose its place in line? + waitingThreads.erase(std::remove(waitingThreads.begin(), waitingThreads.end(), threadID), waitingThreads.end()); + pausedWaits[pauseKey] = pauseTimeout; + return true; +} + +template +inline u64 WaitPauseHelperGet(SceUID pauseKey, SceUID threadID, std::map &pausedWaits, WaitInfoType &waitData) { + waitData = pausedWaits[pauseKey]; + u64 waitDeadline = waitData.pausedTimeout; + pausedWaits.erase(pauseKey); + return waitDeadline; +} + +template <> +inline u64 WaitPauseHelperGet(SceUID pauseKey, SceUID threadID, std::map &pausedWaits, SceUID &waitData) { + waitData = threadID; + u64 waitDeadline = pausedWaits[pauseKey]; + pausedWaits.erase(pauseKey); + return waitDeadline; +} + +enum WaitBeginEndCallbackResult { + WAIT_CB_BAD_WAIT_DATA = -2, + WAIT_CB_BAD_WAIT_ID = -1, + WAIT_CB_SUCCESS = 0, + WAIT_CB_RESUMED_WAIT = 1, +}; + +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 runs inside itself. + // TODO: Handle this better? + if (pausedWaits.find(pauseKey) != pausedWaits.end()) { + return WAIT_CB_SUCCESS; + } + + u64 pausedTimeout = 0; + if (doTimeout && waitTimer != -1) { + s64 cyclesLeft = CoreTiming::UnscheduleEvent(waitTimer, threadID); + pausedTimeout = CoreTiming::GetTicks() + cyclesLeft; + } + + if (!WaitPauseHelperUpdate(pauseKey, threadID, waitingThreads, pausedWaits, pausedTimeout)) { + return WAIT_CB_BAD_WAIT_DATA; + } + + return WAIT_CB_SUCCESS; +} + +template +WaitBeginEndCallbackResult WaitBeginCallback(SceUID threadID, SceUID prevCallbackId, int waitTimer) { + u32 error; + SceUID uid = __KernelGetWaitID(threadID, waitType, error); + u32 timeoutPtr = __KernelGetWaitTimeoutPtr(threadID, error); + KO *ko = uid == 0 ? NULL : kernelObjects.Get(uid, error); + if (ko) { + return WaitBeginCallback(threadID, prevCallbackId, waitTimer, ko->waitingThreads, ko->pausedWaits, timeoutPtr != 0); + } else { + return WAIT_CB_BAD_WAIT_ID; + } +} + +template +WaitBeginEndCallbackResult WaitEndCallback(SceUID threadID, SceUID prevCallbackId, int waitTimer, TryUnlockFunc TryUnlock, std::vector &waitingThreads, std::map &pausedWaits) { + SceUID pauseKey = prevCallbackId == 0 ? threadID : prevCallbackId; + + // Note: Cancel does not affect suspended semaphore waits, probably same for others. + + u32 error; + SceUID uid = __KernelGetWaitID(threadID, waitType, error); + u32 timeoutPtr = __KernelGetWaitTimeoutPtr(threadID, error); + KO *ko = uid == 0 ? NULL : kernelObjects.Get(uid, error); + if (!ko || pausedWaits.find(pauseKey) == 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 && waitTimer != -1) { + Memory::Write_U32(0, timeoutPtr); + } + + __KernelResumeThreadFromWait(threadID, SCE_KERNEL_ERROR_WAIT_DELETE); + return WAIT_CB_SUCCESS; + } + + WaitInfoType waitData; + u64 waitDeadline = WaitPauseHelperGet(pauseKey, threadID, pausedWaits, waitData); + + // TODO: Don't wake up if __KernelCurHasReadyCallbacks()? + + bool wokeThreads; + // Attempt to unlock. + if (TryUnlock(ko, waitData, error, 0, wokeThreads)) { + return WAIT_CB_SUCCESS; + } + + // 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 && waitTimer != -1) + Memory::Write_U32(0, timeoutPtr); + + __KernelResumeThreadFromWait(threadID, SCE_KERNEL_ERROR_WAIT_TIMEOUT); + return WAIT_CB_SUCCESS; + } else { + if (timeoutPtr != 0 && waitTimer != -1) { + CoreTiming::ScheduleEvent(cyclesLeft, waitTimer, __KernelGetCurThread()); + } + + // TODO: Should this not go at the end? + waitingThreads.push_back(waitData); + return WAIT_CB_RESUMED_WAIT; + } +} + +template +WaitBeginEndCallbackResult WaitEndCallback(SceUID threadID, SceUID prevCallbackId, int waitTimer, TryUnlockFunc TryUnlock) { + u32 error; + SceUID uid = __KernelGetWaitID(threadID, waitType, error); + u32 timeoutPtr = __KernelGetWaitTimeoutPtr(threadID, error); + KO *ko = uid == 0 ? NULL : kernelObjects.Get(uid, error); + if (!ko) { + // 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 && waitTimer != -1) { + Memory::Write_U32(0, timeoutPtr); + } + + __KernelResumeThreadFromWait(threadID, SCE_KERNEL_ERROR_WAIT_DELETE); + return WAIT_CB_SUCCESS; + } + + return WaitEndCallback(threadID, prevCallbackId, waitTimer, TryUnlock, ko->waitingThreads, ko->pausedWaits); +} + +}; diff --git a/Core/HLE/sceKernelEventFlag.cpp b/Core/HLE/sceKernelEventFlag.cpp index 6e140c1a76..bf2cd26fdc 100644 --- a/Core/HLE/sceKernelEventFlag.cpp +++ b/Core/HLE/sceKernelEventFlag.cpp @@ -26,6 +26,7 @@ #include "Core/HLE/sceKernel.h" #include "Core/HLE/sceKernelThread.h" #include "Core/HLE/sceKernelEventFlag.h" +#include "Core/HLE/KernelWaitHelpers.h" void __KernelEventFlagTimeout(u64 userdata, int cycleslate); @@ -41,7 +42,7 @@ struct NativeEventFlag struct EventFlagTh { - SceUID tid; + SceUID threadID; u32 bits; u32 wait; u32 outAddr; @@ -149,8 +150,8 @@ bool __KernelEventFlagMatches(u32_le *pattern, u32 bits, u8 wait, u32 outAddr) bool __KernelUnlockEventFlagForThread(EventFlag *e, EventFlagTh &th, u32 &error, int result, bool &wokeThreads) { - SceUID waitID = __KernelGetWaitID(th.tid, WAITTYPE_EVENTFLAG, error); - u32 timeoutPtr = __KernelGetWaitTimeoutPtr(th.tid, error); + SceUID waitID = __KernelGetWaitID(th.threadID, WAITTYPE_EVENTFLAG, error); + u32 timeoutPtr = __KernelGetWaitTimeoutPtr(th.threadID, error); // The waitID may be different after a timeout. if (waitID != e->GetUID()) @@ -172,11 +173,11 @@ bool __KernelUnlockEventFlagForThread(EventFlag *e, EventFlagTh &th, u32 &error, if (timeoutPtr != 0 && eventFlagWaitTimer != -1) { // Remove any event for this thread. - s64 cyclesLeft = CoreTiming::UnscheduleEvent(eventFlagWaitTimer, th.tid); + s64 cyclesLeft = CoreTiming::UnscheduleEvent(eventFlagWaitTimer, th.threadID); Memory::Write_U32((u32) cyclesToUs(cyclesLeft), timeoutPtr); } - __KernelResumeThreadFromWait(th.tid, result); + __KernelResumeThreadFromWait(th.threadID, result); wokeThreads = true; return true; } @@ -195,102 +196,20 @@ bool __KernelClearEventFlagThreads(EventFlag *e, int reason) 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); - 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"); - } + auto result = HLEKernel::WaitBeginCallback(threadID, prevCallbackId, eventFlagWaitTimer); + if (result == HLEKernel::WAIT_CB_SUCCESS) + DEBUG_LOG(HLE, "sceKernelWaitEventFlagCB: Suspending lock wait for callback") + else if (result == HLEKernel::WAIT_CB_BAD_WAIT_DATA) + ERROR_LOG_REPORT(HLE, "sceKernelWaitEventFlagCB: wait not found to pause for callback") else WARN_LOG_REPORT(HLE, "sceKernelWaitEventFlagCB: beginning callback with bad wait id?"); } void __KernelEventFlagEndCallback(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 || 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); - - // 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 - { - if (timeoutPtr != 0 && eventFlagWaitTimer != -1) - CoreTiming::ScheduleEvent(cyclesLeft, eventFlagWaitTimer, __KernelGetCurThread()); - - // TODO: Should this not go at the end? - flag->waitingThreads.push_back(waitData); - + auto result = HLEKernel::WaitEndCallback(threadID, prevCallbackId, eventFlagWaitTimer, __KernelUnlockEventFlagForThread); + if (result == HLEKernel::WAIT_CB_RESUMED_WAIT) DEBUG_LOG(HLE, "sceKernelWaitEventFlagCB: Resuming lock wait for callback"); - } } //SceUID sceKernelCreateEventFlag(const char *name, int attr, int bits, SceKernelEventFlagOptParam *opt); @@ -448,7 +367,7 @@ void __KernelEventFlagTimeout(u64 userdata, int cycleslate) for (size_t i = 0; i < e->waitingThreads.size(); i++) { EventFlagTh *t = &e->waitingThreads[i]; - if (t->tid == threadID) + if (t->threadID == threadID) { bool wokeThreads; @@ -485,7 +404,7 @@ void __KernelEventFlagRemoveThread(EventFlag *e, SceUID threadID) for (size_t i = 0; i < e->waitingThreads.size(); i++) { EventFlagTh *t = &e->waitingThreads[i]; - if (t->tid == threadID) + if (t->threadID == threadID) { e->waitingThreads.erase(e->waitingThreads.begin() + i); break; @@ -538,7 +457,7 @@ int sceKernelWaitEventFlag(SceUID id, u32 bits, u32 wait, u32 outBitsPtr, u32 ti DEBUG_LOG(HLE, "sceKernelWaitEventFlag(%i, %08x, %i, %08x, %08x): waiting", id, bits, wait, outBitsPtr, timeoutPtr); // No match - must wait. - th.tid = __KernelGetCurThread(); + th.threadID = __KernelGetCurThread(); th.bits = bits; th.wait = wait; // If < 5ms, sometimes hardware doesn't write this, but it's unpredictable. @@ -613,7 +532,7 @@ int sceKernelWaitEventFlagCB(SceUID id, u32 bits, u32 wait, u32 outBitsPtr, u32 DEBUG_LOG(HLE, "sceKernelWaitEventFlagCB(%i, %08x, %i, %08x, %08x): waiting", id, bits, wait, outBitsPtr, timeoutPtr); // No match - must wait. - th.tid = __KernelGetCurThread(); + th.threadID = __KernelGetCurThread(); th.bits = bits; th.wait = wait; // If < 5ms, sometimes hardware doesn't write this, but it's unpredictable. @@ -707,7 +626,7 @@ u32 sceKernelReferEventFlagStatus(SceUID id, u32 statusPtr) for (auto iter = e->waitingThreads.begin(); iter != e->waitingThreads.end(); ++iter) { - SceUID waitID = __KernelGetWaitID(iter->tid, WAITTYPE_EVENTFLAG, error); + SceUID waitID = __KernelGetWaitID(iter->threadID, WAITTYPE_EVENTFLAG, error); // The thread is no longer waiting for this, clean it up. if (waitID != id) e->waitingThreads.erase(iter--); diff --git a/Core/HLE/sceKernelMbx.cpp b/Core/HLE/sceKernelMbx.cpp index 54482b03aa..986eef63ff 100644 --- a/Core/HLE/sceKernelMbx.cpp +++ b/Core/HLE/sceKernelMbx.cpp @@ -17,13 +17,14 @@ #include #include -#include "sceKernel.h" -#include "sceKernelThread.h" -#include "sceKernelMbx.h" -#include "HLE.h" +#include "Common/ChunkFile.h" +#include "Core/HLE/sceKernel.h" +#include "Core/HLE/sceKernelThread.h" +#include "Core/HLE/sceKernelMbx.h" +#include "Core/HLE/HLE.h" #include "Core/CoreTiming.h" #include "Core/Reporting.h" -#include "ChunkFile.h" +#include "Core/HLE/KernelWaitHelpers.h" #define SCE_KERNEL_MBA_THPRI 0x100 #define SCE_KERNEL_MBA_MSPRI 0x400 @@ -225,107 +226,32 @@ bool __KernelUnlockMbxForThread(Mbx *m, MbxWaitingThread &th, u32 &error, int re return true; } +bool __KernelUnlockMbxForThreadCheck(Mbx *m, MbxWaitingThread &waitData, u32 &error, int result, bool &wokeThreads) +{ + if (m->nmb.numMessages > 0 && __KernelUnlockMbxForThread(m, waitData, error, 0, wokeThreads)) + { + m->ReceiveMessage(waitData.packetAddr); + return true; + } + return false; +} + 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"); - } + auto result = HLEKernel::WaitBeginCallback(threadID, prevCallbackId, mbxWaitTimer); + if (result == HLEKernel::WAIT_CB_SUCCESS) + DEBUG_LOG(HLE, "sceKernelReceiveMbxCB: Suspending mbx wait for callback") + else if (result == HLEKernel::WAIT_CB_BAD_WAIT_DATA) + ERROR_LOG_REPORT(HLE, "sceKernelReceiveMbxCB: wait not found to pause for callback") else WARN_LOG_REPORT(HLE, "sceKernelReceiveMbxCB: beginning callback with bad wait id?"); } void __KernelMbxEndCallback(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 || 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); - + auto result = HLEKernel::WaitEndCallback(threadID, prevCallbackId, mbxWaitTimer, __KernelUnlockMbxForThreadCheck); + if (result == HLEKernel::WAIT_CB_RESUMED_WAIT) DEBUG_LOG(HLE, "sceKernelReceiveMbxCB: Resuming mbx wait from callback"); - } } void __KernelMbxTimeout(u64 userdata, int cyclesLate) diff --git a/Core/HLE/sceKernelMemory.cpp b/Core/HLE/sceKernelMemory.cpp index 9f1634def7..ac7e5e24b0 100644 --- a/Core/HLE/sceKernelMemory.cpp +++ b/Core/HLE/sceKernelMemory.cpp @@ -30,6 +30,7 @@ #include "Core/HLE/sceKernelThread.h" #include "Core/HLE/sceKernelInterrupt.h" #include "Core/HLE/sceKernelMemory.h" +#include "Core/HLE/KernelWaitHelpers.h" const int TLS_NUM_INDEXES = 16; @@ -278,102 +279,20 @@ bool __KernelUnlockFplForThread(FPL *fpl, FplWaitingThread &threadInfo, u32 &err void __KernelFplBeginCallback(SceUID threadID, SceUID prevCallbackId) { - SceUID pauseKey = prevCallbackId == 0 ? threadID : prevCallbackId; - - u32 error; - SceUID fplID = __KernelGetWaitID(threadID, WAITTYPE_FPL, error); - u32 timeoutPtr = __KernelGetWaitTimeoutPtr(threadID, error); - FPL *fpl = fplID == 0 ? NULL : kernelObjects.Get(fplID, error); - if (fpl) - { - // This means two callbacks in a row. PSP crashes if the same callback runs inside itself. - // TODO: Handle this better? - if (fpl->pausedWaits.find(pauseKey) != fpl->pausedWaits.end()) - return; - - FplWaitingThread waitData = {0}; - for (size_t i = 0; i < fpl->waitingThreads.size(); i++) - { - FplWaitingThread *t = &fpl->waitingThreads[i]; - if (t->threadID == threadID) - { - waitData = *t; - // TODO: Hmm, what about priority/fifo order? Does it lose its place in line? - fpl->waitingThreads.erase(fpl->waitingThreads.begin() + i); - break; - } - } - - if (waitData.threadID != threadID) - { - ERROR_LOG_REPORT(HLE, "sceKernelAllocateFplCB: wait not found to pause for callback"); - return; - } - - if (timeoutPtr != 0 && fplWaitTimer != -1) - { - s64 cyclesLeft = CoreTiming::UnscheduleEvent(fplWaitTimer, threadID); - waitData.pausedTimeout = CoreTiming::GetTicks() + cyclesLeft; - } - else - waitData.pausedTimeout = 0; - - fpl->pausedWaits[pauseKey] = waitData; - DEBUG_LOG(HLE, "sceKernelAllocateFplCB: Suspending fpl wait for callback"); - } + auto result = HLEKernel::WaitBeginCallback(threadID, prevCallbackId, fplWaitTimer); + if (result == HLEKernel::WAIT_CB_SUCCESS) + DEBUG_LOG(HLE, "sceKernelAllocateFplCB: Suspending fpl wait for callback") + else if (result == HLEKernel::WAIT_CB_BAD_WAIT_DATA) + ERROR_LOG_REPORT(HLE, "sceKernelAllocateFplCB: wait not found to pause for callback") else WARN_LOG_REPORT(HLE, "sceKernelAllocateFplCB: beginning callback with bad wait id?"); } void __KernelFplEndCallback(SceUID threadID, SceUID prevCallbackId) { - SceUID pauseKey = prevCallbackId == 0 ? threadID : prevCallbackId; - - u32 error; - SceUID fplID = __KernelGetWaitID(threadID, WAITTYPE_FPL, error); - u32 timeoutPtr = __KernelGetWaitTimeoutPtr(threadID, error); - FPL *fpl = fplID == 0 ? NULL : kernelObjects.Get(fplID, error); - if (!fpl || fpl->pausedWaits.find(pauseKey) == fpl->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 && fplWaitTimer != -1) - Memory::Write_U32(0, timeoutPtr); - - __KernelResumeThreadFromWait(threadID, SCE_KERNEL_ERROR_WAIT_DELETE); - return; - } - - FplWaitingThread waitData = fpl->pausedWaits[pauseKey]; - u64 waitDeadline = waitData.pausedTimeout; - fpl->pausedWaits.erase(pauseKey); - - // TODO: Don't wake up if __KernelCurHasReadyCallbacks()? - - bool wokeThreads; - // Attempt to unlock. - if (__KernelUnlockFplForThread(fpl, waitData, error, 0, wokeThreads)) - 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 && fplWaitTimer != -1) - Memory::Write_U32(0, timeoutPtr); - - __KernelResumeThreadFromWait(threadID, SCE_KERNEL_ERROR_WAIT_TIMEOUT); - } - else - { - if (timeoutPtr != 0 && fplWaitTimer != -1) - CoreTiming::ScheduleEvent(cyclesLeft, fplWaitTimer, __KernelGetCurThread()); - - // TODO: Should this not go at the end? - fpl->waitingThreads.push_back(waitData); - - DEBUG_LOG(HLE, "sceKernelAllocateFplCB: Resuming fpl wait from callback"); - } + auto result = HLEKernel::WaitEndCallback(threadID, prevCallbackId, fplWaitTimer, __KernelUnlockFplForThread); + if (result == HLEKernel::WAIT_CB_RESUMED_WAIT) + DEBUG_LOG(HLE, "sceKernelReceiveMbxCB: Resuming mbx wait from callback"); } void __KernelFplRemoveThread(FPL *fpl, SceUID threadID) @@ -1267,102 +1186,20 @@ bool __KernelUnlockVplForThread(VPL *vpl, VplWaitingThread &threadInfo, u32 &err void __KernelVplBeginCallback(SceUID threadID, SceUID prevCallbackId) { - SceUID pauseKey = prevCallbackId == 0 ? threadID : prevCallbackId; - - u32 error; - SceUID vplID = __KernelGetWaitID(threadID, WAITTYPE_VPL, error); - u32 timeoutPtr = __KernelGetWaitTimeoutPtr(threadID, error); - VPL *vpl = vplID == 0 ? NULL : kernelObjects.Get(vplID, error); - if (vpl) - { - // This means two callbacks in a row. PSP crashes if the same callback runs inside itself. - // TODO: Handle this better? - if (vpl->pausedWaits.find(pauseKey) != vpl->pausedWaits.end()) - return; - - VplWaitingThread waitData = {0}; - for (size_t i = 0; i < vpl->waitingThreads.size(); i++) - { - VplWaitingThread *t = &vpl->waitingThreads[i]; - if (t->threadID == threadID) - { - waitData = *t; - // TODO: Hmm, what about priority/fifo order? Does it lose its place in line? - vpl->waitingThreads.erase(vpl->waitingThreads.begin() + i); - break; - } - } - - if (waitData.threadID != threadID) - { - ERROR_LOG_REPORT(HLE, "sceKernelAllocateVplCB: wait not found to pause for callback"); - return; - } - - if (timeoutPtr != 0 && vplWaitTimer != -1) - { - s64 cyclesLeft = CoreTiming::UnscheduleEvent(vplWaitTimer, threadID); - waitData.pausedTimeout = CoreTiming::GetTicks() + cyclesLeft; - } - else - waitData.pausedTimeout = 0; - - vpl->pausedWaits[pauseKey] = waitData; - DEBUG_LOG(HLE, "sceKernelAllocateVplCB: Suspending vpl wait for callback"); - } + auto result = HLEKernel::WaitBeginCallback(threadID, prevCallbackId, vplWaitTimer); + if (result == HLEKernel::WAIT_CB_SUCCESS) + DEBUG_LOG(HLE, "sceKernelAllocateVplCB: Suspending vpl wait for callback") + else if (result == HLEKernel::WAIT_CB_BAD_WAIT_DATA) + ERROR_LOG_REPORT(HLE, "sceKernelAllocateVplCB: wait not found to pause for callback") else WARN_LOG_REPORT(HLE, "sceKernelAllocateVplCB: beginning callback with bad wait id?"); } void __KernelVplEndCallback(SceUID threadID, SceUID prevCallbackId) { - SceUID pauseKey = prevCallbackId == 0 ? threadID : prevCallbackId; - - u32 error; - SceUID vplID = __KernelGetWaitID(threadID, WAITTYPE_VPL, error); - u32 timeoutPtr = __KernelGetWaitTimeoutPtr(threadID, error); - VPL *vpl = vplID == 0 ? NULL : kernelObjects.Get(vplID, error); - if (!vpl || vpl->pausedWaits.find(pauseKey) == vpl->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 && vplWaitTimer != -1) - Memory::Write_U32(0, timeoutPtr); - - __KernelResumeThreadFromWait(threadID, SCE_KERNEL_ERROR_WAIT_DELETE); - return; - } - - VplWaitingThread waitData = vpl->pausedWaits[pauseKey]; - u64 waitDeadline = waitData.pausedTimeout; - vpl->pausedWaits.erase(pauseKey); - - // TODO: Don't wake up if __KernelCurHasReadyCallbacks()? - - bool wokeThreads; - // Attempt to unlock. - if (__KernelUnlockVplForThread(vpl, waitData, error, 0, wokeThreads)) - 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 && vplWaitTimer != -1) - Memory::Write_U32(0, timeoutPtr); - - __KernelResumeThreadFromWait(threadID, SCE_KERNEL_ERROR_WAIT_TIMEOUT); - } - else - { - if (timeoutPtr != 0 && vplWaitTimer != -1) - CoreTiming::ScheduleEvent(cyclesLeft, vplWaitTimer, __KernelGetCurThread()); - - // TODO: Should this not go at the end? - vpl->waitingThreads.push_back(waitData); - - DEBUG_LOG(HLE, "sceKernelAllocateVplCB: Resuming vpl wait from callback"); - } + auto result = HLEKernel::WaitEndCallback(threadID, prevCallbackId, vplWaitTimer, __KernelUnlockVplForThread); + if (result == HLEKernel::WAIT_CB_RESUMED_WAIT) + DEBUG_LOG(HLE, "sceKernelReceiveMbxCB: Resuming mbx wait from callback"); } void __KernelVplRemoveThread(VPL *vpl, SceUID threadID) diff --git a/Core/HLE/sceKernelMutex.cpp b/Core/HLE/sceKernelMutex.cpp index df4375843d..73015078ae 100644 --- a/Core/HLE/sceKernelMutex.cpp +++ b/Core/HLE/sceKernelMutex.cpp @@ -17,14 +17,15 @@ #include #include -#include "HLE.h" -#include "../MIPS/MIPS.h" +#include "Common/ChunkFile.h" +#include "Core/HLE/HLE.h" +#include "Core/MIPS/MIPS.h" #include "Core/CoreTiming.h" #include "Core/Reporting.h" -#include "ChunkFile.h" -#include "sceKernel.h" -#include "sceKernelMutex.h" -#include "sceKernelThread.h" +#include "Core/HLE/sceKernel.h" +#include "Core/HLE/sceKernelMutex.h" +#include "Core/HLE/sceKernelThread.h" +#include "Core/HLE/KernelWaitHelpers.h" #define PSP_MUTEX_ATTR_FIFO 0 #define PSP_MUTEX_ATTR_PRIORITY 0x100 @@ -72,14 +73,14 @@ struct Mutex : public KernelObject p.Do(nm); SceUID dv = 0; p.Do(waitingThreads, dv); - p.Do(pausedWaitTimeouts); + p.Do(pausedWaits); p.DoMarker("Mutex"); } NativeMutex nm; std::vector waitingThreads; // Key is the callback id it was for, or if no callback, the thread id. - std::map pausedWaitTimeouts; + std::map pausedWaits; }; @@ -134,14 +135,14 @@ struct LwMutex : public KernelObject p.Do(nm); SceUID dv = 0; p.Do(waitingThreads, dv); - p.Do(pausedWaitTimeouts); + p.Do(pausedWaits); p.DoMarker("LwMutex"); } NativeLwMutex nm; std::vector waitingThreads; // Key is the callback id it was for, or if no callback, the thread id. - std::map pausedWaitTimeouts; + std::map pausedWaits; }; static int mutexWaitTimer = -1; @@ -274,85 +275,27 @@ bool __KernelUnlockMutexForThread(Mutex *mutex, SceUID threadID, u32 &error, int return true; } +bool __KernelUnlockMutexForThreadCheck(Mutex *mutex, SceUID threadID, u32 &error, int result, bool &wokeThreads) +{ + if (mutex->nm.lockThread == -1 && __KernelUnlockMutexForThread(mutex, threadID, error, 0)) + return true; + return false; +} + void __KernelMutexBeginCallback(SceUID threadID, SceUID prevCallbackId) { - SceUID pauseKey = prevCallbackId == 0 ? threadID : prevCallbackId; - - u32 error; - SceUID mutexID = __KernelGetWaitID(threadID, WAITTYPE_MUTEX, error); - u32 timeoutPtr = __KernelGetWaitTimeoutPtr(threadID, error); - Mutex *mutex = mutexID == 0 ? NULL : kernelObjects.Get(mutexID, error); - if (mutex) - { - // This means two callbacks in a row. PSP crashes if the same callback runs inside itself. - // TODO: Handle this better? - if (mutex->pausedWaitTimeouts.find(pauseKey) != mutex->pausedWaitTimeouts.end()) - return; - - if (timeoutPtr != 0 && mutexWaitTimer != -1) - { - s64 cyclesLeft = CoreTiming::UnscheduleEvent(mutexWaitTimer, threadID); - mutex->pausedWaitTimeouts[pauseKey] = CoreTiming::GetTicks() + cyclesLeft; - } - else - mutex->pausedWaitTimeouts[pauseKey] = 0; - - // TODO: Hmm, what about priority/fifo order? Does it lose its place in line? - mutex->waitingThreads.erase(std::remove(mutex->waitingThreads.begin(), mutex->waitingThreads.end(), threadID), mutex->waitingThreads.end()); - - DEBUG_LOG(HLE, "sceKernelLockMutexCB: Suspending lock wait for callback"); - } + auto result = HLEKernel::WaitBeginCallback(threadID, prevCallbackId, mutexWaitTimer); + if (result == HLEKernel::WAIT_CB_SUCCESS) + DEBUG_LOG(HLE, "sceKernelLockMutexCB: Suspending lock wait for callback") else WARN_LOG_REPORT(HLE, "sceKernelLockMutexCB: beginning callback with bad wait id?"); } void __KernelMutexEndCallback(SceUID threadID, SceUID prevCallbackId) { - SceUID pauseKey = prevCallbackId == 0 ? threadID : prevCallbackId; - - u32 error; - SceUID mutexID = __KernelGetWaitID(threadID, WAITTYPE_MUTEX, error); - u32 timeoutPtr = __KernelGetWaitTimeoutPtr(threadID, error); - Mutex *mutex = mutexID == 0 ? NULL : kernelObjects.Get(mutexID, error); - if (!mutex || mutex->pausedWaitTimeouts.find(pauseKey) == mutex->pausedWaitTimeouts.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 && mutexWaitTimer != -1) - Memory::Write_U32(0, timeoutPtr); - - __KernelResumeThreadFromWait(threadID, SCE_KERNEL_ERROR_WAIT_DELETE); - return; - } - - u64 waitDeadline = mutex->pausedWaitTimeouts[pauseKey]; - mutex->pausedWaitTimeouts.erase(pauseKey); - - // TODO: Don't wake up if __KernelCurHasReadyCallbacks()? - - // Attempt to unlock. - if (mutex->nm.lockThread == -1 && __KernelUnlockMutexForThread(mutex, threadID, error, 0)) - 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 && mutexWaitTimer != -1) - Memory::Write_U32(0, timeoutPtr); - - __KernelResumeThreadFromWait(threadID, SCE_KERNEL_ERROR_WAIT_TIMEOUT); - } - else - { - if (timeoutPtr != 0 && mutexWaitTimer != -1) - CoreTiming::ScheduleEvent(cyclesLeft, mutexWaitTimer, __KernelGetCurThread()); - - // TODO: Should this not go at the end? - mutex->waitingThreads.push_back(threadID); - + auto result = HLEKernel::WaitEndCallback(threadID, prevCallbackId, mutexWaitTimer, __KernelUnlockMutexForThreadCheck); + if (result == HLEKernel::WAIT_CB_RESUMED_WAIT) DEBUG_LOG(HLE, "sceKernelLockMutexCB: Resuming lock wait for callback"); - } } int sceKernelCreateMutex(const char *name, u32 attr, int initialCount, u32 optionsPtr) @@ -996,85 +939,27 @@ void __KernelWaitLwMutex(LwMutex *mutex, u32 timeoutPtr) CoreTiming::ScheduleEvent(usToCycles(micro), lwMutexWaitTimer, __KernelGetCurThread()); } +bool __KernelUnlockLwMutexForThreadCheck(LwMutex *mutex, SceUID threadID, u32 &error, int result, bool &wokeThreads) +{ + if (mutex->nm.lockThread == -1 && __KernelUnlockLwMutexForThread(mutex, mutex->nm.workarea, threadID, error, 0)) + return true; + return false; +} + void __KernelLwMutexBeginCallback(SceUID threadID, SceUID prevCallbackId) { - SceUID pauseKey = prevCallbackId == 0 ? threadID : prevCallbackId; - - u32 error; - SceUID mutexID = __KernelGetWaitID(threadID, WAITTYPE_LWMUTEX, error); - u32 timeoutPtr = __KernelGetWaitTimeoutPtr(threadID, error); - LwMutex *mutex = mutexID == 0 ? NULL : kernelObjects.Get(mutexID, error); - if (mutex) - { - // This means two callbacks in a row. PSP crashes if the same callback runs inside itself. - // TODO: Handle this better? - if (mutex->pausedWaitTimeouts.find(pauseKey) != mutex->pausedWaitTimeouts.end()) - return; - - if (timeoutPtr != 0 && lwMutexWaitTimer != -1) - { - s64 cyclesLeft = CoreTiming::UnscheduleEvent(lwMutexWaitTimer, threadID); - mutex->pausedWaitTimeouts[pauseKey] = CoreTiming::GetTicks() + cyclesLeft; - } - else - mutex->pausedWaitTimeouts[pauseKey] = 0; - - // TODO: Hmm, what about priority/fifo order? Does it lose its place in line? - mutex->waitingThreads.erase(std::remove(mutex->waitingThreads.begin(), mutex->waitingThreads.end(), threadID), mutex->waitingThreads.end()); - - DEBUG_LOG(HLE, "sceKernelLockLwMutexCB: Suspending lock wait for callback"); - } + auto result = HLEKernel::WaitBeginCallback(threadID, prevCallbackId, lwMutexWaitTimer); + if (result == HLEKernel::WAIT_CB_SUCCESS) + DEBUG_LOG(HLE, "sceKernelLockLwMutexCB: Suspending lock wait for callback") else WARN_LOG_REPORT(HLE, "sceKernelLockLwMutexCB: beginning callback with bad wait id?"); } void __KernelLwMutexEndCallback(SceUID threadID, SceUID prevCallbackId) { - SceUID pauseKey = prevCallbackId == 0 ? threadID : prevCallbackId; - - u32 error; - SceUID mutexID = __KernelGetWaitID(threadID, WAITTYPE_LWMUTEX, error); - u32 timeoutPtr = __KernelGetWaitTimeoutPtr(threadID, error); - LwMutex *mutex = mutexID == 0 ? NULL : kernelObjects.Get(mutexID, error); - if (!mutex || mutex->pausedWaitTimeouts.find(pauseKey) == mutex->pausedWaitTimeouts.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 && lwMutexWaitTimer != -1) - Memory::Write_U32(0, timeoutPtr); - - __KernelResumeThreadFromWait(threadID, SCE_KERNEL_ERROR_WAIT_DELETE); - return; - } - - u64 waitDeadline = mutex->pausedWaitTimeouts[pauseKey]; - mutex->pausedWaitTimeouts.erase(pauseKey); - - // TODO: Don't wake up if __KernelCurHasReadyCallbacks()? - - // Attempt to unlock. - if (mutex->nm.lockThread == -1 && __KernelUnlockLwMutexForThread(mutex, mutex->nm.workarea, threadID, error, 0)) - 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 && lwMutexWaitTimer != -1) - Memory::Write_U32(0, timeoutPtr); - - __KernelResumeThreadFromWait(threadID, SCE_KERNEL_ERROR_WAIT_TIMEOUT); - } - else - { - if (timeoutPtr != 0 && lwMutexWaitTimer != -1) - CoreTiming::ScheduleEvent(cyclesLeft, lwMutexWaitTimer, __KernelGetCurThread()); - - // TODO: Should this not go at the end? - mutex->waitingThreads.push_back(threadID); - + auto result = HLEKernel::WaitEndCallback(threadID, prevCallbackId, lwMutexWaitTimer, __KernelUnlockLwMutexForThreadCheck); + if (result == HLEKernel::WAIT_CB_RESUMED_WAIT) DEBUG_LOG(HLE, "sceKernelLockLwMutexCB: Resuming lock wait for callback"); - } } int sceKernelTryLockLwMutex(u32 workareaPtr, int count) diff --git a/Core/HLE/sceKernelSemaphore.cpp b/Core/HLE/sceKernelSemaphore.cpp index 5060d3e024..307b11c79b 100644 --- a/Core/HLE/sceKernelSemaphore.cpp +++ b/Core/HLE/sceKernelSemaphore.cpp @@ -24,6 +24,7 @@ #include "Core/HLE/sceKernel.h" #include "Core/HLE/sceKernelThread.h" #include "Core/HLE/sceKernelSemaphore.h" +#include "Core/HLE/KernelWaitHelpers.h" #define PSP_SEMA_ATTR_FIFO 0 #define PSP_SEMA_ATTR_PRIORITY 0x100 @@ -65,14 +66,14 @@ struct Semaphore : public KernelObject p.Do(ns); SceUID dv = 0; p.Do(waitingThreads, dv); - p.Do(pausedWaitTimeouts); + p.Do(pausedWaits); p.DoMarker("Semaphore"); } NativeSemaphore ns; std::vector waitingThreads; // Key is the callback id it was for, or if no callback, the thread id. - std::map pausedWaitTimeouts; + std::map pausedWaits; }; static int semaWaitTimer = -1; @@ -134,86 +135,18 @@ bool __KernelUnlockSemaForThread(Semaphore *s, SceUID threadID, u32 &error, int void __KernelSemaBeginCallback(SceUID threadID, SceUID prevCallbackId) { - SceUID pauseKey = prevCallbackId == 0 ? threadID : prevCallbackId; - - u32 error; - SceUID semaID = __KernelGetWaitID(threadID, WAITTYPE_SEMA, error); - u32 timeoutPtr = __KernelGetWaitTimeoutPtr(threadID, error); - Semaphore *s = semaID == 0 ? NULL : kernelObjects.Get(semaID, error); - if (s) - { - // This means two callbacks in a row. PSP crashes if the same callback runs inside itself. - // TODO: Handle this better? - if (s->pausedWaitTimeouts.find(pauseKey) != s->pausedWaitTimeouts.end()) - return; - - if (timeoutPtr != 0 && semaWaitTimer != -1) - { - s64 cyclesLeft = CoreTiming::UnscheduleEvent(semaWaitTimer, threadID); - s->pausedWaitTimeouts[pauseKey] = CoreTiming::GetTicks() + cyclesLeft; - } - else - s->pausedWaitTimeouts[pauseKey] = 0; - - // TODO: Hmm, what about priority/fifo order? Does it lose its place in line? - s->waitingThreads.erase(std::remove(s->waitingThreads.begin(), s->waitingThreads.end(), threadID), s->waitingThreads.end()); - - DEBUG_LOG(HLE, "sceKernelWaitSemaCB: Suspending sema wait for callback"); - } + auto result = HLEKernel::WaitBeginCallback(threadID, prevCallbackId, semaWaitTimer); + if (result == HLEKernel::WAIT_CB_SUCCESS) + DEBUG_LOG(HLE, "sceKernelWaitSemaCB: Suspending sema wait for callback") else WARN_LOG_REPORT(HLE, "sceKernelWaitSemaCB: beginning callback with bad wait id?"); } void __KernelSemaEndCallback(SceUID threadID, SceUID prevCallbackId) { - SceUID pauseKey = prevCallbackId == 0 ? threadID : prevCallbackId; - - // Note: Cancel does not affect suspended semaphore waits. - - u32 error; - SceUID semaID = __KernelGetWaitID(threadID, WAITTYPE_SEMA, error); - u32 timeoutPtr = __KernelGetWaitTimeoutPtr(threadID, error); - Semaphore *s = semaID == 0 ? NULL : kernelObjects.Get(semaID, error); - if (!s || s->pausedWaitTimeouts.find(pauseKey) == s->pausedWaitTimeouts.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 && semaWaitTimer != -1) - Memory::Write_U32(0, timeoutPtr); - - __KernelResumeThreadFromWait(threadID, SCE_KERNEL_ERROR_WAIT_DELETE); - return; - } - - u64 waitDeadline = s->pausedWaitTimeouts[pauseKey]; - s->pausedWaitTimeouts.erase(pauseKey); - - // TODO: Don't wake up if __KernelCurHasReadyCallbacks()? - - bool wokeThreads; - // Attempt to unlock. - if (__KernelUnlockSemaForThread(s, threadID, 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 && semaWaitTimer != -1) - Memory::Write_U32(0, timeoutPtr); - - __KernelResumeThreadFromWait(threadID, SCE_KERNEL_ERROR_WAIT_TIMEOUT); - } - else - { - if (timeoutPtr != 0 && semaWaitTimer != -1) - CoreTiming::ScheduleEvent(cyclesLeft, semaWaitTimer, __KernelGetCurThread()); - - // TODO: Should this not go at the end? - s->waitingThreads.push_back(threadID); - + auto result = HLEKernel::WaitEndCallback(threadID, prevCallbackId, semaWaitTimer, __KernelUnlockSemaForThread); + if (result == HLEKernel::WAIT_CB_RESUMED_WAIT) DEBUG_LOG(HLE, "sceKernelWaitSemaCB: Resuming sema wait for callback"); - } } // Resume all waiting threads (for delete / cancel.) diff --git a/Core/HLE/sceKernelThread.cpp b/Core/HLE/sceKernelThread.cpp index 2a7d71d6c3..0a5959bb3d 100644 --- a/Core/HLE/sceKernelThread.cpp +++ b/Core/HLE/sceKernelThread.cpp @@ -22,22 +22,23 @@ #include "Common/LogManager.h" #include "Common/CommonTypes.h" -#include "HLE.h" -#include "HLETables.h" -#include "../MIPS/MIPSInt.h" -#include "../MIPS/MIPSCodeUtils.h" -#include "../MIPS/MIPS.h" +#include "Core/HLE/HLE.h" +#include "Core/HLE/HLETables.h" +#include "Core/MIPS/MIPSInt.h" +#include "Core/MIPS/MIPSCodeUtils.h" +#include "Core/MIPS/MIPS.h" #include "Core/CoreTiming.h" #include "Core/MemMap.h" #include "Core/Reporting.h" -#include "ChunkFile.h" +#include "Common/ChunkFile.h" -#include "sceAudio.h" -#include "sceKernel.h" -#include "sceKernelMemory.h" -#include "sceKernelThread.h" -#include "sceKernelModule.h" -#include "sceKernelInterrupt.h" +#include "Core/HLE/sceAudio.h" +#include "Core/HLE/sceKernel.h" +#include "Core/HLE/sceKernelMemory.h" +#include "Core/HLE/sceKernelThread.h" +#include "Core/HLE/sceKernelModule.h" +#include "Core/HLE/sceKernelInterrupt.h" +#include "Core/HLE/KernelWaitHelpers.h" typedef struct { @@ -924,14 +925,9 @@ void __KernelDelayBeginCallback(SceUID threadID, SceUID prevCallbackId) { u32 error; SceUID waitID = __KernelGetWaitID(threadID, WAITTYPE_DELAY, error); if (waitID == threadID) { - // This means two callbacks in a row. PSP crashes if the same callback runs inside itself. - // TODO: Handle this better? - if (pausedDelays.find(pauseKey) != pausedDelays.end()) - return; - - s64 cyclesLeft = CoreTiming::UnscheduleEvent(eventScheduledWakeup, threadID); - pausedDelays[pauseKey] = CoreTiming::GetTicks() + cyclesLeft; - + // Most waits need to keep track of waiting threads, delays don't. Use a fake list. + std::vector dummy; + HLEKernel::WaitBeginCallback(threadID, prevCallbackId, eventScheduledWakeup, dummy, pausedDelays, true); DEBUG_LOG(HLE, "sceKernelDelayThreadCB: Suspending delay for callback"); } else diff --git a/Core/HLE/sceKernelThread.h b/Core/HLE/sceKernelThread.h index 8f723b3cc7..dd022abda2 100644 --- a/Core/HLE/sceKernelThread.h +++ b/Core/HLE/sceKernelThread.h @@ -333,4 +333,4 @@ struct DebugThreadInfo std::vector GetThreadsInfo(); void __KernelChangeThreadState(SceUID threadId, ThreadStatus newStatus); -int hleLoadExecForUser_362A956B(); \ No newline at end of file +int hleLoadExecForUser_362A956B();