|
@ -6,28 +6,34 @@ |
|
|
#include "gp/vector.hpp"
|
|
|
#include "gp/vector.hpp"
|
|
|
#include "gp/vfs/file_description.hpp"
|
|
|
#include "gp/vfs/file_description.hpp"
|
|
|
#include "gp/vfs/filesystem.hpp"
|
|
|
#include "gp/vfs/filesystem.hpp"
|
|
|
|
|
|
#include "gp/vfs/runqueue.hpp"
|
|
|
#include "gp/vfs/scheduler.hpp"
|
|
|
#include "gp/vfs/scheduler.hpp"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
namespace gp{ |
|
|
namespace gp{ |
|
|
// TODO: thread safety
|
|
|
// TODO: thread safety
|
|
|
|
|
|
|
|
|
|
|
|
class scheduling_scheme { |
|
|
|
|
|
public: |
|
|
|
|
|
virtual topic_list::node_ptr one(size_t token) = 0; |
|
|
|
|
|
virtual topic_list::node_ptr next(size_t token, topic_list::node_ptr current) = 0; |
|
|
|
|
|
virtual scheduler& current_scheduler() = 0; |
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
class system { |
|
|
class system { |
|
|
gp::reference_wrapper<gp::allocator> system_allocator; |
|
|
gp::reference_wrapper<gp::allocator> system_allocator; |
|
|
gp::vector<gp::filesystem*> filesystems{system_allocator}; |
|
|
gp::vector<gp::filesystem*> filesystems{system_allocator}; |
|
|
gp::vector<gp::scheduler> process_managers; |
|
|
gp::vector<gp::scheduler> process_managers; |
|
|
gp::indexed_array<gp::process_data*, gp_config::limits::max_processes> processes; |
|
|
|
|
|
|
|
|
scheduling_scheme& scheme; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
friend class scheduler; |
|
|
friend class scheduler; |
|
|
public: |
|
|
public: |
|
|
system(allocator& v, size_t scheduler_count) |
|
|
|
|
|
|
|
|
system(allocator& v, scheduling_scheme& scheme_) |
|
|
: system_allocator{v} |
|
|
: system_allocator{v} |
|
|
, process_managers{system_allocator} |
|
|
, 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); |
|
|
|
|
|
}); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
, scheme{scheme_} |
|
|
|
|
|
{} |
|
|
|
|
|
|
|
|
size_t spawn(gp::function<void()> fn) { |
|
|
size_t spawn(gp::function<void()> fn) { |
|
|
constexpr size_t stack_sz = gp_config::limits::process_stack; |
|
|
constexpr size_t stack_sz = gp_config::limits::process_stack; |
|
@ -36,70 +42,61 @@ public: |
|
|
process_data* created_process = (process_data*)system_allocator.get().allocate(sizeof(process_data)); |
|
|
process_data* created_process = (process_data*)system_allocator.get().allocate(sizeof(process_data)); |
|
|
gp_config::assertion(stack != nullptr, "failed to allocate a process data"); |
|
|
gp_config::assertion(stack != nullptr, "failed to allocate a process data"); |
|
|
new(created_process) process_data(fn, stack, stack_sz); |
|
|
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); |
|
|
|
|
|
|
|
|
topic_list::node_ptr pp = (topic_list::node_ptr)system_allocator.get().allocate( |
|
|
|
|
|
sizeof(topic_list::node) |
|
|
|
|
|
); |
|
|
|
|
|
new(pp) topic_list::node(); |
|
|
|
|
|
pp->value = created_process; |
|
|
|
|
|
|
|
|
|
|
|
auto& sched = scheme.current_scheduler(); |
|
|
|
|
|
sched.yield_to(scheme.next(sched.id, pp)); |
|
|
|
|
|
return pp->value->pid; |
|
|
} |
|
|
} |
|
|
}; |
|
|
}; |
|
|
|
|
|
|
|
|
scheduler::scheduler(class system& v) |
|
|
|
|
|
: sys(v){} |
|
|
|
|
|
|
|
|
scheduler::scheduler(class system& v, size_t token) |
|
|
|
|
|
: id(token) |
|
|
|
|
|
, sys(v) |
|
|
|
|
|
{} |
|
|
|
|
|
|
|
|
void scheduler::yield_to(size_t target_pid) |
|
|
|
|
|
|
|
|
void scheduler::yield_to(topic_list::node_ptr target) |
|
|
{ |
|
|
{ |
|
|
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)); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
previous = current; |
|
|
|
|
|
current->value->specifics.pull(); |
|
|
|
|
|
current = target; |
|
|
|
|
|
no_inline_decl([&](scheduler* new_p)){ |
|
|
|
|
|
*new_p->previous->release(); |
|
|
|
|
|
auto& proc = *new_p->current->value; |
|
|
|
|
|
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(new_p->sys.scheme.next(new_p->id, new_p->current)); |
|
|
|
|
|
} |
|
|
|
|
|
}(static_cast<scheduler*>(target->value->specifics.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::yield(){ |
|
|
|
|
|
current = sys.scheme.next(id, current); |
|
|
|
|
|
yield_to(current); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
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); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void scheduler::startup() |
|
|
|
|
|
{ |
|
|
|
|
|
current = sys.scheme.one(id); |
|
|
|
|
|
no_inline_decl([&](scheduler* new_p)){ |
|
|
|
|
|
auto& proc = *new_p->current->value; |
|
|
|
|
|
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(new_p->sys.scheme.next(new_p->id, new_p->current)); |
|
|
} |
|
|
} |
|
|
); |
|
|
|
|
|
|
|
|
}(static_cast<scheduler*>(target->value->specifics.push(this))); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
} |
|
|
} |