From 97cce6ccfe01c759cb89c2433d61977fa4dd9296 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 28 May 2016 21:14:19 -0700 Subject: [PATCH] ThreadEvent: Trigger handlers for start/exit too. Start isn't in the right place or on the right thread, and exit isn't on the right thread either. But these don't seem very particular about what thread they run on. --- Core/HLE/ThreadQueueList.h | 11 +++++++++ Core/HLE/sceKernelThread.cpp | 48 +++++++++++++++++++++++++++++++++--- Core/HLE/sceKernelThread.h | 1 + 3 files changed, 57 insertions(+), 3 deletions(-) diff --git a/Core/HLE/ThreadQueueList.h b/Core/HLE/ThreadQueueList.h index 9c1b1e5da2..beb149657a 100644 --- a/Core/HLE/ThreadQueueList.h +++ b/Core/HLE/ThreadQueueList.h @@ -99,6 +99,17 @@ struct ThreadQueueList { return 0; } + inline SceUID peek_first(u32 priority) { + 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; diff --git a/Core/HLE/sceKernelThread.cpp b/Core/HLE/sceKernelThread.cpp index f8eaac9d7c..06fb085d72 100644 --- a/Core/HLE/sceKernelThread.cpp +++ b/Core/HLE/sceKernelThread.cpp @@ -2010,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; } @@ -2077,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. } @@ -2090,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. } @@ -2103,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. } @@ -2117,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); @@ -2230,9 +2247,17 @@ int sceKernelTerminateDeleteThread(int threadID) Thread *t = kernelObjects.Get(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 @@ -2263,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"); @@ -2864,8 +2893,11 @@ void ActionAfterMipsCall::run(MipsCall &call) { u32 error; Thread *thread = kernelObjects.Get(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; @@ -3025,6 +3057,10 @@ static bool __CanExecuteCallbackNow(Thread *thread) { void __KernelCallAddress(Thread *thread, u32 entryPoint, Action *afterAction, const u32 args[], int numargs, bool reschedAfter, SceUID cbId) { + if (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) { @@ -3554,6 +3590,12 @@ KernelObject *__KernelThreadEventHandlerObject() { } void __KernelThreadTriggerEvent(const ThreadEventHandlerList &handlers, SceUID threadID, ThreadEventType type) { + Thread *thread = __GetCurrentThread(); + if (thread->isStopped()) { + SceUID nextThreadID = threadReadyQueue.peek_first(thread->nt.currentPriority); + thread = kernelObjects.GetFast(nextThreadID); + } + for (auto it = handlers.begin(), end = handlers.end(); it != end; ++it) { u32 error; const auto teh = kernelObjects.Get(*it, error); @@ -3562,7 +3604,7 @@ void __KernelThreadTriggerEvent(const ThreadEventHandlerList &handlers, SceUID t } const u32 args[] = {(u32)type, (u32)threadID, teh->nteh.commonArg}; - __KernelCallAddress(__GetCurrentThread(), teh->nteh.handlerPtr, nullptr, args, ARRAY_SIZE(args), true, 0); + __KernelCallAddress(thread, teh->nteh.handlerPtr, nullptr, args, ARRAY_SIZE(args), true, 0); } } diff --git a/Core/HLE/sceKernelThread.h b/Core/HLE/sceKernelThread.h index 0e92d2d369..53fa5e4367 100644 --- a/Core/HLE/sceKernelThread.h +++ b/Core/HLE/sceKernelThread.h @@ -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);