diff --git a/include/kernel/kernel.hpp b/include/kernel/kernel.hpp index e62053c8..a9facddf 100644 --- a/include/kernel/kernel.hpp +++ b/include/kernel/kernel.hpp @@ -95,6 +95,8 @@ public: void signalArbiter(u32 waitingAddress, s32 threadCount); void sleepThread(s64 ns); void sleepThreadOnArbiter(u32 waitingAddress); + void sleepThreadOnArbiterWithTimeout(u32 waitingAddress, s64 timeoutNs); + void switchThread(int newThreadIndex); void sortThreads(); std::optional getNextThread(); diff --git a/include/kernel/kernel_types.hpp b/include/kernel/kernel_types.hpp index a3a60c34..a5b27498 100644 --- a/include/kernel/kernel_types.hpp +++ b/include/kernel/kernel_types.hpp @@ -98,16 +98,17 @@ struct Session { }; enum class ThreadStatus { - Running, // Currently running - Ready, // Ready to run - WaitArbiter, // Waiting on an address arbiter - WaitSleep, // Waiting due to a SleepThread SVC - WaitSync1, // Waiting for the single object in the wait list to be ready - WaitSyncAny, // Wait for one object of the many that might be in the wait list to be ready - WaitSyncAll, // Waiting for ALL sync objects in its wait list to be ready - WaitIPC, // Waiting for the reply from an IPC request - Dormant, // Created but not yet made ready - Dead // Run to completion, or forcefully terminated + Running, // Currently running + Ready, // Ready to run + WaitArbiter, // Waiting on an address arbiter + WaitArbiterTimeout, // Waiting on an address arbiter with timeout + WaitSleep, // Waiting due to a SleepThread SVC + WaitSync1, // Waiting for the single object in the wait list to be ready + WaitSyncAny, // Wait for one object of the many that might be in the wait list to be ready + WaitSyncAll, // Waiting for ALL sync objects in its wait list to be ready + WaitIPC, // Waiting for the reply from an IPC request + Dormant, // Created but not yet made ready + Dead // Run to completion, or forcefully terminated }; struct Thread { diff --git a/src/core/kernel/address_arbiter.cpp b/src/core/kernel/address_arbiter.cpp index d15c81b8..276f6e68 100644 --- a/src/core/kernel/address_arbiter.cpp +++ b/src/core/kernel/address_arbiter.cpp @@ -79,6 +79,14 @@ void Kernel::arbitrateAddress() { break; } + case ArbitrationType::WaitIfLessTimeout: { + s32 word = static_cast(mem.read32(address)); // Yes this is meant to be signed + if (word < value) { + sleepThreadOnArbiterWithTimeout(address, ns); + } + break; + } + case ArbitrationType::Signal: signalArbiter(address, value); break; diff --git a/src/core/kernel/threads.cpp b/src/core/kernel/threads.cpp index 1fbaea08..e905a22f 100644 --- a/src/core/kernel/threads.cpp +++ b/src/core/kernel/threads.cpp @@ -52,8 +52,7 @@ void Kernel::sortThreads() { bool Kernel::canThreadRun(const Thread& t) { if (t.status == ThreadStatus::Ready) { return true; - } else if (t.status == ThreadStatus::WaitSleep || t.status == ThreadStatus::WaitSync1 - || t.status == ThreadStatus::WaitSyncAny || t.status == ThreadStatus::WaitSyncAll) { + } else if (t.status == ThreadStatus::WaitSleep || t.status == ThreadStatus::WaitSync1 || t.status == ThreadStatus::WaitSyncAny || t.status == ThreadStatus::WaitSyncAll || t.status == ThreadStatus::WaitArbiterTimeout) { // TODO: Set r0 to the correct error code on timeout for WaitSync{1/Any/All} return cpu.getTicks() >= t.wakeupTick; } @@ -218,6 +217,23 @@ void Kernel::sleepThreadOnArbiter(u32 waitingAddress) { requireReschedule(); } +void Kernel::sleepThreadOnArbiterWithTimeout(u32 waitingAddress, s64 timeoutNs) { + regs[0] = Result::OS::Timeout; // This will be overwritten with success if we don't timeout + + // Timeout is 0, don't bother waiting, instantly timeout + if (timeoutNs == 0) { + return; + } + + Thread& t = threads[currentThreadIndex]; + t.status = ThreadStatus::WaitArbiterTimeout; + t.waitingAddress = waitingAddress; + t.wakeupTick = getWakeupTick(timeoutNs); + + addWakeupEvent(t.wakeupTick); + requireReschedule(); +} + // Acquires an object that is **ready to be acquired** without waiting on it void Kernel::acquireSyncObject(KernelObject* object, const Thread& thread) { switch (object->type) { @@ -311,6 +327,11 @@ int Kernel::wakeupOneThread(u64 waitlist, Handle handle) { } break; + case ThreadStatus::WaitArbiterTimeout: + t.status = ThreadStatus::Ready; + t.gprs[0] = Result::Success; // The thread did not timeout, so write success to r0 + break; + case ThreadStatus::WaitSyncAll: Helpers::panic("WakeupOneThread: Thread on WaitSyncAll"); break; @@ -345,6 +366,11 @@ void Kernel::wakeupAllThreads(u64 waitlist, Handle handle) { } } break; + + case ThreadStatus::WaitArbiterTimeout: + t.status = ThreadStatus::Ready; + t.gprs[0] = Result::Success; // The thread did not timeout, so write success to r0 + break; case ThreadStatus::WaitSyncAll: Helpers::panic("WakeupAllThreads: Thread on WaitSyncAll"); @@ -709,7 +735,8 @@ void Kernel::pollThreadWakeups() { for (auto index : threadIndices) { const Thread& t = threads[index]; - if (t.status == ThreadStatus::WaitSleep || t.status == ThreadStatus::WaitSync1 || t.status == ThreadStatus::WaitSyncAny || t.status == ThreadStatus::WaitSyncAll) { + if (t.status == ThreadStatus::WaitSleep || t.status == ThreadStatus::WaitSync1 || t.status == ThreadStatus::WaitSyncAny || + t.status == ThreadStatus::WaitSyncAll || t.status == ThreadStatus::WaitArbiterTimeout) { nextWakeupTick = std::min(nextWakeupTick, t.wakeupTick); haveSleepingThread = true; }