mirror of
https://github.com/hrydgard/ppsspp.git
synced 2025-04-02 11:01:50 -04:00
Make event flags wait / do callbacks correctly.
This commit is contained in:
parent
d1dbaced5b
commit
9b80ae2ee0
1 changed files with 108 additions and 0 deletions
|
@ -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)
|
||||
{
|
||||
|
|
Loading…
Add table
Reference in a new issue