From fa9b9840d0516084d004d431ba35b904ca605106 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Wed, 21 Nov 2012 00:21:10 -0800 Subject: [PATCH] Implement LwMutex locking and unlocking. --- Core/HLE/FunctionWrappers.h | 8 + Core/HLE/sceKernelInterrupt.cpp | 11 +- Core/HLE/sceKernelMutex.cpp | 281 ++++++++++++++++++++++++++++++-- Core/HLE/sceKernelMutex.h | 9 +- Core/HLE/sceKernelThread.cpp | 1 + Core/HLE/sceKernelThread.h | 2 + 6 files changed, 286 insertions(+), 26 deletions(-) diff --git a/Core/HLE/FunctionWrappers.h b/Core/HLE/FunctionWrappers.h index f18108ac25..652713475a 100644 --- a/Core/HLE/FunctionWrappers.h +++ b/Core/HLE/FunctionWrappers.h @@ -108,6 +108,10 @@ template void WrapV_IU() { func(PARAM(0), PARAM(1)); } +template void WrapV_UI() { + func(PARAM(0), PARAM(1)); +} + template void WrapI_CU() { int retval = func(Memory::GetCharPointer(PARAM(0)), PARAM(1)); RETURN(retval); @@ -146,6 +150,10 @@ template void WrapV_IIU() { func(PARAM(0), PARAM(1), PARAM(2)); } +template void WrapV_UIU() { + func(PARAM(0), PARAM(1), PARAM(2)); +} + template void WrapV_IUUUU() { func(PARAM(0), PARAM(1), PARAM(2), PARAM(3), PARAM(4)); } diff --git a/Core/HLE/sceKernelInterrupt.cpp b/Core/HLE/sceKernelInterrupt.cpp index 030efbace1..6e0213cff9 100644 --- a/Core/HLE/sceKernelInterrupt.cpp +++ b/Core/HLE/sceKernelInterrupt.cpp @@ -26,6 +26,7 @@ #include "sceKernel.h" #include "sceKernelThread.h" #include "sceKernelInterrupt.h" +#include "sceKernelMutex.h" struct Interrupt { @@ -428,11 +429,11 @@ const HLEFunction Kernel_Library[] = {0x47a0b729,sceKernelIsCpuIntrSuspended, "sceKernelIsCpuIntrSuspended"}, //flags {0xb55249d2,sceKernelIsCpuIntrEnable, "sceKernelIsCpuIntrEnable"}, {0xa089eca4,sceKernelMemset, "sceKernelMemset"}, - {0xDC692EE3,0, "sceKernelTryLockLwMutex"}, - {0x37431849,0, "sceKernelTryLockLwMutex_600"}, - {0xbea46419,0, "sceKernelLockLwMutex"}, - {0x1FC64E09,0, "sceKernelLockLwMutexCB"}, - {0x15b6446b,0, "sceKernelUnlockLwMutex"}, + {0xDC692EE3,&WrapV_UI, "sceKernelTryLockLwMutex"}, + {0x37431849,&WrapV_UI, "sceKernelTryLockLwMutex_600"}, + {0xbea46419,&WrapV_UIU, "sceKernelLockLwMutex"}, + {0x1FC64E09,&WrapV_UIU, "sceKernelLockLwMutexCB"}, + {0x15b6446b,&WrapV_UI, "sceKernelUnlockLwMutex"}, {0x293b45b8,sceKernelGetThreadId, "sceKernelGetThreadId"}, {0x1839852A,0,"sce_paf_private_memcpy"}, {0xA089ECA4,0,"sce_paf_private_memset"}, diff --git a/Core/HLE/sceKernelMutex.cpp b/Core/HLE/sceKernelMutex.cpp index f8c8375992..5a7002b827 100644 --- a/Core/HLE/sceKernelMutex.cpp +++ b/Core/HLE/sceKernelMutex.cpp @@ -38,7 +38,13 @@ #define PSP_MUTEX_ERROR_UNLOCK_UNDERFLOW 0x800201C7 #define PSP_MUTEX_ERROR_ALREADY_LOCKED 0x800201C8 -#define PSP_LWMUTEX_ERROR_NOT_FOUND 0x800201CA +#define PSP_LWMUTEX_ERROR_NO_SUCH_LWMUTEX 0x800201CA +// Note: used only for _600. +#define PSP_LWMUTEX_ERROR_TRYLOCK_FAILED 0x800201CB +#define PSP_LWMUTEX_ERROR_NOT_LOCKED 0x800201CC +#define PSP_LWMUTEX_ERROR_LOCK_OVERFLOW 0x800201CD +#define PSP_LWMUTEX_ERROR_UNLOCK_UNDERFLOW 0x800201CE +#define PSP_LWMUTEX_ERROR_ALREADY_LOCKED 0x800201CF // Guesswork - not exposed anyway struct NativeMutex @@ -96,7 +102,7 @@ struct LwMutex : public KernelObject { const char *GetName() {return nm.name;} const char *GetTypeName() {return "LwMutex";} - static u32 GetMissingErrorCode() { return PSP_LWMUTEX_ERROR_NOT_FOUND; } + static u32 GetMissingErrorCode() { return PSP_LWMUTEX_ERROR_NO_SUCH_LWMUTEX; } int GetIDType() const { return SCE_KERNEL_TMID_LwMutex; } NativeLwMutex nm; std::vector waitingThreads; @@ -111,10 +117,10 @@ std::map mutexHeldLocks; void __KernelMutexInit() { mutexWaitTimer = CoreTiming::RegisterEvent("MutexTimeout", &__KernelMutexTimeout); + lwMutexWaitTimer = CoreTiming::RegisterEvent("LwMutexTimeout", &__KernelLwMutexTimeout); + // TODO: Install on first mutex (if it's slow?) __KernelListenThreadEnd(&__KernelMutexThreadEnd); - // TODO: Write / enable. - //lwMutexWaitTimer = CoreTiming::RegisterEvent("LwMutexTimeout", &__KernelLwMutexTimeout); mutexInitComplete = true; } @@ -225,7 +231,7 @@ bool __KernelLockMutex(Mutex *mutex, int count, u32 &error) error = SCE_KERNEL_ERROR_ILLEGAL_COUNT; else if (count > 1 && !(mutex->nm.attr & PSP_MUTEX_ATTR_ALLOW_RECURSIVE)) error = SCE_KERNEL_ERROR_ILLEGAL_COUNT; - // Two positive ints will always sum to negative. + // Two positive ints will always overflow to negative. else if (count + mutex->nm.lockLevel < 0) error = PSP_MUTEX_ERROR_LOCK_OVERFLOW; } @@ -243,7 +249,7 @@ bool __KernelLockMutex(Mutex *mutex, int count, u32 &error) if (mutex->nm.lockThread == __KernelGetCurThread()) { // Recursive mutex, let's just increase the lock count and keep going - if ((mutex->nm.attr & PSP_MUTEX_ATTR_ALLOW_RECURSIVE)) + if (mutex->nm.attr & PSP_MUTEX_ATTR_ALLOW_RECURSIVE) { mutex->nm.lockLevel += count; return true; @@ -287,6 +293,9 @@ bool __KernelUnlockMutex(Mutex *mutex, u32 &error) break; } + if (!wokeThreads) + mutex->nm.lockThread = -1; + return wokeThreads; } @@ -541,26 +550,264 @@ void sceKernelDeleteLwMutex(u32 workareaPtr) RETURN(error); } -void sceKernelTryLockLwMutex() +bool __KernelLockLwMutex(NativeLwMutexWorkarea &workarea, int count, u32 &error) { - ERROR_LOG(HLE,"UNIMPL sceKernelTryLockLwMutex()"); - RETURN(0); + if (!error) + { + if (count <= 0) + error = SCE_KERNEL_ERROR_ILLEGAL_COUNT; + else if (count > 1 && !(workarea.attr & PSP_MUTEX_ATTR_ALLOW_RECURSIVE)) + error = SCE_KERNEL_ERROR_ILLEGAL_COUNT; + // Two positive ints will always overflow to negative. + else if (count + workarea.lockLevel < 0) + error = PSP_LWMUTEX_ERROR_LOCK_OVERFLOW; + else if (workarea.uid == -1) + error = PSP_LWMUTEX_ERROR_NO_SUCH_LWMUTEX; + } + + if (error) + return false; + + if (workarea.lockLevel == 0) + { + if (workarea.lockThread != 0) + { + // Validate that it actually exists so we can return an error if not. + kernelObjects.Get(workarea.uid, error); + if (error) + return false; + } + + workarea.lockLevel = count; + workarea.lockThread = __KernelGetCurThread(); + return true; + } + + if (workarea.lockThread == __KernelGetCurThread()) + { + // Recursive mutex, let's just increase the lock count and keep going + if (workarea.attr & PSP_MUTEX_ATTR_ALLOW_RECURSIVE) + { + workarea.lockLevel += count; + return true; + } + else + { + error = PSP_LWMUTEX_ERROR_ALREADY_LOCKED; + return false; + } + } + + return false; } -void sceKernelLockLwMutex() +bool __KernelUnlockLwMutex(NativeLwMutexWorkarea &workarea, u32 &error) { - ERROR_LOG(HLE,"UNIMPL sceKernelLockLwMutex()"); - RETURN(0); + LwMutex *mutex = kernelObjects.Get(workarea.uid, error); + if (error) + { + workarea.lockThread = 0; + return false; + } + + // TODO: PSP_MUTEX_ATTR_PRIORITY + bool wokeThreads = false; + std::vector::iterator iter, end; + for (iter = mutex->waitingThreads.begin(), end = mutex->waitingThreads.end(); iter != end; ++iter) + { + SceUID threadID = *iter; + + int wVal = (int)__KernelGetWaitValue(threadID, error); + u32 timeoutPtr = __KernelGetWaitTimeoutPtr(threadID, error); + + workarea.lockLevel = wVal; + workarea.lockThread = threadID; + + if (timeoutPtr != 0 && lwMutexWaitTimer != 0) + { + // Remove any event for this thread. + int cyclesLeft = CoreTiming::UnscheduleEvent(lwMutexWaitTimer, threadID); + Memory::Write_U32(cyclesToUs(cyclesLeft), timeoutPtr); + } + + __KernelResumeThreadFromWait(threadID, 0); + wokeThreads = true; + mutex->waitingThreads.erase(iter); + break; + } + + if (!wokeThreads) + workarea.lockThread = 0; + + return wokeThreads; } -void sceKernelLockLwMutexCB() +void __KernelLwMutexTimeout(u64 userdata, int cyclesLate) { - ERROR_LOG(HLE,"UNIMPL sceKernelLockLwMutexCB()"); - RETURN(0); + SceUID threadID = (SceUID)userdata; + + u32 error; + u32 timeoutPtr = __KernelGetWaitTimeoutPtr(threadID, error); + if (timeoutPtr != 0) + Memory::Write_U32(0, timeoutPtr); + + SceUID mutexID = __KernelGetWaitID(threadID, WAITTYPE_LWMUTEX, error); + LwMutex *mutex = kernelObjects.Get(mutexID, error); + if (mutex) + { + // This thread isn't waiting anymore. + mutex->waitingThreads.erase(std::remove(mutex->waitingThreads.begin(), mutex->waitingThreads.end(), threadID), mutex->waitingThreads.end()); + } + + __KernelResumeThreadFromWait(threadID, SCE_KERNEL_ERROR_WAIT_TIMEOUT); } -void sceKernelUnlockLwMutex() +void __KernelWaitLwMutex(LwMutex *mutex, u32 timeoutPtr) { - ERROR_LOG(HLE,"UNIMPL void sceKernelUnlockLwMutex()"); + if (timeoutPtr == 0 || lwMutexWaitTimer == 0) + return; + + // This should call __KernelMutexTimeout() later, unless we cancel it. + int micro = (int) Memory::Read_U32(timeoutPtr); + CoreTiming::ScheduleEvent(usToCycles(micro), lwMutexWaitTimer, __KernelGetCurThread()); +} + +void sceKernelTryLockLwMutex(u32 workareaPtr, int count) +{ + DEBUG_LOG(HLE,"sceKernelTryLockLwMutex(%08x, %i)", workareaPtr, count); + + NativeLwMutexWorkarea workarea; + Memory::ReadStruct(workareaPtr, &workarea); + + u32 error = 0; + if (__KernelLockLwMutex(workarea, count, error)) + { + Memory::WriteStruct(workareaPtr, &workarea); + RETURN(0); + __KernelReSchedule("lwmutex trylocked"); + } + else if (error) + RETURN(PSP_MUTEX_ERROR_TRYLOCK_FAILED); + else + RETURN(PSP_MUTEX_ERROR_TRYLOCK_FAILED); +} + +void sceKernelTryLockLwMutex_600(u32 workareaPtr, int count) +{ + DEBUG_LOG(HLE,"sceKernelTryLockLwMutex_600(%08x, %i)", workareaPtr, count); + + NativeLwMutexWorkarea workarea; + Memory::ReadStruct(workareaPtr, &workarea); + + u32 error = 0; + if (__KernelLockLwMutex(workarea, count, error)) + { + Memory::WriteStruct(workareaPtr, &workarea); + RETURN(0); + __KernelReSchedule("lwmutex trylocked"); + } + else if (error) + RETURN(error); + else + RETURN(PSP_LWMUTEX_ERROR_TRYLOCK_FAILED); +} + +void sceKernelLockLwMutex(u32 workareaPtr, int count, u32 timeoutPtr) +{ + DEBUG_LOG(HLE,"sceKernelLockLwMutex(%08x, %i, %08x)", workareaPtr, count, timeoutPtr); + + NativeLwMutexWorkarea workarea; + Memory::ReadStruct(workareaPtr, &workarea); + + u32 error = 0; + if (__KernelLockLwMutex(workarea, count, error)) + { + Memory::WriteStruct(workareaPtr, &workarea); + RETURN(0); + __KernelReSchedule("lwmutex locked"); + } + else if (error) + RETURN(error); + else + { + LwMutex *mutex = kernelObjects.Get(workarea.uid, error); + if (mutex) + { + mutex->waitingThreads.push_back(__KernelGetCurThread()); + __KernelWaitLwMutex(mutex, timeoutPtr); + __KernelWaitCurThread(WAITTYPE_LWMUTEX, workarea.uid, count, timeoutPtr, false); + } + else + RETURN(error); + } +} + +void sceKernelLockLwMutexCB(u32 workareaPtr, int count, u32 timeoutPtr) +{ + DEBUG_LOG(HLE,"sceKernelLockLwMutexCB(%08x, %i, %08x)", workareaPtr, count, timeoutPtr); + + NativeLwMutexWorkarea workarea; + Memory::ReadStruct(workareaPtr, &workarea); + + u32 error = 0; + if (__KernelLockLwMutex(workarea, count, error)) + { + Memory::WriteStruct(workareaPtr, &workarea); + RETURN(0); + __KernelReSchedule("lwmutex locked"); + } + else if (error) + RETURN(error); + else + { + LwMutex *mutex = kernelObjects.Get(workarea.uid, error); + if (mutex) + { + mutex->waitingThreads.push_back(__KernelGetCurThread()); + __KernelWaitLwMutex(mutex, timeoutPtr); + __KernelWaitCurThread(WAITTYPE_LWMUTEX, workarea.uid, count, timeoutPtr, true); + __KernelCheckCallbacks(); + } + else + RETURN(error); + } +} + +void sceKernelUnlockLwMutex(u32 workareaPtr, int count) +{ + DEBUG_LOG(HLE,"sceKernelUnlockLwMutex(%08x, %i)", workareaPtr, count); + + NativeLwMutexWorkarea workarea; + Memory::ReadStruct(workareaPtr, &workarea); + + u32 error = 0; + if (workarea.uid == -1) + error = PSP_LWMUTEX_ERROR_NO_SUCH_LWMUTEX; + else if (count <= 0) + error = SCE_KERNEL_ERROR_ILLEGAL_COUNT; + else if ((workarea.attr & PSP_MUTEX_ATTR_ALLOW_RECURSIVE) == 0 && count > 1) + error = SCE_KERNEL_ERROR_ILLEGAL_COUNT; + else if (workarea.lockLevel == 0) + error = PSP_LWMUTEX_ERROR_NOT_LOCKED; + else if (workarea.lockLevel < count) + error = PSP_LWMUTEX_ERROR_UNLOCK_UNDERFLOW; + + if (error) + { + RETURN(error); + return; + } + + workarea.lockLevel -= count; RETURN(0); + + if (workarea.lockLevel == 0) + { + __KernelUnlockLwMutex(workarea, error); + Memory::WriteStruct(workareaPtr, &workarea); + __KernelReSchedule("mutex unlocked"); + } + else + Memory::WriteStruct(workareaPtr, &workarea); + } \ No newline at end of file diff --git a/Core/HLE/sceKernelMutex.h b/Core/HLE/sceKernelMutex.h index ef7141fb34..bb89691fb7 100644 --- a/Core/HLE/sceKernelMutex.h +++ b/Core/HLE/sceKernelMutex.h @@ -26,10 +26,11 @@ void sceKernelUnlockMutex(SceUID id, int count); void sceKernelCreateLwMutex(u32 workareaPtr, const char *name, u32 attr, int initialCount, u32 optionsPtr); void sceKernelDeleteLwMutex(u32 workareaPtr); -void sceKernelTryLockLwMutex(); -void sceKernelLockLwMutex(); -void sceKernelLockLwMutexCB(); -void sceKernelUnlockLwMutex(); +void sceKernelTryLockLwMutex(u32 workareaPtr, int count); +void sceKernelTryLockLwMutex_600(u32 workareaPtr, int count); +void sceKernelLockLwMutex(u32 workareaPtr, int count, u32 timeoutPtr); +void sceKernelLockLwMutexCB(u32 workareaPtr, int count, u32 timeoutPtr); +void sceKernelUnlockLwMutex(u32 workareaPtr, int count); void __KernelMutexTimeout(u64 userdata, int cyclesLate); void __KernelLwMutexTimeout(u64 userdata, int cyclesLate); diff --git a/Core/HLE/sceKernelThread.cpp b/Core/HLE/sceKernelThread.cpp index cc542f0a1e..ea5fdf0671 100644 --- a/Core/HLE/sceKernelThread.cpp +++ b/Core/HLE/sceKernelThread.cpp @@ -72,6 +72,7 @@ const char *waitTypeStrings[] = "Umd", "Vblank", "Mutex", + "LwMutex", }; struct SceKernelSysClock { diff --git a/Core/HLE/sceKernelThread.h b/Core/HLE/sceKernelThread.h index e0918aab90..4e80b2afaf 100644 --- a/Core/HLE/sceKernelThread.h +++ b/Core/HLE/sceKernelThread.h @@ -69,6 +69,8 @@ enum WaitType //probably not the real values WAITTYPE_UMD = 11, // this is fake, should be replaced with 1 eventflag ( ?? ) WAITTYPE_VBLANK = 12, // fake WAITTYPE_MUTEX = 13, + WAITTYPE_LWMUTEX = 14, + // Remember to update sceKernelThread.cpp's waitTypeStrings to match. };