diff --git a/CMakeLists.txt b/CMakeLists.txt index 599cffb..65f4f5d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -15,7 +15,7 @@ add_subdirectory(sys) add_custom_command( OUTPUT disk.img COMMAND sudo sh ../scripts/build_image.sh disk.img - DEPENDS zion + DEPENDS zion test USES_TERMINAL ) diff --git a/zion/include/zcall.h b/zion/include/zcall.h index 67bca77..2b35c77 100644 --- a/zion/include/zcall.h +++ b/zion/include/zcall.h @@ -2,7 +2,7 @@ #include -#define Z_DEBUG_PRINT 100 - -uint64_t ZDebug(const char* message); +#define Z_THREAD_EXIT 0x01 +#define Z_DEBUG_PRINT 0x100 +uint64_t ZDebug(const char* message); diff --git a/zion/scheduler/process.cpp b/zion/scheduler/process.cpp index 8557715..43bd613 100644 --- a/zion/scheduler/process.cpp +++ b/zion/scheduler/process.cpp @@ -25,7 +25,7 @@ Process* Process::RootProcess() { return proc; } -Process::Process(uint64_t elf_ptr) : id_(gNextId++) { +Process::Process(uint64_t elf_ptr) : id_(gNextId++), state_(RUNNING) { cr3_ = phys_mem::AllocatePage(); InitializePml4(cr3_); CreateThread(elf_ptr); @@ -60,3 +60,15 @@ Thread* Process::GetThread(uint64_t tid) { panic("Bad thread access."); return nullptr; } + +void Process::CheckState() { + ThreadEntry* entry = thread_list_front_; + + while (entry != nullptr) { + if (entry->thread->GetState() != Thread::FINISHED) { + return; + } + entry = entry->next; + } + state_ = FINISHED; +} diff --git a/zion/scheduler/process.h b/zion/scheduler/process.h index 3ede000..1e7d38f 100644 --- a/zion/scheduler/process.h +++ b/zion/scheduler/process.h @@ -7,6 +7,12 @@ class Thread; class Process { public: + enum State { + UNSPECIFIED, + SETUP, + RUNNING, + FINISHED, + }; // Caller takes ownership of returned process. static Process* RootProcess(); Process(uint64_t elf_ptr); @@ -17,10 +23,17 @@ class Process { void CreateThread(uint64_t elf_ptr); Thread* GetThread(uint64_t tid); + // Checks the state of all child threads and transitions to + // finished if all have finished. + void CheckState(); + + State GetState() { return state_; } + private: Process(uint64_t id, uint64_t cr3) : id_(id), cr3_(cr3) {} uint64_t id_; uint64_t cr3_; + State state_; uint64_t next_thread_id_ = 0; diff --git a/zion/scheduler/scheduler.cpp b/zion/scheduler/scheduler.cpp index 2930d2d..76cde33 100644 --- a/zion/scheduler/scheduler.cpp +++ b/zion/scheduler/scheduler.cpp @@ -34,6 +34,15 @@ class ProcList { }; } + void DumpProcessStates() { + ProcEntry* p = front_; + dbgln("Process States:"); + while (p != nullptr) { + dbgln("%u: %u", p->proc->id(), p->proc->GetState()); + p = p->next; + } + } + private: struct ProcEntry { Process* proc; @@ -71,16 +80,26 @@ class Scheduler { asm volatile("cli"); if (current_thread_->next_thread_ == nullptr) { - dbgln("No next thread, continue"); - return; + if (current_thread_->GetState() == Thread::RUNNING) { + dbgln("No next thread, continue"); + return; + } else { + proc_list_.DumpProcessStates(); + panic("FIXME: Implement Sleep"); + } } Thread* prev = current_thread_; current_thread_ = current_thread_->next_thread_; prev->next_thread_ = nullptr; - if (prev->pid() != 0) { + if (prev->pid() != 0 && prev->GetState() == Thread::RUNNING) { + prev->SetState(Thread::RUNNABLE); Enqueue(prev); } + if (current_thread_->GetState() != Thread::RUNNABLE) { + panic("Non-runnable thread in the queue"); + } + current_thread_->SetState(Thread::RUNNING); context_switch(prev->Rsp0Ptr(), current_thread_->Rsp0Ptr()); asm volatile("sti"); diff --git a/zion/scheduler/thread.cpp b/zion/scheduler/thread.cpp index fb4fef7..2521abc 100644 --- a/zion/scheduler/thread.cpp +++ b/zion/scheduler/thread.cpp @@ -47,3 +47,10 @@ void Thread::Init() { SetRsp0(rsp0_start_); jump_user_space(rip, rsp); } + +void Thread::Exit() { + dbgln("[%u.%u] Exiting", pid(), id_); + state_ = FINISHED; + process_->CheckState(); + sched::Yield(); +} diff --git a/zion/scheduler/thread.h b/zion/scheduler/thread.h index b442dc5..06f4a52 100644 --- a/zion/scheduler/thread.h +++ b/zion/scheduler/thread.h @@ -7,6 +7,14 @@ class Process; class Thread { public: + enum State { + UNSPECIFIED, + CREATED, + RUNNING, + RUNNABLE, + BLOCKED, + FINISHED, + }; static Thread* RootThread(Process* root_proc); explicit Thread(Process* proc, uint64_t tid, uint64_t elf_ptr); @@ -22,6 +30,11 @@ class Thread { // Called the first time the thread starts up. void Init(); + // State Management. + State GetState() { return state_; }; + void SetState(State state) { state_ = state; } + void Exit(); + // FIXME: Probably make this private. Thread* next_thread_; @@ -30,6 +43,7 @@ class Thread { Thread(Process* proc) : process_(proc), id_(0) {} Process* process_; uint64_t id_; + State state_ = RUNNABLE; uint64_t elf_ptr_; diff --git a/zion/syscall/syscall.cpp b/zion/syscall/syscall.cpp index 4b78e88..d13ede3 100644 --- a/zion/syscall/syscall.cpp +++ b/zion/syscall/syscall.cpp @@ -56,8 +56,12 @@ void InitSyscall() { extern "C" void SyscallHandler(uint64_t call_id, char* message) { Thread& thread = sched::CurrentThread(); switch (call_id) { + case Z_THREAD_EXIT: + thread.Exit(); + panic("Returned from thread exit"); + break; case Z_DEBUG_PRINT: - dbgln("[%u.%u] %s", thread.pid(), thread.tid(), message); + dbgln("[%u.%u] [Debug] %s", thread.pid(), thread.tid(), message); break; default: panic("Unhandled syscall number: %u", call_id);