Implement deferred rescheduling/callbacks.

This way most HLE functions can be wrapped normally.  Hurray, sanity.
This commit is contained in:
Unknown W. Brackets 2012-12-08 22:39:36 -08:00
parent 000884fadb
commit 5e8aa4c071
6 changed files with 90 additions and 28 deletions

View File

@ -25,10 +25,27 @@
#include "sceIo.h"
#include "sceAudio.h"
#include "sceKernelMemory.h"
#include "sceKernelThread.h"
#include "../MIPS/MIPSCodeUtils.h"
static std::vector<HLEModule> moduleDB;
static std::vector<Syscall> unresolvedSyscalls;
static int hleAfterSyscall;
static const char *hleAfterSyscallReschedReason;
enum
{
// Do nothing after the syscall.
HLE_AFTER_NOTHING = 0x00,
// Reschedule immediately after the syscall.
HLE_AFTER_RESCHED = 0x01,
// Call current thread's callbacks after the syscall.
HLE_AFTER_CURRENT_CALLBACKS = 0x02,
// Check all threads' callbacks after the syscall.
HLE_AFTER_ALL_CALLBACKS = 0x04,
// Reschedule and process current thread's callbacks after the syscall.
HLE_AFTER_RESCHED_CALLBACKS = 0x08,
};
void HLEInit()
{
@ -177,6 +194,30 @@ const char *GetFuncName(int moduleIndex, int func)
return "[unknown]";
}
void hleCheckAllCallbacks()
{
hleAfterSyscall |= HLE_AFTER_ALL_CALLBACKS;
}
void hleCheckCurrentCallbacks()
{
hleAfterSyscall |= HLE_AFTER_CURRENT_CALLBACKS;
}
void hleReSchedule(const char *reason)
{
_dbg_assert_msg_(HLE, reason != 0, "hleReSchedule: Expecting a valid reason.");
hleAfterSyscall |= HLE_AFTER_RESCHED;
hleAfterSyscallReschedReason = reason;
}
void hleReSchedule(bool callbacks, const char *reason)
{
hleReSchedule(reason);
hleAfterSyscall |= HLE_AFTER_RESCHED_CALLBACKS;
}
void CallSyscall(u32 op)
{
u32 callno = (op >> 6) & 0xFFFFF; //20 bits
@ -191,7 +232,21 @@ void CallSyscall(u32 op)
HLEFunc func = moduleDB[modulenum].funcTable[funcnum].func;
if (func)
{
hleAfterSyscall = HLE_AFTER_NOTHING;
hleAfterSyscallReschedReason = 0;
func();
if ((hleAfterSyscall & HLE_AFTER_CURRENT_CALLBACKS) != 0)
__KernelForceCallbacks();
// Rescheduling will also do HLE_AFTER_ALL_CALLBACKS.
if ((hleAfterSyscall & HLE_AFTER_RESCHED_CALLBACKS) != 0)
__KernelReSchedule(true, hleAfterSyscallReschedReason);
else if ((hleAfterSyscall & HLE_AFTER_RESCHED) != 0)
__KernelReSchedule(hleAfterSyscallReschedReason);
else if ((hleAfterSyscall & HLE_AFTER_ALL_CALLBACKS) != 0)
__KernelCheckCallbacks();
}
else
{

View File

@ -73,6 +73,14 @@ int GetModuleIndex(const char *modulename);
void RegisterModule(const char *name, int numFunctions, const HLEFunction *funcTable);
// Run the current thread's callbacks after the syscall finishes.
void hleCheckCurrentCallbacks();
// Check and potentially run all thread's callbacks after the syscall finishes.
void hleCheckAllCallbacks();
// Reschedule after the syscall finishes.
void hleReSchedule(const char *reason);
// Reschedule and go into a callback processing state after the syscall finishes.
void hleReSchedule(bool callbacks, const char *reason);
void HLEInit();
void HLEShutdown();

View File

@ -269,7 +269,7 @@ void sceKernelDeleteMutex(SceUID id)
RETURN(kernelObjects.Destroy<Mutex>(id));
if (wokeThreads)
__KernelReSchedule("mutex deleted");
hleReSchedule("mutex deleted");
}
else
RETURN(error);
@ -430,7 +430,7 @@ void sceKernelLockMutexCB(SceUID id, int count, u32 timeoutPtr)
if (__KernelLockMutex(mutex, count, error))
{
RETURN(0);
__KernelForceCallbacks();
hleCheckCurrentCallbacks();
}
else if (error)
RETURN(error);
@ -490,7 +490,7 @@ void sceKernelUnlockMutex(SceUID id, int count)
if (mutex->nm.lockLevel == 0)
{
if (__KernelUnlockMutex(mutex, error))
__KernelReSchedule("mutex unlocked");
hleReSchedule("mutex unlocked");
}
}
@ -596,7 +596,7 @@ void sceKernelDeleteLwMutex(u32 workareaPtr)
Memory::WriteStruct(workareaPtr, &workarea);
if (wokeThreads)
__KernelReSchedule("lwmutex deleted");
hleReSchedule("lwmutex deleted");
}
else
RETURN(error);
@ -789,7 +789,7 @@ void sceKernelLockLwMutexCB(u32 workareaPtr, int count, u32 timeoutPtr)
{
Memory::WriteStruct(workareaPtr, &workarea);
RETURN(0);
__KernelForceCallbacks();
hleCheckCurrentCallbacks();
}
else if (error)
RETURN(error);
@ -838,7 +838,7 @@ void sceKernelUnlockLwMutex(u32 workareaPtr, int count)
if (workarea.lockLevel == 0)
{
if (__KernelUnlockLwMutex(workarea, error))
__KernelReSchedule("lwmutex unlocked");
hleReSchedule("lwmutex unlocked");
Memory::WriteStruct(workareaPtr, &workarea);
}
else

