mirror of
https://github.com/wheremyfoodat/Panda3DS.git
synced 2025-04-06 22:25:41 +12:00
commit
9ee2c76a4a
5 changed files with 171 additions and 20 deletions
|
@ -83,6 +83,9 @@ private:
|
|||
bool canThreadRun(const Thread& t);
|
||||
bool shouldWaitOnObject(KernelObject* object);
|
||||
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
|
||||
|
|
|
@ -173,6 +173,19 @@ 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 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), startTick(0), currentDelay(0), interval(0), waitlist(0), fired(false), running(false) {}
|
||||
};
|
||||
|
||||
struct MemoryBlock {
|
||||
u32 addr = 0;
|
||||
u32 size = 0;
|
||||
|
@ -206,21 +219,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<Event>()->waitlist;
|
||||
case KernelObjectType::Mutex: return getData<Mutex>()->waitlist;
|
||||
case KernelObjectType::Semaphore: return getData<Mutex>()->waitlist;
|
||||
case KernelObjectType::Thread: return getData<Thread>()->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<Event>()->waitlist;
|
||||
case KernelObjectType::Mutex: return getData<Mutex>()->waitlist;
|
||||
case KernelObjectType::Semaphore: return getData<Mutex>()->waitlist;
|
||||
case KernelObjectType::Thread: return getData<Thread>()->threadsWaitingForTermination;
|
||||
case KernelObjectType::Timer: return getData<Timer>()->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());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
|
@ -124,6 +124,7 @@ void Kernel::deleteObjectData(KernelObject& object) {
|
|||
case KernelObjectType::Session: delete object.getData<Session>(); return;
|
||||
case KernelObjectType::Mutex: delete object.getData<Mutex>(); return;
|
||||
case KernelObjectType::Semaphore: delete object.getData<Semaphore>(); return;
|
||||
case KernelObjectType::Timer: delete object.getData<Timer>(); return;
|
||||
case KernelObjectType::Thread: return;
|
||||
case KernelObjectType::Dummy: return;
|
||||
default: [[unlikely]] Helpers::warn("unknown object type"); return;
|
||||
|
|
|
@ -252,6 +252,14 @@ void Kernel::acquireSyncObject(KernelObject* object, const Thread& thread) {
|
|||
case KernelObjectType::Thread:
|
||||
break;
|
||||
|
||||
case KernelObjectType::Timer: {
|
||||
Timer* timer = object->getData<Timer>();
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
@ -652,6 +660,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<Thread>()->status != ThreadStatus::Dead;
|
||||
|
||||
case KernelObjectType::Timer: // We should wait on a timer only if it has not been signalled
|
||||
return !object->getData<Timer>()->fired;
|
||||
|
||||
case KernelObjectType::Semaphore: // Wait if the semaphore count <= 0
|
||||
return object->getData<Semaphore>()->availableCount <= 0;
|
||||
|
||||
|
|
|
@ -1,6 +1,127 @@
|
|||
#include "kernel.hpp"
|
||||
#include "cpu.hpp"
|
||||
|
||||
void Kernel::svcCreateTimer() { Helpers::panic("Kernel::CreateTimer"); }
|
||||
void Kernel::svcSetTimer() { Helpers::panic("Kernel::SetTimer"); }
|
||||
void Kernel::svcClearTimer() { Helpers::panic("Kernel::ClearTimer"); }
|
||||
void Kernel::svcCancelTimer() { Helpers::panic("Kernel::CancelTimer"); }
|
||||
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");
|
||||
}
|
||||
|
||||
// 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;
|
||||
requireReschedule();
|
||||
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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. Timers are currently not updated nor triggered properly!");
|
||||
|
||||
logSVC("CreateTimer (resetType = %s)\n", resetTypeToString(resetType));
|
||||
regs[0] = Result::Success;
|
||||
regs[1] = makeTimer(static_cast<ResetType>(resetType));
|
||||
}
|
||||
|
||||
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 = %llX, 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<Timer>();
|
||||
cancelTimer(timer);
|
||||
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) {
|
||||
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<Timer>()->fired = false;
|
||||
regs[0] = Result::Success;
|
||||
}
|
||||
}
|
||||
|
||||
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<Timer>());
|
||||
regs[0] = Result::Success;
|
||||
}
|
||||
}
|
Loading…
Add table
Reference in a new issue