diff --git a/zion/interrupt/interrupt.cpp b/zion/interrupt/interrupt.cpp index 509fc13..963f03f 100644 --- a/zion/interrupt/interrupt.cpp +++ b/zion/interrupt/interrupt.cpp @@ -123,7 +123,7 @@ extern "C" void interrupt_timer(InterruptFrame*) { dbgln("timer: %us", cnt * 50 / 1000); } outb(PIC1_COMMAND, PIC_EOI); - sched::Preempt(); + gScheduler->Preempt(); } void EnablePic() { diff --git a/zion/scheduler/process.cpp b/zion/scheduler/process.cpp index d3bfdbc..32f6a08 100644 --- a/zion/scheduler/process.cpp +++ b/zion/scheduler/process.cpp @@ -31,7 +31,7 @@ Process::Process(uint64_t elf_ptr) : id_(gNextId++), state_(RUNNING) { void Process::CreateThread(uint64_t elf_ptr) { Thread* thread = new Thread(this, next_thread_id_++, elf_ptr); threads_.PushBack(thread); - sched::EnqueueThread(thread); + gScheduler->Enqueue(thread); } SharedPtr Process::GetThread(uint64_t tid) { diff --git a/zion/scheduler/scheduler.cpp b/zion/scheduler/scheduler.cpp index 007f61b..79c2f78 100644 --- a/zion/scheduler/scheduler.cpp +++ b/zion/scheduler/scheduler.cpp @@ -4,121 +4,84 @@ #include "lib/linked_list.h" #include "scheduler/process_manager.h" -namespace sched { namespace { extern "C" void context_switch(uint64_t* current_esp, uint64_t* next_esp); -class Scheduler { - public: - Scheduler() { - Process& root = gProcMan->FromId(0); - sleep_thread_ = root.GetThread(0); - // TODO: Implement a separate sleep thread? - current_thread_ = sleep_thread_; - } - void Enable() { enabled_ = true; } - - Process& CurrentProcess() { return CurrentThread().process(); } - Thread& CurrentThread() { return *current_thread_; } - - void Enqueue(Thread* thread) { runnable_threads_.PushBack(thread); } - - void SwapToCurrent(Thread& prev) { - if (current_thread_->GetState() != Thread::RUNNABLE) { - panic("Swapping to non-runnable thread."); - } - current_thread_->SetState(Thread::RUNNING); - - context_switch(prev.Rsp0Ptr(), current_thread_->Rsp0Ptr()); - - asm volatile("sti"); - } - - void Preempt() { - if (!enabled_) { - return; - } - - asm volatile("cli"); - if (current_thread_ == sleep_thread_) { - // Sleep should never be preempted. (We should yield it if another thread - // becomes scheduleable). - return; - } - - if (runnable_threads_.size() == 0) { - // Continue. - return; - } - - SharedPtr prev = current_thread_; - prev->SetState(Thread::RUNNABLE); - current_thread_ = runnable_threads_.CycleFront(prev); - - SwapToCurrent(*prev); - } - - void Yield() { - if (!enabled_) { - dbgln("WARN Scheduler skipped yield."); - return; - } - asm volatile("cli"); - - SharedPtr prev = current_thread_; - if (prev == sleep_thread_) { - if (runnable_threads_.size() == 0) { - panic("Sleep thread yielded without next."); - return; - } else { - // FIXME: Memory operation. - current_thread_ = runnable_threads_.PopFront(); - prev->SetState(Thread::RUNNABLE); - } - } else { - if (runnable_threads_.size() == 0) { - current_thread_ = sleep_thread_; - dbgln("Sleeping"); - gProcMan->DumpProcessStates(); - } else { - // FIXME: Memory operation. - current_thread_ = runnable_threads_.PopFront(); - } - } - - SwapToCurrent(*prev); - } - - private: - bool enabled_ = false; - - SharedPtr current_thread_; - LinkedList> runnable_threads_; - - SharedPtr sleep_thread_; -}; - -static Scheduler* gScheduler = nullptr; - -Scheduler& GetScheduler() { - if (!gScheduler) { - panic("Scheduler not initialized"); - } - return *gScheduler; -} - } // namespace -void InitScheduler() { gScheduler = new Scheduler(); } -void EnableScheduler() { GetScheduler().Enable(); } +Scheduler* gScheduler = nullptr; -void Preempt() { GetScheduler().Preempt(); } -void Yield() { GetScheduler().Yield(); } +void Scheduler::Init() { gScheduler = new Scheduler(); } +Scheduler::Scheduler() { + Process& root = gProcMan->FromId(0); + sleep_thread_ = root.GetThread(0); + // TODO: Implement a separate sleep thread? + current_thread_ = sleep_thread_; +} -void EnqueueThread(Thread* thread) { GetScheduler().Enqueue(thread); } +void Scheduler::SwapToCurrent(Thread& prev) { + if (current_thread_->GetState() != Thread::RUNNABLE) { + panic("Swapping to non-runnable thread."); + } + current_thread_->SetState(Thread::RUNNING); -Process& CurrentProcess() { return GetScheduler().CurrentProcess(); } -Thread& CurrentThread() { return GetScheduler().CurrentThread(); } + context_switch(prev.Rsp0Ptr(), current_thread_->Rsp0Ptr()); -} // namespace sched + asm volatile("sti"); +} + +void Scheduler::Preempt() { + if (!enabled_) { + return; + } + + asm volatile("cli"); + if (current_thread_ == sleep_thread_) { + // Sleep should never be preempted. (We should yield it if another thread + // becomes scheduleable). + return; + } + + if (runnable_threads_.size() == 0) { + // Continue. + return; + } + + SharedPtr prev = current_thread_; + prev->SetState(Thread::RUNNABLE); + current_thread_ = runnable_threads_.CycleFront(prev); + + SwapToCurrent(*prev); +} + +void Scheduler::Yield() { + if (!enabled_) { + dbgln("WARN Scheduler skipped yield."); + return; + } + asm volatile("cli"); + + SharedPtr prev = current_thread_; + if (prev == sleep_thread_) { + if (runnable_threads_.size() == 0) { + panic("Sleep thread yielded without next."); + return; + } else { + // FIXME: Memory operation. + current_thread_ = runnable_threads_.PopFront(); + prev->SetState(Thread::RUNNABLE); + } + } else { + if (runnable_threads_.size() == 0) { + current_thread_ = sleep_thread_; + dbgln("Sleeping"); + gProcMan->DumpProcessStates(); + } else { + // FIXME: Memory operation. + current_thread_ = runnable_threads_.PopFront(); + } + } + + SwapToCurrent(*prev); +} diff --git a/zion/scheduler/scheduler.h b/zion/scheduler/scheduler.h index 7253739..f36772c 100644 --- a/zion/scheduler/scheduler.h +++ b/zion/scheduler/scheduler.h @@ -3,27 +3,35 @@ #include "scheduler/process.h" #include "scheduler/thread.h" -namespace sched { +class Scheduler { + public: + // Initializes the scheduler in a disabled state. + // + // Requires the process manager to have been initialized. + static void Init(); -// Create the scheduler object in a disabled state, -// processes can be added but will not be scheduled. -void InitScheduler(); + void Enable() { enabled_ = true; } -// Enables the scheduler such that processes will yield on ticks. -void EnableScheduler(); + Process& CurrentProcess() { return current_thread_->process(); } + Thread& CurrentThread() { return *current_thread_; } -// Preempts the current thread and flags it as runnable in the queue. -// Generally used by the timer to move to the next timeslice. -void Preempt(); + void Enqueue(const SharedPtr thread) { + runnable_threads_.PushBack(thread); + } -// Current thread yields and is not rescheduled until some external process -// adds it. -// Used when a thread blocks or exits. -void Yield(); + void Preempt(); + void Yield(); -void EnqueueThread(Thread* thread); + private: + bool enabled_ = false; -Process& CurrentProcess(); -Thread& CurrentThread(); + SharedPtr current_thread_; + LinkedList> runnable_threads_; -} // namespace sched + SharedPtr sleep_thread_; + + Scheduler(); + void SwapToCurrent(Thread& prev); +}; + +extern Scheduler* gScheduler; diff --git a/zion/scheduler/thread.cpp b/zion/scheduler/thread.cpp index a513d3b..4851478 100644 --- a/zion/scheduler/thread.cpp +++ b/zion/scheduler/thread.cpp @@ -13,7 +13,7 @@ extern "C" void jump_user_space(uint64_t rip, uint64_t rsp); extern "C" void thread_init() { asm("sti"); - sched::CurrentThread().Init(); + gScheduler->CurrentThread().Init(); panic("Reached end of thread."); } @@ -54,5 +54,5 @@ void Thread::Exit() { dbgln("[%u.%u] Exiting", pid(), id_); state_ = FINISHED; process_->CheckState(); - sched::Yield(); + gScheduler->Yield(); } diff --git a/zion/syscall/syscall.cpp b/zion/syscall/syscall.cpp index d13ede3..1bd1a4c 100644 --- a/zion/syscall/syscall.cpp +++ b/zion/syscall/syscall.cpp @@ -30,7 +30,7 @@ extern "C" void syscall_enter(); // Used by syscall_enter.s extern "C" uint64_t GetKernelRsp() { - return sched::CurrentThread().Rsp0Start(); + return gScheduler->CurrentThread().Rsp0Start(); } void InitSyscall() { @@ -54,7 +54,7 @@ void InitSyscall() { } extern "C" void SyscallHandler(uint64_t call_id, char* message) { - Thread& thread = sched::CurrentThread(); + Thread& thread = gScheduler->CurrentThread(); switch (call_id) { case Z_THREAD_EXIT: thread.Exit(); diff --git a/zion/zion.cpp b/zion/zion.cpp index a37b529..988bc10 100644 --- a/zion/zion.cpp +++ b/zion/zion.cpp @@ -30,16 +30,16 @@ extern "C" void zion() { dbgln("[boot] Init scheduler."); ProcessManager::Init(); + Scheduler::Init(); // Schedule every 50ms. SetFrequency(/* hertz= */ 20); - sched::InitScheduler(); dbgln("[boot] Loading sys init program."); LoadInitProgram(); dbgln("[boot] Init finished, yielding."); - sched::EnableScheduler(); - sched::Yield(); + gScheduler->Enable(); + gScheduler->Yield(); dbgln("Sleeping!"); while (1)