Merge pull request #177 from unknownbrackets/event-flags

Event flag fixes
This commit is contained in:
Henrik Rydgård 2012-12-15 10:03:31 -08:00
commit e04edab111
7 changed files with 371 additions and 87 deletions

View File

@ -326,10 +326,10 @@ const HLEFunction ThreadManForUser[] =
{0x812346E4,&WrapU_IU<sceKernelClearEventFlag>, "sceKernelClearEventFlag"},
{0xEF9E4C70,&WrapU_I<sceKernelDeleteEventFlag>, "sceKernelDeleteEventFlag"},
{0x1fb15a32,&WrapU_IU<sceKernelSetEventFlag>, "sceKernelSetEventFlag"},
{0x402FCF22,&WrapV_IUUUU<sceKernelWaitEventFlag>, "sceKernelWaitEventFlag"},
{0x328C546A,&WrapV_IUUUU<sceKernelWaitEventFlagCB>, "sceKernelWaitEventFlagCB"},
{0x402FCF22,&WrapI_IUUUU<sceKernelWaitEventFlag>, "sceKernelWaitEventFlag"},
{0x328C546A,&WrapI_IUUUU<sceKernelWaitEventFlagCB>, "sceKernelWaitEventFlagCB"},
{0x30FD48F0,&WrapI_IUUUU<sceKernelPollEventFlag>, "sceKernelPollEventFlag"},
{0xCD203292,&WrapU_V<sceKernelCancelEventFlag>, "sceKernelCancelEventFlag"},
{0xCD203292,&WrapU_IUU<sceKernelCancelEventFlag>, "sceKernelCancelEventFlag"},
{0xA66B0120,&WrapU_IU<sceKernelReferEventFlagStatus>, "sceKernelReferEventFlagStatus"},
{0x8FFDF9A2,&WrapI_IIU<sceKernelCancelSema>, "sceKernelCancelSema"},

View File

