diff --git a/zion/lib/linked_list.h b/zion/lib/linked_list.h index 40f3b64..e7c34f5 100644 --- a/zion/lib/linked_list.h +++ b/zion/lib/linked_list.h @@ -44,16 +44,22 @@ class LinkedList { return ret; } - T CycleFront() { + /* + * Returns the front item in the list and pushes the passed item to the back. + * + * Done in one function to avoid a memory alloc/dealloc during scheduling. + **/ + T CycleFront(const T& new_item) { if (size_ == 0 || front_ == nullptr) { panic("Cycling empty list"); } + T ret = front_->item; + front_->item = new_item; if (size_ == 1) { - return front_->item; + return ret; } - T ret = front_->item; ListItem* old_front = front_; ListItem* iter = front_; front_ = front_->next; diff --git a/zion/scheduler/scheduler.cpp b/zion/scheduler/scheduler.cpp index 91c653f..c9c10eb 100644 --- a/zion/scheduler/scheduler.cpp +++ b/zion/scheduler/scheduler.cpp @@ -22,13 +22,14 @@ class Scheduler { Scheduler() { SharedPtr root = Process::RootProcess(); sleep_thread_ = root->GetThread(0); - runnable_threads_.PushBack(sleep_thread_); - proc_list_.PushBack(Process::RootProcess()); + // TODO: Implement a separate sleep thread? + current_thread_ = sleep_thread_; + proc_list_.PushBack(root); } void Enable() { enabled_ = true; } Process& CurrentProcess() { return CurrentThread().process(); } - Thread& CurrentThread() { return *runnable_threads_.PeekFront(); } + Thread& CurrentThread() { return *current_thread_; } void InsertProcess(Process* process) { proc_list_.PushBack(process); } void Enqueue(Thread* thread) { runnable_threads_.PushBack(thread); } @@ -39,35 +40,45 @@ class Scheduler { } asm volatile("cli"); - SharedPtr prev; - 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(); - } - + SharedPtr prev = current_thread_; SharedPtr next; - if (runnable_threads_.size() == 0) { - next = sleep_thread_; - DumpProcessStates(proc_list_); + if (prev == sleep_thread_) { + if (runnable_threads_.size() == 0) { + // Continue sleeping. + return; + } else { + // FIXME: Memory operation. + next = runnable_threads_.PopFront(); + prev->SetState(Thread::RUNNABLE); + } } else { - next = runnable_threads_.PeekFront(); + // Normal thread running. + if (prev->GetState() == Thread::RUNNING) { + if (runnable_threads_.size() == 0) { + // This thread can continue. + return; + } + prev->SetState(Thread::RUNNABLE); + next = runnable_threads_.CycleFront(prev); + } else { + // Thread blocked/exited. + if (runnable_threads_.size() == 0) { + next = sleep_thread_; + dbgln("Sleeping"); + DumpProcessStates(proc_list_); + } else { + // FIXME: Memory operation. + next = runnable_threads_.PopFront(); + } + } } if (next->GetState() != Thread::RUNNABLE) { panic("Non-runnable thread in the queue"); } - // 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; - } + next->SetState(Thread::RUNNING); + current_thread_ = next; context_switch(prev->Rsp0Ptr(), next->Rsp0Ptr()); @@ -78,6 +89,8 @@ class Scheduler { bool enabled_ = false; // TODO: move this to a separate process manager class. LinkedList> proc_list_; + + SharedPtr current_thread_; LinkedList> runnable_threads_; SharedPtr sleep_thread_;