diff --git a/zion/lib/linked_list.h b/zion/lib/linked_list.h new file mode 100644 index 0000000..9377913 --- /dev/null +++ b/zion/lib/linked_list.h @@ -0,0 +1,79 @@ +#pragma once + +#include + +#include "debug/debug.h" + +template +class LinkedList { + public: + LinkedList() {} + + LinkedList(const LinkedList&) = delete; + + uint64_t size() { return size_; } + + void PushBack(const T& item) { + size_++; + ListItem* new_item = new ListItem{ + .item = item, + .next = nullptr, + }; + if (front_ == nullptr) { + front_ = new_item; + return; + } + ListItem* litem = front_; + while (litem->next != nullptr) { + litem = litem->next; + } + litem->next = new_item; + } + + T PopFront() { + if (size_ == 0 || front_ == nullptr) { + panic("Popping from empty list"); + } + + size_--; + + ListItem* old_front = front_; + front_ = front_->next; + T ret = old_front->item; + delete old_front; + return ret; + } + + T CycleFront() { + if (size_ == 0 || front_ == nullptr) { + panic("Cycling empty list"); + } + + if (size_ == 1) { + return front_->item; + } + + T ret = front_->item; + ListItem* old_front = front_; + ListItem* iter = front_; + front_ = front_->next; + while (iter->next != nullptr) { + iter = iter->next; + } + iter->next = old_front; + old_front->next = nullptr; + return ret; + } + + T PeekFront() { return front_->item; } + + private: + uint64_t size_ = 0; + + struct ListItem { + T item; + ListItem* next; + }; + + ListItem* front_ = nullptr; +}; diff --git a/zion/scheduler/scheduler.cpp b/zion/scheduler/scheduler.cpp index 76cde33..41e19b6 100644 --- a/zion/scheduler/scheduler.cpp +++ b/zion/scheduler/scheduler.cpp @@ -1,6 +1,7 @@ #include "scheduler/scheduler.h" #include "debug/debug.h" +#include "lib/linked_list.h" namespace sched { namespace { @@ -56,22 +57,16 @@ class Scheduler { public: Scheduler() { Process* root = Process::RootProcess(); - current_thread_ = root->GetThread(0); + runnable_threads_.PushBack(root->GetThread(0)); proc_list_.InsertProcess(Process::RootProcess()); } void Enable() { enabled_ = true; } - Process& CurrentProcess() { return current_thread_->process(); } - Thread& CurrentThread() { return *current_thread_; } + Process& CurrentProcess() { return CurrentThread().process(); } + Thread& CurrentThread() { return *runnable_threads_.PeekFront(); } void InsertProcess(Process* process) { proc_list_.InsertProcess(process); } - void Enqueue(Thread* thread) { - Thread* back = current_thread_; - while (back->next_thread_ != nullptr) { - back = back->next_thread_; - } - back->next_thread_ = thread; - } + void Enqueue(Thread* thread) { runnable_threads_.PushBack(thread); } void Yield() { if (!enabled_) { @@ -79,28 +74,35 @@ class Scheduler { } asm volatile("cli"); - if (current_thread_->next_thread_ == nullptr) { - if (current_thread_->GetState() == Thread::RUNNING) { - dbgln("No next thread, continue"); - return; - } else { - proc_list_.DumpProcessStates(); - panic("FIXME: Implement Sleep"); - } + Thread* prev = nullptr; + if (CurrentThread().GetState() == Thread::RUNNING) { + prev = runnable_threads_.CycleFront(); + prev->SetState(Thread::RUNNABLE); + } else { + // This technically is a memory operation but should only occur when a + // thread is blocking so may be ok? + prev = runnable_threads_.PopFront(); } - Thread* prev = current_thread_; - current_thread_ = current_thread_->next_thread_; - prev->next_thread_ = nullptr; - if (prev->pid() != 0 && prev->GetState() == Thread::RUNNING) { - prev->SetState(Thread::RUNNABLE); - Enqueue(prev); + if (runnable_threads_.size() == 0) { + proc_list_.DumpProcessStates(); + panic("FIXME: Implement Sleep"); } - if (current_thread_->GetState() != Thread::RUNNABLE) { + + Thread* next = runnable_threads_.PeekFront(); + if (next->GetState() != Thread::RUNNABLE) { panic("Non-runnable thread in the queue"); } - current_thread_->SetState(Thread::RUNNING); - context_switch(prev->Rsp0Ptr(), current_thread_->Rsp0Ptr()); + // Needs to be before the next == prev check + // otherwise the active thread will be RUNNABLE instead of RUNNING. + next->SetState(Thread::RUNNING); + + if (next == prev) { + dbgln("No next thread, continue"); + return; + } + + context_switch(prev->Rsp0Ptr(), next->Rsp0Ptr()); asm volatile("sti"); } @@ -109,7 +111,7 @@ class Scheduler { bool enabled_ = false; ProcList proc_list_; - Thread* current_thread_; + LinkedList runnable_threads_; }; static Scheduler* gScheduler = nullptr; diff --git a/zion/scheduler/thread.h b/zion/scheduler/thread.h index 06f4a52..f47e775 100644 --- a/zion/scheduler/thread.h +++ b/zion/scheduler/thread.h @@ -35,9 +35,6 @@ class Thread { void SetState(State state) { state_ = state; } void Exit(); - // FIXME: Probably make this private. - Thread* next_thread_; - private: // Special constructor for the root thread only. Thread(Process* proc) : process_(proc), id_(0) {}