@ -19,13 +19,13 @@
#include "HLE.h"
#include "../MIPS/MIPS.h"
#include "../../Core/CoreTiming.h"
#include "sceKernel.h"
#include "sceKernelThread.h"
#include "sceKernelEventFlag.h"
#include <queue>
void __KernelEventFlagTimeout(u64 userdata, int cycleslate);
struct NativeEventFlag
{
@ -83,33 +83,154 @@ enum PspEventFlagWaitTypes
/** Wait for one or more bits in the pattern to be set */
PSP_EVENT_WAITOR = 1,
/** Clear the wait pattern when it matches */
PSP_EVENT_WAITCLEAR = 0x20
PSP_EVENT_WAITCLEAR = 0x20,
PSP_EVENT_WAITKNOWN = PSP_EVENT_WAITCLEAR | PSP_EVENT_WAITOR,
};
bool eventFlagInitComplete = false;
int eventFlagWaitTimer = 0;
void __KernelEventFlagInit()
{
eventFlagWaitTimer = CoreTiming::RegisterEvent("EventFlagTimeout", &__KernelEventFlagTimeout);
eventFlagInitComplete = true;
}
bool __KernelEventFlagMatches(u32 *pattern, u32 bits, u8 wait, u32 outAddr)
{
if ((wait & PSP_EVENT_WAITOR)
? (bits & *pattern) /* one or more bits of the mask */
: ((bits & *pattern) == bits)) /* all the bits of the mask */
{
if (Memory::IsValidAddress(outAddr))
Memory::Write_U32(*pattern, outAddr);
if (wait & PSP_EVENT_WAITCLEAR)
*pattern &= ~bits;
return true;
}
return false;
}
bool __KernelUnlockEventFlagForThread(EventFlag *e, EventFlagTh &th, u32 &error, int result, bool &wokeThreads)
{
SceUID waitID = __KernelGetWaitID(th.tid, WAITTYPE_EVENTFLAG, error);
u32 timeoutPtr = __KernelGetWaitTimeoutPtr(th.tid, error);
// The waitID may be different after a timeout.
if (waitID != e->GetUID())
return true;
// If result is an error code, we're just letting it go.
if (result == 0)
{
if (!__KernelEventFlagMatches(&e->nef.currentPattern, th.bits, th.wait, th.outAddr))
return false;
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 true;
}
bool __KernelClearEventFlagThreads(EventFlag *e, int reason)
{
u32 error;
bool wokeThreads = false;
std::vector<EventFlagTh>::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)
{
if (!eventFlagInitComplete)
__KernelEventFlagInit();
if (!name)
{
WARN_LOG(HLE, "%08x=sceKernelCreateEventFlag(): invalid name", SCE_KERNEL_ERROR_ERROR);
return SCE_KERNEL_ERROR_ERROR;
}
// These attributes aren't valid.
if ((flag_attr & 0x100) != 0 || flag_attr >= 0x300)
{
WARN_LOG(HLE, "%08x=sceKernelCreateEventFlag(): invalid attr parameter: %08x", SCE_KERNEL_ERROR_ILLEGAL_ATTR, flag_attr);
return SCE_KERNEL_ERROR_ILLEGAL_ATTR;
}
EventFlag *e = new EventFlag();
SceUID id = kernelObjects.Create(e);
e->nef.size = sizeof(NativeEventFlag);
strncpy(e->nef.name, name, 32);
strncpy(e->nef.name, name, 31);
e->nef.name[31] = 0;
e->nef.attr = flag_attr;
e->nef.initPattern = flag_initPattern;
e->nef.currentPattern = e->nef.initPattern;
e->nef.numWaitThreads = 0;
DEBUG_LOG(HLE,"%i=sceKernelCreateEventFlag(\"%s\", %08x, %08x, %08x)", id, e->nef.name, e->nef.attr, e->nef.currentPattern, optPtr);
DEBUG_LOG(HLE, "%i=sceKernelCreateEventFlag(\"%s\", %08x, %08x, %08x)", id, e->nef.name, e->nef.attr, e->nef.currentPattern, optPtr);
if (optPtr != 0)
WARN_LOG(HLE, "sceKernelCreateEventFlag(%s) unsupported options parameter: %08x", name, optPtr);
if ((flag_attr & ~PSP_EVENT_WAITMULTIPLE) != 0)
WARN_LOG(HLE, "sceKernelCreateEventFlag(%s) unsupported attr parameter: %08x", name, flag_attr);
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<EventFlag>(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<EventFlag>(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;
@ -123,30 +244,26 @@ u32 sceKernelClearEventFlag(SceUID id, u32 bits)
u32 sceKernelDeleteEventFlag(SceUID uid)
{
DEBUG_LOG(HLE,"sceKernelDeleteEventFlag(%i)", uid);
return kernelObjects.Destroy<EventFlag>(uid);
}
DEBUG_LOG(HLE, "sceKernelDeleteEventFlag(%i)", uid);
u8 __KernelEventFlagMatches(u32 *pattern, u32 bits, u8 wait, u32 outAddr)
{
if ((wait & PSP_EVENT_WAITOR)
? (bits & *pattern) /* one or more bits of the mask */
: ((bits & *pattern) == bits)) /* all the bits of the mask */
u32 error;
EventFlag *e = kernelObjects.Get<EventFlag>(uid, error);
if (e)
{
if (Memory::IsValidAddress(outAddr))
Memory::Write_U32(*pattern, outAddr);
bool wokeThreads = __KernelClearEventFlagThreads(e, SCE_KERNEL_ERROR_WAIT_DELETE);
if (wokeThreads)
hleReSchedule("event flag deleted");
if (wait & PSP_EVENT_WAITCLEAR)
*pattern &= ~bits;
return 1;
return kernelObjects.Destroy<EventFlag>(uid);
}
return 0;
}
else
return error;
}
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<EventFlag>(id, error);
if (e)
{
@ -154,19 +271,20 @@ 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))
if (__KernelUnlockEventFlagForThread(e, *t, error, 0, wokeThreads))
{
__KernelResumeThreadFromWait(t->tid);
wokeThreads = true;
e->nef.numWaitThreads--;
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
@ -175,43 +293,80 @@ retry:
}
}
// Actually RETURNs a u32
void sceKernelWaitEventFlag(SceUID id, u32 bits, u32 wait, u32 outBitsPtr, u32 timeoutPtr)
void __KernelEventFlagTimeout(u64 userdata, int cycleslate)
{
DEBUG_LOG(HLE,"sceKernelWaitEventFlag(%i, %08x, %i, %08x, %08x)", id, bits, wait, outBitsPtr, timeoutPtr);
SceUID threadID = (SceUID)userdata;
u32 error;
EventFlag *e = kernelObjects.Get<EventFlag>(id, error);
u32 timeoutPtr = __KernelGetWaitTimeoutPtr(threadID, error);
if (timeoutPtr != 0)
Memory::Write_U32(0, timeoutPtr);
SceUID flagID = __KernelGetWaitID(threadID, WAITTYPE_EVENTFLAG, error);
EventFlag *e = kernelObjects.Get<EventFlag>(flagID, error);
if (e)
{
EventFlagTh th;
if (!__KernelEventFlagMatches(&e->nef.currentPattern, bits, wait, outBitsPtr))
for (size_t i = 0; i < e->waitingThreads.size(); i++)
{
// No match - must wait.
e->nef.numWaitThreads++;
th.tid = __KernelGetCurThread();
th.bits = bits;
th.wait = wait;
th.outAddr = outBitsPtr;
e->waitingThreads.push_back(th);
u32 timeout;
if (Memory::IsValidAddress(timeoutPtr))
timeout = Memory::Read_U32(timeoutPtr);
EventFlagTh *t = &e->waitingThreads[i];
if (t->tid == threadID)
{
bool wokeThreads;
__KernelWaitCurThread(WAITTYPE_EVENTFLAG, id, 0, 0, false); // sets RETURN
// Do not set RETURN here; it's already set for us and we'd overwrite the wrong thread's RETURN
// This thread isn't waiting anymore, but we'll remove it from waitingThreads later.
// The reason is, if it times out, but what it was waiting on is DELETED prior to it
// actually running, it will get a DELETE result instead of a TIMEOUT.
// So, we need to remember it or we won't be able to mark it DELETE instead later.
__KernelUnlockEventFlagForThread(e, *t, error, SCE_KERNEL_ERROR_WAIT_TIMEOUT, wokeThreads);
e->nef.numWaitThreads--;
break;
}
}
}
else
{
RETURN(error);
}
}
// Actually RETURNs a u32
void sceKernelWaitEventFlagCB(SceUID id, u32 bits, u32 wait, u32 outBitsPtr, u32 timeoutPtr)
void __KernelSetEventFlagTimeout(EventFlag *e, u32 timeoutPtr)
{
DEBUG_LOG(HLE,"sceKernelWaitEventFlagCB(%i, %08x, %i, %08x, %08x)", id, bits, wait, outBitsPtr, timeoutPtr);
if (timeoutPtr == 0 || eventFlagWaitTimer == 0)
return;
int micro = (int) Memory::Read_U32(timeoutPtr);
// This seems like the actual timing of timeouts on hardware.
if (micro <= 1)
micro = 5;
else if (micro <= 209)
micro = 240;
// This should call __KernelEventFlagTimeout() later, unless we cancel it.
CoreTiming::ScheduleEvent(usToCycles(micro), eventFlagWaitTimer, __KernelGetCurThread());
}
void __KernelEventFlagRemoveThread(EventFlag *e, SceUID threadID)
{
for (size_t i = 0; i < e->waitingThreads.size(); i++)
{
EventFlagTh *t = &e->waitingThreads[i];
if (t->tid == threadID)
{
e->waitingThreads.erase(e->waitingThreads.begin() + i);
break;
}
}
}
int sceKernelWaitEventFlag(SceUID id, u32 bits, u32 wait, u32 outBitsPtr, u32 timeoutPtr)
{
DEBUG_LOG(HLE, "sceKernelWaitEventFlag(%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, that's guaranteed to wait forever.
if (bits == 0)
return SCE_KERNEL_ERROR_EVF_ILPAT;
u32 error;
EventFlag *e = kernelObjects.Get<EventFlag>(id, error);
@ -220,29 +375,106 @@ void sceKernelWaitEventFlagCB(SceUID id, u32 bits, u32 wait, u32 outBitsPtr, u32
EventFlagTh th;
if (!__KernelEventFlagMatches(&e->nef.currentPattern, bits, wait, outBitsPtr))
{
// If this thread was left in waitingThreads after a timeout, remove it.
// Otherwise we might write the outBitsPtr in the wrong place.
__KernelEventFlagRemoveThread(e, __KernelGetCurThread());
u32 timeout = 0xFFFFFFFF;
if (Memory::IsValidAddress(timeoutPtr))
timeout = Memory::Read_U32(timeoutPtr);
// Do we allow more than one thread to wait?
if (e->nef.numWaitThreads > 0 && (e->nef.attr & PSP_EVENT_WAITMULTIPLE) == 0)
return SCE_KERNEL_ERROR_EVF_MULTI;
// No match - must wait.
e->nef.numWaitThreads++;
th.tid = __KernelGetCurThread();
th.bits = bits;
th.wait = wait;
th.outAddr = outBitsPtr;
// If < 5ms, sometimes hardware doesn't write this, but it's unpredictable.
th.outAddr = timeout == 0 ? NULL : outBitsPtr;
e->waitingThreads.push_back(th);
u32 timeout;
if (Memory::IsValidAddress(timeoutPtr))
timeout = Memory::Read_U32(timeoutPtr);
__KernelWaitCurThread(WAITTYPE_EVENTFLAG, id, 0, 0, true); // sets RETURN
__KernelSetEventFlagTimeout(e, timeoutPtr);
__KernelWaitCurThread(WAITTYPE_EVENTFLAG, id, 0, timeoutPtr, false);
}
return 0;
}
else
{
RETURN(error);
return error;
}
}
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);
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, that's guaranteed to wait forever.
if (bits == 0)
return SCE_KERNEL_ERROR_EVF_ILPAT;
u32 error;
EventFlag *e = kernelObjects.Get<EventFlag>(id, error);
if (e)
{
EventFlagTh th;
if (!__KernelEventFlagMatches(&e->nef.currentPattern, bits, wait, outBitsPtr))
{
// If this thread was left in waitingThreads after a timeout, remove it.
// Otherwise we might write the outBitsPtr in the wrong place.
__KernelEventFlagRemoveThread(e, __KernelGetCurThread());
u32 timeout = 0xFFFFFFFF;
if (Memory::IsValidAddress(timeoutPtr))
timeout = Memory::Read_U32(timeoutPtr);
// Do we allow more than one thread to wait?
if (e->nef.numWaitThreads > 0 && (e->nef.attr & PSP_EVENT_WAITMULTIPLE) == 0)
return SCE_KERNEL_ERROR_EVF_MULTI;
// No match - must wait.
e->nef.numWaitThreads++;
th.tid = __KernelGetCurThread();
th.bits = bits;
th.wait = wait;
// If < 5ms, sometimes hardware doesn't write this, but it's unpredictable.
th.outAddr = timeout == 0 ? NULL : outBitsPtr;
e->waitingThreads.push_back(th);
__KernelSetEventFlagTimeout(e, timeoutPtr);
__KernelWaitCurThread(WAITTYPE_EVENTFLAG, id, 0, timeoutPtr, true);
}
else
hleCheckCurrentCallbacks();
return 0;
}
else
{
return error;
}
}
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<EventFlag>(id, error);
@ -252,6 +484,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;
}
@ -269,7 +505,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<EventFlag>(id, error);
if (e)
@ -282,10 +518,3 @@ u32 sceKernelReferEventFlagStatus(SceUID id, u32 statusPtr)
return error;
}
}
// never seen this one
u32 sceKernelCancelEventFlag()
{
ERROR_LOG(HLE,"UNIMPL: sceKernelCancelEventFlag()");
return 0;
}

