Explorar el Código

Coroutines/light threads work

channel
Ludovic 'Archivist' Lagouardette hace 3 años
padre
commit
f29fe07676
Se han modificado 12 ficheros con 246 adiciones y 61 borrados
  1. +5
    -4
      Makefile
  2. +3
    -0
      blacklist.txt
  3. +8
    -1
      include/gp/indexed_array.hpp
  4. +18
    -1
      include/gp/vfs/file_description.hpp
  5. +3
    -3
      include/gp/vfs/filesystem.hpp
  6. +43
    -32
      include/gp/vfs/platforms/gcc-x86_64.hpp
  7. +17
    -3
      include/gp/vfs/process_data.hpp
  8. +21
    -8
      include/gp/vfs/scheduler.hpp
  9. +92
    -3
      include/gp/vfs/system.hpp
  10. +0
    -6
      include/gp_config.hpp
  11. +1
    -0
      tests.cpp
  12. +35
    -0
      tests/channel_test.cpp

+ 5
- 4
Makefile Ver fichero

@ -1,14 +1,15 @@
CXX= clang++-10
CXXFLAGS= --std=c++20 -O0 -g -pthread -DGP_TESTS -DFUZZ_STRENGTH=500 -DNO_BENCH=1 -pedantic \
-fprofile-instr-generate -fcoverage-mapping -Wno-unknown-attributes \
-fsanitize=address -fno-omit-frame-pointer
-fprofile-instr-generate -fcoverage-mapping -Wno-unknown-attributes -fno-omit-frame-pointer \
# -fsanitize=address -fsanitize-blacklist=blacklist.txt
all: tests
tests: bin/tests
LLVM_PROFILE_FILE="./bin/tests.profraw" ASAN_SYMBOLIZER_PATH=/usr/bin/llvm-symbolizer ./bin/tests
@llvm-profdata merge -sparse ./bin/tests.profraw -o ./bin/tests.profdata
@llvm-cov report ./bin/tests -instr-profile=./bin/tests.profdata include/*.hpp include/gp/*.hpp include/gp/algorithm/*.hpp include/gp/enveloppe/*.hpp include/gp/allocator/*.hpp
@llvm-cov report ./bin/tests -instr-profile=./bin/tests.profdata include/*.hpp include/gp/*.hpp include/gp/algorithm/*.hpp include/gp/enveloppe/*.hpp include/gp/allocator/*.hpp | tail -n 1 | tr -s " " | sed -e 's/ /,/g' -- | awk -F "," '{print $$9}' | sed -e 's/^/Untested lines: /g'
@llvm-cov report ./bin/tests -instr-profile=./bin/tests.profdata include/*.hpp include/gp/*.hpp include/gp/algorithm/*.hpp include/gp/enveloppe/*.hpp include/gp/vfs/*.hpp include/gp/allocator/*.hpp
@llvm-cov report ./bin/tests -instr-profile=./bin/tests.profdata include/*.hpp include/gp/*.hpp include/gp/algorithm/*.hpp include/gp/enveloppe/*.hpp include/gp/vfs/*.hpp include/gp/allocator/*.hpp | tail -n 1 | tr -s " " | sed -e 's/ /,/g' -- | awk -F "," '{print $$9}' | sed -e 's/^/Untested lines: /g'
bin/tests: tests.cpp $(wildcard tests/*.cpp) $(wildcard include/*.hpp) ./tests/test_scaffold.h
@mkdir -p $(@D)

+ 3
- 0
blacklist.txt Ver fichero

@ -0,0 +1,3 @@
src:gp/vfs/scheduler.hpp
src:gp/vfs/platforms/gcc-x86_64.hpp
fun:gp::specifics::platform_data::push(void*)

+ 8
- 1
include/gp/indexed_array.hpp Ver fichero

@ -55,7 +55,7 @@ namespace gp{
--data_top;
if(v_idx < data_top) {
size_t u_idx = reverse_translation_table[data_top];
data_table.as_buffer().template cast<T>()[v_idx] = gp::move(data_table[data_top]);
data_table.as_buffer().template cast<T>()[v_idx] = gp::move(k">reinterpret_cast<T&>(data_table[data_top]));
::operator delete(&data_table.as_buffer().template cast<T>()[data_top], &(data_table.as_buffer().template cast<T>()[data_top]));
data_table.as_buffer().template cast<T>()[data_top].~T();
translation_table[u_idx] = v_idx;
@ -100,6 +100,13 @@ namespace gp{
return true;
}
template<typename fn_t>
void foreach(fn_t function) {
for(size_t i = 0; i < data_top; ++i) {
function(reverse_translation_table[i], operator[](reverse_translation_table[i]));
}
}
pointer_iterator<T> begin()
{
return data_table.as_buffer().template cast<T>().begin();

+ 18
- 1
include/gp/vfs/file_description.hpp Ver fichero

@ -1,9 +1,26 @@
#pragma once
#include <cstddef>
#include <atomic>
namespace gp {
class file_description {
template<typename> class buffer;
class file_description {
std::atomic_int64_t ref_counter;
public:
virtual void read() = 0;
virtual void write() = 0;
virtual void set_attr() = 0;
virtual void get_attr() = 0;
virtual void seek() = 0;
virtual void close() = 0;
static file_description* open(class system&, gp::buffer<std::byte>);
static file_description* create(class system&, gp::buffer<std::byte>);
static file_description* remove(class system&, gp::buffer<std::byte>);
};
}

+ 3
- 3
include/gp/vfs/filesystem.hpp Ver fichero

@ -17,8 +17,8 @@ namespace gp{
};
class filesystem {
virtual file_description create(gp::buffer<file_char>, fs_user_representation) = 0;
virtual file_description open(gp::buffer<file_char>, fs_user_representation) = 0;
virtual file_description remove(gp::buffer<file_char>, fs_user_representation) = 0;
virtual file_description create(gp::buffer<file_char>, fs_user_representationo">&) = 0;
virtual file_description open(gp::buffer<file_char>, fs_user_representationo">&) = 0;
virtual file_description remove(gp::buffer<file_char>, fs_user_representationo">&) = 0;
};
}

+ 43
- 32
include/gp/vfs/platforms/gcc-x86_64.hpp Ver fichero

@ -1,12 +1,21 @@
#pragma once
#include "gp/buffer.hpp"
#include <cstdint>
#define no_inline_decl(a) [[gnu::noinline]] a
#define no_inline_decl(a) a __attribute__((noinline))
namespace gp{
namespace specifics {
struct platform_data {
platform_data() = default;
platform_data(gp::buffer<char> stack_str)
: stack_ptr((stack_str.end()-16).data)
, base_ptr(stack_ptr)
{}
uint64_t rbx, r12, r13, r14, r15;
void* stack_ptr;
@ -15,42 +24,44 @@ namespace gp{
void pull() __attribute__((always_inline))
{
__asm__ __volatile__(
"movq %%rsp, %0\n"
"movq %%rbp, %1\n"
"movq %%rbx, %2\n"
"movq %%r12, %3\n"
"movq %%r13, %4\n"
"movq %%r14, %5\n"
"movq %%r15, %6\n"
: "=m"(stack_ptr)
, "=m"(base_ptr)
, "=m"(rbx)
, "=m"(r12)
, "=m"(r13)
, "=m"(r14)
, "=m"(r15));
"movq %%rsp, %0\n"
"movq %%rbp, %1\n"
"movq %%rbx, %2\n"
"movq %%r12, %3\n"
"movq %%r13, %4\n"
"movq %%r14, %5\n"
"movq %%r15, %6\n"
: "=m"(stack_ptr)
, "=m"(base_ptr)
, "=m"(rbx)
, "=m"(r12)
, "=m"(r13)
, "=m"(r14)
, "=m"(r15)
);
}
void* push(void* location) __attribute__((always_inline))
{
void* tmp = static_cast<char*>(stack_ptr) - sizeof(void*);
*static_cast<void**>(tmp) = location;
">volatile void* volatile tmp = static_cast<char*>(stack_ptr) - sizeof(void*);
*static_cast<">volatile void* volatile * volatile>(tmp) = location;
__asm__ __volatile__(
"movq %1, %%rsp\n"
"movq %2, %%rbp\n"
"movq %3, %%r12\n"
"movq %4, %%r13\n"
"movq %5, %%r14\n"
"movq %6, %%r15\n"
"popq %0\n"
: "+r"(location)
: "m"(tmp)
, "m"(base_ptr)
, "m"(r12)
, "m"(r13)
, "m"(r14)
, "m"(r15)
: "memory");
"movq %1, %%rsp\n"
"movq %2, %%rbp\n"
"movq %3, %%r12\n"
"movq %4, %%r13\n"
"movq %5, %%r14\n"
"movq %6, %%r15\n"
"popq %0\n"
: "+r"(location)
: "m"(tmp)
, "m"(base_ptr)
, "m"(r12)
, "m"(r13)
, "m"(r14)
, "m"(r15)
: "memory"
);
return location;
}
};

+ 17
- 3
include/gp/vfs/process_data.hpp Ver fichero

@ -7,20 +7,34 @@
#include "gp/vfs/file_description.hpp"
#include "gp/vfs/platforms/platform_autopicker.hpp"
#include <atomic>
namespace gp {
enum class process_status {
inactive = 0,
running = 1,
waiting = 2,
zombie = 3
finished = 3,
zombie = 4
};
struct process_data{
[[no_unique_address]] gp::specifics::platform_data specifics;
gp::function<void()> fn;
void* stack;
size_t stack_sz;
gp::process_status state;
std::atomic_bool is_running;
[[no_unique_address]] gp::specifics::platform_data specifics;
gp::indexed_array<gp::file_description*, gp_config::limits::max_fd_per_process> fds;
process_data(gp::function<void()> _fn, void* _stack, size_t _stack_sz)
: fn(_fn)
, stack(_stack)
, stack_sz(_stack_sz)
, state(gp::process_status::inactive)
, specifics(gp::buffer<char>{(char*)stack, stack_sz})
{}
};
}

+ 21
- 8
include/gp/vfs/scheduler.hpp Ver fichero

@ -5,22 +5,35 @@
namespace gp{
class system;
class scheduler {
gp::indexed_array<gp::process_data*, gp_config::limits::max_processes> processes;
gp::specifics::platform_data root;
size_t current;
size_t current = 0;
system& sys;
no_inline_decl(
void yield_impl(bool terminate)
) {
}
void yield_to(size_t target_pid)
);
public:
[[noreturn]] void run();
scheduler(class system&);
[[noreturn]] void run(allocator& alloc) {
again:
run_once();
cleanup(alloc);
goto again;
}
void cleanup(allocator& alloc);
void run_once();
void yield(){
yield_impl(false);
yield_to(0);
}
};

+ 92
- 3
include/gp/vfs/system.hpp Ver fichero

@ -1,17 +1,106 @@
#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;
gp::scheduler process_manager;
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){}
no_inline_decl(
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 {
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);
}
}
);
}
}

+ 0
- 6
include/gp_config.hpp Ver fichero

@ -28,12 +28,6 @@ namespace gp_config{
}
namespace memory_module{
#ifdef GP_TESTS
using default_allocator = static_mapper;
#else
using default_allocator = gp::c_allocator;
#endif
constexpr bool is_ok = true;
}

+ 1
- 0
tests.cpp Ver fichero

@ -3,6 +3,7 @@
#include "allocator.hpp"
#include "bloomfilter.cpp"
#include "cbor_test.cpp"
#include "channel_test.cpp"
#include "gp_test.cpp"
#include "math.cpp"
#include "meta_test.cpp"

+ 35
- 0
tests/channel_test.cpp Ver fichero

@ -0,0 +1,35 @@
#include <gp/algorithm/foreach.hpp>
#include <gp/allocator/buddy.hpp>
#include <gp/array.hpp>
#include <gp/enveloppe/cbor.hpp>
#include <gp/vfs/system.hpp>
#include "test_scaffold.h"
#include <atomic>
struct channel_test : public test_scaffold {
channel_test() {
name = __FILE__ ":1";
}
virtual int run() {
auto store = std::make_unique<gp::array<char, 4096*512>>();
gp::buddy<> alloc{&*store->begin(), store->size()};
gp::system sys{alloc, 1};
std::atomic_int a = 0;
sys.spawn(gp::function<void()>{[&](){
a.fetch_add(1);
}, alloc});
sys.run_once();
gp_config::assertion(a.load(), "did not increment (aka run the fiber)");
return 0;
}
};
append_test dummy_hke41r43(new channel_test{});

Cargando…
Cancelar
Guardar