From 35a38ac6b9643d3f2416704ce878ee60088ffc42 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Tue, 18 Dec 2012 00:58:46 -0800 Subject: [PATCH] Ensure interrupts don't run on real threads. If a thread waits on a sema, and an interrupt (such as an alarm) happens, and the interrupt signals or deletes the sema, it needs to return the value correctly. For callbacks, it's injected, since they run on thread. But interrupts aren't supposed to run on thread anyway, so this switches to idle. --- Core/HLE/sceKernelInterrupt.cpp | 4 ++++ Core/HLE/sceKernelThread.cpp | 24 ++++++++++++++++++++++++ Core/HLE/sceKernelThread.h | 4 ++++ 3 files changed, 32 insertions(+) diff --git a/Core/HLE/sceKernelInterrupt.cpp b/Core/HLE/sceKernelInterrupt.cpp index bf298e4d40..7d38ff6802 100644 --- a/Core/HLE/sceKernelInterrupt.cpp +++ b/Core/HLE/sceKernelInterrupt.cpp @@ -278,6 +278,10 @@ bool __RunOnePendingInterrupt() // Can easily prioritize between different kinds of interrupts if necessary. if (pendingInterrupts.size()) { + // If we came from CoreTiming::Advance(), we might've come from a waiting thread's callback. + // To avoid "injecting" return values into our saved state, we context switch here. + __KernelSwitchOffThread("interrupt"); + PendingInterrupt pend = pendingInterrupts.front(); pendingInterrupts.pop_front(); intState.save(); diff --git a/Core/HLE/sceKernelThread.cpp b/Core/HLE/sceKernelThread.cpp index b6ad2c7a00..a35a984362 100644 --- a/Core/HLE/sceKernelThread.cpp +++ b/Core/HLE/sceKernelThread.cpp @@ -395,6 +395,30 @@ void __KernelStartIdleThreads() } } +bool __KernelSwitchOffThread(const char *reason) +{ + if (!reason) + reason = "switch off thread"; + + SceUID threadID = currentThread->GetUID(); + + if (threadID != threadIdleID[0] && threadID != threadIdleID[1]) + { + u32 error; + // Idle 0 chosen entirely arbitrarily. + Thread *t = kernelObjects.Get(threadIdleID[0], error); + if (t) + { + __KernelSwitchContext(t, reason); + return true; + } + else + ERROR_LOG(HLE, "Unable to switch to idle thread."); + } + + return false; +} + void __KernelIdle() { CoreTiming::Idle(); diff --git a/Core/HLE/sceKernelThread.h b/Core/HLE/sceKernelThread.h index eb99e239e4..913511fe65 100644 --- a/Core/HLE/sceKernelThread.h +++ b/Core/HLE/sceKernelThread.h @@ -175,6 +175,10 @@ void __KernelSwitchContext(Thread *target, const char *reason); bool __KernelExecutePendingMipsCalls(bool reschedAfter); void __KernelNotifyCallback(RegisteredCallbackType type, SceUID cbId, int notifyArg); +// Switch to an idle / non-user thread, if not already on one. +// Returns whether a switch occurred. +bool __KernelSwitchOffThread(const char *reason); + // A call into game code. These can be pending on a thread. // Similar to Callback-s (NOT CallbackInfos) in JPCSP. class Action;