#pragma once #include "gp/algorithm/foreach.hpp" #include "gp/algorithm/reference.hpp" #include "gp/allocator/allocator.hpp" #include "gp/vector.hpp" #include "gp/vfs/file_description.hpp" #include "gp/vfs/filesystem.hpp" #include "gp/vfs/scheduler.hpp" namespace gp{ // TODO: thread safety class system { gp::reference_wrapper system_allocator; gp::vector filesystems{system_allocator}; gp::vector process_managers; gp::indexed_array processes; friend class scheduler; public: system(allocator& v, size_t scheduler_count) : system_allocator{v} , process_managers{system_allocator} { gp_config::assertion(scheduler_count >= 1, "no scheduling in the system"); process_managers.reserve(scheduler_count); gp::repeat(scheduler_count,[&](){ process_managers.emplace_back(*this); }); } size_t spawn(gp::function fn) { constexpr size_t stack_sz = gp_config::limits::process_stack; void* stack = system_allocator.get().allocate(stack_sz); gp_config::assertion(stack != nullptr, "failed to allocate a stack"); process_data* created_process = (process_data*)system_allocator.get().allocate(sizeof(process_data)); gp_config::assertion(stack != nullptr, "failed to allocate a process data"); new(created_process) process_data(fn, stack, stack_sz); return processes.push(created_process); } void run_once() { (*process_managers.begin()).run_once(); (*process_managers.begin()).cleanup(system_allocator); } }; scheduler::scheduler(class system& v) : sys(v){} void scheduler::yield_to(size_t target_pid) { auto& cur = current ? sys.processes[current-1]->specifics : root; auto& target = target_pid ? sys.processes[target_pid-1]->specifics : root; current = target_pid; cur.pull(); if(target_pid) { no_inline_decl([&](scheduler* new_p)){ auto& proc = *new_p->sys.processes[new_p->current-1]; if(proc.state == gp::process_status::inactive) { proc.state = gp::process_status::running; proc.fn(); proc.state = gp::process_status::finished; new_p->yield_to(0); } }(static_cast(target.push(this))); } else { [[maybe_unused]] volatile scheduler* new_p = static_cast(target.push(this)); } } void scheduler::cleanup(allocator& alloc) { sys.processes.foreach( [&] (size_t pid, process_data* process) { if( process->state == process_status::finished ) { process->fds.foreach([](size_t, file_description* fdes){ fdes->close(); }); gp_config::assertion(alloc.deallocate(process->stack), "can't deallocate the stack"); sys.processes.mark_for_removal(pid); gp_config::assertion(alloc.deallocate(process), "can't deallocate the process data"); } } ); sys.processes.sweep_removed(); } void scheduler::run_once() { sys.processes.foreach( [&] (size_t pid, process_data* process) { if( process->state == process_status::inactive || process->state == process_status::running ) { yield_to(pid+1); } } ); } }