General Purpose library for Freestanding C++ and POSIX systems
25개 이상의 토픽을 선택하실 수 없습니다. Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

105 lines
3.0 KiB

#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<gp::allocator> system_allocator;
gp::vector<gp::filesystem*> filesystems{system_allocator};
gp::vector<gp::scheduler> process_managers;
gp::indexed_array<gp::process_data*, gp_config::limits::max_processes> 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<void()> 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<scheduler*>(target.push(this)));
} else {
[[maybe_unused]] volatile scheduler* new_p = static_cast<scheduler*>(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);
}
}
);
}
}