View File

@ -168,7 +168,7 @@ void sceKernelCancelSema(SceUID id, int newCount, u32 numWaitThreadsPtr)
RETURN(0);
if (__KernelClearSemaThreads(s, SCE_KERNEL_ERROR_WAIT_CANCEL))
__KernelReSchedule("semaphore canceled");
hleReSchedule("semaphore canceled");
}
else
{
@ -224,7 +224,7 @@ void sceKernelDeleteSema(SceUID id)
RETURN(kernelObjects.Destroy<Semaphore>(id));
if (wokeThreads)
__KernelReSchedule("semaphore deleted");
hleReSchedule("semaphore deleted");
}
else
{
@ -291,12 +291,12 @@ retry:
}
if (wokeThreads)
__KernelReSchedule("semaphore signaled");
hleReSchedule("semaphore signaled");
}
else
{
ERROR_LOG(HLE, "sceKernelSignalSema : Trying to signal invalid semaphore %i", id);
RETURN(error;)
RETURN(error);
}
}
@ -356,7 +356,7 @@ void __KernelWaitSema(SceUID id, int wantedCount, u32 timeoutPtr, const char *ba
{
s->ns.currentCount -= wantedCount;
if (processCallbacks)
__KernelForceCallbacks();
hleCheckCurrentCallbacks();
}
else
{

View File

@ -397,7 +397,7 @@ void __KernelIdle()
// In Advance, we might trigger an interrupt such as vblank.
// If we end up in an interrupt, we don't want to reschedule.
// However, we have to reschedule... damn.
__KernelReSchedule("idle");
hleReSchedule("idle");
}
void __KernelThreadingShutdown()
@ -618,10 +618,9 @@ u32 __KernelResumeThreadFromWait(SceUID threadID, int retval)
}
}
// 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
// If any changes were made, it will context switch
// If any changes were made, it will context switch after the syscall
bool __KernelTriggerWait(WaitType type, int id, bool useRetVal, int retVal, bool dontSwitch)
{
bool doneAnything = false;
@ -649,7 +648,7 @@ bool __KernelTriggerWait(WaitType type, int id, bool useRetVal, int retVal, bool
// TODO: time waster
char temp[256];
sprintf(temp, "resumed from wait %s", waitTypeStrings[(int)type]);
__KernelReSchedule(temp);
hleReSchedule(temp);
}
}
return true;
@ -681,7 +680,7 @@ void __KernelWaitCurThread(WaitType type, SceUID waitID, u32 waitValue, u32 time
char temp[256];
sprintf(temp, "started wait %s", waitTypeStrings[(int)type]);
__KernelReSchedule(processCallbacks, temp);
hleReSchedule(processCallbacks, temp);
// TODO: Remove thread from Ready queue?
}
@ -998,7 +997,7 @@ void sceKernelStartThread(SceUID threadToStartID, u32 argSize, u32 argBlockPtr)
}
RETURN(0);
__KernelReSchedule("thread started");
hleReSchedule("thread started");
}
else
{
@ -1058,7 +1057,7 @@ void __KernelReturnFromThread()
// Find threads that waited for me
// Wake them
if (!__KernelTriggerWait(WAITTYPE_THREADEND, __KernelGetCurThread()))
__KernelReSchedule("return from thread");
hleReSchedule("return from thread");
// The stack will be deallocated when the thread is deleted.
}
@ -1073,7 +1072,7 @@ void sceKernelExitThread()
//Find threads that waited for me
// Wake them
if (!__KernelTriggerWait(WAITTYPE_THREADEND, __KernelGetCurThread()))
__KernelReSchedule("exited thread");
hleReSchedule("exited thread");
// The stack will be deallocated when the thread is deleted.
}
@ -1088,7 +1087,7 @@ void _sceKernelExitThread()
//Find threads that waited for this one
// Wake them
if (!__KernelTriggerWait(WAITTYPE_THREADEND, __KernelGetCurThread()))
__KernelReSchedule("exit-deleted thread");
hleReSchedule("exit-deleted thread");
// The stack will be deallocated when the thread is deleted.
}
@ -1140,7 +1139,7 @@ u32 sceKernelResumeDispatchThread(u32 suspended)
void sceKernelRotateThreadReadyQueue()
{
DEBUG_LOG(HLE,"sceKernelRotateThreadReadyQueue : rescheduling");
__KernelReSchedule("rotatethreadreadyqueue");
hleReSchedule("rotatethreadreadyqueue");
}
void sceKernelDeleteThread()
@ -1164,7 +1163,7 @@ void sceKernelDeleteThread()
//TODO: should we really reschedule here?
//if (!__KernelTriggerWait(WAITTYPE_THREADEND, threadHandle))
// __KernelReSchedule("thread deleted");
// hleReSchedule("thread deleted");
}
}
else
@ -1185,7 +1184,7 @@ void sceKernelTerminateDeleteThread()
//TODO: should we really reschedule here?
if (!__KernelTriggerWait(WAITTYPE_THREADEND, threadno))
__KernelReSchedule("termdeletethread");
hleReSchedule("termdeletethread");
}
else
{
@ -1766,7 +1765,7 @@ void __KernelReturnFromMipsCall()
{
// Sometimes, we want to stay on the thread.
if (call->reschedAfter)
__KernelReSchedule("return from callback");
hleReSchedule("return from callback");
}
}

