From 65de2637aee039dbcf15adb6ca7e60b7b356d3c5 Mon Sep 17 00:00:00 2001 From: wheremyfoodat <44909372+wheremyfoodat@users.noreply.github.com> Date: Tue, 1 Aug 2023 23:45:43 +0300 Subject: [PATCH] More threading fixes --- src/core/kernel/events.cpp | 17 +++++------------ src/core/kernel/ports.cpp | 5 +++++ src/core/kernel/threads.cpp | 17 ++++++++++++++--- 3 files changed, 24 insertions(+), 15 deletions(-) diff --git a/src/core/kernel/events.cpp b/src/core/kernel/events.cpp index 13f2b8cb..ca72add1 100644 --- a/src/core/kernel/events.cpp +++ b/src/core/kernel/events.cpp @@ -35,22 +35,15 @@ bool Kernel::signalEvent(Handle handle) { // Check if there's any thread waiting on this event if (event->waitlist != 0) { - // One-shot events get cleared once they are acquired by some thread and only wake up 1 thread at a time + wakeupAllThreads(event->waitlist, handle); + event->waitlist = 0; // No threads waiting; + if (event->resetType == ResetType::OneShot) { - int index = wakeupOneThread(event->waitlist, handle); // Wake up one thread with the highest priority - event->waitlist ^= (1ull << index); // Remove thread from waitlist event->fired = false; - } else { - wakeupAllThreads(event->waitlist, handle); - event->waitlist = 0; // No threads waiting; } - - // 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 - requireReschedule(); } - + + rescheduleThreads(); return true; } diff --git a/src/core/kernel/ports.cpp b/src/core/kernel/ports.cpp index a7351fd0..84c8cc05 100644 --- a/src/core/kernel/ports.cpp +++ b/src/core/kernel/ports.cpp @@ -76,6 +76,11 @@ void Kernel::sendSyncRequest() { u32 messagePointer = getTLSPointer() + 0x80; // The message is stored starting at TLS+0x80 logSVC("SendSyncRequest(session handle = %X)\n", handle); + // Service calls via SendSyncRequest and file access needs to put the caller to sleep for a given amount of time + // To make sure that the other threads don't get starved. Various games rely on this (including Sonic Boom: Shattering Crystal it seems) + constexpr u64 syncRequestDelayNs = 39000; + sleepThread(syncRequestDelayNs); + // The sync request is being sent at a service rather than whatever port, so have the service manager intercept it if (KernelHandles::isServiceHandle(handle)) { // The service call might cause a reschedule and change threads. Hence, set r0 before executing the service call diff --git a/src/core/kernel/threads.cpp b/src/core/kernel/threads.cpp index aff9028e..8d4c477b 100644 --- a/src/core/kernel/threads.cpp +++ b/src/core/kernel/threads.cpp @@ -178,6 +178,7 @@ void Kernel::releaseMutex(Mutex* moo) { // If the lock count reached 0 then the thread no longer owns the mootex and it can be given to a new one if (moo->lockCount == 0) { moo->locked = false; + if (moo->waitlist != 0) { int index = wakeupOneThread(moo->waitlist, moo->handle); // Wake up one thread and get its index moo->waitlist ^= (1ull << index); // Remove thread from waitlist @@ -338,10 +339,20 @@ void Kernel::wakeupAllThreads(u64 waitlist, Handle handle) { 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 - requireReschedule(); - } else { // If we're sleeping for > 0 ns + } else if (ns == 0) { + // TODO: This is garbage, absolutely not getting merged Thread& t = threads[currentThreadIndex]; + + t.status = ThreadStatus::Dead; + auto nextThreadIndex = getNextThread(); + t.status = ThreadStatus::Ready; + + if (nextThreadIndex.has_value()) { + switchThread(nextThreadIndex.value()); + } + } else { // If we're sleeping for >= 0 ns + Thread& t = threads[currentThreadIndex]; + t.status = ThreadStatus::WaitSleep; t.waitingNanoseconds = ns; t.sleepTick = cpu.getTicks();