mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-11-12 09:09:29 +01:00
Merge pull request #8782 from unknownbrackets/threadevent
Add basic thread event handler support
This commit is contained in:
commit
da34e31cba
4 files changed
+267
-35
No files matched your search
@@ -99,6 +99,17 @@ struct ThreadQueueList {
|
||||
return 0;
|
||||
}
|
||||
|
||||
inline SceUID peek_first() {
|
||||
Queue *cur = first;
|
||||
while (cur != invalid()) {
|
||||
if (cur->size() > 0)
|
||||
return cur->data[cur->first];
|
||||
cur = cur->next;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
inline void push_front(u32 priority, const SceUID threadID) {
|
||||
Queue *cur = &queues[priority];
|
||||
cur->data[--cur->first] = threadID;
|
||||
|
||||
@@ -596,6 +596,8 @@ KernelObject *KernelObjectPool::CreateByIDType(int type) {
|
||||
return __KernelFileNodeObject();
|
||||
case PPSSPP_KERNEL_TMID_DirList:
|
||||
return __KernelDirListingObject();
|
||||
case SCE_KERNEL_TMID_ThreadEventHandler:
|
||||
return __KernelThreadEventHandlerObject();
|
||||
|
||||
default:
|
||||
ERROR_LOG(COMMON, "Unable to load state: could not find object type %d.", type);
|
||||
@@ -786,10 +788,6 @@ const HLEFunction ThreadManForUser[] =
|
||||
{0X278C0DF5, &WrapI_IU<sceKernelWaitThreadEnd>, "sceKernelWaitThreadEnd", 'i', "ix" },
|
||||
{0XD59EAD2F, &WrapI_I<sceKernelWakeupThread>, "sceKernelWakeupThread", 'i', "i" }, //AI Go, audio?
|
||||
|
||||
{0X0C106E53, nullptr, "sceKernelRegisterThreadEventHandler", '?', "" },
|
||||
{0X72F3C145, nullptr, "sceKernelReleaseThreadEventHandler", '?', "" },
|
||||
{0X369EEB6B, nullptr, "sceKernelReferThreadEventHandlerStatus", '?', "" },
|
||||
|
||||
{0x349d6d6c, &sceKernelCheckCallback, "sceKernelCheckCallback", 'i', "" },
|
||||
{0XE81CAF8F, &WrapI_CUU<sceKernelCreateCallback>, "sceKernelCreateCallback", 'i', "sxx" },
|
||||
{0XEDBA5844, &WrapI_I<sceKernelDeleteCallback>, "sceKernelDeleteCallback", 'i', "i" },
|
||||
@@ -864,6 +862,9 @@ const HLEFunction ThreadManForUser[] =
|
||||
{0x0E927AED, &_sceKernelReturnFromTimerHandler, "_sceKernelReturnFromTimerHandler", 'v', "" },
|
||||
{0X532A522E, &WrapV_I<_sceKernelExitThread>, "_sceKernelExitThread", 'v', "i" },
|
||||
|
||||
{0x0C106E53, &WrapI_CIUUU<sceKernelRegisterThreadEventHandler>, "sceKernelRegisterThreadEventHandler", 'i', "sixxx", },
|
||||
{0x72F3C145, &WrapI_I<sceKernelReleaseThreadEventHandler>, "sceKernelReleaseThreadEventHandler", 'i', "i" },
|
||||
{0x369EEB6B, &WrapI_IU<sceKernelReferThreadEventHandlerStatus>, "sceKernelReferThreadEventHandlerStatus", 'i', "ip" },
|
||||
|
||||
// Shouldn't hook this up. No games should import this function manually and call it.
|
||||
// {0x6E9EA350, _sceKernelReturnFromCallback,"_sceKernelReturnFromCallback"},
|
||||
|
||||
+245
-30
@@ -91,7 +91,17 @@ const char *getWaitTypeName(WaitType type)
|
||||
return "Unknown";
|
||||
}
|
||||
|
||||
enum {
|
||||
enum ThreadEventType {
|
||||
THREADEVENT_CREATE = 1,
|
||||
THREADEVENT_START = 2,
|
||||
THREADEVENT_EXIT = 4,
|
||||
THREADEVENT_DELETE = 8,
|
||||
THREADEVENT_SUPPORTED = THREADEVENT_CREATE | THREADEVENT_START | THREADEVENT_EXIT | THREADEVENT_DELETE,
|
||||
};
|
||||
|
||||
void __KernelThreadTriggerEvent(bool isKernel, SceUID threadID, ThreadEventType type);
|
||||
|
||||
enum {
|
||||
PSP_THREAD_ATTR_KERNEL = 0x00001000,
|
||||
PSP_THREAD_ATTR_VFPU = 0x00004000,
|
||||
PSP_THREAD_ATTR_SCRATCH_SRAM = 0x00008000, // Save/restore scratch as part of context???
|
||||
@@ -149,21 +159,16 @@ public:
|
||||
return;
|
||||
|
||||
p.Do(nc);
|
||||
p.Do(savedPC);
|
||||
p.Do(savedRA);
|
||||
p.Do(savedV0);
|
||||
p.Do(savedV1);
|
||||
// No longer used.
|
||||
u32 legacySavedIdRegister = 0;
|
||||
p.Do(legacySavedIdRegister);
|
||||
// Saved values were moved to mips call, ignoring here.
|
||||
u32 legacySaved = 0;
|
||||
p.Do(legacySaved);
|
||||
p.Do(legacySaved);
|
||||
p.Do(legacySaved);
|
||||
p.Do(legacySaved);
|
||||
p.Do(legacySaved);
|
||||
}
|
||||
|
||||
NativeCallback nc;
|
||||
|
||||
u32 savedPC;
|
||||
u32 savedRA;
|
||||
u32 savedV0;
|
||||
u32 savedV1;
|
||||
};
|
||||
|
||||
#if COMMON_LITTLE_ENDIAN
|
||||
@@ -607,7 +612,7 @@ struct WaitTypeFuncs
|
||||
WaitEndCallbackFunc endFunc;
|
||||
};
|
||||
|
||||
void __KernelExecuteMipsCallOnCurrentThread(u32 callId, bool reschedAfter);
|
||||
bool __KernelExecuteMipsCallOnCurrentThread(u32 callId, bool reschedAfter);
|
||||
|
||||
Thread *__KernelCreateThread(SceUID &id, SceUID moduleID, const char *name, u32 entryPoint, u32 priority, int stacksize, u32 attr);
|
||||
void __KernelResetThread(Thread *t, int lowestPriority);
|
||||
@@ -632,6 +637,9 @@ u32 extendReturnHackAddr;
|
||||
u32 moduleReturnHackAddr;
|
||||
std::vector<ThreadCallback> threadEndListeners;
|
||||
|
||||
typedef std::vector<SceUID> ThreadEventHandlerList;
|
||||
static std::map<SceUID, ThreadEventHandlerList> threadEventHandlers;
|
||||
|
||||
// Lists all thread ids that aren't deleted/etc.
|
||||
std::vector<SceUID> threadqueue;
|
||||
|
||||
@@ -690,7 +698,8 @@ void MipsCall::DoState(PointerWrap &p)
|
||||
// No longer used.
|
||||
u32 legacySavedIdRegister = 0;
|
||||
p.Do(legacySavedIdRegister);
|
||||
p.Do(savedRa);
|
||||
u32 legacySavedRa = 0;
|
||||
p.Do(legacySavedRa);
|
||||
p.Do(savedPc);
|
||||
p.Do(savedV0);
|
||||
p.Do(savedV1);
|
||||
@@ -943,7 +952,7 @@ void __KernelThreadingInit()
|
||||
|
||||
void __KernelThreadingDoState(PointerWrap &p)
|
||||
{
|
||||
auto s = p.Section("sceKernelThread", 1);
|
||||
auto s = p.Section("sceKernelThread", 1, 2);
|
||||
if (!s)
|
||||
return;
|
||||
|
||||
@@ -978,6 +987,9 @@ void __KernelThreadingDoState(PointerWrap &p)
|
||||
|
||||
__SetCurrentThread(kernelObjects.GetFast<Thread>(currentThread), currentThread, __KernelGetThreadName(currentThread));
|
||||
lastSwitchCycles = CoreTiming::GetTicks();
|
||||
|
||||
if (s >= 2)
|
||||
p.Do(threadEventHandlers);
|
||||
}
|
||||
|
||||
void __KernelThreadingDoStateLate(PointerWrap &p)
|
||||
@@ -1146,6 +1158,7 @@ void __KernelThreadingShutdown()
|
||||
__SetCurrentThread(NULL, 0, NULL);
|
||||
intReturnHackAddr = 0;
|
||||
pausedDelays.clear();
|
||||
threadEventHandlers.clear();
|
||||
}
|
||||
|
||||
const char *__KernelGetThreadName(SceUID threadID)
|
||||
@@ -1633,6 +1646,11 @@ u32 __KernelDeleteThread(SceUID threadID, int exitStatus, const char *reason)
|
||||
t->Cleanup();
|
||||
}
|
||||
|
||||
// TODO: Thread should not be deleted yet...
|
||||
// Before triggering, set v0. It'll be restored if one is called.
|
||||
RETURN(error);
|
||||
__KernelThreadTriggerEvent((t->nt.attr & PSP_THREAD_ATTR_KERNEL) != 0, threadID, THREADEVENT_DELETE);
|
||||
|
||||
return kernelObjects.Destroy<Thread>(threadID);
|
||||
}
|
||||
|
||||
@@ -1940,6 +1958,10 @@ int __KernelCreateThread(const char *threadName, SceUID moduleID, u32 entry, u32
|
||||
// This won't schedule to the new thread, but it may to one woken from eating cycles.
|
||||
// Technically, this should not eat all at once, and reschedule in the middle, but that's hard.
|
||||
hleReSchedule("thread created");
|
||||
|
||||
// Before triggering, set v0, since we restore on return.
|
||||
RETURN(id);
|
||||
__KernelThreadTriggerEvent((attr & PSP_THREAD_ATTR_KERNEL) != 0, id, THREADEVENT_CREATE);
|
||||
return hleLogSuccessInfoI(SCEKERNEL, id);
|
||||
}
|
||||
|
||||
@@ -1988,6 +2010,11 @@ int __KernelStartThread(SceUID threadToStartID, int argSize, u32 argBlockPtr, bo
|
||||
dispatchEnabled = true;
|
||||
|
||||
__KernelChangeReadyState(startThread, threadToStartID, true);
|
||||
|
||||
// Need to write out v0 before triggering event.
|
||||
// TODO: Technically the wrong place. This should trigger when the thread actually starts (e.g. if suspended.)
|
||||
RETURN(0);
|
||||
__KernelThreadTriggerEvent((startThread->nt.attr & PSP_THREAD_ATTR_KERNEL) != 0, threadToStartID, THREADEVENT_START);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -2055,6 +2082,9 @@ void __KernelReturnFromThread()
|
||||
|
||||
hleReSchedule("thread returned");
|
||||
|
||||
// TODO: This should trigger ON the thread when it exits.
|
||||
__KernelThreadTriggerEvent((thread->nt.attr & PSP_THREAD_ATTR_KERNEL) != 0, thread->GetUID(), THREADEVENT_EXIT);
|
||||
|
||||
// The stack will be deallocated when the thread is deleted.
|
||||
}
|
||||
|
||||
@@ -2068,6 +2098,9 @@ void sceKernelExitThread(int exitStatus)
|
||||
|
||||
hleReSchedule("thread exited");
|
||||
|
||||
// TODO: This should trigger ON the thread when it exits.
|
||||
__KernelThreadTriggerEvent((thread->nt.attr & PSP_THREAD_ATTR_KERNEL) != 0, thread->GetUID(), THREADEVENT_EXIT);
|
||||
|
||||
// The stack will be deallocated when the thread is deleted.
|
||||
}
|
||||
|
||||
@@ -2081,6 +2114,9 @@ void _sceKernelExitThread(int exitStatus)
|
||||
|
||||
hleReSchedule("thread _exited");
|
||||
|
||||
// TODO: This should trigger ON the thread when it exits.
|
||||
__KernelThreadTriggerEvent((thread->nt.attr & PSP_THREAD_ATTR_KERNEL) != 0, thread->GetUID(), THREADEVENT_EXIT);
|
||||
|
||||
// The stack will be deallocated when the thread is deleted.
|
||||
}
|
||||
|
||||
@@ -2095,6 +2131,9 @@ void sceKernelExitDeleteThread(int exitStatus)
|
||||
g_inCbCount = 0;
|
||||
|
||||
hleReSchedule("thread exited with delete");
|
||||
|
||||
// TODO: This should trigger ON the thread when it exits.
|
||||
__KernelThreadTriggerEvent((thread->nt.attr & PSP_THREAD_ATTR_KERNEL) != 0, thread->GetUID(), THREADEVENT_EXIT);
|
||||
}
|
||||
else
|
||||
ERROR_LOG_REPORT(SCEKERNEL, "sceKernelExitDeleteThread(%d) ERROR - could not find myself!", exitStatus);
|
||||
@@ -2208,9 +2247,17 @@ int sceKernelTerminateDeleteThread(int threadID)
|
||||
Thread *t = kernelObjects.Get<Thread>(threadID, error);
|
||||
if (t)
|
||||
{
|
||||
bool wasStopped = t->isStopped();
|
||||
|
||||
INFO_LOG(SCEKERNEL, "sceKernelTerminateDeleteThread(%i)", threadID);
|
||||
error = __KernelDeleteThread(threadID, SCE_KERNEL_ERROR_THREAD_TERMINATED, "thread terminated with delete");
|
||||
|
||||
if (!wasStopped) {
|
||||
// Set v0 before calling the handler, or it'll get lost.
|
||||
RETURN(error);
|
||||
__KernelThreadTriggerEvent((t->nt.attr & PSP_THREAD_ATTR_KERNEL) != 0, t->GetUID(), THREADEVENT_EXIT);
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
else
|
||||
@@ -2241,6 +2288,10 @@ int sceKernelTerminateThread(SceUID threadID) {
|
||||
// On terminate, we reset the thread priority. On exit, we don't always (see __KernelResetThread.)
|
||||
t->nt.currentPriority = t->nt.initialPriority;
|
||||
|
||||
// Need to set v0 since it'll be restored.
|
||||
RETURN(0);
|
||||
__KernelThreadTriggerEvent((t->nt.attr & PSP_THREAD_ATTR_KERNEL) != 0, t->GetUID(), THREADEVENT_EXIT);
|
||||
|
||||
return hleLogSuccessInfoI(SCEKERNEL, 0);
|
||||
} else {
|
||||
return hleLogError(SCEKERNEL, error, "thread doesn't exist");
|
||||
@@ -2716,7 +2767,7 @@ int sceKernelDeleteCallback(SceUID cbId)
|
||||
{
|
||||
Thread *thread = kernelObjects.Get<Thread>(cb->nc.threadId, error);
|
||||
if (thread)
|
||||
thread->callbacks.erase(std::find(thread->callbacks.begin(), thread->callbacks.end(), cbId), thread->callbacks.end());
|
||||
thread->callbacks.erase(std::remove(thread->callbacks.begin(), thread->callbacks.end(), cbId), thread->callbacks.end());
|
||||
if (cb->nc.notifyCount != 0)
|
||||
readyCallbacksCount--;
|
||||
|
||||
@@ -2842,8 +2893,11 @@ void ActionAfterMipsCall::run(MipsCall &call) {
|
||||
u32 error;
|
||||
Thread *thread = kernelObjects.Get<Thread>(threadID, error);
|
||||
if (thread) {
|
||||
__KernelChangeReadyState(thread, threadID, (status & THREADSTATUS_READY) != 0);
|
||||
thread->nt.status = status;
|
||||
// Resume waiting after a callback, but not from terminate/delete.
|
||||
if ((thread->nt.status & (THREADSTATUS_DEAD | THREADSTATUS_DORMANT)) == 0) {
|
||||
__KernelChangeReadyState(thread, threadID, (status & THREADSTATUS_READY) != 0);
|
||||
thread->nt.status = status;
|
||||
}
|
||||
thread->nt.waitType = waitType;
|
||||
thread->nt.waitID = waitID;
|
||||
thread->waitInfo = waitInfo;
|
||||
@@ -3003,7 +3057,10 @@ static bool __CanExecuteCallbackNow(Thread *thread) {
|
||||
|
||||
void __KernelCallAddress(Thread *thread, u32 entryPoint, Action *afterAction, const u32 args[], int numargs, bool reschedAfter, SceUID cbId)
|
||||
{
|
||||
hleSkipDeadbeef();
|
||||
if (!thread || thread->isStopped()) {
|
||||
WARN_LOG_REPORT(SCEKERNEL, "Running mipscall on dormant thread");
|
||||
}
|
||||
|
||||
_dbg_assert_msg_(SCEKERNEL, numargs <= 6, "MipsCalls can only take 6 args.");
|
||||
|
||||
if (thread) {
|
||||
@@ -3053,8 +3110,7 @@ void __KernelCallAddress(Thread *thread, u32 entryPoint, Action *afterAction, co
|
||||
if (__CanExecuteCallbackNow(thread)) {
|
||||
thread = __GetCurrentThread();
|
||||
__KernelChangeThreadState(thread, THREADSTATUS_RUNNING);
|
||||
__KernelExecuteMipsCallOnCurrentThread(callId, reschedAfter);
|
||||
called = true;
|
||||
called = __KernelExecuteMipsCallOnCurrentThread(callId, reschedAfter);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3073,13 +3129,14 @@ void __KernelDirectMipsCall(u32 entryPoint, Action *afterAction, u32 args[], int
|
||||
__KernelCallAddress(__GetCurrentThread(), entryPoint, afterAction, args, numargs, reschedAfter, 0);
|
||||
}
|
||||
|
||||
void __KernelExecuteMipsCallOnCurrentThread(u32 callId, bool reschedAfter)
|
||||
bool __KernelExecuteMipsCallOnCurrentThread(u32 callId, bool reschedAfter)
|
||||
{
|
||||
hleSkipDeadbeef();
|
||||
|
||||
Thread *cur = __GetCurrentThread();
|
||||
if (cur == NULL)
|
||||
{
|
||||
if (cur == nullptr) {
|
||||
ERROR_LOG(SCEKERNEL, "__KernelExecuteMipsCallOnCurrentThread(): Bad current thread");
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (g_inCbCount > 0) {
|
||||
@@ -3088,9 +3145,24 @@ void __KernelExecuteMipsCallOnCurrentThread(u32 callId, bool reschedAfter)
|
||||
DEBUG_LOG(SCEKERNEL, "Executing mipscall %i", callId);
|
||||
MipsCall *call = mipsCalls.get(callId);
|
||||
|
||||
// Grab some MIPS stack space.
|
||||
u32 &sp = currentMIPS->r[MIPS_REG_SP];
|
||||
if (!Memory::IsValidAddress(sp - 32 * 4)) {
|
||||
ERROR_LOG_REPORT(SCEKERNEL, "__KernelExecuteMipsCallOnCurrentThread(): Not enough free stack");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Let's just save regs generously. Better to be safe.
|
||||
sp -= 32 * 4;
|
||||
for (int i = MIPS_REG_A0; i <= MIPS_REG_T7; ++i) {
|
||||
Memory::Write_U32(currentMIPS->r[i], sp + i * 4);
|
||||
}
|
||||
Memory::Write_U32(currentMIPS->r[MIPS_REG_T8], sp + MIPS_REG_T8 * 4);
|
||||
Memory::Write_U32(currentMIPS->r[MIPS_REG_T9], sp + MIPS_REG_T9 * 4);
|
||||
Memory::Write_U32(currentMIPS->r[MIPS_REG_RA], sp + MIPS_REG_RA * 4);
|
||||
|
||||
// Save the few regs that need saving
|
||||
call->savedPc = currentMIPS->pc;
|
||||
call->savedRa = currentMIPS->r[MIPS_REG_RA];
|
||||
call->savedV0 = currentMIPS->r[MIPS_REG_V0];
|
||||
call->savedV1 = currentMIPS->r[MIPS_REG_V1];
|
||||
call->savedId = cur->currentMipscallId;
|
||||
@@ -3107,6 +3179,8 @@ void __KernelExecuteMipsCallOnCurrentThread(u32 callId, bool reschedAfter)
|
||||
if (call->cbId != 0)
|
||||
g_inCbCount++;
|
||||
currentCallbackThreadID = currentThread;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void __KernelReturnFromMipsCall()
|
||||
@@ -3134,8 +3208,17 @@ void __KernelReturnFromMipsCall()
|
||||
delete call->doAfter;
|
||||
}
|
||||
|
||||
u32 &sp = currentMIPS->r[MIPS_REG_SP];
|
||||
for (int i = MIPS_REG_A0; i <= MIPS_REG_T7; ++i) {
|
||||
currentMIPS->r[i] = Memory::Read_U32(sp + i * 4);
|
||||
}
|
||||
currentMIPS->r[MIPS_REG_T8] = Memory::Read_U32(sp + MIPS_REG_T8 * 4);
|
||||
currentMIPS->r[MIPS_REG_T9] = Memory::Read_U32(sp + MIPS_REG_T9 * 4);
|
||||
currentMIPS->r[MIPS_REG_RA] = Memory::Read_U32(sp + MIPS_REG_RA * 4);
|
||||
sp += 32 * 4;
|
||||
|
||||
currentMIPS->pc = call->savedPc;
|
||||
currentMIPS->r[MIPS_REG_RA] = call->savedRa;
|
||||
// This is how we set the return value.
|
||||
currentMIPS->r[MIPS_REG_V0] = call->savedV0;
|
||||
currentMIPS->r[MIPS_REG_V1] = call->savedV1;
|
||||
cur->currentMipscallId = call->savedId;
|
||||
@@ -3184,8 +3267,9 @@ bool __KernelExecutePendingMipsCalls(Thread *thread, bool reschedAfter)
|
||||
// Pop off the first pending mips call
|
||||
u32 callId = thread->pendingMipsCalls.front();
|
||||
thread->pendingMipsCalls.pop_front();
|
||||
__KernelExecuteMipsCallOnCurrentThread(callId, reschedAfter);
|
||||
return true;
|
||||
if (__KernelExecuteMipsCallOnCurrentThread(callId, reschedAfter)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -3470,3 +3554,134 @@ int LoadExecForUser_362A956B()
|
||||
Memory::Write_U32(-1, parameterArea + 8);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const SceUID SCE_TE_THREADID_ALL_USER = 0xFFFFFFF0;
|
||||
|
||||
struct NativeThreadEventHandler {
|
||||
u32 size;
|
||||
char name[KERNELOBJECT_MAX_NAME_LENGTH + 1];
|
||||
SceUID threadID;
|
||||
u32 mask;
|
||||
u32 handlerPtr;
|
||||
u32 commonArg;
|
||||
};
|
||||
|
||||
struct ThreadEventHandler : public KernelObject {
|
||||
const char *GetName() { return nteh.name; }
|
||||
const char *GetTypeName() { return "ThreadEventHandler"; }
|
||||
static u32 GetMissingErrorCode() { return SCE_KERNEL_ERROR_UNKNOWN_TEID; }
|
||||
static int GetStaticIDType() { return SCE_KERNEL_TMID_ThreadEventHandler; }
|
||||
int GetIDType() const { return SCE_KERNEL_TMID_ThreadEventHandler; }
|
||||
|
||||
virtual void DoState(PointerWrap &p) {
|
||||
auto s = p.Section("ThreadEventHandler", 1);
|
||||
if (!s)
|
||||
return;
|
||||
|
||||
p.Do(nteh);
|
||||
}
|
||||
|
||||
NativeThreadEventHandler nteh;
|
||||
};
|
||||
|
||||
KernelObject *__KernelThreadEventHandlerObject() {
|
||||
// Default object to load from state.
|
||||
return new ThreadEventHandler;
|
||||
}
|
||||
|
||||
void __KernelThreadTriggerEvent(const ThreadEventHandlerList &handlers, SceUID threadID, ThreadEventType type) {
|
||||
Thread *thread = __GetCurrentThread();
|
||||
if (!thread || thread->isStopped()) {
|
||||
SceUID nextThreadID = threadReadyQueue.peek_first();
|
||||
thread = kernelObjects.GetFast<Thread>(nextThreadID);
|
||||
}
|
||||
|
||||
for (auto it = handlers.begin(), end = handlers.end(); it != end; ++it) {
|
||||
u32 error;
|
||||
const auto teh = kernelObjects.Get<ThreadEventHandler>(*it, error);
|
||||
if (!teh || (teh->nteh.mask & type) == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const u32 args[] = {(u32)type, (u32)threadID, teh->nteh.commonArg};
|
||||
__KernelCallAddress(thread, teh->nteh.handlerPtr, nullptr, args, ARRAY_SIZE(args), true, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void __KernelThreadTriggerEvent(bool isKernel, SceUID threadID, ThreadEventType type) {
|
||||
auto exactHandlers = threadEventHandlers.find(threadID);
|
||||
if (exactHandlers != threadEventHandlers.end()) {
|
||||
__KernelThreadTriggerEvent(exactHandlers->second, threadID, type);
|
||||
}
|
||||
if (isKernel) {
|
||||
auto kernelHandlers = threadEventHandlers.find(SCE_TE_THREADID_ALL_USER);
|
||||
if (kernelHandlers != threadEventHandlers.end()) {
|
||||
__KernelThreadTriggerEvent(kernelHandlers->second, threadID, type);
|
||||
}
|
||||
} else {
|
||||
auto userHandlers = threadEventHandlers.find(SCE_TE_THREADID_ALL_USER);
|
||||
if (userHandlers != threadEventHandlers.end()) {
|
||||
__KernelThreadTriggerEvent(userHandlers->second, threadID, type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SceUID sceKernelRegisterThreadEventHandler(const char *name, SceUID threadID, u32 mask, u32 handlerPtr, u32 commonArg) {
|
||||
if (!name) {
|
||||
return hleReportError(SCEKERNEL, SCE_KERNEL_ERROR_ERROR, "invalid name");
|
||||
}
|
||||
if (threadID == 0) {
|
||||
// "atexit"?
|
||||
if (mask != THREADEVENT_EXIT) {
|
||||
return hleReportError(SCEKERNEL, SCE_KERNEL_ERROR_ILLEGAL_ATTR, "invalid thread id");
|
||||
}
|
||||
}
|
||||
u32 error;
|
||||
if (kernelObjects.Get<Thread>(threadID, error) == NULL && threadID != SCE_TE_THREADID_ALL_USER) {
|
||||
return hleReportError(SCEKERNEL, error, "bad thread id");
|
||||
}
|
||||
if ((mask & ~THREADEVENT_SUPPORTED) != 0) {
|
||||
return hleReportError(SCEKERNEL, SCE_KERNEL_ERROR_ILLEGAL_MASK, "invalid event mask");
|
||||
}
|
||||
|
||||
auto teh = new ThreadEventHandler;
|
||||
teh->nteh.size = sizeof(teh->nteh);
|
||||
strncpy(teh->nteh.name, name, KERNELOBJECT_MAX_NAME_LENGTH);
|
||||
teh->nteh.name[KERNELOBJECT_MAX_NAME_LENGTH] = '\0';
|
||||
teh->nteh.threadID = threadID;
|
||||
teh->nteh.mask = mask;
|
||||
teh->nteh.handlerPtr = handlerPtr;
|
||||
teh->nteh.commonArg = commonArg;
|
||||
|
||||
SceUID uid = kernelObjects.Create(teh);
|
||||
threadEventHandlers[threadID].push_back(uid);
|
||||
|
||||
return hleLogSuccessI(SCEKERNEL, uid);
|
||||
}
|
||||
|
||||
int sceKernelReleaseThreadEventHandler(SceUID uid) {
|
||||
u32 error;
|
||||
auto teh = kernelObjects.Get<ThreadEventHandler>(uid, error);
|
||||
if (!teh) {
|
||||
return hleReportError(SCEKERNEL, error, "bad handler id");
|
||||
}
|
||||
|
||||
auto &handlers = threadEventHandlers[teh->nteh.threadID];
|
||||
handlers.erase(std::remove(handlers.begin(), handlers.end(), uid), handlers.end());
|
||||
return hleLogSuccessI(SCEKERNEL, kernelObjects.Destroy<ThreadEventHandler>(uid));
|
||||
}
|
||||
|
||||
int sceKernelReferThreadEventHandlerStatus(SceUID uid, u32 infoPtr) {
|
||||
u32 error;
|
||||
auto teh = kernelObjects.Get<ThreadEventHandler>(uid, error);
|
||||
if (!teh) {
|
||||
return hleReportError(SCEKERNEL, error, "bad handler id");
|
||||
}
|
||||
|
||||
if (Memory::IsValidAddress(infoPtr) && Memory::Read_U32(infoPtr) != 0) {
|
||||
Memory::WriteStruct(infoPtr, &teh->nteh);
|
||||
return hleLogSuccessI(SCEKERNEL, 0);
|
||||
} else {
|
||||
return hleLogDebug(SCEKERNEL, 0, "struct size was 0");
|
||||
}
|
||||
}
|
||||
@@ -44,6 +44,7 @@ void sceKernelExitThread(int exitStatus);
|
||||
void _sceKernelExitThread(int exitStatus);
|
||||
SceUID sceKernelGetThreadId();
|
||||
int sceKernelGetThreadCurrentPriority();
|
||||
// Warning: will alter v0 in current MIPS state.
|
||||
int __KernelStartThread(SceUID threadToStartID, int argSize, u32 argBlockPtr, bool forceArgs = false);
|
||||
int __KernelStartThreadValidate(SceUID threadToStartID, int argSize, u32 argBlockPtr, bool forceArgs = false);
|
||||
int sceKernelStartThread(SceUID threadToStartID, int argSize, u32 argBlockPtr);
|
||||
@@ -254,7 +255,6 @@ struct MipsCall {
|
||||
u32 args[6];
|
||||
int numArgs;
|
||||
Action *doAfter;
|
||||
u32 savedRa;
|
||||
u32 savedPc;
|
||||
u32 savedV0;
|
||||
u32 savedV1;
|
||||
@@ -320,3 +320,8 @@ void __KernelChangeThreadState(SceUID threadId, ThreadStatus newStatus);
|
||||
|
||||
int LoadExecForUser_362A956B();
|
||||
int sceKernelRegisterExitCallback(SceUID cbId);
|
||||
|
||||
KernelObject *__KernelThreadEventHandlerObject();
|
||||
SceUID sceKernelRegisterThreadEventHandler(const char *name, SceUID threadID, u32 mask, u32 handlerPtr, u32 commonArg);
|
||||
int sceKernelReleaseThreadEventHandler(SceUID uid);
|
||||
int sceKernelReferThreadEventHandlerStatus(SceUID uid, u32 infoPtr);
|
||||
Reference in new issue
Block a user