Make event flags wait / do callbacks correctly.

This commit is contained in:
Unknown W. Brackets 2013-04-14 21:00:02 -07:00
parent d1dbaced5b
commit 9b80ae2ee0

View file

@ -45,6 +45,7 @@ struct EventFlagTh
u32 bits;
u32 wait;
u32 outAddr;
u64 pausedTimeout;
};
class EventFlag : public KernelObject
@ -70,11 +71,14 @@ public:
p.Do(nef);
EventFlagTh eft = {0};
p.Do(waitingThreads, eft);
p.Do(pausedWaits);
p.DoMarker("EventFlag");
}
NativeEventFlag nef;
std::vector<EventFlagTh> waitingThreads;
// Key is the callback id it was for, or if no callback, the thread id.
std::map<SceUID, EventFlagTh> pausedWaits;
};
@ -102,9 +106,13 @@ enum PspEventFlagWaitTypes
int eventFlagWaitTimer = -1;
void __KernelEventFlagBeginCallback(SceUID threadID, SceUID prevCallbackId);
void __KernelEventFlagEndCallback(SceUID threadID, SceUID prevCallbackId, u32 &returnValue);
void __KernelEventFlagInit()
{
eventFlagWaitTimer = CoreTiming::RegisterEvent("EventFlagTimeout", __KernelEventFlagTimeout);
__KernelRegisterWaitTypeFuncs(WAITTYPE_EVENTFLAG, __KernelEventFlagBeginCallback, __KernelEventFlagEndCallback);
}
void __KernelEventFlagDoState(PointerWrap &p)
@ -186,6 +194,106 @@ bool __KernelClearEventFlagThreads(EventFlag *e, int reason)
return wokeThreads;
}
void __KernelEventFlagBeginCallback(SceUID threadID, SceUID prevCallbackId)
{
SceUID pauseKey = prevCallbackId == 0 ? threadID : prevCallbackId;
u32 error;
SceUID flagID = __KernelGetWaitID(threadID, WAITTYPE_EVENTFLAG, error);
u32 timeoutPtr = __KernelGetWaitTimeoutPtr(threadID, error);
EventFlag *flag = flagID == 0 ? NULL : kernelObjects.Get<EventFlag>(flagID, error);
if (flag)
{
// This means two callbacks in a row. PSP crashes if the same callback runs inside itself.
// TODO: Handle this better?
if (flag->pausedWaits.find(pauseKey) != flag->pausedWaits.end())
return;
EventFlagTh waitData = {0};
for (size_t i = 0; i < flag->waitingThreads.size(); i++)
{
EventFlagTh *t = &flag->waitingThreads[i];
if (t->tid == threadID)
{
waitData = *t;
// TODO: Hmm, what about priority/fifo order? Does it lose its place in line?
flag->waitingThreads.erase(flag->waitingThreads.begin() + i);
flag->nef.numWaitThreads--;
break;
}
}
if (waitData.tid != threadID)
{
ERROR_LOG_REPORT(HLE, "sceKernelWaitEventFlagCB: wait not found to pause for callback");
return;
}
if (timeoutPtr != 0 && eventFlagWaitTimer != -1)
{
s64 cyclesLeft = CoreTiming::UnscheduleEvent(eventFlagWaitTimer, threadID);
waitData.pausedTimeout = CoreTiming::GetTicks() + cyclesLeft;
}
else
waitData.pausedTimeout = 0;
flag->pausedWaits[pauseKey] = waitData;
DEBUG_LOG(HLE, "sceKernelWaitEventFlagCB: Suspending lock wait for callback");
}
else
WARN_LOG_REPORT(HLE, "sceKernelWaitEventFlagCB: beginning callback with bad wait id?");
}
void __KernelEventFlagEndCallback(SceUID threadID, SceUID prevCallbackId, u32 &returnValue)
{
SceUID pauseKey = prevCallbackId == 0 ? threadID : prevCallbackId;
u32 error;
SceUID flagID = __KernelGetWaitID(threadID, WAITTYPE_EVENTFLAG, error);
u32 timeoutPtr = __KernelGetWaitTimeoutPtr(threadID, error);
EventFlag *flag = flagID == 0 ? NULL : kernelObjects.Get<EventFlag>(flagID, error);
if (!flag || flag->pausedWaits.find(pauseKey) == flag->pausedWaits.end())
{
// TODO: Since it was deleted, we don't know how long was actually left.
// For now, we just say the full time was taken.
if (timeoutPtr != 0 && eventFlagWaitTimer != -1)
Memory::Write_U32(0, timeoutPtr);
__KernelResumeThreadFromWait(threadID, SCE_KERNEL_ERROR_WAIT_DELETE);
return;
}
EventFlagTh waitData = flag->pausedWaits[pauseKey];
u64 waitDeadline = waitData.pausedTimeout;
flag->pausedWaits.erase(pauseKey);
flag->nef.numWaitThreads++;
// TODO: Don't wake up if __KernelCurHasReadyCallbacks()?
bool wokeThreads;
// Attempt to unlock.
if (__KernelUnlockEventFlagForThread(flag, waitData, error, 0, wokeThreads))
return;
// We only check if it timed out if it couldn't unlock.
s64 cyclesLeft = waitDeadline - CoreTiming::GetTicks();
if (cyclesLeft < 0 && waitDeadline != 0)
{
if (timeoutPtr != 0 && eventFlagWaitTimer != -1)
Memory::Write_U32(0, timeoutPtr);
__KernelResumeThreadFromWait(threadID, SCE_KERNEL_ERROR_WAIT_TIMEOUT);
}
else
{
// TODO: Should this not go at the end?
flag->waitingThreads.push_back(waitData);
DEBUG_LOG(HLE, "sceKernelWaitEventFlagCB: Resuming lock wait for callback");
}
}
//SceUID sceKernelCreateEventFlag(const char *name, int attr, int bits, SceKernelEventFlagOptParam *opt);
int sceKernelCreateEventFlag(const char *name, u32 flag_attr, u32 flag_initPattern, u32 optPtr)
{