From 8725bfe4111ecb734f6caecfa1314d155d105ab0 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 3 Feb 2013 12:09:22 -0800 Subject: [PATCH 1/5] Add a priority-indexed ready queue (not used yet.) Also, not actually only ready threads yet. --- Core/HLE/sceKernelThread.cpp | 56 ++++++++++++++++++++++++++++-------- 1 file changed, 44 insertions(+), 12 deletions(-) diff --git a/Core/HLE/sceKernelThread.cpp b/Core/HLE/sceKernelThread.cpp index c31eab435..d8e81f1cf 100644 --- a/Core/HLE/sceKernelThread.cpp +++ b/Core/HLE/sceKernelThread.cpp @@ -470,9 +470,14 @@ u32 idleThreadHackAddr; u32 threadReturnHackAddr; u32 cbReturnHackAddr; u32 intReturnHackAddr; -std::vector threadqueue; std::vector threadEndListeners; +typedef std::vector ThreadList; +// Lists all thread ids that aren't deleted/etc. +ThreadList threadqueue; +// Lists only ready thread ids. +std::map threadReadyQueue; + SceUID threadIdleID[2]; int eventScheduledWakeup; @@ -625,6 +630,29 @@ void __KernelThreadingDoState(PointerWrap &p) p.Do(dispatchEnabled); p.Do(curModule); + int n = (int) threadReadyQueue.size(); + p.Do(n); + if (p.mode == p.MODE_READ) + { + threadReadyQueue.clear(); + for (int i = 0; i < n; ++i) + { + u32 prio; + p.Do(prio); + ThreadList threads; + p.Do(threads, dv); + threadReadyQueue[prio] = threads; + } + } + else + { + for (auto it = threadReadyQueue.begin(), end = threadReadyQueue.end(); it != end; ++it) + { + p.Do(it->first); + p.Do(it->second, dv); + } + } + p.Do(eventScheduledWakeup); CoreTiming::RestoreRegisterEvent(eventScheduledWakeup, "ScheduledWakeup", &hleScheduledWakeup); p.Do(eventThreadEndTimeout); @@ -744,6 +772,7 @@ void __KernelThreadingShutdown() { kernelMemory.Free(threadReturnHackAddr); threadqueue.clear(); + threadReadyQueue.clear(); threadEndListeners.clear(); mipsCalls.clear(); threadReturnHackAddr = 0; @@ -1101,15 +1130,11 @@ void __KernelCancelThreadEndTimeout(SceUID threadID) void __KernelRemoveFromThreadQueue(SceUID threadID) { - for (size_t i = 0; i < threadqueue.size(); i++) - { - if (threadqueue[i] == threadID) - { - DEBUG_LOG(HLE, "Deleted thread %08x (%i) from thread queue", threadID, threadID); - threadqueue.erase(threadqueue.begin() + i); - return; - } - } + int prio = __KernelGetThreadPrio(threadID); + if (prio != 0) + threadReadyQueue[prio].erase(std::remove(threadReadyQueue[prio].begin(), threadReadyQueue[prio].end(), threadID), threadReadyQueue[prio].end()); + + threadqueue.erase(std::remove(threadqueue.begin(), threadqueue.end(), threadID), threadqueue.end()); } u32 __KernelDeleteThread(SceUID threadID, int exitStatus, const char *reason, bool dontSwitch) @@ -1314,6 +1339,7 @@ Thread *__KernelCreateThread(SceUID &id, SceUID moduleId, const char *name, u32 id = kernelObjects.Create(t); threadqueue.push_back(id); + threadReadyQueue[priority].push_back(id); memset(&t->nt, 0xCD, sizeof(t->nt)); @@ -1602,7 +1628,6 @@ int sceKernelDeleteThread(int threadHandle) { if (threadHandle != currentThread) { - //TODO: remove from threadqueue! DEBUG_LOG(HLE,"sceKernelDeleteThread(%i)",threadHandle); u32 error; @@ -1627,7 +1652,6 @@ int sceKernelTerminateDeleteThread(int threadno) { if (threadno != currentThread) { - //TODO: remove from threadqueue! INFO_LOG(HLE, "sceKernelTerminateDeleteThread(%i)", threadno); u32 error; @@ -1728,7 +1752,15 @@ void sceKernelChangeThreadPriority() if (thread) { DEBUG_LOG(HLE,"sceKernelChangeThreadPriority(%i, %i)", id, PARAM(1)); + + int prio = thread->nt.currentPriority; + threadReadyQueue[prio].erase(std::remove(threadReadyQueue[prio].begin(), threadReadyQueue[prio].end(), id), threadReadyQueue[prio].end()); + thread->nt.currentPriority = PARAM(1); + + // TODO: Only if ready. + threadReadyQueue[thread->nt.currentPriority].push_back(id); + RETURN(0); } else From ce2e62317bfacd2e43d4fa5dd4ab6d0c0013b178 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 3 Feb 2013 12:26:09 -0800 Subject: [PATCH 2/5] Make the new queue only have ready threads. --- Core/HLE/sceKernelThread.cpp | 68 +++++++++++++++++++++++++++--------- 1 file changed, 51 insertions(+), 17 deletions(-) diff --git a/Core/HLE/sceKernelThread.cpp b/Core/HLE/sceKernelThread.cpp index d8e81f1cf..5d5fd25ee 100644 --- a/Core/HLE/sceKernelThread.cpp +++ b/Core/HLE/sceKernelThread.cpp @@ -397,11 +397,11 @@ public: ThreadWaitInfo getWaitInfo(); // Utils - bool isRunning() const { return (nt.status & THREADSTATUS_RUNNING) != 0; } - bool isStopped() const { return (nt.status & THREADSTATUS_DORMANT) != 0; } - bool isReady() const { return (nt.status & THREADSTATUS_READY) != 0; } - bool isWaiting() const { return (nt.status & THREADSTATUS_WAIT) != 0; } - bool isSuspended() const { return (nt.status & THREADSTATUS_SUSPEND) != 0; } + inline bool isRunning() const { return (nt.status & THREADSTATUS_RUNNING) != 0; } + inline bool isStopped() const { return (nt.status & THREADSTATUS_DORMANT) != 0; } + inline bool isReady() const { return (nt.status & THREADSTATUS_READY) != 0; } + inline bool isWaiting() const { return (nt.status & THREADSTATUS_WAIT) != 0; } + inline bool isSuspended() const { return (nt.status & THREADSTATUS_SUSPEND) != 0; } virtual void DoState(PointerWrap &p) { @@ -696,6 +696,32 @@ void __KernelFireThreadEnd(SceUID threadID) } } +// TODO: Use __KernelChangeThreadState instead? It has other affects... +void __KernelChangeReadyState(Thread *thread, SceUID threadID, bool ready) +{ + int prio = thread->nt.currentPriority; + + if (thread->isReady()) + { + if (!ready) + threadReadyQueue[prio].erase(std::remove(threadReadyQueue[prio].begin(), threadReadyQueue[prio].end(), threadID), threadReadyQueue[prio].end()); + } + else if (ready) + threadReadyQueue[prio].push_back(threadID); + + thread->nt.status = THREADSTATUS_READY; +} + +void __KernelChangeReadyState(SceUID threadID, bool ready) +{ + u32 error; + Thread *thread = kernelObjects.Get(threadID, error); + if (thread) + __KernelChangeReadyState(thread, threadID, ready); + else + WARN_LOG(HLE, "Trying to change the ready state of an unknown thread?"); +} + void __KernelStartIdleThreads() { for (int i = 0; i < 2; i++) @@ -705,7 +731,7 @@ void __KernelStartIdleThreads() t->nt.gpreg = __KernelGetModuleGP(curModule); t->context.r[MIPS_REG_GP] = t->nt.gpreg; //t->context.pc += 4; // ADJUSTPC - t->nt.status = THREADSTATUS_READY; + __KernelChangeReadyState(t, threadIdleID[i], true); } } @@ -720,7 +746,7 @@ bool __KernelSwitchOffThread(const char *reason) { Thread *current = __GetCurrentThread(); if (current && current->isRunning()) - current->nt.status = (current->nt.status | THREADSTATUS_READY) & ~THREADSTATUS_RUNNING; + __KernelChangeReadyState(current, threadID, true); u32 error; // Idle 0 chosen entirely arbitrarily. @@ -751,6 +777,7 @@ void __KernelIdle() Thread *t = kernelObjects.Get(currentCallbackThreadID, error); if (t) { + __KernelChangeReadyState(t, currentCallbackThreadID, false); t->nt.status = (t->nt.status | THREADSTATUS_RUNNING) & ~THREADSTATUS_READY; __KernelSwitchContext(t, "idle"); } @@ -1339,7 +1366,6 @@ Thread *__KernelCreateThread(SceUID &id, SceUID moduleId, const char *name, u32 id = kernelObjects.Create(t); threadqueue.push_back(id); - threadReadyQueue[priority].push_back(id); memset(&t->nt, 0xCD, sizeof(t->nt)); @@ -1381,7 +1407,7 @@ void __KernelSetupRootThread(SceUID moduleID, int args, const char *argp, int pr __KernelResetThread(thread); currentThread = id; - thread->nt.status = THREADSTATUS_READY; // do not schedule + __KernelChangeReadyState(thread, id, true); // do not schedule strcpy(thread->nt.name, "root"); @@ -1459,7 +1485,7 @@ int sceKernelStartThread(SceUID threadToStartID, u32 argSize, u32 argBlockPtr) __KernelResetThread(startThread); - startThread->nt.status = THREADSTATUS_READY; + __KernelChangeReadyState(startThread, threadToStartID, true); u32 sp = startThread->context.r[MIPS_REG_SP]; if (argBlockPtr && argSize > 0) { @@ -1537,11 +1563,10 @@ void __KernelReturnFromThread() } thread->nt.exitStatus = currentMIPS->r[2]; + __KernelChangeReadyState(thread, currentThread, false); thread->nt.status = THREADSTATUS_DORMANT; __KernelFireThreadEnd(thread->GetUID()); - // TODO: Need to remove the thread from any ready queues. - __KernelTriggerWait(WAITTYPE_THREADEND, __KernelGetCurThread(), thread->nt.exitStatus, "thread returned", true); hleReSchedule("thread returned"); @@ -1554,6 +1579,7 @@ void sceKernelExitThread() _dbg_assert_msg_(HLE, thread != NULL, "Exited from a NULL thread."); ERROR_LOG(HLE,"sceKernelExitThread FAKED"); + __KernelChangeReadyState(thread, currentThread, false); thread->nt.status = THREADSTATUS_DORMANT; thread->nt.exitStatus = PARAM(0); __KernelFireThreadEnd(thread->GetUID()); @@ -1588,6 +1614,7 @@ void sceKernelExitDeleteThread() if (t) { INFO_LOG(HLE,"sceKernelExitDeleteThread()"); + __KernelChangeReadyState(t, threadHandle, false); t->nt.status = THREADSTATUS_DORMANT; t->nt.exitStatus = PARAM(0); error = __KernelDeleteThread(threadHandle, PARAM(0), "thread exited with delete", true); @@ -1686,6 +1713,7 @@ int sceKernelTerminateThread(u32 threadID) if (t) { t->nt.exitStatus = SCE_KERNEL_ERROR_THREAD_TERMINATED; + __KernelChangeReadyState(t, threadID, false); t->nt.status = THREADSTATUS_DORMANT; __KernelFireThreadEnd(threadID); // TODO: Should this really reschedule? @@ -1758,8 +1786,8 @@ void sceKernelChangeThreadPriority() thread->nt.currentPriority = PARAM(1); - // TODO: Only if ready. - threadReadyQueue[thread->nt.currentPriority].push_back(id); + if (thread->isReady()) + threadReadyQueue[thread->nt.currentPriority].push_back(id); RETURN(0); } @@ -2134,6 +2162,7 @@ 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; thread->nt.waitType = waitType; thread->nt.waitID = waitID; @@ -2206,7 +2235,7 @@ void Thread::resumeFromWait() this->nt.status &= ~THREADSTATUS_WAIT; // TODO: What if DORMANT or DEAD? if (!(this->nt.status & THREADSTATUS_WAITSUSPEND)) - this->nt.status = THREADSTATUS_READY; + __KernelChangeReadyState(this, this->GetUID(), true); // Non-waiting threads do not process callbacks. this->isProcessingCallbacks = false; @@ -2273,12 +2302,16 @@ void __KernelSwitchContext(Thread *target, const char *reason) oldName = cur->GetName(); if (cur->isRunning()) + { + __KernelChangeReadyState(cur, oldUID, false); cur->nt.status = (cur->nt.status | THREADSTATUS_READY) & ~THREADSTATUS_RUNNING; + } } - if (target && target->isRunning()) - cur->nt.status = (cur->nt.status | THREADSTATUS_RUNNING) & ~THREADSTATUS_READY; currentThread = target->GetUID(); + if (target && target->isRunning()) + __KernelChangeReadyState(target, currentThread, true); + __KernelLoadContext(&target->context); DEBUG_LOG(HLE,"Context switched: %s -> %s (%s) (%i - pc: %08x -> %i - pc: %08x)", oldName, target->GetName(), @@ -2304,6 +2337,7 @@ void __KernelChangeThreadState(Thread *thread, ThreadStatus newStatus) { // TODO: JPSCP has many conditions here, like removing wait timeout actions etc. // if (thread->nt.status == THREADSTATUS_WAIT && newStatus != THREADSTATUS_WAITSUSPEND) { + __KernelChangeReadyState(thread, thread->GetUID(), (newStatus & THREADSTATUS_READY) != 0); thread->nt.status = newStatus; if (newStatus == THREADSTATUS_WAIT) { From 996eb7a64825709bee7592477d5a5eb1659d77f9 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 3 Feb 2013 13:27:23 -0800 Subject: [PATCH 3/5] Use the new ready queue for thread switching. --- Core/HLE/sceKernelThread.cpp | 55 ++++++++++++++++-------------------- 1 file changed, 25 insertions(+), 30 deletions(-) diff --git a/Core/HLE/sceKernelThread.cpp b/Core/HLE/sceKernelThread.cpp index 5d5fd25ee..2329ee4e5 100644 --- a/Core/HLE/sceKernelThread.cpp +++ b/Core/HLE/sceKernelThread.cpp @@ -697,7 +697,7 @@ void __KernelFireThreadEnd(SceUID threadID) } // TODO: Use __KernelChangeThreadState instead? It has other affects... -void __KernelChangeReadyState(Thread *thread, SceUID threadID, bool ready) +void __KernelChangeReadyState(Thread *thread, SceUID threadID, bool ready, bool atStart = false) { int prio = thread->nt.currentPriority; @@ -707,7 +707,18 @@ void __KernelChangeReadyState(Thread *thread, SceUID threadID, bool ready) threadReadyQueue[prio].erase(std::remove(threadReadyQueue[prio].begin(), threadReadyQueue[prio].end(), threadID), threadReadyQueue[prio].end()); } else if (ready) - threadReadyQueue[prio].push_back(threadID); + { + if (atStart) + { + size_t oldSize = threadReadyQueue[prio].size(); + threadReadyQueue[prio].resize(oldSize + 1); + if (oldSize > 0) + memmove(&threadReadyQueue[prio][1], &threadReadyQueue[prio][0], oldSize * sizeof(SceUID)); + threadReadyQueue[prio][0] = threadID; + } + else + threadReadyQueue[prio].push_back(threadID); + } thread->nt.status = THREADSTATUS_READY; } @@ -1191,46 +1202,30 @@ u32 __KernelDeleteThread(SceUID threadID, int exitStatus, const char *reason, bo } Thread *__KernelNextThread() { - // round-robin scheduler - // seems to work ? - // not accurate! - int bestthread = -1; - int prio = 0xffffff; - - int next = 0; - for (size_t i = 0; i < threadqueue.size(); i++) + SceUID bestThread = -1; + // This goes in priority order. + for (auto it = threadReadyQueue.begin(), end = threadReadyQueue.end(); it != end; ++it) { - if (currentThread == threadqueue[i]) + if (!it->second.empty()) { - next = (int)i; + bestThread = it->second[0]; break; } } u32 error; - for (size_t i = 0; i < threadqueue.size(); i++) - { - next = (next + 1) % threadqueue.size(); - - Thread *t = kernelObjects.Get(threadqueue[next], error); - if (t && t->nt.currentPriority < prio) - { - if (t->nt.status & THREADSTATUS_READY) - { - bestthread = next; - prio = t->nt.currentPriority; - } - } - } - - if (bestthread != -1) - return kernelObjects.Get(threadqueue[bestthread], error); + if (bestThread != -1) + return kernelObjects.Get(bestThread, error); else return 0; } void __KernelReSchedule(const char *reason) { + // TODO: Not sure if this is correct? + if (__GetCurrentThread() && __GetCurrentThread()->isRunning()) + __KernelChangeReadyState(currentThread, true); + // cancel rescheduling when in interrupt or callback, otherwise everything will be fucked up if (__IsInInterrupt() || __KernelInCallback()) { @@ -1484,8 +1479,8 @@ int sceKernelStartThread(SceUID threadToStartID, u32 argSize, u32 argBlockPtr) threadToStartID,argSize,argBlockPtr); __KernelResetThread(startThread); + __KernelChangeReadyState(startThread, threadToStartID, true, true); - __KernelChangeReadyState(startThread, threadToStartID, true); u32 sp = startThread->context.r[MIPS_REG_SP]; if (argBlockPtr && argSize > 0) { From c7204797e7dc105775b9c439cabac1f1496e3af1 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 3 Feb 2013 13:36:08 -0800 Subject: [PATCH 4/5] Don't log idle => idle context switches. --- Core/HLE/sceKernelThread.cpp | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/Core/HLE/sceKernelThread.cpp b/Core/HLE/sceKernelThread.cpp index 2329ee4e5..928e0ab84 100644 --- a/Core/HLE/sceKernelThread.cpp +++ b/Core/HLE/sceKernelThread.cpp @@ -2308,10 +2308,16 @@ void __KernelSwitchContext(Thread *target, const char *reason) __KernelChangeReadyState(target, currentThread, true); __KernelLoadContext(&target->context); - DEBUG_LOG(HLE,"Context switched: %s -> %s (%s) (%i - pc: %08x -> %i - pc: %08x)", - oldName, target->GetName(), - reason, - oldUID, oldPC, target->GetUID(), currentMIPS->pc); + + bool fromIdle = oldUID == threadIdleID[0] || oldUID == threadIdleID[1]; + bool toIdle = currentThread == threadIdleID[0] || currentThread == threadIdleID[1]; + if (!(fromIdle && toIdle)) + { + DEBUG_LOG(HLE,"Context switched: %s -> %s (%s) (%i - pc: %08x -> %i - pc: %08x)", + oldName, target->GetName(), + reason, + oldUID, oldPC, currentThread, currentMIPS->pc); + } // No longer waiting. target->nt.waitType = WAITTYPE_NONE; From 2fa0cf040d6172c71238fdeaf2b66d14b7c529f3 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 3 Feb 2013 13:41:36 -0800 Subject: [PATCH 5/5] Fix alarm test by not interrupting when disabled. vblank was interrupting/rescheduling when interrupts were disabled. That's bad. --- Core/HLE/sceDisplay.cpp | 6 ++---- Core/HLE/sceKernelInterrupt.cpp | 5 +++++ Core/HLE/sceKernelInterrupt.h | 3 +++ 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/Core/HLE/sceDisplay.cpp b/Core/HLE/sceDisplay.cpp index 92e2d77a1..89fe11b3c 100644 --- a/Core/HLE/sceDisplay.cpp +++ b/Core/HLE/sceDisplay.cpp @@ -182,10 +182,8 @@ void hleEnterVblank(u64 userdata, int cyclesLate) { } vblankWaitingThreads.clear(); - // We want to "interrupt" even if there are no installed vblank handlers. - __KernelSwitchOffThread("vblank"); - // Trigger VBlank interrupt handlers. - __TriggerInterrupt(PSP_INTR_IMMEDIATE | PSP_INTR_ONLY_IF_ENABLED, PSP_VBLANK_INTR); + // Trigger VBlank interrupt handlers - and resched even if there's none registered. + __TriggerInterrupt(PSP_INTR_IMMEDIATE | PSP_INTR_ONLY_IF_ENABLED | PSP_INTR_ALWAYS_RESCHED, PSP_VBLANK_INTR); CoreTiming::ScheduleEvent(msToCycles(vblankMs) - cyclesLate, leaveVblankEvent, vbCount+1); diff --git a/Core/HLE/sceKernelInterrupt.cpp b/Core/HLE/sceKernelInterrupt.cpp index 2eb73d1b5..22cc0a69e 100644 --- a/Core/HLE/sceKernelInterrupt.cpp +++ b/Core/HLE/sceKernelInterrupt.cpp @@ -386,6 +386,11 @@ void __TriggerRunInterrupts(int type) { if ((type & PSP_INTR_HLE) != 0) hleRunInterrupts(); + else if ((type & PSP_INTR_ALWAYS_RESCHED) != 0) + { + if (!__RunOnePendingInterrupt()) + __KernelSwitchOffThread("interrupt"); + } else __RunOnePendingInterrupt(); } diff --git a/Core/HLE/sceKernelInterrupt.h b/Core/HLE/sceKernelInterrupt.h index f93bbf2ca..cd3b6f6b8 100644 --- a/Core/HLE/sceKernelInterrupt.h +++ b/Core/HLE/sceKernelInterrupt.h @@ -63,6 +63,9 @@ enum PSPInterruptTriggerType { PSP_INTR_HLE = 0x1, // Only trigger (as above) if interrupts are not suspended. PSP_INTR_ONLY_IF_ENABLED = 0x2, + // Always reschedule, even if there's no handler registered. + // TODO: Maybe this should just always do this? Not sure. + PSP_INTR_ALWAYS_RESCHED = 0x4, }; struct PendingInterrupt {