From fa58c7a7d6e3323d093065a1be5a373e7708f96a Mon Sep 17 00:00:00 2001 From: wheremyfoodat <44909372+wheremyfoodat@users.noreply.github.com> Date: Sun, 13 Aug 2023 23:32:07 +0300 Subject: [PATCH 1/6] OS Timers part 2 --- include/kernel/kernel_types.hpp | 11 +++++++++++ src/core/kernel/kernel.cpp | 1 + src/core/kernel/threads.cpp | 3 +++ src/core/kernel/timers.cpp | 19 ++++++++++++++++++- 4 files changed, 33 insertions(+), 1 deletion(-) diff --git a/include/kernel/kernel_types.hpp b/include/kernel/kernel_types.hpp index 56bed359..abefbcd9 100644 --- a/include/kernel/kernel_types.hpp +++ b/include/kernel/kernel_types.hpp @@ -163,6 +163,17 @@ struct Semaphore { Semaphore(s32 initialCount, s32 maximumCount) : availableCount(initialCount), maximumCount(maximumCount), waitlist(0) {} }; +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? + + Timer(ResetType type) : resetType(type), initialDelay(0), interval(0), waitlist(0), fired(false) {} +}; + struct MemoryBlock { u32 addr = 0; u32 size = 0; diff --git a/src/core/kernel/kernel.cpp b/src/core/kernel/kernel.cpp index 900ad559..a80f4c7b 100644 --- a/src/core/kernel/kernel.cpp +++ b/src/core/kernel/kernel.cpp @@ -122,6 +122,7 @@ void Kernel::deleteObjectData(KernelObject& object) { case KernelObjectType::Session: delete object.getData(); return; case KernelObjectType::Mutex: delete object.getData(); return; case KernelObjectType::Semaphore: delete object.getData(); return; + case KernelObjectType::Timer: delete object.getData(); return; case KernelObjectType::Thread: return; case KernelObjectType::Dummy: return; default: [[unlikely]] Helpers::warn("unknown object type"); return; diff --git a/src/core/kernel/threads.cpp b/src/core/kernel/threads.cpp index 440045d3..08fc697d 100644 --- a/src/core/kernel/threads.cpp +++ b/src/core/kernel/threads.cpp @@ -612,6 +612,9 @@ bool Kernel::shouldWaitOnObject(KernelObject* object) { case KernelObjectType::Thread: // Waiting on a thread waits until it's dead. If it's dead then no need to wait return object->getData()->status != ThreadStatus::Dead; + case KernelObjectType::Timer: // We should wait on a timer only if it has not been signalled + return !object->getData()->fired; + case KernelObjectType::Semaphore: // Wait if the semaphore count <= 0 return object->getData()->availableCount <= 0; diff --git a/src/core/kernel/timers.cpp b/src/core/kernel/timers.cpp index f4a89407..6750bcc1 100644 --- a/src/core/kernel/timers.cpp +++ b/src/core/kernel/timers.cpp @@ -1,6 +1,23 @@ #include "kernel.hpp" -void Kernel::svcCreateTimer() { Helpers::panic("Kernel::CreateTimer"); } +Handle Kernel::makeTimer(ResetType type) { + Handle ret = makeObject(KernelObjectType::Timer); + objects[ret].data = new Timer(type); + + return ret; +} + +void Kernel::svcCreateTimer() { + const u32 resetType = regs[1]; + if (resetType > 2) { + Helpers::panic("Invalid reset type for event %d", resetType); + } + + logSVC("CreateTimer (resetType = %s)\n", resetTypeToString(resetType)); + regs[0] = Result::Success; + regs[1] = makeTimer(static_cast(resetType)); +} + void Kernel::svcSetTimer() { Helpers::panic("Kernel::SetTimer"); } void Kernel::svcClearTimer() { Helpers::panic("Kernel::ClearTimer"); } void Kernel::svcCancelTimer() { Helpers::panic("Kernel::CancelTimer"); } \ No newline at end of file From 1354b0f7faaf5bc1d3fdc90dcf682e0181247d43 Mon Sep 17 00:00:00 2001 From: wheremyfoodat <44909372+wheremyfoodat@users.noreply.github.com> Date: Mon, 14 Aug 2023 15:13:37 +0300 Subject: [PATCH 2/6] Moar timer --- include/kernel/kernel.hpp | 2 + include/kernel/kernel_types.hpp | 34 +++++++++-------- src/core/kernel/threads.cpp | 8 ++++ src/core/kernel/timers.cpp | 65 ++++++++++++++++++++++++++++++++- 4 files changed, 91 insertions(+), 18 deletions(-) diff --git a/include/kernel/kernel.hpp b/include/kernel/kernel.hpp index 163b19e1..55dfea55 100644 --- a/include/kernel/kernel.hpp +++ b/include/kernel/kernel.hpp @@ -82,6 +82,8 @@ private: bool canThreadRun(const Thread& t); bool shouldWaitOnObject(KernelObject* object); void releaseMutex(Mutex* moo); + void cancelTimer(Timer* timer); + void signalTimer(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 diff --git a/include/kernel/kernel_types.hpp b/include/kernel/kernel_types.hpp index abefbcd9..30340221 100644 --- a/include/kernel/kernel_types.hpp +++ b/include/kernel/kernel_types.hpp @@ -207,21 +207,23 @@ struct KernelObject { } // Retrieves a reference to the waitlist for a specified object - // We return a reference because this function is only called in the kernel threading internals - // We want the kernel to be able to easily manage waitlists, by reading/parsing them or setting/clearing bits. - // As we mention in the definition of the "Event" struct, the format for wailists is very simple and made to be efficient. - // Each bit corresponds to a thread index and denotes whether the corresponding thread is waiting on this object - // For example if bit 0 of the wait list is set, then the thread with index 0 is waiting on our object - u64& getWaitlist() { - // This code is actually kinda trash but eh good enough - switch (type) { - case KernelObjectType::Event: return getData()->waitlist; - case KernelObjectType::Mutex: return getData()->waitlist; - case KernelObjectType::Semaphore: return getData()->waitlist; - case KernelObjectType::Thread: return getData()->threadsWaitingForTermination; - // This should be unreachable once we fully implement sync objects - default: [[unlikely]] + // We return a reference because this function is only called in the kernel threading internals + // We want the kernel to be able to easily manage waitlists, by reading/parsing them or setting/clearing bits. + // As we mention in the definition of the "Event" struct, the format for wailists is very simple and made to be efficient. + // Each bit corresponds to a thread index and denotes whether the corresponding thread is waiting on this object + // For example if bit 0 of the wait list is set, then the thread with index 0 is waiting on our object + u64& getWaitlist() { + // This code is actually kinda trash but eh good enough + switch (type) { + case KernelObjectType::Event: return getData()->waitlist; + case KernelObjectType::Mutex: return getData()->waitlist; + case KernelObjectType::Semaphore: return getData()->waitlist; + case KernelObjectType::Thread: return getData()->threadsWaitingForTermination; + case KernelObjectType::Timer: return getData()->waitlist; + + // This should be unreachable once we fully implement sync objects + default: [[unlikely]] Helpers::panic("Called GetWaitList on kernel object without a waitlist (Type: %s)", getTypeName()); - } - } + } + } }; \ No newline at end of file diff --git a/src/core/kernel/threads.cpp b/src/core/kernel/threads.cpp index 08fc697d..53d7342b 100644 --- a/src/core/kernel/threads.cpp +++ b/src/core/kernel/threads.cpp @@ -252,6 +252,14 @@ void Kernel::acquireSyncObject(KernelObject* object, const Thread& thread) { case KernelObjectType::Thread: break; + case KernelObjectType::Timer: { + Timer* timer = object->getData(); + if (timer->resetType == ResetType::OneShot) { // One-shot timers automatically get cleared after waking up a thread + timer->fired = false; + } + break; + } + default: Helpers::panic("Acquiring unimplemented sync object %s", object->getTypeName()); } } diff --git a/src/core/kernel/timers.cpp b/src/core/kernel/timers.cpp index 6750bcc1..917fa573 100644 --- a/src/core/kernel/timers.cpp +++ b/src/core/kernel/timers.cpp @@ -4,20 +4,81 @@ 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"); + return ret; } +void Kernel::cancelTimer(Timer* timer) { + // 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); + + switch (timer->resetType) { + case ResetType::OneShot: timer->fired = false; break; + case ResetType::Sticky: break; + case ResetType::Pulse: Helpers::panic("Signalled pulsing timer"); break; + } +} + void Kernel::svcCreateTimer() { const u32 resetType = regs[1]; if (resetType > 2) { Helpers::panic("Invalid reset type for event %d", resetType); } + // Have a warning here until our timers don't suck + Helpers::warn("Called Kernel::CreateTimer"); + logSVC("CreateTimer (resetType = %s)\n", resetTypeToString(resetType)); regs[0] = Result::Success; regs[1] = makeTimer(static_cast(resetType)); } -void Kernel::svcSetTimer() { Helpers::panic("Kernel::SetTimer"); } -void Kernel::svcClearTimer() { Helpers::panic("Kernel::ClearTimer"); } +void Kernel::svcSetTimer() { + Handle handle = regs[0]; + // TODO: Is this actually s64 or u64? 3DBrew says s64, but u64 makes more sense + const s64 initial = s64(u64(regs[1]) | (u64(regs[2]) << 32)); + const s64 interval = s64(u64(regs[3]) | (u64(regs[4]) << 32)); + logSVC("SetTimer (handle = %X, initial delay = %llxX, interval delay = %llx)\n", handle, initial, interval); + + KernelObject* object = getObject(handle, KernelObjectType::Timer); + + if (object == nullptr) { + Helpers::panic("Tried to set non-existent timer %X\n", handle); + regs[0] = Result::Kernel::InvalidHandle; + } + + Timer* timer = object->getData(); + cancelTimer(timer); + timer->initialDelay = initial; + timer->interval = interval; + + // If the initial delay is 0 then instantly signal the timer + if (initial == 0) { + signalTimer(handle, timer); + } else { + // This should schedule an event in the scheduler when we have one + } + + regs[0] = Result::Success; +} + +void Kernel::svcClearTimer() { + Handle handle = regs[0]; + logSVC("ClearTimer (handle = %X)\n", handle); + KernelObject* object = getObject(handle, KernelObjectType::Timer); + + if (object == nullptr) { + Helpers::panic("Tried to clear non-existent timer %X\n", handle); + regs[0] = Result::Kernel::InvalidHandle; + } else { + object->getData()->fired = false; + regs[0] = Result::Success; + } +} + void Kernel::svcCancelTimer() { Helpers::panic("Kernel::CancelTimer"); } \ No newline at end of file From 8881467505ce119fd1a98761b66f178045339506 Mon Sep 17 00:00:00 2001 From: wheremyfoodat <44909372+wheremyfoodat@users.noreply.github.com> Date: Mon, 14 Aug 2023 17:24:53 +0300 Subject: [PATCH 3/6] I hate timers --- include/kernel/kernel.hpp | 10 ++++++ include/kernel/kernel_types.hpp | 10 +++--- src/core/kernel/kernel.cpp | 2 ++ src/core/kernel/timers.cpp | 59 ++++++++++++++++++++++++++++----- 4 files changed, 69 insertions(+), 12 deletions(-) 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 From 56b17c3e8da30b77c2f53241852d703a0a65da8d Mon Sep 17 00:00:00 2001 From: wheremyfoodat <44909372+wheremyfoodat@users.noreply.github.com> Date: Mon, 14 Aug 2023 17:26:02 +0300 Subject: [PATCH 4/6] Fix format specifiers --- src/core/kernel/timers.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/kernel/timers.cpp b/src/core/kernel/timers.cpp index d6e38e5e..5d021679 100644 --- a/src/core/kernel/timers.cpp +++ b/src/core/kernel/timers.cpp @@ -72,7 +72,7 @@ void Kernel::svcSetTimer() { // TODO: Is this actually s64 or u64? 3DBrew says s64, but u64 makes more sense const s64 initial = s64(u64(regs[1]) | (u64(regs[2]) << 32)); const s64 interval = s64(u64(regs[3]) | (u64(regs[4]) << 32)); - logSVC("SetTimer (handle = %X, initial delay = %llxX, interval delay = %llx)\n", handle, initial, interval); + logSVC("SetTimer (handle = %X, initial delay = %llX, interval delay = %llX)\n", handle, initial, interval); KernelObject* object = getObject(handle, KernelObjectType::Timer); From cb3f7fa1345fa5526e6d3c77b987caa5556dbe8b Mon Sep 17 00:00:00 2001 From: wheremyfoodat <44909372+wheremyfoodat@users.noreply.github.com> Date: Thu, 31 Aug 2023 22:47:43 +0300 Subject: [PATCH 5/6] Remove hacky timer hacks --- include/kernel/kernel.hpp | 9 --------- src/core/kernel/kernel.cpp | 2 -- src/core/kernel/timers.cpp | 2 +- 3 files changed, 1 insertion(+), 12 deletions(-) diff --git a/include/kernel/kernel.hpp b/include/kernel/kernel.hpp index e0021541..3d4b6917 100644 --- a/include/kernel/kernel.hpp +++ b/include/kernel/kernel.hpp @@ -36,7 +36,6 @@ class Kernel { std::vector objects; std::vector portHandles; std::vector mutexHandles; - std::vector timerHandles; // Thread indices, sorted by priority std::vector threadIndices; @@ -187,14 +186,6 @@ 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/src/core/kernel/kernel.cpp b/src/core/kernel/kernel.cpp index 72af8dd2..5eb3cddb 100644 --- a/src/core/kernel/kernel.cpp +++ b/src/core/kernel/kernel.cpp @@ -7,7 +7,6 @@ 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) { 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); @@ -149,7 +148,6 @@ 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 5d021679..1f5fefaf 100644 --- a/src/core/kernel/timers.cpp +++ b/src/core/kernel/timers.cpp @@ -9,7 +9,7 @@ Handle Kernel::makeTimer(ResetType type) { Helpers::panic("Created pulse timer"); } - timerHandles.push_back(ret); + // timerHandles.push_back(ret); return ret; } From 137f29509fbf928cf24fa8669ffaf9137ccab6b1 Mon Sep 17 00:00:00 2001 From: wheremyfoodat <44909372+wheremyfoodat@users.noreply.github.com> Date: Thu, 31 Aug 2023 22:48:35 +0300 Subject: [PATCH 6/6] Add warning --- src/core/kernel/timers.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/kernel/timers.cpp b/src/core/kernel/timers.cpp index 1f5fefaf..1c18d9de 100644 --- a/src/core/kernel/timers.cpp +++ b/src/core/kernel/timers.cpp @@ -60,7 +60,7 @@ void Kernel::svcCreateTimer() { } // Have a warning here until our timers don't suck - Helpers::warn("Called Kernel::CreateTimer"); + Helpers::warn("Called Kernel::CreateTimer. Timers are currently not updated nor triggered properly!"); logSVC("CreateTimer (resetType = %s)\n", resetTypeToString(resetType)); regs[0] = Result::Success;