#ifndef fiber_h #define fiber_h 1 #include #include #include #include #include #include #include #include namespace as { //-------------------------------------------------------------------- // struct stack //-------------------------------------------------------------------- class stack { public: static constexpr std::size_t default_align = 16; static constexpr std::size_t default_size = 65536; explicit stack(std::size_t size = default_size) : storage(new(std::align_val_t(default_align)) std::byte[size]), pointer(storage.get() + size) { } void __attribute__((always_inline)) set() const { asm volatile("mov %0,%%rsp"::"r"(pointer)); } private: std::unique_ptr storage = nullptr; std::byte *pointer = nullptr; }; // stack //-------------------------------------------------------------------- // class scheduler //-------------------------------------------------------------------- class context; class scheduler { public: enum scheduling_state { INIT = 0, SCHEDULE, EXIT }; scheduler() = default; static scheduler *active() { static std::unique_ptr initializer = std::make_unique(); return initializer.get(); } void attach(context *c) { ready.push_back(c); } void run(); void yield(); private: jmp_buf cpu; std::list ready; }; // scheduler //-------------------------------------------------------------------- // struct context_initializer //-------------------------------------------------------------------- struct context_initializer { static context *active; static std::size_t counter; context_initializer(); ~context_initializer(); }; context *context_initializer::active = nullptr; std::size_t context_initializer::counter = 0; //-------------------------------------------------------------------- // class context //-------------------------------------------------------------------- class context { public: friend class scheduler; class id { public: id() = default; explicit id(const context *other) noexcept : impl(other) {} auto operator<=>(const id &) const noexcept = default; template friend std::basic_ostream & operator<<(std::basic_ostream &os, const id &other) { if (other.impl) return os << other.impl; else return os << "{not-valid}"; } private: const context *impl = nullptr; }; enum context_state { NEW, RUNNING, MAIN, FINISH }; explicit context(context_state _state = NEW) : state(_state) { scheduler::active()->attach(this); } virtual ~context() = default; void activate() { context_initializer::active = this; } static context *active() { static context_initializer initializer; return context_initializer::active; } void join() { while (state != FINISH) scheduler::active()->run(); } id get_id() const noexcept { return id(this); } virtual void run() {} private: jmp_buf cpu; as::stack stack; context_state state = NEW; }; // context //-------------------------------------------------------------------- // class fiber_context //-------------------------------------------------------------------- template class fiber_context final : public context { public: explicit fiber_context(Callable &&_callable, Arguments &&..._arguments) : callable(std::forward(_callable)), arguments(std::forward(_arguments)...) { } void run() override { std::apply(callable, arguments); } private: typename std::decay::type callable; std::tuple arguments; }; //-------------------------------------------------------------------- // class fiber //-------------------------------------------------------------------- class fiber { public: using id = context::id; fiber() = default; template explicit fiber(Callable &&callable, Arguments &&...arguments) : impl(std::make_unique>( std::forward(callable), std::forward(arguments)...)) { } ~fiber() { if (joinable()) std::terminate(); } bool joinable() { // return get_id() != id(); // std // implementación idéntica return impl != nullptr; // boost // implementación idéntica } void join() { if (context::active()->get_id() == get_id()) throw std::runtime_error("as::fiber trying to join itself!"); if (!joinable()) throw std::runtime_error("as:fiber not joinable!"); impl->join(); impl.reset(); } id get_id() const noexcept { return impl ? impl->get_id() : id(); } private: std::unique_ptr impl = nullptr; }; // fiber //-------------------------------------------------------------------- // namespace this_fiber //-------------------------------------------------------------------- namespace this_fiber { fiber::id get_id() { return context::active()->get_id(); } void yield() { scheduler::active()->yield(); } } // namespace this_fiber //-------------------------------------------------------------------- // implementation of methods with class cross dependencies //-------------------------------------------------------------------- context_initializer::context_initializer() { if (counter++ == 0) active = new context(context::MAIN); } context_initializer::~context_initializer() { if (--counter == 0) delete active; } //-------------------------------------------------------------------- void scheduler::run() { context *next = nullptr; switch (setjmp(cpu)) // backup scheduler context { case EXIT: ready.pop_back(); case SCHEDULE: case INIT: next = ready.front(); if (next) { ready.pop_front(); ready.push_back(next); next->activate(); switch (next->state) { case context::NEW: next->state = context::RUNNING; next->stack.set(); // after this point stack data is // fucked! next->run(); next->state = context::FINISH; longjmp( scheduler::active()->cpu, EXIT); // go back to scheduler context backup break; case context::RUNNING: longjmp(next->cpu, context::RUNNING); // ... case context::MAIN: break; default: throw std::runtime_error( "unexpected context state: " + std::to_string(next->state)); break; } } break; default: throw std::runtime_error( "unexpected return from setjmp in scheduler"); break; } } void scheduler::yield() { if (setjmp(context::active()->cpu) == INIT) longjmp(cpu, SCHEDULE); } //-------------------------------------------------------------------- } // namespace as #endif // fiber_h