From 2a4709dcfa5c415c8a256a96dd16d5f56e4b8a0d Mon Sep 17 00:00:00 2001 From: wheremyfoodat Date: Tue, 11 Oct 2022 22:45:25 +0300 Subject: [PATCH] [Kernel] Implement thread sleeping --- include/cpu_dynarmic.hpp | 4 +++- include/kernel/kernel.hpp | 2 ++ include/kernel/kernel_types.hpp | 5 +++++ src/core/kernel/events.cpp | 11 ++++++++- src/core/kernel/kernel.cpp | 5 +++++ src/core/kernel/threads.cpp | 40 ++++++++++++++++++++++++++++----- 6 files changed, 60 insertions(+), 7 deletions(-) diff --git a/include/cpu_dynarmic.hpp b/include/cpu_dynarmic.hpp index 5d54d6cb..fc0de46a 100644 --- a/include/cpu_dynarmic.hpp +++ b/include/cpu_dynarmic.hpp @@ -107,6 +107,8 @@ class CPU { Memory& mem; public: + static constexpr u64 ticksPerSec = 268111856; + CPU(Memory& mem, Kernel& kernel); void reset(); @@ -161,7 +163,7 @@ public: } void runFrame() { - env.ticksLeft = 268111856 / 60; + env.ticksLeft = ticksPerSec / 60; const auto exitReason = jit->Run(); if (static_cast(exitReason) != 0) [[unlikely]] { diff --git a/include/kernel/kernel.hpp b/include/kernel/kernel.hpp index 373253de..8c052224 100644 --- a/include/kernel/kernel.hpp +++ b/include/kernel/kernel.hpp @@ -66,6 +66,7 @@ class Kernel { Handle makeThread(u32 entrypoint, u32 initialSP, u32 priority, s32 id, u32 arg,ThreadStatus status = ThreadStatus::Dormant); void signalArbiter(u32 waitingAddress, s32 threadCount); + void sleepThread(s64 ns); void sleepThreadOnArbiter(u32 waitingAddress); void switchThread(int newThreadIndex); void sortThreads(); @@ -116,6 +117,7 @@ class Kernel { void sendSyncRequest(); void signalEvent(); void svcCloseHandle(); + void svcSleepThread(); void connectToPort(); void outputDebugString(); void waitSynchronization1(); diff --git a/include/kernel/kernel_types.hpp b/include/kernel/kernel_types.hpp index be44dce5..aa56632a 100644 --- a/include/kernel/kernel_types.hpp +++ b/include/kernel/kernel_types.hpp @@ -116,6 +116,11 @@ struct Thread { // The waiting address for threads that are waiting on an AddressArbiter u32 waitingAddress; + // The nanoseconds until a thread wakes up from being asleep or from timing out while waiting on an arbiter + s64 waitingNanoseconds; + // The tick this thread went to sleep on + u64 sleepTick; + // Thread context used for switching between threads std::array gprs; std::array fprs; // Stored as u32 because dynarmic does it diff --git a/src/core/kernel/events.cpp b/src/core/kernel/events.cpp index 49b9f39d..3d69d059 100644 --- a/src/core/kernel/events.cpp +++ b/src/core/kernel/events.cpp @@ -94,5 +94,14 @@ void Kernel::waitSynchronizationN() { printf("Hacky WaitSync stuff for OoT triggered!!!\n"); threads[currentThreadIndex].status = ThreadStatus::Ready; - switchThread(rand() % threadCount); + + while (1) { + auto index = rand() % threadCount; + auto& thread = threads[index]; + + if (canThreadRun(thread)) { + switchThread(rand() % threadCount); + break; + } + } } \ No newline at end of file diff --git a/src/core/kernel/kernel.cpp b/src/core/kernel/kernel.cpp index bf2397bd..66289da0 100644 --- a/src/core/kernel/kernel.cpp +++ b/src/core/kernel/kernel.cpp @@ -23,6 +23,7 @@ void Kernel::serviceSVC(u32 svc) { case 0x01: controlMemory(); break; case 0x02: queryMemory(); break; case 0x08: createThread(); break; + case 0x0A: svcSleepThread(); break; case 0x14: releaseMutex(); break; case 0x17: createEvent(); break; case 0x18: signalEvent(); break; @@ -95,6 +96,10 @@ void Kernel::reset() { arbiterCount = 0; threadCount = 0; + for (auto& t : threads) { + t.status = ThreadStatus::Dead; + } + for (auto& object : objects) { deleteObjectData(object); } diff --git a/src/core/kernel/threads.cpp b/src/core/kernel/threads.cpp index a47b579e..34f6e458 100644 --- a/src/core/kernel/threads.cpp +++ b/src/core/kernel/threads.cpp @@ -46,6 +46,14 @@ void Kernel::sortThreads() { bool Kernel::canThreadRun(const Thread& t) { if (t.status == ThreadStatus::Ready) { return true; + } else if (t.status == ThreadStatus::WaitSleep) { + const u64 elapsedTicks = cpu.getTicks() - t.sleepTick; + + constexpr double ticksPerSec = double(CPU::ticksPerSec); + constexpr double nsPerTick = ticksPerSec / 1000000000.0; + + const s64 elapsedNs = s64(double(elapsedTicks) * nsPerTick); + return elapsedNs >= t.waitingNanoseconds; } // Handle timeouts and stuff here @@ -129,6 +137,28 @@ Handle Kernel::makeThread(u32 entrypoint, u32 initialSP, u32 priority, s32 id, u return ret; } +void Kernel::sleepThreadOnArbiter(u32 waitingAddress) { + Thread& t = threads[currentThreadIndex]; + t.status = ThreadStatus::WaitArbiter; + t.waitingAddress = waitingAddress; + + switchToNextThread(); +} + +// Make a thread sleep for a certain amount of nanoseconds at minimum +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 + switchToNextThread(); + } else { // If we're sleeping for > 0 ns + Thread& t = threads[currentThreadIndex]; + t.status = ThreadStatus::WaitSleep; + t.waitingNanoseconds = ns; + t.sleepTick = cpu.getTicks(); + } +} + // Result CreateThread(s32 priority, ThreadFunc entrypoint, u32 arg, u32 stacktop, s32 threadPriority, s32 processorID) void Kernel::createThread() { u32 priority = regs[0]; @@ -150,12 +180,12 @@ void Kernel::createThread() { regs[1] = makeThread(entrypoint, initialSP, priority, id, arg, ThreadStatus::Ready); } -void Kernel::sleepThreadOnArbiter(u32 waitingAddress) { - Thread& t = threads[currentThreadIndex]; - t.status = ThreadStatus::WaitArbiter; - t.waitingAddress = waitingAddress; +// void SleepThread(s64 nanoseconds) +void Kernel::svcSleepThread() { + const s64 ns = s64(u64(regs[0]) | (u64(regs[1]) << 32)); + logSVC("SleepThread(ns = %lld)\n", ns); - switchToNextThread(); + sleepThread(ns); } void Kernel::getThreadID() {