View File

@ -21,8 +21,8 @@ int sceKernelCreateEventFlag(const char *name, u32 flag_attr, u32 flag_initPatte
u32 sceKernelClearEventFlag(SceUID id, u32 bits);
u32 sceKernelDeleteEventFlag(SceUID uid);
u32 sceKernelSetEventFlag(SceUID id, u32 bitsToSet);
void sceKernelWaitEventFlag(SceUID id, u32 bits, u32 wait, u32 outBitsPtr, u32 timeoutPtr);
void sceKernelWaitEventFlagCB(SceUID id, u32 bits, u32 wait, u32 outBitsPtr, u32 timeoutPtr);
int sceKernelWaitEventFlag(SceUID id, u32 bits, u32 wait, u32 outBitsPtr, u32 timeoutPtr);
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();
u32 sceKernelCancelEventFlag(SceUID uid, u32 pattern, u32 numWaitThreadsPtr);

View File

@ -27,6 +27,7 @@
#define PSP_MUTEX_ATTR_FIFO 0
#define PSP_MUTEX_ATTR_PRIORITY 0x100
#define PSP_MUTEX_ATTR_ALLOW_RECURSIVE 0x200
#define PSP_MUTEX_ATTR_KNOWN (PSP_MUTEX_ATTR_PRIORITY | PSP_MUTEX_ATTR_ALLOW_RECURSIVE)
// Not sure about the names of these
#define PSP_MUTEX_ERROR_NO_SUCH_MUTEX 0x800201C3
@ -187,14 +188,21 @@ int sceKernelCreateMutex(const char *name, u32 attr, int initialCount, u32 optio
__KernelMutexInit();
if (!name)
{
WARN_LOG(HLE, "%08x=sceKernelCreateMutex(): invalid name", SCE_KERNEL_ERROR_ERROR);
return SCE_KERNEL_ERROR_ERROR;
}
if (attr >= 0xC00)
{
WARN_LOG(HLE, "%08x=sceKernelCreateMutex(): invalid attr parameter: %08x", SCE_KERNEL_ERROR_ILLEGAL_ATTR, attr);
return SCE_KERNEL_ERROR_ILLEGAL_ATTR;
}
if (initialCount < 0)
return SCE_KERNEL_ERROR_ILLEGAL_COUNT;
if ((attr & PSP_MUTEX_ATTR_ALLOW_RECURSIVE) == 0 && initialCount > 1)
return SCE_KERNEL_ERROR_ILLEGAL_COUNT;
DEBUG_LOG(HLE, "sceKernelCreateMutex(%s, %08x, %d, %08x)", name, attr, initialCount, optionsPtr);
Mutex *mutex = new Mutex();
SceUID id = kernelObjects.Create(mutex);
@ -210,8 +218,12 @@ int sceKernelCreateMutex(const char *name, u32 attr, int initialCount, u32 optio
else
__KernelMutexAcquireLock(mutex, initialCount);
DEBUG_LOG(HLE, "%i=sceKernelCreateMutex(%s, %08x, %d, %08x)", id, name, attr, initialCount, optionsPtr);
if (optionsPtr != 0)
WARN_LOG(HLE, "sceKernelCreateMutex(%s) unsupported options parameter.", name);
WARN_LOG(HLE, "sceKernelCreateMutex(%s) unsupported options parameter: %08x", name, optionsPtr);
if ((attr & ~PSP_MUTEX_ATTR_KNOWN) != 0)
WARN_LOG(HLE, "sceKernelCreateMutex(%s) unsupported attr parameter: %08x", name, attr);
return id;
}
@ -342,6 +354,11 @@ void __KernelMutexTimeout(u64 userdata, int cyclesLate)
Memory::Write_U32(0, timeoutPtr);
__KernelResumeThreadFromWait(threadID, SCE_KERNEL_ERROR_WAIT_TIMEOUT);
// We intentionally don't remove from waitingThreads here yet.
// The reason is, if it times out, but what it was waiting on is DELETED prior to it
// actually running, it will get a DELETE result instead of a TIMEOUT.
// So, we need to remember it or we won't be able to mark it DELETE instead later.
}
void __KernelMutexThreadEnd(SceUID threadID)
@ -486,13 +503,20 @@ int sceKernelCreateLwMutex(u32 workareaPtr, const char *name, u32 attr, int init
if (!mutexInitComplete)
__KernelMutexInit();
DEBUG_LOG(HLE, "sceKernelCreateLwMutex(%08x, %s, %08x, %d, %08x)", workareaPtr, name, attr, initialCount, optionsPtr);
if (!name)
{
WARN_LOG(HLE, "%08x=sceKernelCreateLwMutex(): invalid name", SCE_KERNEL_ERROR_ERROR);
return SCE_KERNEL_ERROR_ERROR;
else if (initialCount < 0)
}
if (attr >= 0x400)
{
WARN_LOG(HLE, "%08x=sceKernelCreateLwMutex(): invalid attr parameter: %08x", SCE_KERNEL_ERROR_ILLEGAL_ATTR, attr);
return SCE_KERNEL_ERROR_ILLEGAL_ATTR;
}
if (initialCount < 0)
return SCE_KERNEL_ERROR_ILLEGAL_COUNT;
else if ((attr & PSP_MUTEX_ATTR_ALLOW_RECURSIVE) == 0 && initialCount > 1)
if ((attr & PSP_MUTEX_ATTR_ALLOW_RECURSIVE) == 0 && initialCount > 1)
return SCE_KERNEL_ERROR_ILLEGAL_COUNT;
LwMutex *mutex = new LwMutex();
@ -515,8 +539,12 @@ int sceKernelCreateLwMutex(u32 workareaPtr, const char *name, u32 attr, int init
Memory::WriteStruct(workareaPtr, &workarea);
DEBUG_LOG(HLE, "sceKernelCreateLwMutex(%08x, %s, %08x, %d, %08x)", workareaPtr, name, attr, initialCount, optionsPtr);
if (optionsPtr != 0)
WARN_LOG(HLE, "sceKernelCreateLwMutex(%s) unsupported options parameter.", name);
WARN_LOG(HLE, "sceKernelCreateLwMutex(%s) unsupported options parameter: %08x", name, optionsPtr);
if ((attr & ~PSP_MUTEX_ATTR_KNOWN) != 0)
WARN_LOG(HLE, "sceKernelCreateLwMutex(%s) unsupported attr parameter: %08x", name, attr);
return 0;
}
@ -669,6 +697,11 @@ void __KernelLwMutexTimeout(u64 userdata, int cyclesLate)
Memory::Write_U32(0, timeoutPtr);
__KernelResumeThreadFromWait(threadID, SCE_KERNEL_ERROR_WAIT_TIMEOUT);
// We intentionally don't remove from waitingThreads here yet.
// The reason is, if it times out, but what it was waiting on is DELETED prior to it
// actually running, it will get a DELETE result instead of a TIMEOUT.
// So, we need to remember it or we won't be able to mark it DELETE instead later.
}
void __KernelWaitLwMutex(LwMutex *mutex, u32 timeoutPtr)

View File

@ -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)
@ -177,7 +177,15 @@ int sceKernelCreateSema(const char* name, u32 attr, int initVal, int maxVal, u32
__KernelSemaInit();
if (!name)
{
WARN_LOG(HLE, "%08x=sceKernelCreateSema(): invalid name", SCE_KERNEL_ERROR_ERROR);
return SCE_KERNEL_ERROR_ERROR;
}
if (attr >= 0x200)
{
WARN_LOG(HLE, "%08x=sceKernelCreateSema(): invalid attr parameter: %08x", SCE_KERNEL_ERROR_ILLEGAL_ATTR, attr);
return SCE_KERNEL_ERROR_ILLEGAL_ATTR;
}
Semaphore *s = new Semaphore;
SceUID id = kernelObjects.Create(s);
@ -194,7 +202,9 @@ int sceKernelCreateSema(const char* name, u32 attr, int initVal, int maxVal, u32
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);
WARN_LOG(HLE, "sceKernelCreateSema(%s) unsupported options parameter: %08x", name, optionPtr);
if ((attr & ~PSP_SEMA_ATTR_PRIORITY) != 0)
WARN_LOG(HLE, "sceKernelCreateSema(%s) unsupported attr parameter: %08x", name, attr);
return id;
}
@ -296,6 +306,9 @@ void __KernelSemaTimeout(u64 userdata, int cycleslate)
if (s)
{
// This thread isn't waiting anymore, but we'll remove it from waitingThreads later.
// The reason is, if it times out, but what it was waiting on is DELETED prior to it
// actually running, it will get a DELETE result instead of a TIMEOUT.
// So, we need to remember it or we won't be able to mark it DELETE instead later.
s->ns.numWaitThreads--;
}

@ -1 +1 @@
Subproject commit e9b4496e32bb592dfff14b93abf284ff20783d3c
Subproject commit b41feca84bd700770791b17bde2d0a7bf910538d

11
test.py
View File

@ -58,6 +58,15 @@ tests_good = [
"misc/testgp",
"string/string",
"gpu/callbacks/ge_callbacks",
"threads/events/events",
"threads/events/cancel/cancel",
"threads/events/clear/clear",
"threads/events/create/create",
"threads/events/delete/delete",
"threads/events/poll/poll",
"threads/events/refer/refer",
"threads/events/set/set",
"threads/events/wait/wait",
"threads/lwmutex/create/create",
"threads/lwmutex/delete/delete",
"threads/lwmutex/lock/lock",
@ -84,6 +93,7 @@ tests_good = [
"threads/semaphores/wait/wait",
"power/power",
"umd/callbacks/umd",
"umd/wait/wait",
"io/directory/directory",
]
@ -115,7 +125,6 @@ tests_next = [
"video/pmf_simple/pmf_simple",
# Currently hang or crash.
"threads/events/events",
"audio/atrac/atractest",
]