From 4cddd3092a66d74bc50f4c1054e3f39b7aa3b988 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Wed, 14 Nov 2012 08:00:57 -0800 Subject: [PATCH 01/25] Implement basic locking for sceKernel mutexes. Test still not passing, due to odd order the mutex is unlocked in? But, at least, it does lock now. --- Core/HLE/sceKernel.cpp | 2 +- Core/HLE/sceKernelMutex.cpp | 96 ++++++++++++++++++++++++++++++++----- Core/HLE/sceKernelMutex.h | 2 +- 3 files changed, 86 insertions(+), 14 deletions(-) diff --git a/Core/HLE/sceKernel.cpp b/Core/HLE/sceKernel.cpp index 212edfb60b..6cfc721498 100644 --- a/Core/HLE/sceKernel.cpp +++ b/Core/HLE/sceKernel.cpp @@ -342,7 +342,7 @@ const HLEFunction ThreadManForUser[] = {0xf8170fbe,&WrapU_U,"sceKernelDeleteMutex"}, {0xB011B11F,&WrapU_UUU,"sceKernelLockMutex"}, {0x5bf4dd27,&WrapU_UUU,"sceKernelLockMutexCB"}, - {0x6b30100f,&WrapU_UU,"sceKernelUnlockMutex"}, + {0x6b30100f,&WrapV_UU,"sceKernelUnlockMutex"}, {0xb7d098c6,&WrapU_CUU,"sceKernelCreateMutex"}, {0x0DDCD2C9, 0, "sceKernelTryLockMutex"}, // NOTE: LockLwMutex and UnlockLwMutex are in Kernel_Library, see sceKernelInterrupt.cpp. diff --git a/Core/HLE/sceKernelMutex.cpp b/Core/HLE/sceKernelMutex.cpp index 40448dca8a..52f3abe8c3 100644 --- a/Core/HLE/sceKernelMutex.cpp +++ b/Core/HLE/sceKernelMutex.cpp @@ -30,6 +30,7 @@ // Not sure about the names of these #define PSP_MUTEX_ERROR_NOT_LOCKED 0x800201C7 #define PSP_MUTEX_ERROR_NO_SUCH_MUTEX 0x800201C3 +#define PSP_MUTEX_ERROR_UNLOCK_UNDERFLOW 0x800201C7 // Guesswork - not exposed anyway struct NativeMutex @@ -96,6 +97,11 @@ u32 sceKernelLockMutex(u32 id, u32 count, u32 timeoutPtr) Mutex *mutex = kernelObjects.Get(id, error); if (!mutex) return PSP_MUTEX_ERROR_NO_SUCH_MUTEX; + if (count <= 0) + return SCE_KERNEL_ERROR_ILLEGAL_COUNT; + if (count > 1 && !(mutex->nm.attr & PSP_MUTEX_ATTR_ALLOW_RECURSIVE)) + return SCE_KERNEL_ERROR_ILLEGAL_COUNT; + if (mutex->nm.lockLevel == 0) { mutex->nm.lockLevel += count; @@ -109,32 +115,98 @@ u32 sceKernelLockMutex(u32 id, u32 count, u32 timeoutPtr) } else { - // Yeah, we need to block. Somehow. - ERROR_LOG(HLE,"Mutex should block!"); + mutex->waitingThreads.push_back(__KernelGetCurThread()); + __KernelWaitCurThread(WAITTYPE_MUTEX, id, count, 0, false); } return 0; } u32 sceKernelLockMutexCB(u32 id, u32 count, u32 timeoutPtr) { - ERROR_LOG(HLE,"UNIMPL sceKernelLockMutexCB(%i, %i, %08x)", id, count, timeoutPtr); - return 0; -} - -u32 sceKernelUnlockMutex(u32 id, u32 count) -{ - DEBUG_LOG(HLE,"UNFINISHED sceKernelUnlockMutex(%i, %i)", id, count); + DEBUG_LOG(HLE,"sceKernelLockMutexCB(%i, %i, %08x)", id, count, timeoutPtr); u32 error; Mutex *mutex = kernelObjects.Get(id, error); if (!mutex) return PSP_MUTEX_ERROR_NO_SUCH_MUTEX; + if (count <= 0) + return SCE_KERNEL_ERROR_ILLEGAL_COUNT; + if (count > 1 && !(mutex->nm.attr & PSP_MUTEX_ATTR_ALLOW_RECURSIVE)) + return SCE_KERNEL_ERROR_ILLEGAL_COUNT; + if (mutex->nm.lockLevel == 0) - return PSP_MUTEX_ERROR_NOT_LOCKED; - mutex->nm.lockLevel -= count; - // TODO.... + { + mutex->nm.lockLevel += count; + mutex->nm.lockThread = __KernelGetCurThread(); + // Nobody had it locked - no need to block + } + else if ((mutex->nm.attr & PSP_MUTEX_ATTR_ALLOW_RECURSIVE) && mutex->nm.lockThread == __KernelGetCurThread()) + { + // Recursive mutex, let's just increase the lock count and keep going + mutex->nm.lockLevel += count; + } + else + { + mutex->waitingThreads.push_back(__KernelGetCurThread()); + __KernelWaitCurThread(WAITTYPE_MUTEX, id, count, 0, true); + __KernelCheckCallbacks(); + } return 0; } +// int sceKernelUnlockMutex(SceUID id, int count) +// void because it changes threads. +void sceKernelUnlockMutex(u32 id, u32 count) +{ + DEBUG_LOG(HLE,"sceKernelUnlockMutex(%i, %i)", id, count); + u32 error; + Mutex *mutex = kernelObjects.Get(id, error); + if (!mutex) + { + RETURN(PSP_MUTEX_ERROR_NO_SUCH_MUTEX); + return; + } + if (mutex->nm.lockLevel == 0) + { + RETURN(PSP_MUTEX_ERROR_NOT_LOCKED); + return; + } + if (mutex->nm.lockLevel < count) + { + RETURN(PSP_MUTEX_ERROR_UNLOCK_UNDERFLOW); + return; + } + mutex->nm.lockLevel -= count; + RETURN(0); + + if (mutex->nm.lockLevel == 0) + { + mutex->nm.lockThread = -1; + + // TODO: PSP_MUTEX_ATTR_PRIORITY + bool wokeThreads = false; + // TODO: Seems to go in reverse order. Maybe do more testing / related to creation order? + std::vector::reverse_iterator iter, rend; + for (iter = mutex->waitingThreads.rbegin(), rend = mutex->waitingThreads.rend(); iter != rend; ++iter) + { + SceUID threadID = *iter; + int wVal = (int)__KernelGetWaitValue(threadID, error); + + mutex->nm.lockThread = threadID; + mutex->nm.lockLevel = wVal; + + __KernelResumeThreadFromWait(threadID); + wokeThreads = true; + // Plus/minus 0 - need an iterator not a reverse_iterator. + mutex->waitingThreads.erase((++iter).base()); + break; + } + + // Not sure if this should actually resched, need to test. + if (wokeThreads) + __KernelReSchedule("mutex unlocked"); + } +} + struct NativeLwMutex { SceSize size; diff --git a/Core/HLE/sceKernelMutex.h b/Core/HLE/sceKernelMutex.h index f5f19c67fb..7f8c982b0c 100644 --- a/Core/HLE/sceKernelMutex.h +++ b/Core/HLE/sceKernelMutex.h @@ -22,7 +22,7 @@ u32 sceKernelCreateMutex(const char *name, u32 attr, u32 options); u32 sceKernelDeleteMutex(u32 id); u32 sceKernelLockMutex(u32 id, u32 count, u32 timeoutPtr); u32 sceKernelLockMutexCB(u32 id, u32 count, u32 timeoutPtr); -u32 sceKernelUnlockMutex(u32 id, u32 count); +void sceKernelUnlockMutex(u32 id, u32 count); void sceKernelCreateLwMutex(); void sceKernelDeleteLwMutex(); From 4bab714db5111f82322f18121ca253188d956ea8 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Wed, 14 Nov 2012 23:14:41 -0800 Subject: [PATCH 02/25] Mutexes go in FIFO order as expected. The test was wrong. --- Core/HLE/sceKernelMutex.cpp | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/Core/HLE/sceKernelMutex.cpp b/Core/HLE/sceKernelMutex.cpp index 52f3abe8c3..5db6ea4e7f 100644 --- a/Core/HLE/sceKernelMutex.cpp +++ b/Core/HLE/sceKernelMutex.cpp @@ -184,9 +184,8 @@ void sceKernelUnlockMutex(u32 id, u32 count) // TODO: PSP_MUTEX_ATTR_PRIORITY bool wokeThreads = false; - // TODO: Seems to go in reverse order. Maybe do more testing / related to creation order? - std::vector::reverse_iterator iter, rend; - for (iter = mutex->waitingThreads.rbegin(), rend = mutex->waitingThreads.rend(); iter != rend; ++iter) + 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); @@ -196,8 +195,7 @@ void sceKernelUnlockMutex(u32 id, u32 count) __KernelResumeThreadFromWait(threadID); wokeThreads = true; - // Plus/minus 0 - need an iterator not a reverse_iterator. - mutex->waitingThreads.erase((++iter).base()); + mutex->waitingThreads.erase(iter); break; } From 429ac9de8797d973bafe6dcad89dbbf492063bd0 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Wed, 14 Nov 2012 23:15:33 -0800 Subject: [PATCH 03/25] Oops, CreateMutex was missing a parameter. --- Core/HLE/FunctionWrappers.h | 5 +++++ Core/HLE/sceKernel.cpp | 2 +- Core/HLE/sceKernelMutex.cpp | 7 ++++--- Core/HLE/sceKernelMutex.h | 2 +- 4 files changed, 11 insertions(+), 5 deletions(-) diff --git a/Core/HLE/FunctionWrappers.h b/Core/HLE/FunctionWrappers.h index 706faa3c05..768b31bf11 100644 --- a/Core/HLE/FunctionWrappers.h +++ b/Core/HLE/FunctionWrappers.h @@ -133,6 +133,11 @@ template void WrapU_CUU() { RETURN((u32)retval); } +template void WrapU_CUUU() { + int retval = func(Memory::GetCharPointer(PARAM(0)), PARAM(1), PARAM(2), PARAM(3)); + RETURN((u32)retval); +} + template void WrapU_UUU() { u32 retval = func(PARAM(0), PARAM(1), PARAM(2)); RETURN(retval); diff --git a/Core/HLE/sceKernel.cpp b/Core/HLE/sceKernel.cpp index 6cfc721498..480c640f7f 100644 --- a/Core/HLE/sceKernel.cpp +++ b/Core/HLE/sceKernel.cpp @@ -343,7 +343,7 @@ const HLEFunction ThreadManForUser[] = {0xB011B11F,&WrapU_UUU,"sceKernelLockMutex"}, {0x5bf4dd27,&WrapU_UUU,"sceKernelLockMutexCB"}, {0x6b30100f,&WrapV_UU,"sceKernelUnlockMutex"}, - {0xb7d098c6,&WrapU_CUU,"sceKernelCreateMutex"}, + {0xb7d098c6,&WrapU_CUUU,"sceKernelCreateMutex"}, {0x0DDCD2C9, 0, "sceKernelTryLockMutex"}, // NOTE: LockLwMutex and UnlockLwMutex are in Kernel_Library, see sceKernelInterrupt.cpp. diff --git a/Core/HLE/sceKernelMutex.cpp b/Core/HLE/sceKernelMutex.cpp index 5db6ea4e7f..56823dc7c1 100644 --- a/Core/HLE/sceKernelMutex.cpp +++ b/Core/HLE/sceKernelMutex.cpp @@ -28,7 +28,7 @@ #define PSP_MUTEX_ATTR_ALLOW_RECURSIVE 0x200 // Not sure about the names of these -#define PSP_MUTEX_ERROR_NOT_LOCKED 0x800201C7 +#define PSP_MUTEX_ERROR_NOT_LOCKED 0x800201C5 #define PSP_MUTEX_ERROR_NO_SUCH_MUTEX 0x800201C3 #define PSP_MUTEX_ERROR_UNLOCK_UNDERFLOW 0x800201C7 @@ -63,7 +63,7 @@ struct LWMutex : public KernelObject std::vector waitingThreads; }; -u32 sceKernelCreateMutex(const char *name, u32 attr, u32 options) +u32 sceKernelCreateMutex(const char *name, u32 attr, u32 initial_count, u32 options) { DEBUG_LOG(HLE,"sceKernelCreateMutex(%s, %08x, %08x)", name, attr, options); @@ -72,7 +72,8 @@ u32 sceKernelCreateMutex(const char *name, u32 attr, u32 options) mutex->nm.size = sizeof(mutex); mutex->nm.attr = attr; - mutex->nm.lockLevel = 0; + mutex->nm.lockLevel = initial_count; + // TODO: Does initial_count > 0 mean lock automatically by the current thread? Would make sense. mutex->nm.lockThread = -1; strncpy(mutex->nm.name, name, 32); diff --git a/Core/HLE/sceKernelMutex.h b/Core/HLE/sceKernelMutex.h index 7f8c982b0c..752e04914d 100644 --- a/Core/HLE/sceKernelMutex.h +++ b/Core/HLE/sceKernelMutex.h @@ -18,7 +18,7 @@ #pragma once // TODO -u32 sceKernelCreateMutex(const char *name, u32 attr, u32 options); +u32 sceKernelCreateMutex(const char *name, u32 attr, u32 initial_count, u32 options); u32 sceKernelDeleteMutex(u32 id); u32 sceKernelLockMutex(u32 id, u32 count, u32 timeoutPtr); u32 sceKernelLockMutexCB(u32 id, u32 count, u32 timeoutPtr); From 2613ed8806327876b8f3d02dcf3c99a4d382e04a Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Wed, 14 Nov 2012 23:34:52 -0800 Subject: [PATCH 04/25] Cleanup mutex func types. Looks like mutexes reschedule when they lock, so switch to void. --- Core/HLE/FunctionWrappers.h | 10 +++--- Core/HLE/sceKernel.cpp | 10 +++--- Core/HLE/sceKernelMutex.cpp | 69 +++++++++++++++++++++++++++---------- Core/HLE/sceKernelMutex.h | 10 +++--- 4 files changed, 65 insertions(+), 34 deletions(-) diff --git a/Core/HLE/FunctionWrappers.h b/Core/HLE/FunctionWrappers.h index 768b31bf11..cf131bfb05 100644 --- a/Core/HLE/FunctionWrappers.h +++ b/Core/HLE/FunctionWrappers.h @@ -118,6 +118,11 @@ template void WrapI_CUUU() { RETURN(retval); } +template void WrapI_CUIU() { + int retval = func(Memory::GetCharPointer(PARAM(0)), PARAM(1), PARAM(2), PARAM(3)); + RETURN(retval); +} + template void WrapU_CU() { int retval = func(Memory::GetCharPointer(PARAM(0)), PARAM(1)); RETURN((u32)retval); @@ -133,11 +138,6 @@ template void WrapU_CUU() { RETURN((u32)retval); } -template void WrapU_CUUU() { - int retval = func(Memory::GetCharPointer(PARAM(0)), PARAM(1), PARAM(2), PARAM(3)); - RETURN((u32)retval); -} - template void WrapU_UUU() { u32 retval = func(PARAM(0), PARAM(1), PARAM(2)); RETURN(retval); diff --git a/Core/HLE/sceKernel.cpp b/Core/HLE/sceKernel.cpp index 480c640f7f..3132c16a8c 100644 --- a/Core/HLE/sceKernel.cpp +++ b/Core/HLE/sceKernel.cpp @@ -339,11 +339,11 @@ const HLEFunction ThreadManForUser[] = {0x60107536,0,"sceKernelDeleteLwMutex"}, {0x19CFF145,0,"sceKernelCreateLwMutex"}, - {0xf8170fbe,&WrapU_U,"sceKernelDeleteMutex"}, - {0xB011B11F,&WrapU_UUU,"sceKernelLockMutex"}, - {0x5bf4dd27,&WrapU_UUU,"sceKernelLockMutexCB"}, - {0x6b30100f,&WrapV_UU,"sceKernelUnlockMutex"}, - {0xb7d098c6,&WrapU_CUUU,"sceKernelCreateMutex"}, + {0xf8170fbe,&WrapV_I,"sceKernelDeleteMutex"}, + {0xB011B11F,&WrapV_IIU,"sceKernelLockMutex"}, + {0x5bf4dd27,&WrapV_IIU,"sceKernelLockMutexCB"}, + {0x6b30100f,&WrapV_II,"sceKernelUnlockMutex"}, + {0xb7d098c6,&WrapI_CUIU,"sceKernelCreateMutex"}, {0x0DDCD2C9, 0, "sceKernelTryLockMutex"}, // NOTE: LockLwMutex and UnlockLwMutex are in Kernel_Library, see sceKernelInterrupt.cpp. diff --git a/Core/HLE/sceKernelMutex.cpp b/Core/HLE/sceKernelMutex.cpp index 56823dc7c1..927c29224e 100644 --- a/Core/HLE/sceKernelMutex.cpp +++ b/Core/HLE/sceKernelMutex.cpp @@ -63,16 +63,16 @@ struct LWMutex : public KernelObject std::vector waitingThreads; }; -u32 sceKernelCreateMutex(const char *name, u32 attr, u32 initial_count, u32 options) +SceUID sceKernelCreateMutex(const char *name, u32 attr, int initialCount, u32 optionsPtr) { - DEBUG_LOG(HLE,"sceKernelCreateMutex(%s, %08x, %08x)", name, attr, options); + DEBUG_LOG(HLE,"sceKernelCreateMutex(%s, %08x, %d, %08x)", name, attr, initialCount, optionsPtr); Mutex *mutex = new Mutex(); SceUID id = kernelObjects.Create(mutex); mutex->nm.size = sizeof(mutex); mutex->nm.attr = attr; - mutex->nm.lockLevel = initial_count; + mutex->nm.lockLevel = initialCount; // TODO: Does initial_count > 0 mean lock automatically by the current thread? Would make sense. mutex->nm.lockThread = -1; @@ -80,28 +80,46 @@ u32 sceKernelCreateMutex(const char *name, u32 attr, u32 initial_count, u32 opti return id; } -u32 sceKernelDeleteMutex(u32 id) +void sceKernelDeleteMutex(SceUID id) { DEBUG_LOG(HLE,"sceKernelDeleteMutex(%i)", id); u32 error; Mutex *mutex = kernelObjects.Get(id, error); - if (!mutex) - return PSP_MUTEX_ERROR_NO_SUCH_MUTEX; - kernelObjects.Destroy(id); - return 0; + if (mutex) + { + RETURN(0); + + kernelObjects.Destroy(id); + // TODO: Almost certainly need to reschedule (sometimes?) + } + else + RETURN(PSP_MUTEX_ERROR_NO_SUCH_MUTEX); } -u32 sceKernelLockMutex(u32 id, u32 count, u32 timeoutPtr) +// int sceKernelLockMutex(SceUID id, int count, int *timeout) +// void because it changes threads. +void sceKernelLockMutex(SceUID id, int count, u32 timeoutPtr) { DEBUG_LOG(HLE,"sceKernelLockMutex(%i, %i, %08x)", id, count, timeoutPtr); u32 error; Mutex *mutex = kernelObjects.Get(id, error); if (!mutex) - return PSP_MUTEX_ERROR_NO_SUCH_MUTEX; + { + RETURN(PSP_MUTEX_ERROR_NO_SUCH_MUTEX); + return; + } if (count <= 0) - return SCE_KERNEL_ERROR_ILLEGAL_COUNT; + { + RETURN(SCE_KERNEL_ERROR_ILLEGAL_COUNT); + return; + } if (count > 1 && !(mutex->nm.attr & PSP_MUTEX_ATTR_ALLOW_RECURSIVE)) - return SCE_KERNEL_ERROR_ILLEGAL_COUNT; + { + RETURN(SCE_KERNEL_ERROR_ILLEGAL_COUNT); + return; + } + + RETURN(0); if (mutex->nm.lockLevel == 0) { @@ -119,20 +137,32 @@ u32 sceKernelLockMutex(u32 id, u32 count, u32 timeoutPtr) mutex->waitingThreads.push_back(__KernelGetCurThread()); __KernelWaitCurThread(WAITTYPE_MUTEX, id, count, 0, false); } - return 0; } -u32 sceKernelLockMutexCB(u32 id, u32 count, u32 timeoutPtr) +// int sceKernelLockMutexCB(SceUID id, int count, int *timeout) +// void because it changes threads. +void sceKernelLockMutexCB(SceUID id, int count, u32 timeoutPtr) { DEBUG_LOG(HLE,"sceKernelLockMutexCB(%i, %i, %08x)", id, count, timeoutPtr); u32 error; Mutex *mutex = kernelObjects.Get(id, error); if (!mutex) - return PSP_MUTEX_ERROR_NO_SUCH_MUTEX; + { + RETURN(PSP_MUTEX_ERROR_NO_SUCH_MUTEX); + return; + } if (count <= 0) - return SCE_KERNEL_ERROR_ILLEGAL_COUNT; + { + RETURN(SCE_KERNEL_ERROR_ILLEGAL_COUNT); + return; + } if (count > 1 && !(mutex->nm.attr & PSP_MUTEX_ATTR_ALLOW_RECURSIVE)) - return SCE_KERNEL_ERROR_ILLEGAL_COUNT; + { + RETURN(SCE_KERNEL_ERROR_ILLEGAL_COUNT); + return; + } + + RETURN(0); if (mutex->nm.lockLevel == 0) { @@ -151,12 +181,13 @@ u32 sceKernelLockMutexCB(u32 id, u32 count, u32 timeoutPtr) __KernelWaitCurThread(WAITTYPE_MUTEX, id, count, 0, true); __KernelCheckCallbacks(); } - return 0; + + __KernelReSchedule("mutex locked"); } // int sceKernelUnlockMutex(SceUID id, int count) // void because it changes threads. -void sceKernelUnlockMutex(u32 id, u32 count) +void sceKernelUnlockMutex(SceUID id, int count) { DEBUG_LOG(HLE,"sceKernelUnlockMutex(%i, %i)", id, count); u32 error; diff --git a/Core/HLE/sceKernelMutex.h b/Core/HLE/sceKernelMutex.h index 752e04914d..21713dba81 100644 --- a/Core/HLE/sceKernelMutex.h +++ b/Core/HLE/sceKernelMutex.h @@ -18,11 +18,11 @@ #pragma once // TODO -u32 sceKernelCreateMutex(const char *name, u32 attr, u32 initial_count, u32 options); -u32 sceKernelDeleteMutex(u32 id); -u32 sceKernelLockMutex(u32 id, u32 count, u32 timeoutPtr); -u32 sceKernelLockMutexCB(u32 id, u32 count, u32 timeoutPtr); -void sceKernelUnlockMutex(u32 id, u32 count); +SceUID sceKernelCreateMutex(const char *name, u32 attr, int initialCount, u32 optionsPtr); +void sceKernelDeleteMutex(SceUID id); +void sceKernelLockMutex(SceUID id, int count, u32 timeoutPtr); +void sceKernelLockMutexCB(SceUID id, int count, u32 timeoutPtr); +void sceKernelUnlockMutex(SceUID id, int count); void sceKernelCreateLwMutex(); void sceKernelDeleteLwMutex(); From a539580195ee1a3fcbeb400ea19edc427f278254 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Wed, 14 Nov 2012 23:40:20 -0800 Subject: [PATCH 05/25] Reschedule after acquiring a lock. This matches the actual test results. --- Core/HLE/sceKernelMutex.cpp | 4 +++- test.py | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/Core/HLE/sceKernelMutex.cpp b/Core/HLE/sceKernelMutex.cpp index 927c29224e..2cad2461c5 100644 --- a/Core/HLE/sceKernelMutex.cpp +++ b/Core/HLE/sceKernelMutex.cpp @@ -125,7 +125,9 @@ void sceKernelLockMutex(SceUID id, int count, u32 timeoutPtr) { mutex->nm.lockLevel += count; mutex->nm.lockThread = __KernelGetCurThread(); - // Nobody had it locked - no need to block + + // Needed to get the proper order per real PSP. + __KernelReSchedule("mutex locked"); } else if ((mutex->nm.attr & PSP_MUTEX_ATTR_ALLOW_RECURSIVE) && mutex->nm.lockThread == __KernelGetCurThread()) { diff --git a/test.py b/test.py index 7485b53694..e9686032e1 100644 --- a/test.py +++ b/test.py @@ -31,6 +31,7 @@ tests_good = [ "string/string", "gpu/callbacks/ge_callbacks", "threads/mbx/mbx", + "threads/mutex/mutex", "threads/semaphores/semaphores", "threads/semaphores/cancel/cancel", "threads/semaphores/create/create", @@ -49,7 +50,6 @@ tests_next = [ # These are the next tests up for fixing. These run by default. "threads/fpl/fpl", "threads/msgpipe/msgpipe", - "threads/mutex/mutex", "threads/scheduling/scheduling", "threads/semaphores/priority/priority", "threads/semaphores/wait/wait", From 4bfa24eca8e7a4b9deaf792e13fb19c99291247e Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 17 Nov 2012 19:34:39 -0800 Subject: [PATCH 06/25] Fix sceKernelCreateMutex() to match tests. Still have an issue where reusing threads doesn't work. --- Core/HLE/FunctionWrappers.h | 9 ++++---- Core/HLE/sceKernel.cpp | 2 +- Core/HLE/sceKernelMutex.cpp | 44 ++++++++++++++++++++++++++++--------- Core/HLE/sceKernelMutex.h | 3 +-- 4 files changed, 40 insertions(+), 18 deletions(-) diff --git a/Core/HLE/FunctionWrappers.h b/Core/HLE/FunctionWrappers.h index cf131bfb05..224f39a525 100644 --- a/Core/HLE/FunctionWrappers.h +++ b/Core/HLE/FunctionWrappers.h @@ -118,11 +118,6 @@ template void WrapI_CUUU() { RETURN(retval); } -template void WrapI_CUIU() { - int retval = func(Memory::GetCharPointer(PARAM(0)), PARAM(1), PARAM(2), PARAM(3)); - RETURN(retval); -} - template void WrapU_CU() { int retval = func(Memory::GetCharPointer(PARAM(0)), PARAM(1)); RETURN((u32)retval); @@ -163,6 +158,10 @@ template void WrapV_UUUU() { func(PARAM(0), PARAM(1), PARAM(2), PARAM(3)); } +template void WrapV_CUIU() { + func(Memory::GetCharPointer(PARAM(0)), PARAM(1), PARAM(2), PARAM(3)); +} + template void WrapV_CUIIU() { func(Memory::GetCharPointer(PARAM(0)), PARAM(1), PARAM(2), PARAM(3), PARAM(4)); } diff --git a/Core/HLE/sceKernel.cpp b/Core/HLE/sceKernel.cpp index 3132c16a8c..eee7c7510d 100644 --- a/Core/HLE/sceKernel.cpp +++ b/Core/HLE/sceKernel.cpp @@ -343,7 +343,7 @@ const HLEFunction ThreadManForUser[] = {0xB011B11F,&WrapV_IIU,"sceKernelLockMutex"}, {0x5bf4dd27,&WrapV_IIU,"sceKernelLockMutexCB"}, {0x6b30100f,&WrapV_II,"sceKernelUnlockMutex"}, - {0xb7d098c6,&WrapI_CUIU,"sceKernelCreateMutex"}, + {0xb7d098c6,&WrapV_CUIU,"sceKernelCreateMutex"}, {0x0DDCD2C9, 0, "sceKernelTryLockMutex"}, // NOTE: LockLwMutex and UnlockLwMutex are in Kernel_Library, see sceKernelInterrupt.cpp. diff --git a/Core/HLE/sceKernelMutex.cpp b/Core/HLE/sceKernelMutex.cpp index 2cad2461c5..39db77bfd2 100644 --- a/Core/HLE/sceKernelMutex.cpp +++ b/Core/HLE/sceKernelMutex.cpp @@ -47,7 +47,7 @@ struct Mutex : public KernelObject { const char *GetName() {return nm.name;} const char *GetTypeName() {return "Mutex";} - static u32 GetMissingErrorCode() { return SCE_KERNEL_ERROR_UNKNOWN_SEMID; } // Not sure? + static u32 GetMissingErrorCode() { return PSP_MUTEX_ERROR_NO_SUCH_MUTEX; } // Not sure? int GetIDType() const { return SCE_KERNEL_TMID_Mutex; } NativeMutex nm; std::vector waitingThreads; @@ -63,21 +63,37 @@ struct LWMutex : public KernelObject std::vector waitingThreads; }; -SceUID sceKernelCreateMutex(const char *name, u32 attr, int initialCount, u32 optionsPtr) +void sceKernelCreateMutex(const char *name, u32 attr, int initialCount, u32 optionsPtr) { + u32 error = 0; + if (!error && !name) + error = SCE_KERNEL_ERROR_ERROR; + if (!error && initialCount < 0) + error = SCE_KERNEL_ERROR_ILLEGAL_COUNT; + if (!error && (attr & PSP_MUTEX_ATTR_ALLOW_RECURSIVE) == 0 && initialCount > 1) + error = SCE_KERNEL_ERROR_ILLEGAL_COUNT; + + if (error) + { + RETURN(error); + return; + } + DEBUG_LOG(HLE,"sceKernelCreateMutex(%s, %08x, %d, %08x)", name, attr, initialCount, optionsPtr); Mutex *mutex = new Mutex(); SceUID id = kernelObjects.Create(mutex); mutex->nm.size = sizeof(mutex); + strncpy(mutex->nm.name, name, 31); + mutex->nm.name[31] = 0; mutex->nm.attr = attr; mutex->nm.lockLevel = initialCount; - // TODO: Does initial_count > 0 mean lock automatically by the current thread? Would make sense. - mutex->nm.lockThread = -1; + mutex->nm.lockThread = __KernelGetCurThread(); - strncpy(mutex->nm.name, name, 32); - return id; + RETURN(id); + + __KernelReSchedule("mutex created"); } void sceKernelDeleteMutex(SceUID id) @@ -87,13 +103,21 @@ void sceKernelDeleteMutex(SceUID id) Mutex *mutex = kernelObjects.Get(id, error); if (mutex) { - RETURN(0); + std::vector::iterator iter, end; + for (iter = mutex->waitingThreads.begin(), end = mutex->waitingThreads.end(); iter != end; ++iter) + { + SceUID threadID = *iter; - kernelObjects.Destroy(id); - // TODO: Almost certainly need to reschedule (sometimes?) + // TODO: Set returnValue? + __KernelResumeThreadFromWait(threadID); + } + mutex->waitingThreads.empty(); + + RETURN(kernelObjects.Destroy(id)); + __KernelReSchedule("mutex deleted"); } else - RETURN(PSP_MUTEX_ERROR_NO_SUCH_MUTEX); + RETURN(error); } // int sceKernelLockMutex(SceUID id, int count, int *timeout) diff --git a/Core/HLE/sceKernelMutex.h b/Core/HLE/sceKernelMutex.h index 21713dba81..388d7d7671 100644 --- a/Core/HLE/sceKernelMutex.h +++ b/Core/HLE/sceKernelMutex.h @@ -17,8 +17,7 @@ #pragma once -// TODO -SceUID sceKernelCreateMutex(const char *name, u32 attr, int initialCount, u32 optionsPtr); +void sceKernelCreateMutex(const char *name, u32 attr, int initialCount, u32 optionsPtr); void sceKernelDeleteMutex(SceUID id); void sceKernelLockMutex(SceUID id, int count, u32 timeoutPtr); void sceKernelLockMutexCB(SceUID id, int count, u32 timeoutPtr); From 2290397b9458c1c4af24a24b0e86684d137cbd20 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 17 Nov 2012 20:21:05 -0800 Subject: [PATCH 07/25] sceKernelTryLockMutex() and fix other locking. --- Core/HLE/sceKernel.cpp | 2 +- Core/HLE/sceKernelMutex.cpp | 130 ++++++++++++++++++++++++------------ Core/HLE/sceKernelMutex.h | 1 + 3 files changed, 88 insertions(+), 45 deletions(-) diff --git a/Core/HLE/sceKernel.cpp b/Core/HLE/sceKernel.cpp index eee7c7510d..8c38716ca3 100644 --- a/Core/HLE/sceKernel.cpp +++ b/Core/HLE/sceKernel.cpp @@ -344,7 +344,7 @@ const HLEFunction ThreadManForUser[] = {0x5bf4dd27,&WrapV_IIU,"sceKernelLockMutexCB"}, {0x6b30100f,&WrapV_II,"sceKernelUnlockMutex"}, {0xb7d098c6,&WrapV_CUIU,"sceKernelCreateMutex"}, - {0x0DDCD2C9, 0, "sceKernelTryLockMutex"}, + {0x0DDCD2C9,&WrapV_II, "sceKernelTryLockMutex"}, // NOTE: LockLwMutex and UnlockLwMutex are in Kernel_Library, see sceKernelInterrupt.cpp. {0xFCCFAD26,sceKernelCancelWakeupThread,"sceKernelCancelWakeupThread"}, diff --git a/Core/HLE/sceKernelMutex.cpp b/Core/HLE/sceKernelMutex.cpp index 39db77bfd2..29259bb757 100644 --- a/Core/HLE/sceKernelMutex.cpp +++ b/Core/HLE/sceKernelMutex.cpp @@ -28,9 +28,13 @@ #define PSP_MUTEX_ATTR_ALLOW_RECURSIVE 0x200 // Not sure about the names of these -#define PSP_MUTEX_ERROR_NOT_LOCKED 0x800201C5 #define PSP_MUTEX_ERROR_NO_SUCH_MUTEX 0x800201C3 +#define PSP_MUTEX_ERROR_TRYLOCK_FAILED 0x800201C4 +#define PSP_MUTEX_ERROR_NOT_LOCKED 0x800201C5 #define PSP_MUTEX_ERROR_UNLOCK_UNDERFLOW 0x800201C7 +// TODO: Have not yet found 0x800201C6 +#define PSP_MUTEX_ERROR_ALREADY_LOCKED 0x800201C8 + // Guesswork - not exposed anyway struct NativeMutex @@ -127,19 +131,15 @@ void sceKernelLockMutex(SceUID id, int count, u32 timeoutPtr) DEBUG_LOG(HLE,"sceKernelLockMutex(%i, %i, %08x)", id, count, timeoutPtr); u32 error; Mutex *mutex = kernelObjects.Get(id, error); - if (!mutex) + + if (!error && count <= 0) + error = SCE_KERNEL_ERROR_ILLEGAL_COUNT; + if (!error && count > 1 && !(mutex->nm.attr & PSP_MUTEX_ATTR_ALLOW_RECURSIVE)) + error = SCE_KERNEL_ERROR_ILLEGAL_COUNT; + + if (error) { - RETURN(PSP_MUTEX_ERROR_NO_SUCH_MUTEX); - return; - } - if (count <= 0) - { - RETURN(SCE_KERNEL_ERROR_ILLEGAL_COUNT); - return; - } - if (count > 1 && !(mutex->nm.attr & PSP_MUTEX_ATTR_ALLOW_RECURSIVE)) - { - RETURN(SCE_KERNEL_ERROR_ILLEGAL_COUNT); + RETURN(error); return; } @@ -153,10 +153,13 @@ void sceKernelLockMutex(SceUID id, int count, u32 timeoutPtr) // Needed to get the proper order per real PSP. __KernelReSchedule("mutex locked"); } - else if ((mutex->nm.attr & PSP_MUTEX_ATTR_ALLOW_RECURSIVE) && mutex->nm.lockThread == __KernelGetCurThread()) + else if (mutex->nm.lockThread == __KernelGetCurThread()) { // Recursive mutex, let's just increase the lock count and keep going - mutex->nm.lockLevel += count; + if ((mutex->nm.attr & PSP_MUTEX_ATTR_ALLOW_RECURSIVE)) + mutex->nm.lockLevel += count; + else + RETURN(PSP_MUTEX_ERROR_ALREADY_LOCKED); } else { @@ -172,19 +175,15 @@ void sceKernelLockMutexCB(SceUID id, int count, u32 timeoutPtr) DEBUG_LOG(HLE,"sceKernelLockMutexCB(%i, %i, %08x)", id, count, timeoutPtr); u32 error; Mutex *mutex = kernelObjects.Get(id, error); - if (!mutex) + + if (!error && count <= 0) + error = SCE_KERNEL_ERROR_ILLEGAL_COUNT; + if (!error && count > 1 && !(mutex->nm.attr & PSP_MUTEX_ATTR_ALLOW_RECURSIVE)) + error = SCE_KERNEL_ERROR_ILLEGAL_COUNT; + + if (error) { - RETURN(PSP_MUTEX_ERROR_NO_SUCH_MUTEX); - return; - } - if (count <= 0) - { - RETURN(SCE_KERNEL_ERROR_ILLEGAL_COUNT); - return; - } - if (count > 1 && !(mutex->nm.attr & PSP_MUTEX_ATTR_ALLOW_RECURSIVE)) - { - RETURN(SCE_KERNEL_ERROR_ILLEGAL_COUNT); + RETURN(error); return; } @@ -196,10 +195,13 @@ void sceKernelLockMutexCB(SceUID id, int count, u32 timeoutPtr) mutex->nm.lockThread = __KernelGetCurThread(); // Nobody had it locked - no need to block } - else if ((mutex->nm.attr & PSP_MUTEX_ATTR_ALLOW_RECURSIVE) && mutex->nm.lockThread == __KernelGetCurThread()) + else if (mutex->nm.lockThread == __KernelGetCurThread()) { // Recursive mutex, let's just increase the lock count and keep going - mutex->nm.lockLevel += count; + if ((mutex->nm.attr & PSP_MUTEX_ATTR_ALLOW_RECURSIVE)) + mutex->nm.lockLevel += count; + else + RETURN(PSP_MUTEX_ERROR_ALREADY_LOCKED); } else { @@ -211,6 +213,47 @@ void sceKernelLockMutexCB(SceUID id, int count, u32 timeoutPtr) __KernelReSchedule("mutex locked"); } +// int sceKernelTryLockMutex(SceUID id, int count) +// void because it changes threads. +void sceKernelTryLockMutex(SceUID id, int count) +{ + DEBUG_LOG(HLE,"sceKernelTryLockMutex(%i, %i)", id, count); + u32 error; + Mutex *mutex = kernelObjects.Get(id, error); + + if (!error && count <= 0) + error = SCE_KERNEL_ERROR_ILLEGAL_COUNT; + if (!error && count > 1 && !(mutex->nm.attr & PSP_MUTEX_ATTR_ALLOW_RECURSIVE)) + error = SCE_KERNEL_ERROR_ILLEGAL_COUNT; + + if (error) + { + RETURN(error); + return; + } + + RETURN(0); + + if (mutex->nm.lockLevel == 0) + { + mutex->nm.lockLevel += count; + mutex->nm.lockThread = __KernelGetCurThread(); + // Nobody had it locked - no need to block + } + else 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)) + mutex->nm.lockLevel += count; + else + RETURN(PSP_MUTEX_ERROR_ALREADY_LOCKED); + } + else + RETURN(PSP_MUTEX_ERROR_TRYLOCK_FAILED); + + __KernelReSchedule("mutex trylocked"); +} + // int sceKernelUnlockMutex(SceUID id, int count) // void because it changes threads. void sceKernelUnlockMutex(SceUID id, int count) @@ -218,21 +261,22 @@ void sceKernelUnlockMutex(SceUID id, int count) DEBUG_LOG(HLE,"sceKernelUnlockMutex(%i, %i)", id, count); u32 error; Mutex *mutex = kernelObjects.Get(id, error); - if (!mutex) + + if (!error && count <= 0) + error = SCE_KERNEL_ERROR_ILLEGAL_COUNT; + if (!error && (mutex->nm.attr & PSP_MUTEX_ATTR_ALLOW_RECURSIVE) == 0 && count > 1) + error = SCE_KERNEL_ERROR_ILLEGAL_COUNT; + if (!error && mutex->nm.lockLevel == 0) + error = PSP_MUTEX_ERROR_NOT_LOCKED; + if (!error && mutex->nm.lockLevel < count) + error = PSP_MUTEX_ERROR_UNLOCK_UNDERFLOW; + + if (error) { - RETURN(PSP_MUTEX_ERROR_NO_SUCH_MUTEX); - return; - } - if (mutex->nm.lockLevel == 0) - { - RETURN(PSP_MUTEX_ERROR_NOT_LOCKED); - return; - } - if (mutex->nm.lockLevel < count) - { - RETURN(PSP_MUTEX_ERROR_UNLOCK_UNDERFLOW); + RETURN(error); return; } + mutex->nm.lockLevel -= count; RETURN(0); @@ -257,9 +301,7 @@ void sceKernelUnlockMutex(SceUID id, int count) break; } - // Not sure if this should actually resched, need to test. - if (wokeThreads) - __KernelReSchedule("mutex unlocked"); + __KernelReSchedule("mutex unlocked"); } } diff --git a/Core/HLE/sceKernelMutex.h b/Core/HLE/sceKernelMutex.h index 388d7d7671..5ca8455d80 100644 --- a/Core/HLE/sceKernelMutex.h +++ b/Core/HLE/sceKernelMutex.h @@ -21,6 +21,7 @@ void sceKernelCreateMutex(const char *name, u32 attr, int initialCount, u32 opti void sceKernelDeleteMutex(SceUID id); void sceKernelLockMutex(SceUID id, int count, u32 timeoutPtr); void sceKernelLockMutexCB(SceUID id, int count, u32 timeoutPtr); +void sceKernelTryLockMutex(SceUID id, int count); void sceKernelUnlockMutex(SceUID id, int count); void sceKernelCreateLwMutex(); From 40a6ebfabe6c969adb3c3ad2ff12b40b30e47bff Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 17 Nov 2012 20:27:43 -0800 Subject: [PATCH 08/25] Fix sceKernelCancelSema() to handle any negative. So the test isn't failing. --- Core/HLE/sceKernelSemaphore.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Core/HLE/sceKernelSemaphore.cpp b/Core/HLE/sceKernelSemaphore.cpp index ee015af652..459f37c2c3 100644 --- a/Core/HLE/sceKernelSemaphore.cpp +++ b/Core/HLE/sceKernelSemaphore.cpp @@ -103,7 +103,7 @@ void sceKernelCancelSema(SceUID id, int newCount, u32 numWaitThreadsPtr) *numWaitThreads = s->ns.numWaitThreads; } - if (newCount == -1) + if (newCount < 0) s->ns.currentCount = s->ns.initCount; else s->ns.currentCount = newCount; From c92f1dc8e6559833c9495f49a22441f6def082be Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 17 Nov 2012 20:28:57 -0800 Subject: [PATCH 09/25] Update tests (semaphores/mutexes.) Most mutex tests are failing due to thread scheduling or reuse issues. --- test.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/test.py b/test.py index e9686032e1..b6bbb1967f 100644 --- a/test.py +++ b/test.py @@ -32,9 +32,9 @@ tests_good = [ "gpu/callbacks/ge_callbacks", "threads/mbx/mbx", "threads/mutex/mutex", + "threads/mutex/delete/delete", "threads/semaphores/semaphores", "threads/semaphores/cancel/cancel", - "threads/semaphores/create/create", "threads/semaphores/delete/delete", "threads/semaphores/poll/poll", "threads/semaphores/refer/refer", @@ -50,7 +50,13 @@ tests_next = [ # These are the next tests up for fixing. These run by default. "threads/fpl/fpl", "threads/msgpipe/msgpipe", + "threads/mutex/create/create", + "threads/mutex/lock/lock", + "threads/mutex/priority/priority", + "threads/mutex/try/try", + "threads/mutex/unlock/unlock", "threads/scheduling/scheduling", + "threads/semaphores/create/create", "threads/semaphores/priority/priority", "threads/semaphores/wait/wait", "threads/threads/threads", From 0681c8e5c5a23096b839a6a86792a2e50e5a855a Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 17 Nov 2012 22:55:41 -0800 Subject: [PATCH 10/25] Add overflow error to mutex locking. --- Core/HLE/sceKernelMutex.cpp | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/Core/HLE/sceKernelMutex.cpp b/Core/HLE/sceKernelMutex.cpp index 29259bb757..45a30e24bd 100644 --- a/Core/HLE/sceKernelMutex.cpp +++ b/Core/HLE/sceKernelMutex.cpp @@ -31,8 +31,8 @@ #define PSP_MUTEX_ERROR_NO_SUCH_MUTEX 0x800201C3 #define PSP_MUTEX_ERROR_TRYLOCK_FAILED 0x800201C4 #define PSP_MUTEX_ERROR_NOT_LOCKED 0x800201C5 +#define PSP_MUTEX_ERROR_LOCK_OVERFLOW 0x800201C6 #define PSP_MUTEX_ERROR_UNLOCK_UNDERFLOW 0x800201C7 -// TODO: Have not yet found 0x800201C6 #define PSP_MUTEX_ERROR_ALREADY_LOCKED 0x800201C8 @@ -136,6 +136,9 @@ void sceKernelLockMutex(SceUID id, int count, u32 timeoutPtr) error = SCE_KERNEL_ERROR_ILLEGAL_COUNT; if (!error && count > 1 && !(mutex->nm.attr & PSP_MUTEX_ATTR_ALLOW_RECURSIVE)) error = SCE_KERNEL_ERROR_ILLEGAL_COUNT; + // Two positive ints will always sum to negative. + if (!error && count + mutex->nm.lockLevel < 0) + error = PSP_MUTEX_ERROR_LOCK_OVERFLOW; if (error) { @@ -180,6 +183,9 @@ void sceKernelLockMutexCB(SceUID id, int count, u32 timeoutPtr) error = SCE_KERNEL_ERROR_ILLEGAL_COUNT; if (!error && count > 1 && !(mutex->nm.attr & PSP_MUTEX_ATTR_ALLOW_RECURSIVE)) error = SCE_KERNEL_ERROR_ILLEGAL_COUNT; + // Two positive ints will always sum to negative. + if (!error && count + mutex->nm.lockLevel < 0) + error = PSP_MUTEX_ERROR_LOCK_OVERFLOW; if (error) { @@ -225,6 +231,9 @@ void sceKernelTryLockMutex(SceUID id, int count) error = SCE_KERNEL_ERROR_ILLEGAL_COUNT; if (!error && count > 1 && !(mutex->nm.attr & PSP_MUTEX_ATTR_ALLOW_RECURSIVE)) error = SCE_KERNEL_ERROR_ILLEGAL_COUNT; + // Two positive ints will always sum to negative. + if (!error && count + mutex->nm.lockLevel < 0) + error = PSP_MUTEX_ERROR_LOCK_OVERFLOW; if (error) { From 7fa388952ecc7f9840d471ef25d3dab86f3a15e8 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 17 Nov 2012 23:08:19 -0800 Subject: [PATCH 11/25] Refactor mutex locking to simplify. --- Core/HLE/sceKernelMutex.cpp | 168 +++++++++++++++--------------------- 1 file changed, 70 insertions(+), 98 deletions(-) diff --git a/Core/HLE/sceKernelMutex.cpp b/Core/HLE/sceKernelMutex.cpp index 45a30e24bd..b4d8c9ed52 100644 --- a/Core/HLE/sceKernelMutex.cpp +++ b/Core/HLE/sceKernelMutex.cpp @@ -70,11 +70,11 @@ struct LWMutex : public KernelObject void sceKernelCreateMutex(const char *name, u32 attr, int initialCount, u32 optionsPtr) { u32 error = 0; - if (!error && !name) + if (!name) error = SCE_KERNEL_ERROR_ERROR; - if (!error && initialCount < 0) + else if (initialCount < 0) error = SCE_KERNEL_ERROR_ILLEGAL_COUNT; - if (!error && (attr & PSP_MUTEX_ATTR_ALLOW_RECURSIVE) == 0 && initialCount > 1) + else if ((attr & PSP_MUTEX_ATTR_ALLOW_RECURSIVE) == 0 && initialCount > 1) error = SCE_KERNEL_ERROR_ILLEGAL_COUNT; if (error) @@ -124,6 +124,48 @@ void sceKernelDeleteMutex(SceUID id) RETURN(error); } +bool __KernelLockMutex(Mutex *mutex, int count, u32 &error) +{ + if (!error) + { + if (count <= 0) + 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. + else if (count + mutex->nm.lockLevel < 0) + error = PSP_MUTEX_ERROR_LOCK_OVERFLOW; + } + + if (error) + return false; + + if (mutex->nm.lockLevel == 0) + { + mutex->nm.lockLevel += count; + mutex->nm.lockThread = __KernelGetCurThread(); + // Nobody had it locked - no need to block + return true; + } + + 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)) + { + mutex->nm.lockLevel += count; + return true; + } + else + { + error = PSP_MUTEX_ERROR_ALREADY_LOCKED; + return false; + } + } + + return false; +} + // int sceKernelLockMutex(SceUID id, int count, int *timeout) // void because it changes threads. void sceKernelLockMutex(SceUID id, int count, u32 timeoutPtr) @@ -132,38 +174,13 @@ void sceKernelLockMutex(SceUID id, int count, u32 timeoutPtr) u32 error; Mutex *mutex = kernelObjects.Get(id, error); - if (!error && count <= 0) - error = SCE_KERNEL_ERROR_ILLEGAL_COUNT; - if (!error && count > 1 && !(mutex->nm.attr & PSP_MUTEX_ATTR_ALLOW_RECURSIVE)) - error = SCE_KERNEL_ERROR_ILLEGAL_COUNT; - // Two positive ints will always sum to negative. - if (!error && count + mutex->nm.lockLevel < 0) - error = PSP_MUTEX_ERROR_LOCK_OVERFLOW; - - if (error) + if (__KernelLockMutex(mutex, count, error)) { - RETURN(error); - return; - } - - RETURN(0); - - if (mutex->nm.lockLevel == 0) - { - mutex->nm.lockLevel += count; - mutex->nm.lockThread = __KernelGetCurThread(); - - // Needed to get the proper order per real PSP. + RETURN(0); __KernelReSchedule("mutex locked"); } - else 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)) - mutex->nm.lockLevel += count; - else - RETURN(PSP_MUTEX_ERROR_ALREADY_LOCKED); - } + else if (error) + RETURN(error); else { mutex->waitingThreads.push_back(__KernelGetCurThread()); @@ -179,36 +196,13 @@ void sceKernelLockMutexCB(SceUID id, int count, u32 timeoutPtr) u32 error; Mutex *mutex = kernelObjects.Get(id, error); - if (!error && count <= 0) - error = SCE_KERNEL_ERROR_ILLEGAL_COUNT; - if (!error && count > 1 && !(mutex->nm.attr & PSP_MUTEX_ATTR_ALLOW_RECURSIVE)) - error = SCE_KERNEL_ERROR_ILLEGAL_COUNT; - // Two positive ints will always sum to negative. - if (!error && count + mutex->nm.lockLevel < 0) - error = PSP_MUTEX_ERROR_LOCK_OVERFLOW; - - if (error) + if (__KernelLockMutex(mutex, count, error)) { + RETURN(0); + __KernelReSchedule("mutex locked"); + } + else if (error) RETURN(error); - return; - } - - RETURN(0); - - if (mutex->nm.lockLevel == 0) - { - mutex->nm.lockLevel += count; - mutex->nm.lockThread = __KernelGetCurThread(); - // Nobody had it locked - no need to block - } - else 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)) - mutex->nm.lockLevel += count; - else - RETURN(PSP_MUTEX_ERROR_ALREADY_LOCKED); - } else { mutex->waitingThreads.push_back(__KernelGetCurThread()); @@ -227,40 +221,15 @@ void sceKernelTryLockMutex(SceUID id, int count) u32 error; Mutex *mutex = kernelObjects.Get(id, error); - if (!error && count <= 0) - error = SCE_KERNEL_ERROR_ILLEGAL_COUNT; - if (!error && count > 1 && !(mutex->nm.attr & PSP_MUTEX_ATTR_ALLOW_RECURSIVE)) - error = SCE_KERNEL_ERROR_ILLEGAL_COUNT; - // Two positive ints will always sum to negative. - if (!error && count + mutex->nm.lockLevel < 0) - error = PSP_MUTEX_ERROR_LOCK_OVERFLOW; - - if (error) + if (__KernelLockMutex(mutex, count, error)) { + RETURN(0); + __KernelReSchedule("mutex trylocked"); + } + else if (error) RETURN(error); - return; - } - - RETURN(0); - - if (mutex->nm.lockLevel == 0) - { - mutex->nm.lockLevel += count; - mutex->nm.lockThread = __KernelGetCurThread(); - // Nobody had it locked - no need to block - } - else 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)) - mutex->nm.lockLevel += count; - else - RETURN(PSP_MUTEX_ERROR_ALREADY_LOCKED); - } else RETURN(PSP_MUTEX_ERROR_TRYLOCK_FAILED); - - __KernelReSchedule("mutex trylocked"); } // int sceKernelUnlockMutex(SceUID id, int count) @@ -271,14 +240,17 @@ void sceKernelUnlockMutex(SceUID id, int count) u32 error; Mutex *mutex = kernelObjects.Get(id, error); - if (!error && count <= 0) - error = SCE_KERNEL_ERROR_ILLEGAL_COUNT; - if (!error && (mutex->nm.attr & PSP_MUTEX_ATTR_ALLOW_RECURSIVE) == 0 && count > 1) - error = SCE_KERNEL_ERROR_ILLEGAL_COUNT; - if (!error && mutex->nm.lockLevel == 0) - error = PSP_MUTEX_ERROR_NOT_LOCKED; - if (!error && mutex->nm.lockLevel < count) - error = PSP_MUTEX_ERROR_UNLOCK_UNDERFLOW; + if (!error) + { + if (count <= 0) + error = SCE_KERNEL_ERROR_ILLEGAL_COUNT; + else if ((mutex->nm.attr & PSP_MUTEX_ATTR_ALLOW_RECURSIVE) == 0 && count > 1) + error = SCE_KERNEL_ERROR_ILLEGAL_COUNT; + else if (mutex->nm.lockLevel == 0) + error = PSP_MUTEX_ERROR_NOT_LOCKED; + else if (mutex->nm.lockLevel < count) + error = PSP_MUTEX_ERROR_UNLOCK_UNDERFLOW; + } if (error) { From 7b9a8bf76695d4158bf46a2e5dfbe8f2cd5954e8 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 18 Nov 2012 16:18:06 -0800 Subject: [PATCH 12/25] Minor correctness on create mutex. --- Core/HLE/sceKernelMutex.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/Core/HLE/sceKernelMutex.cpp b/Core/HLE/sceKernelMutex.cpp index b4d8c9ed52..193a9ba781 100644 --- a/Core/HLE/sceKernelMutex.cpp +++ b/Core/HLE/sceKernelMutex.cpp @@ -93,7 +93,13 @@ void sceKernelCreateMutex(const char *name, u32 attr, int initialCount, u32 opti mutex->nm.name[31] = 0; mutex->nm.attr = attr; mutex->nm.lockLevel = initialCount; - mutex->nm.lockThread = __KernelGetCurThread(); + if (mutex->nm.lockLevel == 0) + mutex->nm.lockThread = -1; + else + mutex->nm.lockThread = __KernelGetCurThread(); + + if (optionsPtr != 0) + WARN_LOG(HLE,"sceKernelCreateMutex(%s) unsupported options parameter.", name); RETURN(id); From b9bb5c4ccedda3acf2bb9656683929029cb3c92f Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 18 Nov 2012 16:26:43 -0800 Subject: [PATCH 13/25] Make sceKernelStartThread() reschedule. Mutexes and semaphores are wrong, duh. --- Core/HLE/sceKernel.cpp | 2 +- Core/HLE/sceKernelThread.cpp | 20 +++++++++++--------- Core/HLE/sceKernelThread.h | 2 +- 3 files changed, 13 insertions(+), 11 deletions(-) diff --git a/Core/HLE/sceKernel.cpp b/Core/HLE/sceKernel.cpp index 8c38716ca3..0d1780989d 100644 --- a/Core/HLE/sceKernel.cpp +++ b/Core/HLE/sceKernel.cpp @@ -371,7 +371,7 @@ const HLEFunction ThreadManForUser[] = {0x912354a7,sceKernelRotateThreadReadyQueue,"sceKernelRotateThreadReadyQueue"}, {0x9ACE131E,sceKernelSleepThread,"sceKernelSleepThread"}, {0x82826f70,sceKernelSleepThreadCB,"sceKernelSleepThreadCB"}, - {0xF475845D,&WrapU_V,"sceKernelStartThread"}, + {0xF475845D,&WrapV_IUU,"sceKernelStartThread"}, {0x9944f31f,sceKernelSuspendThread,"sceKernelSuspendThread"}, {0x616403ba,0,"sceKernelTerminateThread"}, {0x383f7bcc,sceKernelTerminateDeleteThread,"sceKernelTerminateDeleteThread"}, diff --git a/Core/HLE/sceKernelThread.cpp b/Core/HLE/sceKernelThread.cpp index b8611ff7bf..eb6af75fee 100644 --- a/Core/HLE/sceKernelThread.cpp +++ b/Core/HLE/sceKernelThread.cpp @@ -829,12 +829,10 @@ void sceKernelCreateThread() } -u32 sceKernelStartThread() +// int sceKernelStartThread(SceUID threadToStartID, SceSize argSize, void *argBlock) +// void because it reschedules. +void sceKernelStartThread(SceUID threadToStartID, u32 argSize, u32 argBlockPtr) { - int threadToStartID = PARAM(0); - u32 argSize = PARAM(1); - u32 argBlockPtr = PARAM(2); - if (threadToStartID != currentThread->GetUID()) { u32 error; @@ -843,13 +841,15 @@ u32 sceKernelStartThread() { ERROR_LOG(HLE,"%08x=sceKernelStartThread(thread=%i, argSize=%i, argPtr= %08x): thread does not exist!", error,threadToStartID,argSize,argBlockPtr) - return error; + RETURN(error); + return; } if (startThread->nt.status != THREADSTATUS_DORMANT) { //Not dormant, WTF? - return ERROR_KERNEL_THREAD_IS_NOT_DORMANT; + RETURN(ERROR_KERNEL_THREAD_IS_NOT_DORMANT); + return; } INFO_LOG(HLE,"sceKernelStartThread(thread=%i, argSize=%i, argPtr= %08x )", @@ -876,12 +876,14 @@ u32 sceKernelStartThread() if (!argBlockPtr && argSize > 0) { WARN_LOG(HLE,"sceKernelStartThread : had NULL arg"); } - return 0; + RETURN(0); + + __KernelReSchedule("thread started"); } else { ERROR_LOG(HLE,"thread %i trying to start itself", threadToStartID); - return -1; + RETURN(-1); } } diff --git a/Core/HLE/sceKernelThread.h b/Core/HLE/sceKernelThread.h index 0a93b52c51..d5dd5b5769 100644 --- a/Core/HLE/sceKernelThread.h +++ b/Core/HLE/sceKernelThread.h @@ -35,7 +35,7 @@ void sceKernelExitThread(); void _sceKernelExitThread(); void sceKernelGetThreadId(); void sceKernelGetThreadCurrentPriority(); -u32 sceKernelStartThread(); +void sceKernelStartThread(SceUID threadToStartID, u32 argSize, u32 argBlockPtr); u32 sceKernelSuspendDispatchThread(); u32 sceKernelResumeDispatchThread(u32 suspended); void sceKernelWaitThreadEnd(); From d88fa153b7d7608c4c980ecbad487fee3eb7c9d0 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 18 Nov 2012 17:54:55 -0800 Subject: [PATCH 14/25] Implement timeouts on mutexes. Still need to set timeoutPtr after they expire. Pretty sure I did this the right way, seems CoreTiming does already allow/use multiple events for the same type? --- Core/CoreTiming.cpp | 37 ++++++++++++ Core/CoreTiming.h | 2 +- Core/HLE/sceKernelMutex.cpp | 46 +++++++++++++-- Core/HLE/sceKernelThread.cpp | 107 ++++++++++++++++++++--------------- Core/HLE/sceKernelThread.h | 5 +- 5 files changed, 143 insertions(+), 54 deletions(-) diff --git a/Core/CoreTiming.cpp b/Core/CoreTiming.cpp index db686e885f..cca5f1f51d 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 a442af20ca..bd8605b8d1 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 193a9ba781..ef256b9e2c 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 eb6af75fee..a2c5e82220 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 d5dd5b5769..a84a75d685 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); From aad7e48a1a194f36c6f54002619290f1bd179e7d Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 18 Nov 2012 18:55:50 -0800 Subject: [PATCH 15/25] Oops, turns out mutex timeouts are in micro. Found it documented as milli somewhere, but it was wrong. --- Core/HLE/sceKernelMutex.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Core/HLE/sceKernelMutex.cpp b/Core/HLE/sceKernelMutex.cpp index ef256b9e2c..36b41aa981 100644 --- a/Core/HLE/sceKernelMutex.cpp +++ b/Core/HLE/sceKernelMutex.cpp @@ -190,12 +190,15 @@ void __KernelMutexTimeout(u64 userdata, int cyclesLate) void __kernelWaitMutex(Mutex *mutex, u32 timeoutPtr) { + if (timeoutPtr == 0) + return; + 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 micro = (int) Memory::Read_U32(timeoutPtr); + CoreTiming::ScheduleEvent(usToCycles(micro), mutex->waitTimer, __KernelGetCurThread()); } // int sceKernelLockMutex(SceUID id, int count, int *timeout) From 4d71e32193a3ac3ae9d9df2e8779ef9f6eee06cb Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 18 Nov 2012 19:13:39 -0800 Subject: [PATCH 16/25] Update the value when mutexes timeout. --- Core/CoreTiming.cpp | 14 +++++++++++--- Core/CoreTiming.h | 6 +++++- Core/HLE/sceKernelMutex.cpp | 37 +++++++++++++++++++++++------------- Core/HLE/sceKernelThread.cpp | 14 ++++++++++++++ Core/HLE/sceKernelThread.h | 1 + 5 files changed, 55 insertions(+), 17 deletions(-) diff --git a/Core/CoreTiming.cpp b/Core/CoreTiming.cpp index cca5f1f51d..eea6b3b4a9 100644 --- a/Core/CoreTiming.cpp +++ b/Core/CoreTiming.cpp @@ -249,14 +249,18 @@ void ScheduleEvent(int cyclesIntoFuture, int event_type, u64 userdata) AddEventToQueue(ne); } -void UnscheduleEvent(int event_type, u64 userdata) +// Returns cycles left in timer. +int UnscheduleEvent(int event_type, u64 userdata) { + int result = 0; if (!first) - return; + return result; while(first) { if (first->type == event_type && first->userdata == userdata) { + result = (int)(first->time - globalTimer); + Event *next = first->next; FreeEvent(first); first = next; @@ -267,13 +271,15 @@ void UnscheduleEvent(int event_type, u64 userdata) } } if (!first) - return; + return result; Event *prev = first; Event *ptr = prev->next; while (ptr) { if (ptr->type == event_type && ptr->userdata == userdata) { + result = (int)(ptr->time - globalTimer); + prev->next = ptr->next; FreeEvent(ptr); ptr = prev->next; @@ -284,6 +290,8 @@ void UnscheduleEvent(int event_type, u64 userdata) ptr = ptr->next; } } + + return result; } void RegisterAdvanceCallback(void (*callback)(int cyclesExecuted)) diff --git a/Core/CoreTiming.h b/Core/CoreTiming.h index bd8605b8d1..dcb9f75e8a 100644 --- a/Core/CoreTiming.h +++ b/Core/CoreTiming.h @@ -58,6 +58,10 @@ inline int usToCycles(int us) { return (int)(CPU_HZ / 1000000 * us); } +inline int cyclesToUs(int cycles) { + return cycles / (CPU_HZ / 1000000); +} + namespace CoreTiming { void Init(); @@ -77,7 +81,7 @@ 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); + int UnscheduleEvent(int event_type, u64 userdata); void RemoveEvent(int event_type); void RemoveThreadsafeEvent(int event_type); diff --git a/Core/HLE/sceKernelMutex.cpp b/Core/HLE/sceKernelMutex.cpp index 36b41aa981..5ad3929218 100644 --- a/Core/HLE/sceKernelMutex.cpp +++ b/Core/HLE/sceKernelMutex.cpp @@ -116,22 +116,25 @@ 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; + + u32 timeoutPtr = __KernelGetWaitTimeoutPtr(threadID, error); + if (timeoutPtr != 0 && mutex->waitTimer != 0) + { + // Remove any event for this thread. + int cyclesLeft = CoreTiming::UnscheduleEvent(mutex->waitTimer, threadID); + Memory::Write_U32(cyclesToUs(cyclesLeft), timeoutPtr); + } + __KernelResumeThreadFromWait(threadID, SCE_KERNEL_ERROR_WAIT_DELETE); - // TODO: set timeoutPtr. } mutex->waitingThreads.empty(); + // TODO: Any way to erase the CoreTiming event type? We leak. + RETURN(kernelObjects.Destroy(id)); __KernelReSchedule("mutex deleted"); } @@ -184,7 +187,12 @@ bool __KernelLockMutex(Mutex *mutex, int count, u32 &error) void __KernelMutexTimeout(u64 userdata, int cyclesLate) { SceUID threadID = (SceUID)userdata; - // TODO: set timeoutPtr. + + u32 error; + u32 timeoutPtr = __KernelGetWaitTimeoutPtr(threadID, error); + if (timeoutPtr != 0) + Memory::Write_U32(0, timeoutPtr); + __KernelResumeThreadFromWait(threadID, SCE_KERNEL_ERROR_WAIT_TIMEOUT); } @@ -310,14 +318,17 @@ void sceKernelUnlockMutex(SceUID id, int count) SceUID threadID = *iter; int wVal = (int)__KernelGetWaitValue(threadID, error); + u32 timeoutPtr = __KernelGetWaitTimeoutPtr(threadID, error); mutex->nm.lockThread = threadID; mutex->nm.lockLevel = wVal; - // Remove any event for this thread. - // TODO: Only if timeoutPtr? - if (mutex->waitTimer != 0) - CoreTiming::UnscheduleEvent(mutex->waitTimer, threadID); + if (timeoutPtr != 0 && mutex->waitTimer != 0) + { + // Remove any event for this thread. + int cyclesLeft = CoreTiming::UnscheduleEvent(mutex->waitTimer, threadID); + Memory::Write_U32(cyclesToUs(cyclesLeft), timeoutPtr); + } __KernelResumeThreadFromWait(threadID, 0); wokeThreads = true; diff --git a/Core/HLE/sceKernelThread.cpp b/Core/HLE/sceKernelThread.cpp index a2c5e82220..49d645e25c 100644 --- a/Core/HLE/sceKernelThread.cpp +++ b/Core/HLE/sceKernelThread.cpp @@ -393,6 +393,20 @@ u32 __KernelGetWaitValue(SceUID threadID, u32 &error) } } +u32 __KernelGetWaitTimeoutPtr(SceUID threadID, u32 &error) +{ + Thread *t = kernelObjects.Get(threadID, error); + if (t) + { + return t->waitInfo.timeoutPtr; + } + else + { + ERROR_LOG(HLE, "__KernelGetWaitValue ERROR: thread %i", threadID); + return 0; + } +} + void sceKernelReferThreadStatus() { SceUID threadID = PARAM(0); diff --git a/Core/HLE/sceKernelThread.h b/Core/HLE/sceKernelThread.h index a84a75d685..57f07b8e11 100644 --- a/Core/HLE/sceKernelThread.h +++ b/Core/HLE/sceKernelThread.h @@ -106,6 +106,7 @@ u32 __KernelResumeThreadFromWait(SceUID threadID); // can return an error value u32 __KernelResumeThreadFromWait(SceUID threadID, int retval); u32 __KernelGetWaitValue(SceUID threadID, u32 &error); +u32 __KernelGetWaitTimeoutPtr(SceUID threadID, u32 &error); 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); From fd4185150323cf3b9ee17ef236168e2b62d749d0 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 18 Nov 2012 19:40:19 -0800 Subject: [PATCH 17/25] Implement timeouts on semaphores too. --- Core/HLE/sceKernelMutex.cpp | 8 ++--- Core/HLE/sceKernelSemaphore.cpp | 60 +++++++++++++++++++++++++++++---- 2 files changed, 58 insertions(+), 10 deletions(-) diff --git a/Core/HLE/sceKernelMutex.cpp b/Core/HLE/sceKernelMutex.cpp index 5ad3929218..69e14952b4 100644 --- a/Core/HLE/sceKernelMutex.cpp +++ b/Core/HLE/sceKernelMutex.cpp @@ -196,13 +196,13 @@ void __KernelMutexTimeout(u64 userdata, int cyclesLate) __KernelResumeThreadFromWait(threadID, SCE_KERNEL_ERROR_WAIT_TIMEOUT); } -void __kernelWaitMutex(Mutex *mutex, u32 timeoutPtr) +void __KernelWaitMutex(Mutex *mutex, u32 timeoutPtr) { if (timeoutPtr == 0) return; if (mutex->waitTimer == 0) - mutex->waitTimer = CoreTiming::RegisterEvent("ScheduledTimeout", &__KernelMutexTimeout); + mutex->waitTimer = CoreTiming::RegisterEvent("MutexTimeout", &__KernelMutexTimeout); // This should call __KernelMutexTimeout() later, unless we cancel it. int micro = (int) Memory::Read_U32(timeoutPtr); @@ -227,7 +227,7 @@ void sceKernelLockMutex(SceUID id, int count, u32 timeoutPtr) else { mutex->waitingThreads.push_back(__KernelGetCurThread()); - __kernelWaitMutex(mutex, timeoutPtr); + __KernelWaitMutex(mutex, timeoutPtr); __KernelWaitCurThread(WAITTYPE_MUTEX, id, count, timeoutPtr, false); } } @@ -250,7 +250,7 @@ void sceKernelLockMutexCB(SceUID id, int count, u32 timeoutPtr) else { mutex->waitingThreads.push_back(__KernelGetCurThread()); - __kernelWaitMutex(mutex, timeoutPtr); + __KernelWaitMutex(mutex, timeoutPtr); __KernelWaitCurThread(WAITTYPE_MUTEX, id, count, timeoutPtr, true); __KernelCheckCallbacks(); } diff --git a/Core/HLE/sceKernelSemaphore.cpp b/Core/HLE/sceKernelSemaphore.cpp index 459f37c2c3..d23861dd6a 100644 --- a/Core/HLE/sceKernelSemaphore.cpp +++ b/Core/HLE/sceKernelSemaphore.cpp @@ -17,7 +17,7 @@ #include "HLE.h" #include "../MIPS/MIPS.h" - +#include "../../Core/CoreTiming.h" #include "sceKernel.h" #include "sceKernelThread.h" #include "sceKernelSemaphore.h" @@ -58,6 +58,7 @@ struct Semaphore : public KernelObject NativeSemaphore ns; std::vector waitingThreads; + int waitTimer; }; // Resume all waiting threads (for delete / cancel.) @@ -72,12 +73,23 @@ bool __KernelClearSemaThreads(Semaphore *s, int reason) { SceUID threadID = *iter; - // TODO: Set returnValue = reason? - __KernelResumeThreadFromWait(threadID); + u32 error; + u32 timeoutPtr = __KernelGetWaitTimeoutPtr(threadID, error); + if (timeoutPtr != 0 && s->waitTimer != 0) + { + // Remove any event for this thread. + int cyclesLeft = CoreTiming::UnscheduleEvent(s->waitTimer, threadID); + Memory::Write_U32(cyclesToUs(cyclesLeft), timeoutPtr); + } + + __KernelResumeThreadFromWait(threadID, reason); wokeThreads = true; + // TODO: set timeoutPtr. } s->waitingThreads.empty(); + // TODO: Any way to erase the CoreTiming event type? We leak. + return wokeThreads; } @@ -143,6 +155,7 @@ void sceKernelCreateSema(const char* name, u32 attr, int initVal, int maxVal, u3 s->ns.currentCount = s->ns.initCount; s->ns.maxCount = maxVal; s->ns.numWaitThreads = 0; + s->waitTimer = 0; DEBUG_LOG(HLE,"%i=sceKernelCreateSema(%s, %08x, %i, %i, %08x)", id, s->ns.name, s->ns.attr, s->ns.initCount, s->ns.maxCount, optionPtr); @@ -221,13 +234,23 @@ retry: for (iter = s->waitingThreads.begin(); iter!=s->waitingThreads.end(); iter++) { SceUID threadID = *iter; + int wVal = (int)__KernelGetWaitValue(threadID, error); + u32 timeoutPtr = __KernelGetWaitTimeoutPtr(threadID, error); + if (wVal <= s->ns.currentCount) { s->ns.currentCount -= wVal; s->ns.numWaitThreads--; - __KernelResumeThreadFromWait(threadID); + if (timeoutPtr != 0 && s->waitTimer != 0) + { + // Remove any event for this thread. + int cyclesLeft = CoreTiming::UnscheduleEvent(s->waitTimer, threadID); + Memory::Write_U32(cyclesToUs(cyclesLeft), timeoutPtr); + } + + __KernelResumeThreadFromWait(threadID, 0); wokeThreads = true; s->waitingThreads.erase(iter); goto retry; @@ -247,6 +270,31 @@ retry: } } +void __KernelSemaTimeout(u64 userdata, int cycleslate) +{ + SceUID threadID = (SceUID)userdata; + + u32 error; + u32 timeoutPtr = __KernelGetWaitTimeoutPtr(threadID, error); + if (timeoutPtr != 0) + Memory::Write_U32(0, timeoutPtr); + + __KernelResumeThreadFromWait(threadID, SCE_KERNEL_ERROR_WAIT_TIMEOUT); +} + +void __KernelSetSemaTimeout(Semaphore *s, u32 timeoutPtr) +{ + if (timeoutPtr == 0) + return; + + if (s->waitTimer == 0) + s->waitTimer = CoreTiming::RegisterEvent("SemaphoreTimeout", &__KernelSemaTimeout); + + // This should call __KernelMutexTimeout() later, unless we cancel it. + int micro = (int) Memory::Read_U32(timeoutPtr); + CoreTiming::ScheduleEvent(usToCycles(micro), s->waitTimer, __KernelGetCurThread()); +} + void __KernelWaitSema(SceUID id, int wantedCount, u32 timeoutPtr, const char *badSemaMessage, bool processCallbacks) { u32 error; @@ -262,8 +310,8 @@ void __KernelWaitSema(SceUID id, int wantedCount, u32 timeoutPtr, const char *ba { s->ns.numWaitThreads++; s->waitingThreads.push_back(__KernelGetCurThread()); - // TODO: timeoutPtr? - __KernelWaitCurThread(WAITTYPE_SEMA, id, wantedCount, 0, processCallbacks); + __KernelSetSemaTimeout(s, timeoutPtr); + __KernelWaitCurThread(WAITTYPE_SEMA, id, wantedCount, timeoutPtr, processCallbacks); if (processCallbacks) __KernelCheckCallbacks(); } From 619a9906de31dd39dd8054a84c8fcfbd14339049 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 18 Nov 2012 19:57:08 -0800 Subject: [PATCH 18/25] Remove threads from semaphores when they timeout. This makes wait tests *almost* pass. There's some timing related issues left, though. --- Core/HLE/sceKernelSemaphore.cpp | 16 ++++++++++++++++ Core/HLE/sceKernelThread.cpp | 16 +++++++++++++++- Core/HLE/sceKernelThread.h | 1 + test.py | 2 +- 4 files changed, 33 insertions(+), 2 deletions(-) diff --git a/Core/HLE/sceKernelSemaphore.cpp b/Core/HLE/sceKernelSemaphore.cpp index d23861dd6a..200343e6ec 100644 --- a/Core/HLE/sceKernelSemaphore.cpp +++ b/Core/HLE/sceKernelSemaphore.cpp @@ -15,6 +15,7 @@ // Official git repository and contact information can be found at // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. +#include #include "HLE.h" #include "../MIPS/MIPS.h" #include "../../Core/CoreTiming.h" @@ -279,6 +280,15 @@ void __KernelSemaTimeout(u64 userdata, int cycleslate) if (timeoutPtr != 0) Memory::Write_U32(0, timeoutPtr); + SceUID semaID = __KernelGetWaitID(threadID, error); + Semaphore *s = kernelObjects.Get(semaID, error); + if (s) + { + // This thread isn't waiting anymore. + s->waitingThreads.erase(std::remove(s->waitingThreads.begin(), s->waitingThreads.end(), threadID), s->waitingThreads.end()); + s->ns.numWaitThreads--; + } + __KernelResumeThreadFromWait(threadID, SCE_KERNEL_ERROR_WAIT_TIMEOUT); } @@ -301,6 +311,12 @@ void __KernelWaitSema(SceUID id, int wantedCount, u32 timeoutPtr, const char *ba Semaphore *s = kernelObjects.Get(id, error); if (s) { + if (wantedCount > s->ns.maxCount || wantedCount <= 0) + { + RETURN(SCE_KERNEL_ERROR_ILLEGAL_COUNT); + return; + } + // We need to set the return value BEFORE processing callbacks / etc. RETURN(0); diff --git a/Core/HLE/sceKernelThread.cpp b/Core/HLE/sceKernelThread.cpp index 49d645e25c..5b35f8c057 100644 --- a/Core/HLE/sceKernelThread.cpp +++ b/Core/HLE/sceKernelThread.cpp @@ -402,7 +402,21 @@ u32 __KernelGetWaitTimeoutPtr(SceUID threadID, u32 &error) } else { - ERROR_LOG(HLE, "__KernelGetWaitValue ERROR: thread %i", threadID); + ERROR_LOG(HLE, "__KernelGetWaitTimeoutPtr ERROR: thread %i", threadID); + return 0; + } +} + +SceUID __KernelGetWaitID(SceUID threadID, u32 &error) +{ + Thread *t = kernelObjects.Get(threadID, error); + if (t) + { + return t->nt.waitID; + } + else + { + ERROR_LOG(HLE, "__KernelGetWaitID ERROR: thread %i", threadID); return 0; } } diff --git a/Core/HLE/sceKernelThread.h b/Core/HLE/sceKernelThread.h index 57f07b8e11..208ec838b8 100644 --- a/Core/HLE/sceKernelThread.h +++ b/Core/HLE/sceKernelThread.h @@ -107,6 +107,7 @@ u32 __KernelResumeThreadFromWait(SceUID threadID, int retval); u32 __KernelGetWaitValue(SceUID threadID, u32 &error); u32 __KernelGetWaitTimeoutPtr(SceUID threadID, u32 &error); +SceUID __KernelGetWaitID(SceUID threadID, u32 &error); 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); diff --git a/test.py b/test.py index b6bbb1967f..b91fa7957b 100644 --- a/test.py +++ b/test.py @@ -34,7 +34,6 @@ tests_good = [ "threads/mutex/mutex", "threads/mutex/delete/delete", "threads/semaphores/semaphores", - "threads/semaphores/cancel/cancel", "threads/semaphores/delete/delete", "threads/semaphores/poll/poll", "threads/semaphores/refer/refer", @@ -56,6 +55,7 @@ tests_next = [ "threads/mutex/try/try", "threads/mutex/unlock/unlock", "threads/scheduling/scheduling", + "threads/semaphores/cancel/cancel", "threads/semaphores/create/create", "threads/semaphores/priority/priority", "threads/semaphores/wait/wait", From a81c138004a8be651dec1f6a4ff3af129fcc348c Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Mon, 19 Nov 2012 01:01:19 -0800 Subject: [PATCH 19/25] Create and Delete LwMutex funcs. --- Core/HLE/FunctionWrappers.h | 4 + Core/HLE/sceKernel.cpp | 16 ++-- Core/HLE/sceKernelMutex.cpp | 144 +++++++++++++++++++++++++++----- Core/HLE/sceKernelMutex.h | 4 +- Core/HLE/sceKernelSemaphore.cpp | 3 + 5 files changed, 140 insertions(+), 31 deletions(-) diff --git a/Core/HLE/FunctionWrappers.h b/Core/HLE/FunctionWrappers.h index 224f39a525..f18108ac25 100644 --- a/Core/HLE/FunctionWrappers.h +++ b/Core/HLE/FunctionWrappers.h @@ -162,6 +162,10 @@ template void WrapV_CUIU() { func(Memory::GetCharPointer(PARAM(0)), PARAM(1), PARAM(2), PARAM(3)); } +template void WrapV_UCUIU() { + func(PARAM(0), Memory::GetCharPointer(PARAM(1)), PARAM(2), PARAM(3), PARAM(4)); +} + template void WrapV_CUIIU() { func(Memory::GetCharPointer(PARAM(0)), PARAM(1), PARAM(2), PARAM(3), PARAM(4)); } diff --git a/Core/HLE/sceKernel.cpp b/Core/HLE/sceKernel.cpp index 0d1780989d..55ac175c8f 100644 --- a/Core/HLE/sceKernel.cpp +++ b/Core/HLE/sceKernel.cpp @@ -337,14 +337,14 @@ const HLEFunction ThreadManForUser[] = {0x4E3A1105,&WrapV_IIU, "sceKernelWaitSema"}, {0x6d212bac,&WrapV_IIU, "sceKernelWaitSemaCB"}, - {0x60107536,0,"sceKernelDeleteLwMutex"}, - {0x19CFF145,0,"sceKernelCreateLwMutex"}, - {0xf8170fbe,&WrapV_I,"sceKernelDeleteMutex"}, - {0xB011B11F,&WrapV_IIU,"sceKernelLockMutex"}, - {0x5bf4dd27,&WrapV_IIU,"sceKernelLockMutexCB"}, - {0x6b30100f,&WrapV_II,"sceKernelUnlockMutex"}, - {0xb7d098c6,&WrapV_CUIU,"sceKernelCreateMutex"}, - {0x0DDCD2C9,&WrapV_II, "sceKernelTryLockMutex"}, + {0x60107536,&WrapV_U, "sceKernelDeleteLwMutex"}, + {0x19CFF145,&WrapV_UCUIU, "sceKernelCreateLwMutex"}, + {0xf8170fbe,&WrapV_I, "sceKernelDeleteMutex"}, + {0xB011B11F,&WrapV_IIU, "sceKernelLockMutex"}, + {0x5bf4dd27,&WrapV_IIU, "sceKernelLockMutexCB"}, + {0x6b30100f,&WrapV_II, "sceKernelUnlockMutex"}, + {0xb7d098c6,&WrapV_CUIU, "sceKernelCreateMutex"}, + {0x0DDCD2C9,&WrapV_II, "sceKernelTryLockMutex"}, // NOTE: LockLwMutex and UnlockLwMutex are in Kernel_Library, see sceKernelInterrupt.cpp. {0xFCCFAD26,sceKernelCancelWakeupThread,"sceKernelCancelWakeupThread"}, diff --git a/Core/HLE/sceKernelMutex.cpp b/Core/HLE/sceKernelMutex.cpp index 69e14952b4..54d541b59b 100644 --- a/Core/HLE/sceKernelMutex.cpp +++ b/Core/HLE/sceKernelMutex.cpp @@ -36,6 +36,7 @@ #define PSP_MUTEX_ERROR_UNLOCK_UNDERFLOW 0x800201C7 #define PSP_MUTEX_ERROR_ALREADY_LOCKED 0x800201C8 +#define PSP_LWMUTEX_ERROR_NOT_FOUND 0x800201CA // Guesswork - not exposed anyway struct NativeMutex @@ -52,21 +53,53 @@ struct Mutex : public KernelObject { const char *GetName() {return nm.name;} const char *GetTypeName() {return "Mutex";} - static u32 GetMissingErrorCode() { return PSP_MUTEX_ERROR_NO_SUCH_MUTEX; } // Not sure? + static u32 GetMissingErrorCode() { return PSP_MUTEX_ERROR_NO_SUCH_MUTEX; } int GetIDType() const { return SCE_KERNEL_TMID_Mutex; } NativeMutex nm; std::vector waitingThreads; int waitTimer; }; -struct LWMutex : public KernelObject +// Guesswork - not exposed anyway +struct NativeLwMutex +{ + SceSize size; + char name[32]; + SceUInt attr; + SceUInt workareaPtr; +}; + +struct NativeLwMutexWorkarea +{ + int lockLevel; + SceUID lockThread; + int attr; + int numWaitThreads; + SceUID uid; + int pad[3]; + + void init() + { + memset(this, 0, sizeof(NativeLwMutexWorkarea)); + } + + void clear() + { + lockLevel = 0; + lockThread = -1; + uid = -1; + } +}; + +struct LwMutex : public KernelObject { const char *GetName() {return nm.name;} - const char *GetTypeName() {return "LWMutex";} - static u32 GetMissingErrorCode() { return SCE_KERNEL_ERROR_UNKNOWN_SEMID; } // Not sure? + const char *GetTypeName() {return "LwMutex";} + static u32 GetMissingErrorCode() { return PSP_LWMUTEX_ERROR_NOT_FOUND; } int GetIDType() const { return SCE_KERNEL_TMID_LwMutex; } - NativeMutex nm; + NativeLwMutex nm; std::vector waitingThreads; + int waitTimer; }; void sceKernelCreateMutex(const char *name, u32 attr, int initialCount, u32 optionsPtr) @@ -340,28 +373,97 @@ void sceKernelUnlockMutex(SceUID id, int count) } } -struct NativeLwMutex +void sceKernelCreateLwMutex(u32 workareaPtr, const char *name, u32 attr, int initialCount, u32 optionsPtr) { - SceSize size; - char name[32]; - SceUInt attr; - SceUID mutexUid; - SceUInt opaqueWorkAreaAddr; - int numWaitThreads; - int locked; - int threadid; // thread holding the lock -}; + DEBUG_LOG(HLE,"sceKernelCreateLwMutex(%08x, %s, %08x, %d, %08x)", workareaPtr, name, attr, initialCount, optionsPtr); + + u32 error = 0; + if (!name) + error = SCE_KERNEL_ERROR_ERROR; + else if (initialCount < 0) + error = SCE_KERNEL_ERROR_ILLEGAL_COUNT; + else if ((attr & PSP_MUTEX_ATTR_ALLOW_RECURSIVE) == 0 && initialCount > 1) + error = SCE_KERNEL_ERROR_ILLEGAL_COUNT; + + if (error) + { + RETURN(error); + return; + } + + LwMutex *mutex = new LwMutex(); + SceUID id = kernelObjects.Create(mutex); + mutex->nm.size = sizeof(mutex); + strncpy(mutex->nm.name, name, 31); + mutex->nm.name[31] = 0; + mutex->nm.attr = attr; + mutex->nm.workareaPtr = workareaPtr; + mutex->waitTimer = 0; + + NativeLwMutexWorkarea workarea; + workarea.init(); + workarea.lockLevel = initialCount; + if (initialCount == 0) + workarea.lockThread = 0; + else + workarea.lockThread = __KernelGetCurThread(); + workarea.attr = attr; + workarea.uid = id; + + Memory::WriteStruct(workareaPtr, &workarea); + + if (optionsPtr != 0) + WARN_LOG(HLE,"sceKernelCreateLwMutex(%s) unsupported options parameter.", name); -void sceKernelCreateLwMutex() -{ - ERROR_LOG(HLE,"UNIMPL sceKernelCreateLwMutex()"); RETURN(0); + + __KernelReSchedule("lwmutex created"); } -void sceKernelDeleteLwMutex() +void sceKernelDeleteLwMutex(u32 workareaPtr) { - ERROR_LOG(HLE,"UNIMPL sceKernelDeleteLwMutex()"); - RETURN(0); + DEBUG_LOG(HLE,"sceKernelDeleteLwMutex(%08x)", workareaPtr); + + if (!workareaPtr || !Memory::IsValidAddress(workareaPtr)) + { + RETURN(SCE_KERNEL_ERROR_ILLEGAL_ADDR); + return; + } + + NativeLwMutexWorkarea workarea; + Memory::ReadStruct(workareaPtr, &workarea); + + u32 error; + LwMutex *mutex = kernelObjects.Get(workarea.uid, error); + if (mutex) + { + std::vector::iterator iter, end; + for (iter = mutex->waitingThreads.begin(), end = mutex->waitingThreads.end(); iter != end; ++iter) + { + SceUID threadID = *iter; + + u32 timeoutPtr = __KernelGetWaitTimeoutPtr(threadID, error); + if (timeoutPtr != 0 && mutex->waitTimer != 0) + { + // Remove any event for this thread. + int cyclesLeft = CoreTiming::UnscheduleEvent(mutex->waitTimer, threadID); + Memory::Write_U32(cyclesToUs(cyclesLeft), timeoutPtr); + } + + __KernelResumeThreadFromWait(threadID, SCE_KERNEL_ERROR_WAIT_DELETE); + } + mutex->waitingThreads.empty(); + + // TODO: Any way to erase the CoreTiming event type? We leak. + + RETURN(kernelObjects.Destroy(workarea.uid)); + workarea.clear(); + Memory::WriteStruct(workareaPtr, &workarea); + + __KernelReSchedule("mutex deleted"); + } + else + RETURN(error); } void sceKernelTryLockLwMutex() diff --git a/Core/HLE/sceKernelMutex.h b/Core/HLE/sceKernelMutex.h index 5ca8455d80..82dd7cfbc4 100644 --- a/Core/HLE/sceKernelMutex.h +++ b/Core/HLE/sceKernelMutex.h @@ -24,8 +24,8 @@ void sceKernelLockMutexCB(SceUID id, int count, u32 timeoutPtr); void sceKernelTryLockMutex(SceUID id, int count); void sceKernelUnlockMutex(SceUID id, int count); -void sceKernelCreateLwMutex(); -void sceKernelDeleteLwMutex(); +void sceKernelCreateLwMutex(u32 workareaPtr, const char *name, u32 attr, int initialCount, u32 optionsPtr); +void sceKernelDeleteLwMutex(u32 workareaPtr); void sceKernelTryLockLwMutex(); void sceKernelLockLwMutex(); void sceKernelLockLwMutexCB(); diff --git a/Core/HLE/sceKernelSemaphore.cpp b/Core/HLE/sceKernelSemaphore.cpp index 200343e6ec..38dc56860e 100644 --- a/Core/HLE/sceKernelSemaphore.cpp +++ b/Core/HLE/sceKernelSemaphore.cpp @@ -160,6 +160,9 @@ void sceKernelCreateSema(const char* name, u32 attr, int initVal, int maxVal, u3 DEBUG_LOG(HLE,"%i=sceKernelCreateSema(%s, %08x, %i, %i, %08x)", id, s->ns.name, s->ns.attr, s->ns.initCount, s->ns.maxCount, optionPtr); + if (optionPtr != 0) + WARN_LOG(HLE,"sceKernelCreateSema(%s) unsupported options parameter.", name); + RETURN(id); __KernelReSchedule("semaphore created"); From 8da2fb074c57a6591f4a5c3f5bd91b341bb57922 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Mon, 19 Nov 2012 07:31:36 -0800 Subject: [PATCH 20/25] For mutexes/semaphores, register a global timer. Instead of leaking one per each. Should be faster. Since we can only wait on one thing per thread at a time, this is fine. --- Core/HLE/sceKernelMutex.cpp | 45 +++++++++++++++++++-------------- Core/HLE/sceKernelMutex.h | 5 +++- Core/HLE/sceKernelSemaphore.cpp | 32 +++++++++++++---------- Core/HLE/sceKernelSemaphore.h | 2 ++ 4 files changed, 50 insertions(+), 34 deletions(-) diff --git a/Core/HLE/sceKernelMutex.cpp b/Core/HLE/sceKernelMutex.cpp index 54d541b59b..27f6bb8cf0 100644 --- a/Core/HLE/sceKernelMutex.cpp +++ b/Core/HLE/sceKernelMutex.cpp @@ -57,7 +57,6 @@ struct Mutex : public KernelObject int GetIDType() const { return SCE_KERNEL_TMID_Mutex; } NativeMutex nm; std::vector waitingThreads; - int waitTimer; }; // Guesswork - not exposed anyway @@ -99,11 +98,25 @@ struct LwMutex : public KernelObject int GetIDType() const { return SCE_KERNEL_TMID_LwMutex; } NativeLwMutex nm; std::vector waitingThreads; - int waitTimer; }; +bool mutexInitComplete = false; +int mutexWaitTimer = 0; +int lwMutexWaitTimer = 0; + +void __KernelMutexInit() +{ + mutexWaitTimer = CoreTiming::RegisterEvent("MutexTimeout", &__KernelMutexTimeout); + // TODO: Write / enable. + //lwMutexWaitTimer = CoreTiming::RegisterEvent("LwMutexTimeout", &__KernelLwMutexTimeout); + mutexInitComplete = true; +} + void sceKernelCreateMutex(const char *name, u32 attr, int initialCount, u32 optionsPtr) { + if (!mutexInitComplete) + __KernelMutexInit(); + u32 error = 0; if (!name) error = SCE_KERNEL_ERROR_ERROR; @@ -132,7 +145,6 @@ 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); @@ -155,10 +167,10 @@ void sceKernelDeleteMutex(SceUID id) SceUID threadID = *iter; u32 timeoutPtr = __KernelGetWaitTimeoutPtr(threadID, error); - if (timeoutPtr != 0 && mutex->waitTimer != 0) + if (timeoutPtr != 0 && mutexWaitTimer != 0) { // Remove any event for this thread. - int cyclesLeft = CoreTiming::UnscheduleEvent(mutex->waitTimer, threadID); + int cyclesLeft = CoreTiming::UnscheduleEvent(mutexWaitTimer, threadID); Memory::Write_U32(cyclesToUs(cyclesLeft), timeoutPtr); } @@ -166,8 +178,6 @@ void sceKernelDeleteMutex(SceUID id) } mutex->waitingThreads.empty(); - // TODO: Any way to erase the CoreTiming event type? We leak. - RETURN(kernelObjects.Destroy(id)); __KernelReSchedule("mutex deleted"); } @@ -231,15 +241,12 @@ void __KernelMutexTimeout(u64 userdata, int cyclesLate) void __KernelWaitMutex(Mutex *mutex, u32 timeoutPtr) { - if (timeoutPtr == 0) + if (timeoutPtr == 0 || mutexWaitTimer == 0) return; - if (mutex->waitTimer == 0) - mutex->waitTimer = CoreTiming::RegisterEvent("MutexTimeout", &__KernelMutexTimeout); - // This should call __KernelMutexTimeout() later, unless we cancel it. int micro = (int) Memory::Read_U32(timeoutPtr); - CoreTiming::ScheduleEvent(usToCycles(micro), mutex->waitTimer, __KernelGetCurThread()); + CoreTiming::ScheduleEvent(usToCycles(micro), mutexWaitTimer, __KernelGetCurThread()); } // int sceKernelLockMutex(SceUID id, int count, int *timeout) @@ -356,10 +363,10 @@ void sceKernelUnlockMutex(SceUID id, int count) mutex->nm.lockThread = threadID; mutex->nm.lockLevel = wVal; - if (timeoutPtr != 0 && mutex->waitTimer != 0) + if (timeoutPtr != 0 && mutexWaitTimer != 0) { // Remove any event for this thread. - int cyclesLeft = CoreTiming::UnscheduleEvent(mutex->waitTimer, threadID); + int cyclesLeft = CoreTiming::UnscheduleEvent(mutexWaitTimer, threadID); Memory::Write_U32(cyclesToUs(cyclesLeft), timeoutPtr); } @@ -375,6 +382,9 @@ void sceKernelUnlockMutex(SceUID id, int count) void sceKernelCreateLwMutex(u32 workareaPtr, const char *name, u32 attr, int initialCount, u32 optionsPtr) { + if (!mutexInitComplete) + __KernelMutexInit(); + DEBUG_LOG(HLE,"sceKernelCreateLwMutex(%08x, %s, %08x, %d, %08x)", workareaPtr, name, attr, initialCount, optionsPtr); u32 error = 0; @@ -398,7 +408,6 @@ void sceKernelCreateLwMutex(u32 workareaPtr, const char *name, u32 attr, int ini mutex->nm.name[31] = 0; mutex->nm.attr = attr; mutex->nm.workareaPtr = workareaPtr; - mutex->waitTimer = 0; NativeLwMutexWorkarea workarea; workarea.init(); @@ -443,10 +452,10 @@ void sceKernelDeleteLwMutex(u32 workareaPtr) SceUID threadID = *iter; u32 timeoutPtr = __KernelGetWaitTimeoutPtr(threadID, error); - if (timeoutPtr != 0 && mutex->waitTimer != 0) + if (timeoutPtr != 0 && lwMutexWaitTimer != 0) { // Remove any event for this thread. - int cyclesLeft = CoreTiming::UnscheduleEvent(mutex->waitTimer, threadID); + int cyclesLeft = CoreTiming::UnscheduleEvent(lwMutexWaitTimer, threadID); Memory::Write_U32(cyclesToUs(cyclesLeft), timeoutPtr); } @@ -454,8 +463,6 @@ void sceKernelDeleteLwMutex(u32 workareaPtr) } mutex->waitingThreads.empty(); - // TODO: Any way to erase the CoreTiming event type? We leak. - RETURN(kernelObjects.Destroy(workarea.uid)); workarea.clear(); Memory::WriteStruct(workareaPtr, &workarea); diff --git a/Core/HLE/sceKernelMutex.h b/Core/HLE/sceKernelMutex.h index 82dd7cfbc4..5f9e170fbd 100644 --- a/Core/HLE/sceKernelMutex.h +++ b/Core/HLE/sceKernelMutex.h @@ -29,4 +29,7 @@ void sceKernelDeleteLwMutex(u32 workareaPtr); void sceKernelTryLockLwMutex(); void sceKernelLockLwMutex(); void sceKernelLockLwMutexCB(); -void sceKernelUnlockLwMutex(); \ No newline at end of file +void sceKernelUnlockLwMutex(); + +void __KernelMutexTimeout(u64 userdata, int cyclesLate); +void __KernelLwMutexTimeout(u64 userdata, int cyclesLate); diff --git a/Core/HLE/sceKernelSemaphore.cpp b/Core/HLE/sceKernelSemaphore.cpp index 38dc56860e..bf48bfa423 100644 --- a/Core/HLE/sceKernelSemaphore.cpp +++ b/Core/HLE/sceKernelSemaphore.cpp @@ -59,9 +59,17 @@ struct Semaphore : public KernelObject NativeSemaphore ns; std::vector waitingThreads; - int waitTimer; }; +bool semaInitComplete = false; +int semaWaitTimer = 0; + +void __KernelSemaInit() +{ + semaWaitTimer = CoreTiming::RegisterEvent("SemaphoreTimeout", &__KernelSemaTimeout); + semaInitComplete = true; +} + // Resume all waiting threads (for delete / cancel.) // Returns true if it woke any threads. bool __KernelClearSemaThreads(Semaphore *s, int reason) @@ -76,21 +84,18 @@ bool __KernelClearSemaThreads(Semaphore *s, int reason) u32 error; u32 timeoutPtr = __KernelGetWaitTimeoutPtr(threadID, error); - if (timeoutPtr != 0 && s->waitTimer != 0) + if (timeoutPtr != 0 && semaWaitTimer != 0) { // Remove any event for this thread. - int cyclesLeft = CoreTiming::UnscheduleEvent(s->waitTimer, threadID); + int cyclesLeft = CoreTiming::UnscheduleEvent(semaWaitTimer, threadID); Memory::Write_U32(cyclesToUs(cyclesLeft), timeoutPtr); } __KernelResumeThreadFromWait(threadID, reason); wokeThreads = true; - // TODO: set timeoutPtr. } s->waitingThreads.empty(); - // TODO: Any way to erase the CoreTiming event type? We leak. - return wokeThreads; } @@ -139,6 +144,9 @@ void sceKernelCancelSema(SceUID id, int newCount, u32 numWaitThreadsPtr) // void because it changes threads. void sceKernelCreateSema(const char* name, u32 attr, int initVal, int maxVal, u32 optionPtr) { + if (!semaInitComplete) + __KernelSemaInit(); + if (!name) { RETURN(SCE_KERNEL_ERROR_ERROR); @@ -156,7 +164,6 @@ void sceKernelCreateSema(const char* name, u32 attr, int initVal, int maxVal, u3 s->ns.currentCount = s->ns.initCount; s->ns.maxCount = maxVal; s->ns.numWaitThreads = 0; - s->waitTimer = 0; DEBUG_LOG(HLE,"%i=sceKernelCreateSema(%s, %08x, %i, %i, %08x)", id, s->ns.name, s->ns.attr, s->ns.initCount, s->ns.maxCount, optionPtr); @@ -247,10 +254,10 @@ retry: s->ns.currentCount -= wVal; s->ns.numWaitThreads--; - if (timeoutPtr != 0 && s->waitTimer != 0) + if (timeoutPtr != 0 && semaWaitTimer != 0) { // Remove any event for this thread. - int cyclesLeft = CoreTiming::UnscheduleEvent(s->waitTimer, threadID); + int cyclesLeft = CoreTiming::UnscheduleEvent(semaWaitTimer, threadID); Memory::Write_U32(cyclesToUs(cyclesLeft), timeoutPtr); } @@ -297,15 +304,12 @@ void __KernelSemaTimeout(u64 userdata, int cycleslate) void __KernelSetSemaTimeout(Semaphore *s, u32 timeoutPtr) { - if (timeoutPtr == 0) + if (timeoutPtr == 0 || semaWaitTimer == 0) return; - if (s->waitTimer == 0) - s->waitTimer = CoreTiming::RegisterEvent("SemaphoreTimeout", &__KernelSemaTimeout); - // This should call __KernelMutexTimeout() later, unless we cancel it. int micro = (int) Memory::Read_U32(timeoutPtr); - CoreTiming::ScheduleEvent(usToCycles(micro), s->waitTimer, __KernelGetCurThread()); + CoreTiming::ScheduleEvent(usToCycles(micro), semaWaitTimer, __KernelGetCurThread()); } void __KernelWaitSema(SceUID id, int wantedCount, u32 timeoutPtr, const char *badSemaMessage, bool processCallbacks) diff --git a/Core/HLE/sceKernelSemaphore.h b/Core/HLE/sceKernelSemaphore.h index 4027fda2d8..e6628fec9d 100644 --- a/Core/HLE/sceKernelSemaphore.h +++ b/Core/HLE/sceKernelSemaphore.h @@ -25,3 +25,5 @@ void sceKernelReferSemaStatus(SceUID id, u32 infoPtr); void sceKernelSignalSema(SceUID id, int signal); void sceKernelWaitSema(SceUID semaid, int signal, u32 timeoutPtr); void sceKernelWaitSemaCB(SceUID semaid, int signal, u32 timeoutPtr); + +void __KernelSemaTimeout(u64 userdata, int cycleslate); From dd3db71b61a113fd4683010d9c51232d10bd34aa Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Mon, 19 Nov 2012 07:37:16 -0800 Subject: [PATCH 21/25] Forget mutex waiting threads when they timeout. --- Core/HLE/sceKernelMutex.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/Core/HLE/sceKernelMutex.cpp b/Core/HLE/sceKernelMutex.cpp index 27f6bb8cf0..3e38b40164 100644 --- a/Core/HLE/sceKernelMutex.cpp +++ b/Core/HLE/sceKernelMutex.cpp @@ -17,6 +17,7 @@ // UNFINISHED +#include #include "HLE.h" #include "../MIPS/MIPS.h" #include "../../Core/CoreTiming.h" @@ -236,6 +237,14 @@ void __KernelMutexTimeout(u64 userdata, int cyclesLate) if (timeoutPtr != 0) Memory::Write_U32(0, timeoutPtr); + SceUID mutexID = __KernelGetWaitID(threadID, error); + Mutex *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); } From b71d998a203818c6ef1d558ae6b4f4918c98a25d Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Tue, 20 Nov 2012 00:18:11 -0800 Subject: [PATCH 22/25] Broadcast a threadend event to wake mutexes. --- Core/HLE/sceKernelMutex.cpp | 125 +++++++++++++++++++++++--------- Core/HLE/sceKernelMutex.h | 1 + Core/HLE/sceKernelSemaphore.cpp | 2 +- Core/HLE/sceKernelThread.cpp | 34 ++++++++- Core/HLE/sceKernelThread.h | 5 +- 5 files changed, 127 insertions(+), 40 deletions(-) diff --git a/Core/HLE/sceKernelMutex.cpp b/Core/HLE/sceKernelMutex.cpp index 3e38b40164..52f08c554d 100644 --- a/Core/HLE/sceKernelMutex.cpp +++ b/Core/HLE/sceKernelMutex.cpp @@ -18,6 +18,7 @@ // UNFINISHED #include +#include #include "HLE.h" #include "../MIPS/MIPS.h" #include "../../Core/CoreTiming.h" @@ -104,15 +105,40 @@ struct LwMutex : public KernelObject bool mutexInitComplete = false; int mutexWaitTimer = 0; int lwMutexWaitTimer = 0; +// Thread -> Mutex locks for thread end. +std::map mutexHeldLocks; void __KernelMutexInit() { mutexWaitTimer = CoreTiming::RegisterEvent("MutexTimeout", &__KernelMutexTimeout); + // TODO: Install on first mutex (if it's slow?) + __KernelListenThreadEnd(&__KernelMutexThreadEnd); // TODO: Write / enable. //lwMutexWaitTimer = CoreTiming::RegisterEvent("LwMutexTimeout", &__KernelLwMutexTimeout); + mutexInitComplete = true; } +void __KernelMutexAcquireLock(Mutex *mutex, int count, SceUID thread) +{ + mutexHeldLocks.insert(std::make_pair(thread, mutex->GetUID())); + + mutex->nm.lockLevel = count; + mutex->nm.lockThread = thread; +} + +void __KernelMutexAcquireLock(Mutex *mutex, int count) +{ + __KernelMutexAcquireLock(mutex, count, __KernelGetCurThread()); +} + +void __KernelMutexEraseLock(Mutex *mutex) +{ + if (mutex->nm.lockThread != -1) + mutexHeldLocks.erase(mutex->nm.lockThread); + mutex->nm.lockThread = -1; +} + void sceKernelCreateMutex(const char *name, u32 attr, int initialCount, u32 optionsPtr) { if (!mutexInitComplete) @@ -141,11 +167,13 @@ void sceKernelCreateMutex(const char *name, u32 attr, int initialCount, u32 opti strncpy(mutex->nm.name, name, 31); mutex->nm.name[31] = 0; mutex->nm.attr = attr; - mutex->nm.lockLevel = initialCount; - if (mutex->nm.lockLevel == 0) + if (initialCount == 0) + { + mutex->nm.lockLevel = 0; mutex->nm.lockThread = -1; + } else - mutex->nm.lockThread = __KernelGetCurThread(); + __KernelMutexAcquireLock(mutex, initialCount); if (optionsPtr != 0) WARN_LOG(HLE,"sceKernelCreateMutex(%s) unsupported options parameter.", name); @@ -177,6 +205,8 @@ void sceKernelDeleteMutex(SceUID id) __KernelResumeThreadFromWait(threadID, SCE_KERNEL_ERROR_WAIT_DELETE); } + if (mutex->nm.lockThread != -1) + __KernelMutexEraseLock(mutex); mutex->waitingThreads.empty(); RETURN(kernelObjects.Destroy(id)); @@ -204,8 +234,7 @@ bool __KernelLockMutex(Mutex *mutex, int count, u32 &error) if (mutex->nm.lockLevel == 0) { - mutex->nm.lockLevel += count; - mutex->nm.lockThread = __KernelGetCurThread(); + __KernelMutexAcquireLock(mutex, count); // Nobody had it locked - no need to block return true; } @@ -228,6 +257,38 @@ bool __KernelLockMutex(Mutex *mutex, int count, u32 &error) return false; } +bool __KernelUnlockMutex(Mutex *mutex, u32 &error) +{ + __KernelMutexEraseLock(mutex); + + // 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); + + __KernelMutexAcquireLock(mutex, wVal, threadID); + + if (timeoutPtr != 0 && mutexWaitTimer != 0) + { + // Remove any event for this thread. + int cyclesLeft = CoreTiming::UnscheduleEvent(mutexWaitTimer, threadID); + Memory::Write_U32(cyclesToUs(cyclesLeft), timeoutPtr); + } + + __KernelResumeThreadFromWait(threadID, 0); + wokeThreads = true; + mutex->waitingThreads.erase(iter); + break; + } + + return wokeThreads; +} + void __KernelMutexTimeout(u64 userdata, int cyclesLate) { SceUID threadID = (SceUID)userdata; @@ -237,7 +298,7 @@ void __KernelMutexTimeout(u64 userdata, int cyclesLate) if (timeoutPtr != 0) Memory::Write_U32(0, timeoutPtr); - SceUID mutexID = __KernelGetWaitID(threadID, error); + SceUID mutexID = __KernelGetWaitID(threadID, WAITTYPE_MUTEX, error); Mutex *mutex = kernelObjects.Get(mutexID, error); if (mutex) { @@ -248,6 +309,29 @@ void __KernelMutexTimeout(u64 userdata, int cyclesLate) __KernelResumeThreadFromWait(threadID, SCE_KERNEL_ERROR_WAIT_TIMEOUT); } +void __KernelMutexThreadEnd(SceUID threadID) +{ + u32 error; + + // If it was waiting on the mutex, it should finish now. + SceUID mutexID = __KernelGetWaitID(threadID, WAITTYPE_MUTEX, error); + if (mutexID) + { + Mutex *mutex = kernelObjects.Get(mutexID, error); + if (mutex) + mutex->waitingThreads.erase(std::remove(mutex->waitingThreads.begin(), mutex->waitingThreads.end(), threadID), mutex->waitingThreads.end()); + } + + std::map::iterator iter = mutexHeldLocks.find(threadID); + if (iter != mutexHeldLocks.end()) + { + SceUID mutexID = (*iter).second; + Mutex *mutex = kernelObjects.Get(mutexID, error); + + __KernelUnlockMutex(mutex, error); + } +} + void __KernelWaitMutex(Mutex *mutex, u32 timeoutPtr) { if (timeoutPtr == 0 || mutexWaitTimer == 0) @@ -357,34 +441,7 @@ void sceKernelUnlockMutex(SceUID id, int count) if (mutex->nm.lockLevel == 0) { - mutex->nm.lockThread = -1; - - // 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); - - mutex->nm.lockThread = threadID; - mutex->nm.lockLevel = wVal; - - if (timeoutPtr != 0 && mutexWaitTimer != 0) - { - // Remove any event for this thread. - int cyclesLeft = CoreTiming::UnscheduleEvent(mutexWaitTimer, threadID); - Memory::Write_U32(cyclesToUs(cyclesLeft), timeoutPtr); - } - - __KernelResumeThreadFromWait(threadID, 0); - wokeThreads = true; - mutex->waitingThreads.erase(iter); - break; - } - + __KernelUnlockMutex(mutex, error); __KernelReSchedule("mutex unlocked"); } } diff --git a/Core/HLE/sceKernelMutex.h b/Core/HLE/sceKernelMutex.h index 5f9e170fbd..ef7141fb34 100644 --- a/Core/HLE/sceKernelMutex.h +++ b/Core/HLE/sceKernelMutex.h @@ -33,3 +33,4 @@ void sceKernelUnlockLwMutex(); void __KernelMutexTimeout(u64 userdata, int cyclesLate); void __KernelLwMutexTimeout(u64 userdata, int cyclesLate); +void __KernelMutexThreadEnd(SceUID thread); \ No newline at end of file diff --git a/Core/HLE/sceKernelSemaphore.cpp b/Core/HLE/sceKernelSemaphore.cpp index bf48bfa423..313f43a76f 100644 --- a/Core/HLE/sceKernelSemaphore.cpp +++ b/Core/HLE/sceKernelSemaphore.cpp @@ -290,7 +290,7 @@ void __KernelSemaTimeout(u64 userdata, int cycleslate) if (timeoutPtr != 0) Memory::Write_U32(0, timeoutPtr); - SceUID semaID = __KernelGetWaitID(threadID, error); + SceUID semaID = __KernelGetWaitID(threadID, WAITTYPE_SEMA, error); Semaphore *s = kernelObjects.Get(semaID, error); if (s) { diff --git a/Core/HLE/sceKernelThread.cpp b/Core/HLE/sceKernelThread.cpp index 5b35f8c057..cc542f0a1e 100644 --- a/Core/HLE/sceKernelThread.cpp +++ b/Core/HLE/sceKernelThread.cpp @@ -273,6 +273,7 @@ u32 threadReturnHackAddr; u32 cbReturnHackAddr; u32 intReturnHackAddr; std::vector threadqueue; //Change to SceUID +std::vector threadEndListeners; SceUID threadIdleID[2]; @@ -343,6 +344,21 @@ void __KernelThreadingInit() // These idle threads are later started in LoadExec, which calls __KernelStartIdleThreads below. } +void __KernelListenThreadEnd(ThreadCallback callback) +{ + threadEndListeners.push_back(callback); +} + +void __KernelFireThreadEnd(Thread *thread) +{ + SceUID threadID = thread->GetUID(); + for (std::vector::iterator iter = threadEndListeners.begin(), end = threadEndListeners.end(); iter != end; ++iter) + { + ThreadCallback cb = *iter; + cb(threadID); + } +} + void __KernelStartIdleThreads() { for (int i = 0; i < 2; i++) @@ -407,12 +423,15 @@ u32 __KernelGetWaitTimeoutPtr(SceUID threadID, u32 &error) } } -SceUID __KernelGetWaitID(SceUID threadID, u32 &error) +SceUID __KernelGetWaitID(SceUID threadID, WaitType type, u32 &error) { Thread *t = kernelObjects.Get(threadID, error); if (t) { - return t->nt.waitID; + if (t->nt.waitType == type) + return t->nt.waitID; + else + return 0; } else { @@ -976,6 +995,7 @@ void __KernelReturnFromThread() currentThread->nt.exitStatus = currentThread->context.r[2]; currentThread->nt.status = THREADSTATUS_DORMANT; + __KernelFireThreadEnd(currentThread); // TODO: Need to remove the thread from any ready queues. @@ -990,8 +1010,10 @@ void __KernelReturnFromThread() void sceKernelExitThread() { ERROR_LOG(HLE,"sceKernelExitThread FAKED"); - currentThread->nt.status = THREADSTATUS_DORMANT; - currentThread->nt.exitStatus = PARAM(0); + currentThread->nt.status = THREADSTATUS_DORMANT; + currentThread->nt.exitStatus = PARAM(0); + __KernelFireThreadEnd(currentThread); + //Find threads that waited for me // Wake them if (!__KernelTriggerWait(WAITTYPE_THREADEND, __KernelGetCurThread())) @@ -1005,6 +1027,8 @@ void _sceKernelExitThread() ERROR_LOG(HLE,"_sceKernelExitThread FAKED"); currentThread->nt.status = THREADSTATUS_DORMANT; currentThread->nt.exitStatus = PARAM(0); + __KernelFireThreadEnd(currentThread); + //Find threads that waited for this one // Wake them if (!__KernelTriggerWait(WAITTYPE_THREADEND, __KernelGetCurThread())) @@ -1023,6 +1047,7 @@ void sceKernelExitDeleteThread() ERROR_LOG(HLE,"sceKernelExitDeleteThread()"); currentThread->nt.status = THREADSTATUS_DORMANT; currentThread->nt.exitStatus = PARAM(0); + __KernelFireThreadEnd(currentThread); //userMemory.Free(currentThread->stackBlock); currentThread->stackBlock = -1; @@ -1075,6 +1100,7 @@ void sceKernelDeleteThread() if (t) { __KernelRemoveFromThreadQueue(t); + __KernelFireThreadEnd(t); RETURN(kernelObjects.Destroy(threadHandle)); diff --git a/Core/HLE/sceKernelThread.h b/Core/HLE/sceKernelThread.h index 208ec838b8..e0918aab90 100644 --- a/Core/HLE/sceKernelThread.h +++ b/Core/HLE/sceKernelThread.h @@ -107,7 +107,7 @@ u32 __KernelResumeThreadFromWait(SceUID threadID, int retval); u32 __KernelGetWaitValue(SceUID threadID, u32 &error); u32 __KernelGetWaitTimeoutPtr(SceUID threadID, u32 &error); -SceUID __KernelGetWaitID(SceUID threadID, u32 &error); +SceUID __KernelGetWaitID(SceUID threadID, WaitType type, u32 &error); 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); @@ -196,3 +196,6 @@ enum ThreadStatus }; void __KernelChangeThreadState(Thread *thread, ThreadStatus newStatus); + +typedef void (*ThreadCallback)(SceUID threadID); +void __KernelListenThreadEnd(ThreadCallback callback); From db16f9309ecf6ae1b10f4fea1598b427879ceef2 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Tue, 20 Nov 2012 22:57:14 -0800 Subject: [PATCH 23/25] Don't crash if something goes wrong with mutexes. Probably can't happen but being defensive is good. --- Core/HLE/sceKernelMutex.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Core/HLE/sceKernelMutex.cpp b/Core/HLE/sceKernelMutex.cpp index 52f08c554d..f8c8375992 100644 --- a/Core/HLE/sceKernelMutex.cpp +++ b/Core/HLE/sceKernelMutex.cpp @@ -121,6 +121,7 @@ void __KernelMutexInit() void __KernelMutexAcquireLock(Mutex *mutex, int count, SceUID thread) { + _dbg_assert_msg_(HLE, mutexHeldLocks.find(threadID) == mutexHeldLocks.end(), "Thread %d wasn't removed from mutexHeldLocks properly."); mutexHeldLocks.insert(std::make_pair(thread, mutex->GetUID())); mutex->nm.lockLevel = count; @@ -328,7 +329,8 @@ void __KernelMutexThreadEnd(SceUID threadID) SceUID mutexID = (*iter).second; Mutex *mutex = kernelObjects.Get(mutexID, error); - __KernelUnlockMutex(mutex, error); + if (mutex) + __KernelUnlockMutex(mutex, error); } } From fa9b9840d0516084d004d431ba35b904ca605106 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Wed, 21 Nov 2012 00:21:10 -0800 Subject: [PATCH 24/25] 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. }; From 879efb25cc168f93383ac2cbef32637e678efd43 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Wed, 21 Nov 2012 00:28:29 -0800 Subject: [PATCH 25/25] Don't allow unlocking someone else's mutex. --- Core/HLE/sceKernelMutex.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Core/HLE/sceKernelMutex.cpp b/Core/HLE/sceKernelMutex.cpp index 5a7002b827..f5530567b2 100644 --- a/Core/HLE/sceKernelMutex.cpp +++ b/Core/HLE/sceKernelMutex.cpp @@ -435,7 +435,7 @@ void sceKernelUnlockMutex(SceUID id, int count) error = SCE_KERNEL_ERROR_ILLEGAL_COUNT; else if ((mutex->nm.attr & PSP_MUTEX_ATTR_ALLOW_RECURSIVE) == 0 && count > 1) error = SCE_KERNEL_ERROR_ILLEGAL_COUNT; - else if (mutex->nm.lockLevel == 0) + else if (mutex->nm.lockLevel == 0 || mutex->nm.lockThread != __KernelGetCurThread()) error = PSP_MUTEX_ERROR_NOT_LOCKED; else if (mutex->nm.lockLevel < count) error = PSP_MUTEX_ERROR_UNLOCK_UNDERFLOW; @@ -787,7 +787,7 @@ void sceKernelUnlockLwMutex(u32 workareaPtr, int count) 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) + else if (workarea.lockLevel == 0 || workarea.lockThread != __KernelGetCurThread()) error = PSP_LWMUTEX_ERROR_NOT_LOCKED; else if (workarea.lockLevel < count) error = PSP_LWMUTEX_ERROR_UNLOCK_UNDERFLOW;