From 45258bb61ff0bc7bcf74eb92e91e77c0d1230523 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Fri, 14 Dec 2012 21:25:29 -0800 Subject: [PATCH] Cleanup behavior in sceKernel*EventFlag() funcs. Biggest change is just that sceKernelSetEventFlag() rescheds. --- Core/HLE/sceKernel.cpp | 2 +- Core/HLE/sceKernelEventFlag.cpp | 149 +++++++++++++++++++++++--------- Core/HLE/sceKernelEventFlag.h | 2 +- Core/HLE/sceKernelSemaphore.cpp | 2 +- test.py | 2 +- 5 files changed, 113 insertions(+), 44 deletions(-) diff --git a/Core/HLE/sceKernel.cpp b/Core/HLE/sceKernel.cpp index 8d89031f08..f733834392 100644 --- a/Core/HLE/sceKernel.cpp +++ b/Core/HLE/sceKernel.cpp @@ -329,7 +329,7 @@ const HLEFunction ThreadManForUser[] = {0x402FCF22,&WrapI_IUUUU, "sceKernelWaitEventFlag"}, {0x328C546A,&WrapI_IUUUU, "sceKernelWaitEventFlagCB"}, {0x30FD48F0,&WrapI_IUUUU, "sceKernelPollEventFlag"}, - {0xCD203292,&WrapU_V, "sceKernelCancelEventFlag"}, + {0xCD203292,&WrapU_IUU, "sceKernelCancelEventFlag"}, {0xA66B0120,&WrapU_IU, "sceKernelReferEventFlagStatus"}, {0x8FFDF9A2,&WrapI_IIU, "sceKernelCancelSema"}, diff --git a/Core/HLE/sceKernelEventFlag.cpp b/Core/HLE/sceKernelEventFlag.cpp index acaa99e632..348aa86d13 100644 --- a/Core/HLE/sceKernelEventFlag.cpp +++ b/Core/HLE/sceKernelEventFlag.cpp @@ -97,6 +97,54 @@ void __KernelEventFlagInit() eventFlagInitComplete = true; } +void __KernelUnlockEventFlagForThread(EventFlag *e, EventFlagTh &th, u32 &error, int result, bool &wokeThreads) +{ + SceUID waitID = __KernelGetWaitID(th.tid, WAITTYPE_EVENTFLAG, error); + u32 timeoutPtr = __KernelGetWaitTimeoutPtr(th.tid, error); + + // The waitID may be different after a timeout. + if (waitID != e->GetUID()) + return; + + // If result is an error code, we're just letting it go. + if (result == 0) + { + if ((th.wait & PSP_EVENT_WAITCLEAR) != 0) + e->nef.currentPattern &= ~th.bits; + + e->nef.numWaitThreads--; + } + else + { + // Otherwise, we set the current result since we're bailing. + if (Memory::IsValidAddress(th.outAddr)) + Memory::Write_U32(e->nef.currentPattern, th.outAddr); + } + + if (timeoutPtr != 0 && eventFlagWaitTimer != 0) + { + // Remove any event for this thread. + u64 cyclesLeft = CoreTiming::UnscheduleEvent(eventFlagWaitTimer, th.tid); + Memory::Write_U32((u32) cyclesToUs(cyclesLeft), timeoutPtr); + } + + __KernelResumeThreadFromWait(th.tid, result); + wokeThreads = true; + return; +} + +bool __KernelClearEventFlagThreads(EventFlag *e, int reason) +{ + u32 error; + bool wokeThreads = false; + std::vector::iterator iter, end; + for (iter = e->waitingThreads.begin(), end = e->waitingThreads.end(); iter != end; ++iter) + __KernelUnlockEventFlagForThread(e, *iter, error, reason, wokeThreads); + e->waitingThreads.clear(); + + return wokeThreads; +} + //SceUID sceKernelCreateEventFlag(const char *name, int attr, int bits, SceKernelEventFlagOptParam *opt); int sceKernelCreateEventFlag(const char *name, u32 flag_attr, u32 flag_initPattern, u32 optPtr) { @@ -137,13 +185,36 @@ int sceKernelCreateEventFlag(const char *name, u32 flag_attr, u32 flag_initPatte return id; } +u32 sceKernelCancelEventFlag(SceUID uid, u32 pattern, u32 numWaitThreadsPtr) +{ + DEBUG_LOG(HLE, "sceKernelCancelEventFlag(%i, %08X, %08X)", uid, pattern, numWaitThreadsPtr); + + u32 error; + EventFlag *e = kernelObjects.Get(uid, error); + if (e) + { + if (Memory::IsValidAddress(numWaitThreadsPtr)) + Memory::Write_U32(e->nef.numWaitThreads, numWaitThreadsPtr); + + e->nef.currentPattern = pattern; + e->nef.numWaitThreads = 0; + + if (__KernelClearEventFlagThreads(e, SCE_KERNEL_ERROR_WAIT_CANCEL)) + hleReSchedule("event flag canceled"); + + return 0; + } + else + return error; +} + u32 sceKernelClearEventFlag(SceUID id, u32 bits) { u32 error; EventFlag *e = kernelObjects.Get(id, error); if (e) { - DEBUG_LOG(HLE,"sceKernelClearEventFlag(%i, %08x)", id, bits); + DEBUG_LOG(HLE, "sceKernelClearEventFlag(%i, %08x)", id, bits); e->nef.currentPattern &= bits; // Note that it's not possible for threads to get woken up by this action. return 0; @@ -157,23 +228,13 @@ u32 sceKernelClearEventFlag(SceUID id, u32 bits) u32 sceKernelDeleteEventFlag(SceUID uid) { - DEBUG_LOG(HLE,"sceKernelDeleteEventFlag(%i)", uid); + DEBUG_LOG(HLE, "sceKernelDeleteEventFlag(%i)", uid); u32 error; EventFlag *e = kernelObjects.Get(uid, error); if (e) { - bool wokeThreads = false; - std::vector::iterator iter, end; - for (iter = e->waitingThreads.begin(), end = e->waitingThreads.end(); iter != end; ++iter) - { - // TODO: timeoutPtr, outPtr? Check still waiting. - __KernelResumeThreadFromWait(iter->tid, SCE_KERNEL_ERROR_WAIT_DELETE); - wokeThreads = true; - } - - e->waitingThreads.clear(); - + bool wokeThreads = __KernelClearEventFlagThreads(e, SCE_KERNEL_ERROR_WAIT_DELETE); if (wokeThreads) hleReSchedule("event flag deleted"); @@ -183,7 +244,7 @@ u32 sceKernelDeleteEventFlag(SceUID uid) return error; } -u8 __KernelEventFlagMatches(u32 *pattern, u32 bits, u8 wait, u32 outAddr) +bool __KernelEventFlagMatches(u32 *pattern, u32 bits, u8 wait, u32 outAddr) { if ((wait & PSP_EVENT_WAITOR) ? (bits & *pattern) /* one or more bits of the mask */ @@ -194,15 +255,15 @@ u8 __KernelEventFlagMatches(u32 *pattern, u32 bits, u8 wait, u32 outAddr) if (wait & PSP_EVENT_WAITCLEAR) *pattern &= ~bits; - return 1; + return true; } - return 0; + return false; } u32 sceKernelSetEventFlag(SceUID id, u32 bitsToSet) { u32 error; - DEBUG_LOG(HLE,"sceKernelSetEventFlag(%i, %08x)", id, bitsToSet); + DEBUG_LOG(HLE, "sceKernelSetEventFlag(%i, %08x)", id, bitsToSet); EventFlag *e = kernelObjects.Get(id, error); if (e) { @@ -210,19 +271,21 @@ u32 sceKernelSetEventFlag(SceUID id, u32 bitsToSet) e->nef.currentPattern |= bitsToSet; -retry: - for (size_t i = 0; i < e->waitingThreads.size(); i++) + for (size_t i = 0; i < e->waitingThreads.size(); ++i) { EventFlagTh *t = &e->waitingThreads[i]; if (__KernelEventFlagMatches(&e->nef.currentPattern, t->bits, t->wait, t->outAddr)) { - __KernelResumeThreadFromWait(t->tid); - wokeThreads = true; - e->nef.numWaitThreads--; + __KernelUnlockEventFlagForThread(e, *t, error, 0, wokeThreads); e->waitingThreads.erase(e->waitingThreads.begin() + i); - goto retry; + // Try the one that used to be in this place next. + --i; } } + + if (wokeThreads) + hleReSchedule("event flag set"); + return 0; } else @@ -249,16 +312,16 @@ void __KernelEventFlagTimeout(u64 userdata, int cycleslate) EventFlagTh *t = &e->waitingThreads[i]; if (t->tid == threadID) { - if (Memory::IsValidAddress(t->outAddr)) - Memory::Write_U32(e->nef.currentPattern, t->outAddr); - e->nef.numWaitThreads--; + bool wokeThreads; + + // This thread isn't waiting anymore, but we'll remove it from waitingThreads later. + __KernelUnlockEventFlagForThread(e, *t, error, SCE_KERNEL_ERROR_WAIT_TIMEOUT, wokeThreads); e->waitingThreads.erase(e->waitingThreads.begin() + i); + e->nef.numWaitThreads--; break; } } } - - __KernelResumeThreadFromWait(threadID, SCE_KERNEL_ERROR_WAIT_TIMEOUT); } void __KernelSetEventFlagTimeout(EventFlag *e, u32 timeoutPtr) @@ -298,7 +361,7 @@ int sceKernelWaitEventFlag(SceUID id, u32 bits, u32 wait, u32 outBitsPtr, u32 ti EventFlagTh th; if (!__KernelEventFlagMatches(&e->nef.currentPattern, bits, wait, outBitsPtr)) { - u32 timeout; + u32 timeout = 0xFFFFFFFF; if (Memory::IsValidAddress(timeoutPtr)) timeout = Memory::Read_U32(timeoutPtr); @@ -329,7 +392,7 @@ int sceKernelWaitEventFlag(SceUID id, u32 bits, u32 wait, u32 outBitsPtr, u32 ti int sceKernelWaitEventFlagCB(SceUID id, u32 bits, u32 wait, u32 outBitsPtr, u32 timeoutPtr) { - DEBUG_LOG(HLE,"sceKernelWaitEventFlagCB(%i, %08x, %i, %08x, %08x)", id, bits, wait, outBitsPtr, timeoutPtr); + DEBUG_LOG(HLE, "sceKernelWaitEventFlagCB(%i, %08x, %i, %08x, %08x)", id, bits, wait, outBitsPtr, timeoutPtr); if ((wait & ~PSP_EVENT_WAITKNOWN) != 0) { @@ -347,7 +410,7 @@ int sceKernelWaitEventFlagCB(SceUID id, u32 bits, u32 wait, u32 outBitsPtr, u32 EventFlagTh th; if (!__KernelEventFlagMatches(&e->nef.currentPattern, bits, wait, outBitsPtr)) { - u32 timeout; + u32 timeout = 0xFFFFFFFF; if (Memory::IsValidAddress(timeoutPtr)) timeout = Memory::Read_U32(timeoutPtr); @@ -380,7 +443,16 @@ int sceKernelWaitEventFlagCB(SceUID id, u32 bits, u32 wait, u32 outBitsPtr, u32 int sceKernelPollEventFlag(SceUID id, u32 bits, u32 wait, u32 outBitsPtr, u32 timeoutPtr) { - DEBUG_LOG(HLE,"sceKernelPollEventFlag(%i, %08x, %i, %08x, %08x)", id, bits, wait, outBitsPtr, timeoutPtr); + DEBUG_LOG(HLE, "sceKernelPollEventFlag(%i, %08x, %i, %08x, %08x)", id, bits, wait, outBitsPtr, timeoutPtr); + + if ((wait & ~PSP_EVENT_WAITKNOWN) != 0) + { + WARN_LOG(HLE, "sceKernelWaitEventFlag(%i) invalid mode parameter: %08x", id, wait); + return SCE_KERNEL_ERROR_ILLEGAL_MODE; + } + // Can't wait on 0, it never matches. + if (bits == 0) + return SCE_KERNEL_ERROR_EVF_ILPAT; u32 error; EventFlag *e = kernelObjects.Get(id, error); @@ -390,6 +462,10 @@ int sceKernelPollEventFlag(SceUID id, u32 bits, u32 wait, u32 outBitsPtr, u32 ti { if (Memory::IsValidAddress(outBitsPtr)) Memory::Write_U32(e->nef.currentPattern, outBitsPtr); + + if (e->nef.numWaitThreads > 0 && (e->nef.attr & PSP_EVENT_WAITMULTIPLE) == 0) + return SCE_KERNEL_ERROR_EVF_MULTI; + // No match - return that, this is polling, not waiting. return SCE_KERNEL_ERROR_EVF_COND; } @@ -407,7 +483,7 @@ int sceKernelPollEventFlag(SceUID id, u32 bits, u32 wait, u32 outBitsPtr, u32 ti //int sceKernelReferEventFlagStatus(SceUID event, SceKernelEventFlagInfo *status); u32 sceKernelReferEventFlagStatus(SceUID id, u32 statusPtr) { - DEBUG_LOG(HLE,"sceKernelReferEventFlagStatus(%i, %08x)", id, statusPtr); + DEBUG_LOG(HLE, "sceKernelReferEventFlagStatus(%i, %08x)", id, statusPtr); u32 error; EventFlag *e = kernelObjects.Get(id, error); if (e) @@ -420,10 +496,3 @@ u32 sceKernelReferEventFlagStatus(SceUID id, u32 statusPtr) return error; } } - -// never seen this one -u32 sceKernelCancelEventFlag() -{ - ERROR_LOG(HLE,"UNIMPL: sceKernelCancelEventFlag()"); - return 0; -} diff --git a/Core/HLE/sceKernelEventFlag.h b/Core/HLE/sceKernelEventFlag.h index 4b6b628a32..b47b43c53b 100644 --- a/Core/HLE/sceKernelEventFlag.h +++ b/Core/HLE/sceKernelEventFlag.h @@ -25,4 +25,4 @@ int sceKernelWaitEventFlag(SceUID id, u32 bits, u32 wait, u32 outBitsPtr, u32 ti int sceKernelWaitEventFlagCB(SceUID id, u32 bits, u32 wait, u32 outBitsPtr, u32 timeoutPtr); int sceKernelPollEventFlag(SceUID id, u32 bits, u32 wait, u32 outBitsPtr, u32 timeoutPtr); u32 sceKernelReferEventFlagStatus(SceUID id, u32 statusPtr); -u32 sceKernelCancelEventFlag(); \ No newline at end of file +u32 sceKernelCancelEventFlag(SceUID uid, u32 pattern, u32 numWaitThreadsPtr); diff --git a/Core/HLE/sceKernelSemaphore.cpp b/Core/HLE/sceKernelSemaphore.cpp index 0753420051..84840f3c97 100644 --- a/Core/HLE/sceKernelSemaphore.cpp +++ b/Core/HLE/sceKernelSemaphore.cpp @@ -149,7 +149,7 @@ int sceKernelCancelSema(SceUID id, int newCount, u32 numWaitThreadsPtr) if (newCount > s->ns.maxCount) return SCE_KERNEL_ERROR_ILLEGAL_COUNT; - if (numWaitThreadsPtr) + if (Memory::IsValidAddress(numWaitThreadsPtr)) Memory::Write_U32(s->ns.numWaitThreads, numWaitThreadsPtr); if (newCount < 0) diff --git a/test.py b/test.py index 81f815872f..59bc914eb0 100755 --- a/test.py +++ b/test.py @@ -57,6 +57,7 @@ tests_good = [ "misc/testgp", "string/string", "gpu/callbacks/ge_callbacks", + "threads/events/events", "threads/lwmutex/create/create", "threads/lwmutex/delete/delete", "threads/lwmutex/lock/lock", @@ -114,7 +115,6 @@ tests_next = [ "video/pmf_simple/pmf_simple", # Currently hang or crash. - "threads/events/events", "audio/atrac/atractest", ]