diff --git a/include/kernel/kernel.hpp b/include/kernel/kernel.hpp index 3f20b5e1..e62053c8 100644 --- a/include/kernel/kernel.hpp +++ b/include/kernel/kernel.hpp @@ -58,6 +58,7 @@ class Kernel { // Top 8 bits are the major version, bottom 8 are the minor version u16 kernelVersion = 0; + u64 nextScheduledWakeupTick = std::numeric_limits::max(); // Shows whether a reschedule will be need bool needReschedule = false; @@ -214,6 +215,8 @@ public: } } + void addWakeupEvent(u64 tick); + Handle makeObject(KernelObjectType type) { if (handleCounter > KernelHandles::Max) [[unlikely]] { Helpers::panic("Hlep we somehow created enough kernel objects to overflow this thing"); @@ -253,5 +256,7 @@ public: void sendGPUInterrupt(GPUInterrupt type) { serviceManager.sendGPUInterrupt(type); } void clearInstructionCache(); void clearInstructionCacheRange(u32 start, u32 size); + void pollThreadWakeups(); + u32 getSharedFontVaddr(); }; \ No newline at end of file diff --git a/include/scheduler.hpp b/include/scheduler.hpp index cfc4d5e8..f8eae2fc 100644 --- a/include/scheduler.hpp +++ b/include/scheduler.hpp @@ -11,7 +11,8 @@ struct Scheduler { VBlank = 0, // End of frame event UpdateTimers = 1, // Update kernel timer objects RunDSP = 2, // Make the emulated DSP run for one audio frame - SignalY2R = 3, // Signal that a Y2R conversion has finished + ThreadWakeup = 3, // A thread is going to wake up and we need to reschedule threads + SignalY2R = 4, // Signal that a Y2R conversion has finished Panic = 4, // Dummy event that is always pending and should never be triggered (Timestamp = UINT64_MAX) TotalNumberOfEvents // How many event types do we have in total? }; diff --git a/src/core/kernel/events.cpp b/src/core/kernel/events.cpp index 6d3dfbd7..c06204a9 100644 --- a/src/core/kernel/events.cpp +++ b/src/core/kernel/events.cpp @@ -137,6 +137,7 @@ void Kernel::waitSynchronization1() { // Add the current thread to the object's wait list object->getWaitlist() |= (1ull << currentThreadIndex); + addWakeupEvent(t.wakeupTick); requireReschedule(); } } @@ -231,6 +232,7 @@ void Kernel::waitSynchronizationN() { waitObjects[i].second->getWaitlist() |= (1ull << currentThreadIndex); // And add the thread to the object's waitlist } + addWakeupEvent(t.wakeupTick); requireReschedule(); } else { Helpers::panic("WaitSynchronizationN with waitAll"); diff --git a/src/core/kernel/kernel.cpp b/src/core/kernel/kernel.cpp index e097fab6..4e46fbb0 100644 --- a/src/core/kernel/kernel.cpp +++ b/src/core/kernel/kernel.cpp @@ -1,7 +1,10 @@ -#include #include "kernel.hpp" -#include "kernel_types.hpp" + +#include +#include + #include "cpu.hpp" +#include "kernel_types.hpp" Kernel::Kernel(CPU& cpu, Memory& mem, GPU& gpu, const EmulatorConfig& config) : cpu(cpu), regs(cpu.regs()), mem(mem), handleCounter(0), serviceManager(regs, mem, gpu, currentProcess, *this, config) { @@ -159,6 +162,7 @@ void Kernel::reset() { threadIndices.clear(); serviceManager.reset(); + nextScheduledWakeupTick = std::numeric_limits::max(); needReschedule = false; // Allocate handle #0 to a dummy object and make a main process object diff --git a/src/core/kernel/threads.cpp b/src/core/kernel/threads.cpp index 9eb7a197..1fbaea08 100644 --- a/src/core/kernel/threads.cpp +++ b/src/core/kernel/threads.cpp @@ -1,6 +1,8 @@ +#include #include #include #include +#include #include "arm_defs.hpp" #include "kernel.hpp" @@ -396,6 +398,7 @@ void Kernel::sleepThread(s64 ns) { t.status = ThreadStatus::WaitSleep; t.wakeupTick = getWakeupTick(ns); + addWakeupEvent(t.wakeupTick); requireReschedule(); } } @@ -697,3 +700,38 @@ bool Kernel::shouldWaitOnObject(KernelObject* object) { return true; } } + +void Kernel::pollThreadWakeups() { + rescheduleThreads(); + bool haveSleepingThread = false; + u64 nextWakeupTick = std::numeric_limits::max(); + + 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) { + nextWakeupTick = std::min(nextWakeupTick, t.wakeupTick); + haveSleepingThread = true; + } + } + + auto& scheduler = cpu.getScheduler(); + + if (haveSleepingThread && nextWakeupTick > scheduler.currentTimestamp) { + nextScheduledWakeupTick = nextWakeupTick; + scheduler.addEvent(Scheduler::EventType::ThreadWakeup, nextWakeupTick); + } else { + nextScheduledWakeupTick = std::numeric_limits::max(); + } +} + +void Kernel::addWakeupEvent(u64 tick) { + // We only need to queue the event if the tick of the wakeup is coming sooner than our next scheduled wakeup. + if (nextScheduledWakeupTick > tick) { + nextScheduledWakeupTick = tick; + auto& scheduler = cpu.getScheduler(); + + scheduler.removeEvent(Scheduler::EventType::ThreadWakeup); + scheduler.addEvent(Scheduler::EventType::ThreadWakeup, tick); + } +} \ No newline at end of file diff --git a/src/emulator.cpp b/src/emulator.cpp index 11970d91..ff91aa9e 100644 --- a/src/emulator.cpp +++ b/src/emulator.cpp @@ -177,6 +177,7 @@ void Emulator::pollScheduler() { break; } + case Scheduler::EventType::ThreadWakeup: kernel.pollThreadWakeups(); break; case Scheduler::EventType::UpdateTimers: kernel.pollTimers(); break; case Scheduler::EventType::RunDSP: { dsp->runAudioFrame(time);