From de537fedfb63f856f057e14786366f0ad7ac6d70 Mon Sep 17 00:00:00 2001 From: wheremyfoodat Date: Sat, 6 May 2023 00:06:31 +0300 Subject: [PATCH] [Kernel] Add proper WakeUpAllThreads --- src/core/kernel/events.cpp | 42 +++++++------------------------------ src/core/kernel/threads.cpp | 42 +++++++++++++++++++++++++++++++++++-- 2 files changed, 48 insertions(+), 36 deletions(-) diff --git a/src/core/kernel/events.cpp b/src/core/kernel/events.cpp index a287c834..e395ab22 100644 --- a/src/core/kernel/events.cpp +++ b/src/core/kernel/events.cpp @@ -35,41 +35,15 @@ bool Kernel::signalEvent(Handle handle) { // Check if there's any thread waiting on this event if (event->waitlist != 0) { - // Wake up every single thread in the waitlist using a bit scanning algorithm - while (event->waitlist != 0) { - const uint index = std::countr_zero(event->waitlist); // Get one of the set bits to see which thread is waiting - event->waitlist ^= (1ull << index); // Remove thread from waitlist by toggling its bit - - // Get the thread we'll be signalling - Thread& t = threads[index]; - switch (t.status) { - case ThreadStatus::WaitSync1: - t.status = ThreadStatus::Ready; - t.gprs[0] = SVCResult::Success; // The thread did not timeout, so write success to r0 - break; - - case ThreadStatus::WaitSyncAny: - t.status = ThreadStatus::Ready; - t.gprs[0] = SVCResult::Success; // The thread did not timeout, so write success to r0 - - // Get the index of the event in the object's waitlist, write it to r1 - for (size_t i = 0; i < t.waitList.size(); i++) { - if (t.waitList[i] == handle) { - t.gprs[1] = i; - break; - } - } - break; - - case ThreadStatus::WaitSyncAll: - Helpers::panic("SignalEvent: Thread on WaitSyncAll"); - break; - } - } - - // One-shot events get cleared once they are acquired by some thread - if (event->resetType == ResetType::OneShot) + // One-shot events get cleared once they are acquired by some thread and only wake up 1 thread at a time + 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, diff --git a/src/core/kernel/threads.cpp b/src/core/kernel/threads.cpp index 23a9de5c..a2f29cd5 100644 --- a/src/core/kernel/threads.cpp +++ b/src/core/kernel/threads.cpp @@ -288,6 +288,40 @@ int Kernel::wakeupOneThread(u64 waitlist, Handle handle) { return threadIndex; } +// Wake up every single thread in the waitlist using a bit scanning algorithm +void Kernel::wakeupAllThreads(u64 waitlist, Handle handle) { + while (waitlist != 0) { + const uint index = std::countr_zero(waitlist); // Get one of the set bits to see which thread is waiting + waitlist ^= (1ull << index); // Remove thread from waitlist by toggling its bit + + // Get the thread we'll be signalling + Thread& t = threads[index]; + switch (t.status) { + case ThreadStatus::WaitSync1: + t.status = ThreadStatus::Ready; + t.gprs[0] = SVCResult::Success; // The thread did not timeout, so write success to r0 + break; + + case ThreadStatus::WaitSyncAny: + t.status = ThreadStatus::Ready; + t.gprs[0] = SVCResult::Success; // The thread did not timeout, so write success to r0 + + // Get the index of the event in the object's waitlist, write it to r1 + for (size_t i = 0; i < t.waitList.size(); i++) { + if (t.waitList[i] == handle) { + t.gprs[1] = i; + break; + } + } + break; + + case ThreadStatus::WaitSyncAll: + Helpers::panic("WakeupAllThreads: Thread on WaitSyncAll"); + break; + } + } +} + // Make a thread sleep for a certain amount of nanoseconds at minimum void Kernel::sleepThread(s64 ns) { if (ns < 0) { @@ -419,8 +453,12 @@ void Kernel::exitThread() { aliveThreadCount--; // Check if any threads are sleeping, waiting for this thread to terminate, and wake them up - if (t.threadsWaitingForTermination != 0) - Helpers::panic("TODO: Implement threads sleeping until another thread terminates"); + // This is how thread joining is implemented in the kernel - you wait on a thread, like any other wait object. + if (t.threadsWaitingForTermination != 0) { + // TODO: Handle cloned handles? Not sure how those interact with wait object signalling + wakeupAllThreads(t.threadsWaitingForTermination, t.handle); + t.threadsWaitingForTermination = 0; // No other threads waiting + } switchToNextThread(); }