View File

@ -132,7 +132,7 @@ void sceUmdActivate(u32 unknown, const char *name)
RETURN(0);
if (changed)
__KernelReSchedule("umd activated");
hleReSchedule("umd activated");
}
void sceUmdDeactivate(u32 unknown, const char *name)
@ -161,7 +161,7 @@ void sceUmdDeactivate(u32 unknown, const char *name)
RETURN(0);
if (changed)
__KernelReSchedule("umd deactivated");
hleReSchedule("umd deactivated");
}
u32 sceUmdRegisterUMDCallBack(u32 cbId)
@ -268,7 +268,7 @@ void sceUmdWaitDriveStatCB(u32 stat, u32 timeout)
{
DEBUG_LOG(HLE,"0=sceUmdWaitDriveStatCB(stat = %08x, timeout = %d)", stat, timeout);
__KernelForceCallbacks();
hleCheckCurrentCallbacks();
}
else
{
@ -284,7 +284,7 @@ void sceUmdWaitDriveStatCB(u32 stat, u32 timeout)
__KernelWaitCurThread(WAITTYPE_UMD, 1, stat, 0, true);
}
else if (driveCBId != -1)
__KernelReSchedule("umd stat waited");
hleReSchedule("umd stat waited");
}
void sceUmdCancelWaitDriveStat()