From 8dd5b990be4f0624d4b7fc17bfc85a171f3bbcc1 Mon Sep 17 00:00:00 2001 From: wheremyfoodat <44909372+wheremyfoodat@users.noreply.github.com> Date: Fri, 28 Jul 2023 19:49:11 +0300 Subject: [PATCH] Thread bonk part 1: Better rescheduling maybe --- include/kernel/kernel.hpp | 6 +++- src/core/kernel/events.cpp | 10 +++--- src/core/kernel/kernel.cpp | 7 ++++ src/core/kernel/threads.cpp | 67 ++++++++++++++++++------------------- 4 files changed, 48 insertions(+), 42 deletions(-) diff --git a/include/kernel/kernel.hpp b/include/kernel/kernel.hpp index 2db7cdda..23764dc0 100644 --- a/include/kernel/kernel.hpp +++ b/include/kernel/kernel.hpp @@ -52,6 +52,9 @@ class Kernel { // Top 8 bits are the major version, bottom 8 are the minor version u16 kernelVersion = 0; + // Shows whether a reschedule will be need + bool needReschedule = false; + Handle makeArbiter(); Handle makeProcess(u32 id); Handle makePort(const char* name); @@ -73,7 +76,6 @@ private: void switchThread(int newThreadIndex); void sortThreads(); std::optional getNextThread(); - void switchToNextThread(); void rescheduleThreads(); bool canThreadRun(const Thread& t); bool shouldWaitOnObject(KernelObject* object); @@ -168,6 +170,8 @@ public: void serviceSVC(u32 svc); void reset(); + void requireReschedule() { needReschedule = true; } + Handle makeObject(KernelObjectType type) { if (handleCounter > KernelHandles::Max) [[unlikely]] { Helpers::panic("Hlep we somehow created enough kernel objects to overflow this thing"); diff --git a/src/core/kernel/events.cpp b/src/core/kernel/events.cpp index e117dd62..04a27408 100644 --- a/src/core/kernel/events.cpp +++ b/src/core/kernel/events.cpp @@ -48,7 +48,7 @@ bool Kernel::signalEvent(Handle handle) { // We must reschedule our threads if we signalled one. Some games such as FE: Awakening rely on this // If this does not happen, we can have phenomena such as a thread waiting up a higher priority thread, // and the higher priority thread just never running - rescheduleThreads(); + requireReschedule(); } return true; @@ -121,7 +121,6 @@ void Kernel::waitSynchronization1() { if (!shouldWaitOnObject(object)) { acquireSyncObject(object, threads[currentThreadIndex]); // Acquire the object since it's ready regs[0] = Result::Success; - rescheduleThreads(); } else { // Timeout is 0, don't bother waiting, instantly timeout if (ns == 0) { @@ -141,7 +140,7 @@ void Kernel::waitSynchronization1() { // Add the current thread to the object's wait list object->getWaitlist() |= (1ull << currentThreadIndex); - switchToNextThread(); + requireReschedule(); } } @@ -204,14 +203,13 @@ void Kernel::waitSynchronizationN() { auto& t = threads[currentThreadIndex]; - // We only need to wait on one object. Easy...?! + // We only need to wait on one object. Easy. if (!waitAll) { // If there's ready objects, acquire the first one and return if (oneObjectReady) { regs[0] = Result::Success; regs[1] = firstReadyObjectIndex; // Return index of the acquired object acquireSyncObject(waitObjects[firstReadyObjectIndex].second, t); // Acquire object - rescheduleThreads(); return; } @@ -229,7 +227,7 @@ void Kernel::waitSynchronizationN() { waitObjects[i].second->getWaitlist() |= (1ull << currentThreadIndex); // And add the thread to the object's waitlist } - switchToNextThread(); + requireReschedule(); } else { Helpers::panic("WaitSynchronizatioN with waitAll"); } diff --git a/src/core/kernel/kernel.cpp b/src/core/kernel/kernel.cpp index 8f3aeda0..a8cf40a1 100644 --- a/src/core/kernel/kernel.cpp +++ b/src/core/kernel/kernel.cpp @@ -61,6 +61,11 @@ void Kernel::serviceSVC(u32 svc) { case 0x3D: outputDebugString(); break; default: Helpers::panic("Unimplemented svc: %X @ %08X", svc, regs[15]); break; } + + if (needReschedule) { + needReschedule = false; + rescheduleThreads(); + } } void Kernel::setVersion(u8 major, u8 minor) { @@ -140,6 +145,8 @@ void Kernel::reset() { threadIndices.clear(); serviceManager.reset(); + needReschedule = false; + // Allocate handle #0 to a dummy object and make a main process object makeObject(KernelObjectType::Dummy); currentProcess = makeProcess(1); // Use ID = 1 for main process diff --git a/src/core/kernel/threads.cpp b/src/core/kernel/threads.cpp index 587d5fc4..ae5deb06 100644 --- a/src/core/kernel/threads.cpp +++ b/src/core/kernel/threads.cpp @@ -82,32 +82,34 @@ std::optional Kernel::getNextThread() { return std::nullopt; } -void Kernel::switchToNextThread() { - std::optional newThreadIndex = getNextThread(); - - if (!newThreadIndex.has_value()) { - log("Kernel tried to switch to the next thread but none found. Switching to random thread\n"); - assert(aliveThreadCount != 0); - Helpers::panic("rpog"); - - int index; - do { - index = rand() % threadCount; - } while (threads[index].status == ThreadStatus::Dead); // TODO: Pray this doesn't hang - - switchThread(index); - } else { - switchThread(newThreadIndex.value()); - } -} - -// See if there;s a higher priority, ready thread and switch to that +// See if there is a higher priority, ready thread and switch to that void Kernel::rescheduleThreads() { - std::optional newThreadIndex = getNextThread(); + Thread& current = threads[currentThreadIndex]; // Current running thread + ThreadStatus currentStatus = current.status; // Its status - if (newThreadIndex.has_value() && newThreadIndex.value() != currentThreadIndex) { - threads[currentThreadIndex].status = ThreadStatus::Ready; + // If the current thread is running and hasn't gone to sleep or whatever, set it to Ready instead of Running + // Since rescheduleThreads will put it to wait and run another thread in the meantime + if (currentStatus == ThreadStatus::Running) { + currentStatus = ThreadStatus::Ready; + } + + current.status = ThreadStatus::Dead; // Temporarily mark it as dead so getNextThread will ignore it + std::optional newThreadIndex = getNextThread(); + current.status = currentStatus; // Restore old status + + // Case 1: Another thread can run (that is not the idle thread) + if (newThreadIndex.has_value() && newThreadIndex.value() != idleThreadIndex) { switchThread(newThreadIndex.value()); + } + + // Case 2: No other thread can run but this one can + else if (currentStatus == ThreadStatus::Running) { + switchThread(currentThreadIndex); + } + + // Case 3: No thread can run other than the idle thread + else { + switchThread(idleThreadIndex); } } @@ -194,7 +196,7 @@ void Kernel::releaseMutex(Mutex* moo) { moo->ownerThread = index; } - rescheduleThreads(); + requireReschedule(); } } @@ -210,7 +212,7 @@ void Kernel::sleepThreadOnArbiter(u32 waitingAddress) { t.status = ThreadStatus::WaitArbiter; t.waitingAddress = waitingAddress; - switchToNextThread(); + requireReschedule(); } // Acquires an object that is **ready to be acquired** without waiting on it @@ -339,19 +341,14 @@ void Kernel::sleepThread(s64 ns) { if (ns < 0) { Helpers::panic("Sleeping a thread for a negative amount of ns"); } else if (ns == 0) { // Used when we want to force a thread switch - std::optional newThreadIndex = getNextThread(); - // If there's no other thread waiting, don't bother yielding - if (newThreadIndex.has_value()) { - threads[currentThreadIndex].status = ThreadStatus::Ready; - switchThread(newThreadIndex.value()); - } + requireReschedule(); } else { // If we're sleeping for > 0 ns Thread& t = threads[currentThreadIndex]; t.status = ThreadStatus::WaitSleep; t.waitingNanoseconds = ns; t.sleepTick = cpu.getTicks(); - switchToNextThread(); + requireReschedule(); } } @@ -374,7 +371,7 @@ void Kernel::createThread() { regs[0] = Result::Success; regs[1] = makeThread(entrypoint, initialSP, priority, id, arg, ThreadStatus::Ready); - rescheduleThreads(); + requireReschedule(); } // void SleepThread(s64 nanoseconds) @@ -448,7 +445,7 @@ void Kernel::setThreadPriority() { } } sortThreads(); - rescheduleThreads(); + requireReschedule(); } void Kernel::exitThread() { @@ -472,7 +469,7 @@ void Kernel::exitThread() { t.threadsWaitingForTermination = 0; // No other threads waiting } - switchToNextThread(); + requireReschedule(); } void Kernel::svcCreateMutex() {