diff --git a/examples/work_sharing.cpp b/examples/work_sharing.cpp index 91c5ba43..70e5e23b 100644 --- a/examples/work_sharing.cpp +++ b/examples/work_sharing.cpp @@ -68,8 +68,9 @@ public: // stash it in separate slot local_queue_.push( ctx); } else { + // detach context from current scheduler + boost::fibers::context::active()->detach( ctx); // ordinary fiber, enqueue on shared queue - ctx->worker_unlink(); lock_t lk( mtx_); rqueue_.push( ctx); } @@ -84,7 +85,8 @@ public: rqueue_.pop(); lk.unlock(); BOOST_ASSERT( nullptr != ctx); - ctx->set_scheduler( boost::fibers::context::active()->get_scheduler() ); + // attach context to current scheduler + boost::fibers::context::active()->attach( ctx); } else if ( ! local_queue_.empty() ) { lk.unlock(); // nothing in the ready queue, return dispatcher_ctx_ diff --git a/include/boost/fiber/context.hpp b/include/boost/fiber/context.hpp index e54070d8..e5b3d65c 100644 --- a/include/boost/fiber/context.hpp +++ b/include/boost/fiber/context.hpp @@ -85,14 +85,6 @@ typedef intrusive::list_member_hook< > > remote_ready_hook; -struct yield_tag; -typedef intrusive::list_member_hook< - intrusive::tag< yield_tag >, - intrusive::link_mode< - intrusive::auto_unlink - > -> yield_hook; - struct sleep_tag; typedef intrusive::set_member_hook< intrusive::tag< sleep_tag >, @@ -109,14 +101,22 @@ typedef intrusive::list_member_hook< > > terminated_hook; -struct managed_tag; +struct worker_tag; typedef intrusive::list_member_hook< - intrusive::tag< managed_tag >, + intrusive::tag< worker_tag >, intrusive::link_mode< intrusive::auto_unlink > > worker_hook; +struct yield_tag; +typedef intrusive::list_member_hook< + intrusive::tag< yield_tag >, + intrusive::link_mode< + intrusive::auto_unlink + > +> yield_hook; + } struct main_context_t {}; @@ -131,6 +131,7 @@ const worker_context_t worker_context{}; class BOOST_FIBERS_DECL context { private: friend struct context_initializer; + friend class scheduler; enum flag_t { flag_main_context = 1 << 1, @@ -178,13 +179,13 @@ private: boost::context::execution_context ctx_; public: - detail::worker_hook worker_hook_; - detail::terminated_hook terminated_hook_; detail::ready_hook ready_hook_; detail::remote_ready_hook remote_ready_hook_; - detail::yield_hook yield_hook_; detail::sleep_hook sleep_hook_; + detail::terminated_hook terminated_hook_; detail::wait_hook wait_hook_; + detail::worker_hook worker_hook_; + detail::yield_hook yield_hook_; std::chrono::steady_clock::time_point tp_; typedef intrusive::list< @@ -305,13 +306,13 @@ public: suspend(); BOOST_ASSERT_MSG( false, "fiber already terminated"); }), - worker_hook_(), - terminated_hook_(), ready_hook_(), remote_ready_hook_(), - yield_hook_(), sleep_hook_(), + terminated_hook_(), wait_hook_(), + worker_hook_(), + yield_hook_(), tp_( (std::chrono::steady_clock::time_point::max)() ), fss_data_(), wait_queue_(), @@ -323,8 +324,6 @@ public: virtual ~context(); - void set_scheduler( scheduler *); - scheduler * get_scheduler() const noexcept; id get_id() const noexcept; @@ -391,29 +390,19 @@ public: return properties_; } - bool worker_is_linked(); - - bool terminated_is_linked(); - bool ready_is_linked(); bool remote_ready_is_linked(); - bool yield_is_linked(); - bool sleep_is_linked(); + bool terminated_is_linked(); + bool wait_is_linked(); - template< typename List > - void worker_link( List & lst) { - lst.push_back( * this); - } + bool worker_is_linked(); - template< typename List > - void terminated_link( List & lst) { - lst.push_back( * this); - } + bool yield_is_linked(); template< typename List > void ready_link( List & lst) { @@ -425,33 +414,47 @@ public: lst.push_back( * this); } - template< typename List > - void yield_link( List & lst) { - lst.push_back( * this); - } - template< typename Set > void sleep_link( Set & set) { set.insert( * this); } + template< typename List > + void terminated_link( List & lst) { + lst.push_back( * this); + } + template< typename List > void wait_link( List & lst) { lst.push_back( * this); } - void worker_unlink(); + template< typename List > + void worker_link( List & lst) { + lst.push_back( * this); + } + + template< typename List > + void yield_link( List & lst) { + lst.push_back( * this); + } void ready_unlink(); void remote_ready_unlink(); - void yield_unlink(); - void sleep_unlink(); void wait_unlink(); + void worker_unlink(); + + void yield_unlink(); + + void attach( context *); + + void detach( context *); + friend void intrusive_ptr_add_ref( context * ctx) { BOOST_ASSERT( nullptr != ctx); ++ctx->use_count_; diff --git a/include/boost/fiber/scheduler.hpp b/include/boost/fiber/scheduler.hpp index c98c80ac..283d248b 100644 --- a/include/boost/fiber/scheduler.hpp +++ b/include/boost/fiber/scheduler.hpp @@ -110,10 +110,6 @@ public: virtual ~scheduler() noexcept; - void set_main_context( context *) noexcept; - - void set_dispatcher_context( intrusive_ptr< context >) noexcept; - void dispatch(); void set_ready( context *) noexcept; @@ -131,6 +127,14 @@ public: bool has_ready_fibers() const noexcept; void set_sched_algo( std::unique_ptr< sched_algorithm >); + + void attach_main_context( context *) noexcept; + + void attach_dispatcher_context( intrusive_ptr< context >) noexcept; + + void attach_worker_context( context *) noexcept; + + void detach_worker_context( context *) noexcept; }; }} diff --git a/src/context.cpp b/src/context.cpp index abaa8f14..d95e0bda 100644 --- a/src/context.cpp +++ b/src/context.cpp @@ -26,6 +26,7 @@ context * context::active_; static intrusive_ptr< context > make_dispatcher_context( scheduler * sched) { + BOOST_ASSERT( nullptr != sched); fixedsize_stack salloc; // use default satck-size boost::context::stack_context sctx = salloc.allocate(); #if defined(BOOST_NO_CXX14_CONSTEXPR) || defined(BOOST_NO_CXX11_STD_ALIGN) @@ -71,9 +72,9 @@ context_initializer::context_initializer() { // scheduler of this thread scheduler * sched = new ( static_cast< char * >( vp) + sizeof( context) ) scheduler(); // attach main context to scheduler - sched->set_main_context( main_ctx); + sched->attach_main_context( main_ctx); // create and attach dispatcher context to scheduler - sched->set_dispatcher_context( + sched->attach_dispatcher_context( make_dispatcher_context( sched) ); // make main context to active context context::active_ = main_ctx; @@ -104,9 +105,9 @@ context_initializer::context_initializer() { // scheduler of this thread scheduler * sched = new ( vp1) scheduler(); // attach main context to scheduler - sched->set_main_context( main_ctx); + sched->attach_main_context( main_ctx); // create and attach dispatcher context to scheduler - sched->set_dispatcher_context( + sched->attach_dispatcher_context( make_dispatcher_context( sched) ); // make main context to active context context::active_ = main_ctx; @@ -165,13 +166,13 @@ context::context( main_context_t) : flags_( flag_main_context), scheduler_( nullptr), ctx_( boost::context::execution_context::current() ), - worker_hook_(), - terminated_hook_(), ready_hook_(), remote_ready_hook_(), - yield_hook_(), sleep_hook_(), + terminated_hook_(), wait_hook_(), + worker_hook_(), + yield_hook_(), tp_( (std::chrono::steady_clock::time_point::max)() ), fss_data_(), wait_queue_(), @@ -192,13 +193,13 @@ context::context( dispatcher_context_t, boost::context::preallocated const& pall // dispatcher context should never return from scheduler::dispatch() BOOST_ASSERT_MSG( false, "disatcher fiber already terminated"); }), - worker_hook_(), - terminated_hook_(), ready_hook_(), remote_ready_hook_(), - yield_hook_(), sleep_hook_(), + terminated_hook_(), wait_hook_(), + worker_hook_(), + yield_hook_(), tp_( (std::chrono::steady_clock::time_point::max)() ), fss_data_(), wait_queue_(), @@ -215,12 +216,6 @@ context::~context() { delete properties_; } -void -context::set_scheduler( scheduler * s) { - BOOST_ASSERT( nullptr != s); - scheduler_ = s; -} - scheduler * context::get_scheduler() const noexcept { return scheduler_; @@ -459,6 +454,20 @@ context::wait_unlink() { wait_hook_.unlink(); } +void +context::attach( context * ctx) { + BOOST_ASSERT( nullptr != ctx); + scheduler_->attach_worker_context( ctx); +} + +void +context::detach( context * ctx) { + BOOST_ASSERT( nullptr != ctx); + BOOST_ASSERT( scheduler_ == ctx->scheduler_); + scheduler_->detach_worker_context( ctx); + ctx->scheduler_ = nullptr; +} + }} #ifdef BOOST_HAS_ABI_HEADERS diff --git a/src/fiber.cpp b/src/fiber.cpp index 216c9d26..05007171 100644 --- a/src/fiber.cpp +++ b/src/fiber.cpp @@ -23,6 +23,7 @@ namespace fibers { void fiber::start_() { + context::active()->attach( impl_.get() ); context::active()->get_scheduler()->set_ready( impl_.get() ); } diff --git a/src/scheduler.cpp b/src/scheduler.cpp index 007a15f0..96d2cc0a 100644 --- a/src/scheduler.cpp +++ b/src/scheduler.cpp @@ -62,7 +62,7 @@ scheduler::get_next_() noexcept { ! ctx->worker_is_linked() && ! ctx->is_main_context() && ! ctx->is_dispatcher_context() ) { - ctx->worker_link( worker_queue_); + ctx->worker_link( worker_queue_); //FIXME } return ctx; } @@ -185,35 +185,6 @@ scheduler::~scheduler() noexcept { main_ctx_ = nullptr; } -void -scheduler::set_main_context( context * main_ctx) noexcept { - BOOST_ASSERT( nullptr != main_ctx); - // main-context represents the execution context created - // by the system, e.g. main()- or thread-context - // should not be in worker-queue - main_ctx_ = main_ctx; - main_ctx_->set_scheduler( this); -} - -void -scheduler::set_dispatcher_context( intrusive_ptr< context > dispatcher_ctx) noexcept { - BOOST_ASSERT( dispatcher_ctx); - // dispatcher context has to handle - // - remote ready context' - // - sleeping context' - // - extern event-loops - // - suspending the thread if ready-queue is empty (waiting on external event) - // should not be in worker-queue - dispatcher_ctx_.swap( dispatcher_ctx); - // add dispatcher-context to ready-queue - // so it is the first element in the ready-queue - // if the main context tries to suspend the first time - // the dispatcher-context is resumed and - // scheduler::dispatch() is executed - dispatcher_ctx_->set_scheduler( this); - sched_algo_->awakened( dispatcher_ctx_.get() ); -} - void scheduler::dispatch() { BOOST_ASSERT( context::active() == dispatcher_ctx_); @@ -273,6 +244,9 @@ scheduler::dispatch() { } } } + // release termianted context' + release_terminated_(); + // return to main-context resume_( dispatcher_ctx_.get(), main_ctx_); } @@ -290,9 +264,9 @@ scheduler::set_ready( context * ctx) noexcept { if ( ! ctx->is_main_context() ) { if ( ! ctx->worker_is_linked() ) { // attach context to `this`-scheduler - ctx->set_scheduler( this); + ctx->scheduler_ = this; // push to the worker-queue - ctx->worker_link( worker_queue_); + ctx->worker_link( worker_queue_); // FIXME } } else { // sanity checks, main-context might by signaled @@ -427,6 +401,62 @@ scheduler::set_sched_algo( std::unique_ptr< sched_algorithm > algo) { sched_algo_ = std::move( algo); } +void +scheduler::attach_main_context( context * main_ctx) noexcept { + BOOST_ASSERT( nullptr != main_ctx); + // main-context represents the execution context created + // by the system, e.g. main()- or thread-context + // should not be in worker-queue + main_ctx_ = main_ctx; + main_ctx_->scheduler_ = this; +} + +void +scheduler::attach_dispatcher_context( intrusive_ptr< context > dispatcher_ctx) noexcept { + BOOST_ASSERT( dispatcher_ctx); + // dispatcher context has to handle + // - remote ready context' + // - sleeping context' + // - extern event-loops + // - suspending the thread if ready-queue is empty (waiting on external event) + // should not be in worker-queue + dispatcher_ctx_.swap( dispatcher_ctx); + // add dispatcher-context to ready-queue + // so it is the first element in the ready-queue + // if the main context tries to suspend the first time + // the dispatcher-context is resumed and + // scheduler::dispatch() is executed + dispatcher_ctx_->scheduler_ = this; + sched_algo_->awakened( dispatcher_ctx_.get() ); +} + +void +scheduler::attach_worker_context( context * ctx) noexcept { + BOOST_ASSERT( nullptr != ctx); + BOOST_ASSERT( ! ctx->ready_is_linked() ); + BOOST_ASSERT( ! ctx->remote_ready_is_linked() ); + BOOST_ASSERT( ! ctx->sleep_is_linked() ); + BOOST_ASSERT( ! ctx->terminated_is_linked() ); + BOOST_ASSERT( ! ctx->wait_is_linked() ); + BOOST_ASSERT( ! ctx->worker_is_linked() ); + BOOST_ASSERT( ! ctx->yield_is_linked() ); + ctx->worker_link( worker_queue_); + ctx->scheduler_ = this; +} + +void +scheduler::detach_worker_context( context * ctx) noexcept { + BOOST_ASSERT( nullptr != ctx); + BOOST_ASSERT( ! ctx->ready_is_linked() ); + BOOST_ASSERT( ! ctx->remote_ready_is_linked() ); + BOOST_ASSERT( ! ctx->sleep_is_linked() ); + BOOST_ASSERT( ! ctx->terminated_is_linked() ); + BOOST_ASSERT( ! ctx->wait_is_linked() ); + BOOST_ASSERT( ! ctx->yield_is_linked() ); + BOOST_ASSERT( ! ctx->wait_is_linked() ); + ctx->worker_unlink(); +} + }} #ifdef BOOST_HAS_ABI_HEADERS