diff --git a/Core/CoreTiming.cpp b/Core/CoreTiming.cpp index db686e885..cca5f1f51 100644 --- a/Core/CoreTiming.cpp +++ b/Core/CoreTiming.cpp @@ -249,6 +249,43 @@ void ScheduleEvent(int cyclesIntoFuture, int event_type, u64 userdata) AddEventToQueue(ne); } +void UnscheduleEvent(int event_type, u64 userdata) +{ + if (!first) + return; + while(first) + { + if (first->type == event_type && first->userdata == userdata) + { + Event *next = first->next; + FreeEvent(first); + first = next; + } + else + { + break; + } + } + if (!first) + return; + Event *prev = first; + Event *ptr = prev->next; + while (ptr) + { + if (ptr->type == event_type && ptr->userdata == userdata) + { + prev->next = ptr->next; + FreeEvent(ptr); + ptr = prev->next; + } + else + { + prev = ptr; + ptr = ptr->next; + } + } +} + void RegisterAdvanceCallback(void (*callback)(int cyclesExecuted)) { advanceCallback = callback; diff --git a/Core/CoreTiming.h b/Core/CoreTiming.h index a442af20c..bd8605b8d 100644 --- a/Core/CoreTiming.h +++ b/Core/CoreTiming.h @@ -77,8 +77,8 @@ namespace CoreTiming void ScheduleEvent(int cyclesIntoFuture, int event_type, u64 userdata=0); void ScheduleEvent_Threadsafe(int cyclesIntoFuture, int event_type, u64 userdata=0); void ScheduleEvent_Threadsafe_Immediate(int event_type, u64 userdata=0); + void UnscheduleEvent(int event_type, u64 userdata); - // We only permit one event of each type in the queue at a time. void RemoveEvent(int event_type); void RemoveThreadsafeEvent(int event_type); void RemoveAllEvents(int event_type); diff --git a/Core/HLE/sceKernelMutex.cpp b/Core/HLE/sceKernelMutex.cpp index 193a9ba78..ef256b9e2 100644 --- a/Core/HLE/sceKernelMutex.cpp +++ b/Core/HLE/sceKernelMutex.cpp @@ -19,6 +19,7 @@ #include "HLE.h" #include "../MIPS/MIPS.h" +#include "../../Core/CoreTiming.h" #include "sceKernel.h" #include "sceKernelMutex.h" #include "sceKernelThread.h" @@ -55,6 +56,7 @@ struct Mutex : public KernelObject int GetIDType() const { return SCE_KERNEL_TMID_Mutex; } NativeMutex nm; std::vector waitingThreads; + int waitTimer; }; struct LWMutex : public KernelObject @@ -97,6 +99,7 @@ void sceKernelCreateMutex(const char *name, u32 attr, int initialCount, u32 opti mutex->nm.lockThread = -1; else mutex->nm.lockThread = __KernelGetCurThread(); + mutex->waitTimer = 0; if (optionsPtr != 0) WARN_LOG(HLE,"sceKernelCreateMutex(%s) unsupported options parameter.", name); @@ -113,13 +116,19 @@ void sceKernelDeleteMutex(SceUID id) Mutex *mutex = kernelObjects.Get(id, error); if (mutex) { + // Kill the timer, they're waking up now. + if (mutex->waitTimer != 0) + { + CoreTiming::RemoveEvent(mutex->waitTimer); + mutex->waitTimer = 0; + } + std::vector::iterator iter, end; for (iter = mutex->waitingThreads.begin(), end = mutex->waitingThreads.end(); iter != end; ++iter) { SceUID threadID = *iter; - - // TODO: Set returnValue? - __KernelResumeThreadFromWait(threadID); + __KernelResumeThreadFromWait(threadID, SCE_KERNEL_ERROR_WAIT_DELETE); + // TODO: set timeoutPtr. } mutex->waitingThreads.empty(); @@ -172,6 +181,23 @@ bool __KernelLockMutex(Mutex *mutex, int count, u32 &error) return false; } +void __KernelMutexTimeout(u64 userdata, int cyclesLate) +{ + SceUID threadID = (SceUID)userdata; + // TODO: set timeoutPtr. + __KernelResumeThreadFromWait(threadID, SCE_KERNEL_ERROR_WAIT_TIMEOUT); +} + +void __kernelWaitMutex(Mutex *mutex, u32 timeoutPtr) +{ + if (mutex->waitTimer == 0) + mutex->waitTimer = CoreTiming::RegisterEvent("ScheduledTimeout", &__KernelMutexTimeout); + + // This should call __KernelMutexTimeout() later, unless we cancel it. + int milliseconds = (int) Memory::Read_U32(timeoutPtr); + CoreTiming::ScheduleEvent(msToCycles(milliseconds), mutex->waitTimer, __KernelGetCurThread()); +} + // int sceKernelLockMutex(SceUID id, int count, int *timeout) // void because it changes threads. void sceKernelLockMutex(SceUID id, int count, u32 timeoutPtr) @@ -190,7 +216,8 @@ void sceKernelLockMutex(SceUID id, int count, u32 timeoutPtr) else { mutex->waitingThreads.push_back(__KernelGetCurThread()); - __KernelWaitCurThread(WAITTYPE_MUTEX, id, count, 0, false); + __kernelWaitMutex(mutex, timeoutPtr); + __KernelWaitCurThread(WAITTYPE_MUTEX, id, count, timeoutPtr, false); } } @@ -212,7 +239,8 @@ void sceKernelLockMutexCB(SceUID id, int count, u32 timeoutPtr) else { mutex->waitingThreads.push_back(__KernelGetCurThread()); - __KernelWaitCurThread(WAITTYPE_MUTEX, id, count, 0, true); + __kernelWaitMutex(mutex, timeoutPtr); + __KernelWaitCurThread(WAITTYPE_MUTEX, id, count, timeoutPtr, true); __KernelCheckCallbacks(); } @@ -277,12 +305,18 @@ void sceKernelUnlockMutex(SceUID id, int count) for (iter = mutex->waitingThreads.begin(), end = mutex->waitingThreads.end(); iter != end; ++iter) { SceUID threadID = *iter; + int wVal = (int)__KernelGetWaitValue(threadID, error); mutex->nm.lockThread = threadID; mutex->nm.lockLevel = wVal; - __KernelResumeThreadFromWait(threadID); + // Remove any event for this thread. + // TODO: Only if timeoutPtr? + if (mutex->waitTimer != 0) + CoreTiming::UnscheduleEvent(mutex->waitTimer, threadID); + + __KernelResumeThreadFromWait(threadID, 0); wokeThreads = true; mutex->waitingThreads.erase(iter); break; diff --git a/Core/HLE/sceKernelThread.cpp b/Core/HLE/sceKernelThread.cpp index eb6af75fe..a2c5e8222 100644 --- a/Core/HLE/sceKernelThread.cpp +++ b/Core/HLE/sceKernelThread.cpp @@ -159,6 +159,7 @@ struct NativeThread struct ThreadWaitInfo { u32 waitValue; + u32 timeoutPtr; }; class Thread : public KernelObject @@ -512,6 +513,50 @@ void __KernelLoadContext(ThreadContext *ctx) // currentMIPS->fcr31 = ctx->fcr31; } +void __KernelResumeThreadFromWait(Thread *t) +{ + t->nt.status &= ~THREADSTATUS_WAIT; + // TODO: What if DORMANT or DEAD? + if (!(t->nt.status & THREADSTATUS_WAITSUSPEND)) + t->nt.status = THREADSTATUS_READY; + + // Non-waiting threads do not process callbacks. + t->isProcessingCallbacks = false; +} + +u32 __KernelResumeThreadFromWait(SceUID threadID) +{ + u32 error; + Thread *t = kernelObjects.Get(threadID, error); + if (t) + { + __KernelResumeThreadFromWait(t); + return 0; + } + else + { + ERROR_LOG(HLE, "__KernelResumeThreadFromWait(%d): bad thread: %08x", threadID, error); + return error; + } +} + +u32 __KernelResumeThreadFromWait(SceUID threadID, int retval) +{ + u32 error; + Thread *t = kernelObjects.Get(threadID, error); + if (t) + { + __KernelResumeThreadFromWait(t); + t->setReturnValue(retval); + return 0; + } + else + { + ERROR_LOG(HLE, "__KernelResumeThreadFromWait(%d): bad thread: %08x", threadID, error); + return error; + } +} + // DANGEROUS // Only run when you can safely accept a context switch // Triggers a waitable event, that is, it wakes up all threads that waits for it @@ -527,69 +572,41 @@ bool __KernelTriggerWait(WaitType type, int id, bool dontSwitch) { if (t->nt.waitType == type && t->nt.waitID == id) { - // This threads is waiting for the triggered object - t->nt.status &= ~THREADSTATUS_WAIT; - if (t->nt.status == 0) - { - t->nt.status = THREADSTATUS_READY; - } - // Non-waiting threads do not process callbacks. - t->isProcessingCallbacks = false; + // This thread was waiting for the triggered object. + __KernelResumeThreadFromWait(t); doneAnything = true; } } } -// if (doneAnything) // lumines? - { - if (!dontSwitch) - { - // TODO: time waster - char temp[256]; - sprintf(temp, "resumed from wait %s", waitTypeStrings[(int)type]); - __KernelReSchedule(temp); - } - } +// if (doneAnything) // lumines? + { + if (!dontSwitch) + { + // TODO: time waster + char temp[256]; + sprintf(temp, "resumed from wait %s", waitTypeStrings[(int)type]); + __KernelReSchedule(temp); + } + } return true; } -u32 __KernelResumeThreadFromWait(SceUID threadID) -{ - u32 error; - Thread *t = kernelObjects.Get(threadID, error); - if (t) - { - t->nt.status &= ~THREADSTATUS_WAIT; - if (!(t->nt.status & (THREADSTATUS_SUSPEND | THREADSTATUS_WAIT))) - t->nt.status |= THREADSTATUS_READY; - t->isProcessingCallbacks = false; - return 0; - } - else - { - ERROR_LOG(HLE, "__KernelResumeThreadFromWait(%d): bad thread: %08x", threadID, error); - return error; - } -} - // makes the current thread wait for an event -void __KernelWaitCurThread(WaitType type, SceUID waitID, u32 waitValue, int timeout, bool processCallbacks) +void __KernelWaitCurThread(WaitType type, SceUID waitID, u32 waitValue, u32 timeoutPtr, bool processCallbacks) { currentThread->nt.waitID = waitID; currentThread->nt.waitType = type; __KernelChangeThreadState(currentThread, THREADSTATUS_WAIT); currentThread->nt.numReleases++; currentThread->waitInfo.waitValue = waitValue; - if (timeout) - { - // TODO: - } + currentThread->waitInfo.timeoutPtr = timeoutPtr; RETURN(0); //pretend all went OK - // TODO: time waster - char temp[256]; - sprintf(temp, "started wait %s", waitTypeStrings[(int)type]); + // TODO: time waster + char temp[256]; + sprintf(temp, "started wait %s", waitTypeStrings[(int)type]); __KernelReSchedule(processCallbacks, temp); // TODO: Remove thread from Ready queue? diff --git a/Core/HLE/sceKernelThread.h b/Core/HLE/sceKernelThread.h index d5dd5b576..a84a75d68 100644 --- a/Core/HLE/sceKernelThread.h +++ b/Core/HLE/sceKernelThread.h @@ -68,7 +68,7 @@ enum WaitType //probably not the real values WAITTYPE_AUDIOCHANNEL = 10, // this is fake, should be replaced with 8 eventflags ( ?? ) WAITTYPE_UMD = 11, // this is fake, should be replaced with 1 eventflag ( ?? ) WAITTYPE_VBLANK = 12, // fake - WAITTYPE_MUTEX = 13, + WAITTYPE_MUTEX = 13, }; @@ -103,9 +103,10 @@ void __KernelLoadContext(ThreadContext *ctx); // TODO: Replace this with __KernelResumeThread over time as it's misguided. bool __KernelTriggerWait(WaitType type, int id, bool dontSwitch = false); u32 __KernelResumeThreadFromWait(SceUID threadID); // can return an error value +u32 __KernelResumeThreadFromWait(SceUID threadID, int retval); u32 __KernelGetWaitValue(SceUID threadID, u32 &error); -void __KernelWaitCurThread(WaitType type, SceUID waitId, u32 waitValue, int timeout, bool processCallbacks); +void __KernelWaitCurThread(WaitType type, SceUID waitId, u32 waitValue, u32 timeoutPtr, bool processCallbacks); void __KernelReSchedule(const char *reason = "no reason"); void __KernelReSchedule(bool doCallbacks, const char *reason);