diff --git a/include/kernel/kernel.hpp b/include/kernel/kernel.hpp index 55dfea55..6171bbda 100644 --- a/include/kernel/kernel.hpp +++ b/include/kernel/kernel.hpp @@ -35,6 +35,7 @@ class Kernel { std::vector objects; std::vector portHandles; std::vector mutexHandles; + std::vector timerHandles; // Thread indices, sorted by priority std::vector threadIndices; @@ -84,6 +85,7 @@ private: void releaseMutex(Mutex* moo); void cancelTimer(Timer* timer); void signalTimer(Handle timerHandle, Timer* timer); + void updateTimer(Handle timerHandle, Timer* timer); // Wake up the thread with the highest priority out of all threads in the waitlist // Returns the index of the woken up thread @@ -182,6 +184,14 @@ public: void requireReschedule() { needReschedule = true; } void evalReschedule() { + for (auto handle : timerHandles) { + const auto object = getObject(handle, KernelObjectType::Timer); + if (object != nullptr) { + Timer* timer = object->getData(); + updateTimer(handle, timer); + } + } + if (needReschedule) { needReschedule = false; rescheduleThreads(); diff --git a/include/kernel/kernel_types.hpp b/include/kernel/kernel_types.hpp index 30340221..1309c1d1 100644 --- a/include/kernel/kernel_types.hpp +++ b/include/kernel/kernel_types.hpp @@ -167,11 +167,13 @@ struct Timer { u64 waitlist; // Refer to the getWaitlist function below for documentation ResetType resetType = ResetType::OneShot; - u64 initialDelay; // Number of ns until the timer fires for the first time - u64 interval; // Number of ns until the timer fires for the second and future times - bool fired; // Has this Timer been signalled? + u64 startTick; // CPU tick the timer started + u64 currentDelay; // Number of ns until the timer fires next time + u64 interval; // Number of ns until the timer fires for the second and future times + bool fired; // Has this timer been signalled? + bool running; // Is this timer running or stopped? - Timer(ResetType type) : resetType(type), initialDelay(0), interval(0), waitlist(0), fired(false) {} + Timer(ResetType type) : resetType(type), startTick(0), currentDelay(0), interval(0), waitlist(0), fired(false), running(false) {} }; struct MemoryBlock { diff --git a/src/core/kernel/kernel.cpp b/src/core/kernel/kernel.cpp index a80f4c7b..30908e20 100644 --- a/src/core/kernel/kernel.cpp +++ b/src/core/kernel/kernel.cpp @@ -7,6 +7,7 @@ Kernel::Kernel(CPU& cpu, Memory& mem, GPU& gpu) : cpu(cpu), regs(cpu.regs()), mem(mem), handleCounter(0), serviceManager(regs, mem, gpu, currentProcess, *this) { objects.reserve(512); // Make room for a few objects to avoid further memory allocs later mutexHandles.reserve(8); + timerHandles.reserve(8); portHandles.reserve(32); threadIndices.reserve(appResourceLimits.maxThreads); @@ -146,6 +147,7 @@ void Kernel::reset() { } objects.clear(); mutexHandles.clear(); + timerHandles.clear(); portHandles.clear(); threadIndices.clear(); serviceManager.reset(); diff --git a/src/core/kernel/timers.cpp b/src/core/kernel/timers.cpp index 917fa573..d6e38e5e 100644 --- a/src/core/kernel/timers.cpp +++ b/src/core/kernel/timers.cpp @@ -1,26 +1,55 @@ #include "kernel.hpp" +#include "cpu.hpp" Handle Kernel::makeTimer(ResetType type) { Handle ret = makeObject(KernelObjectType::Timer); objects[ret].data = new Timer(type); - if (type == ResetType::Pulse) Helpers::panic("Created pulse timer"); + if (type == ResetType::Pulse) { + Helpers::panic("Created pulse timer"); + } + timerHandles.push_back(ret); return ret; } +void Kernel::updateTimer(Handle handle, Timer* timer) { + if (timer->running) { + const u64 currentTicks = cpu.getTicks(); + u64 elapsedTicks = currentTicks - timer->startTick; + + constexpr double ticksPerSec = double(CPU::ticksPerSec); + constexpr double nsPerTick = ticksPerSec / 1000000000.0; + const s64 elapsedNs = s64(double(elapsedTicks) * nsPerTick); + + // Timer has fired + if (elapsedNs >= timer->currentDelay) { + timer->startTick = currentTicks; + timer->currentDelay = timer->interval; + signalTimer(handle, timer); + } + } +} + void Kernel::cancelTimer(Timer* timer) { + timer->running = false; // TODO: When we have a scheduler this should properly cancel timer events in the scheduler } void Kernel::signalTimer(Handle timerHandle, Timer* timer) { timer->fired = true; - wakeupAllThreads(timer->waitlist, timerHandle); + requireReschedule(); - switch (timer->resetType) { - case ResetType::OneShot: timer->fired = false; break; - case ResetType::Sticky: break; - case ResetType::Pulse: Helpers::panic("Signalled pulsing timer"); break; + // Check if there's any thread waiting on this event + if (timer->waitlist != 0) { + wakeupAllThreads(timer->waitlist, timerHandle); + timer->waitlist = 0; // No threads waiting; + + switch (timer->resetType) { + case ResetType::OneShot: timer->fired = false; break; + case ResetType::Sticky: break; + case ResetType::Pulse: Helpers::panic("Signalled pulsing timer"); break; + } } } @@ -54,8 +83,10 @@ void Kernel::svcSetTimer() { Timer* timer = object->getData(); cancelTimer(timer); - timer->initialDelay = initial; + timer->currentDelay = initial; timer->interval = interval; + timer->running = true; + timer->startTick = cpu.getTicks(); // If the initial delay is 0 then instantly signal the timer if (initial == 0) { @@ -81,4 +112,16 @@ void Kernel::svcClearTimer() { } } -void Kernel::svcCancelTimer() { Helpers::panic("Kernel::CancelTimer"); } \ No newline at end of file +void Kernel::svcCancelTimer() { + Handle handle = regs[0]; + logSVC("CancelTimer (handle = %X)\n", handle); + KernelObject* object = getObject(handle, KernelObjectType::Timer); + + if (object == nullptr) { + Helpers::panic("Tried to cancel non-existent timer %X\n", handle); + regs[0] = Result::Kernel::InvalidHandle; + } else { + cancelTimer(object->getData()); + regs[0] = Result::Success; + } +} \ No newline at end of file