mirror of
https://github.com/libretro/ppsspp.git
synced 2024-11-24 16:49:50 +00:00
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?
This commit is contained in:
parent
b9bb5c4cce
commit
d88fa153b7
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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<SceUID> 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<Mutex>(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<SceUID>::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;
|
||||
|
@ -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<Thread>(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<Thread>(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<Thread>(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?
|
||||
|
@ -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);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user