diff --git a/include/boost/fiber/context.hpp b/include/boost/fiber/context.hpp index d8733435..846eb9a5 100644 --- a/include/boost/fiber/context.hpp +++ b/include/boost/fiber/context.hpp @@ -18,7 +18,11 @@ #include #include #include -#include +#if (BOOST_EXECUTION_CONTEXT==1) +# include +#else +# include +#endif #include #include #include @@ -168,7 +172,7 @@ private: #if (BOOST_EXECUTION_CONTEXT==1) boost::context::execution_context ctx_; #else - boost::context::execution_context< detail::data_t * > ctx_; + boost::context::continuation c_; #endif void resume_( detail::data_t &) noexcept; @@ -186,6 +190,7 @@ private: } else if ( nullptr != dp->ctx) { active()->set_ready_( dp->ctx); } + // FIXME: use std::apply() if available boost::context::detail::apply( std::move( fn), std::move( tpl) ); } // terminate context @@ -194,19 +199,22 @@ private: } #else template< typename Fn, typename Tpl > - boost::context::execution_context< detail::data_t * > - run_( boost::context::execution_context< detail::data_t * > && ctx, Fn && fn_, Tpl && tpl_, detail::data_t * dp) noexcept { + boost::context::continuation + run_( boost::context::continuation && c, Fn && fn_, Tpl && tpl_) noexcept { { // fn and tpl must be destroyed before calling set_terminated() typename std::decay< Fn >::type fn = std::forward< Fn >( fn_); typename std::decay< Tpl >::type tpl = std::forward< Tpl >( tpl_); - // update execution_context of calling fiber - dp->from->ctx_ = std::move( ctx); + c = boost::context::resume( std::move( c) ); + detail::data_t * dp = boost::context::transfer_data< detail::data_t * >( c); + // update contiunation of calling fiber + dp->from->c_ = std::move( c); if ( nullptr != dp->lk) { dp->lk->unlock(); } else if ( nullptr != dp->ctx) { active()->set_ready_( dp->ctx); } + // FIXME: use std::apply() if available boost::context::detail::apply( std::move( fn), std::move( tpl) ); } // terminate context @@ -333,26 +341,30 @@ public: run_( std::move( fn), std::move( tpl), static_cast< detail::data_t * >( vp) ); }} # endif + {} #else + c_{} + { # if defined(BOOST_NO_CXX14_GENERIC_LAMBDAS) - ctx_{ std::allocator_arg, palloc, salloc, - detail::wrap( - [this]( typename std::decay< Fn >::type & fn, typename std::decay< Tpl >::type & tpl, - boost::context::execution_context< detail::data_t * > && ctx, detail::data_t * dp) mutable noexcept { - return run_( std::forward< boost::context::execution_context< detail::data_t * > >( ctx), std::move( fn), std::move( tpl), dp); - }, - std::forward< Fn >( fn), - std::forward< Tpl >( tpl) )} - + c_ = boost::context::callcc( + std::allocator_arg, palloc, salloc, + detail::wrap( + [this]( typename std::decay< Fn >::type & fn, typename std::decay< Tpl >::type & tpl, + boost::context::continuation && c) mutable noexcept { + return run_( std::forward< boost::context::continuation >( c), std::move( fn), std::move( tpl) ); + }, + std::forward< Fn >( fn), + std::forward< Tpl >( tpl) ) ); # else - ctx_{ std::allocator_arg, palloc, salloc, - [this,fn=detail::decay_copy( std::forward< Fn >( fn) ),tpl=std::forward< Tpl >( tpl)] - (boost::context::execution_context< detail::data_t * > && ctx, detail::data_t * dp) mutable noexcept { - return run_( std::forward< boost::context::execution_context< detail::data_t * > >( ctx), std::move( fn), std::move( tpl), dp); - }} + c_ = boost::context::callcc( + std::allocator_arg, palloc, salloc, + [this,fn=detail::decay_copy( std::forward< Fn >( fn) ),tpl=std::forward< Tpl >( tpl)] + (boost::context::continuation && c) mutable noexcept { + return run_( std::forward< boost::context::continuation >( c), std::move( fn), std::move( tpl) ); + }); # endif + } #endif - {} context( context const&) = delete; context & operator=( context const&) = delete; @@ -379,8 +391,8 @@ public: #if (BOOST_EXECUTION_CONTEXT==1) void set_terminated() noexcept; #else - boost::context::execution_context< detail::data_t * > suspend_with_cc() noexcept; - boost::context::execution_context< detail::data_t * > set_terminated() noexcept; + boost::context::continuation suspend_with_cc() noexcept; + boost::context::continuation set_terminated() noexcept; #endif void join(); @@ -484,11 +496,11 @@ public: // deallocates stack (execution_context is ref counted) ctx->~context(); #else - boost::context::execution_context< detail::data_t * > cc( std::move( ctx->ctx_) ); + boost::context::continuation cc( std::move( ctx->c_) ); // destruct context ctx->~context(); // deallocated stack - cc( nullptr); + boost::context::resume( std::move( cc), nullptr); #endif } } diff --git a/include/boost/fiber/detail/wrap.hpp b/include/boost/fiber/detail/wrap.hpp index 0369e61e..6cab305d 100644 --- a/include/boost/fiber/detail/wrap.hpp +++ b/include/boost/fiber/detail/wrap.hpp @@ -11,7 +11,11 @@ #include #include -#include +#if (BOOST_EXECUTION_CONTEXT==1) +# include +#else +# include +#endif #include #include @@ -86,14 +90,13 @@ public: wrapper( wrapper && other) = default; wrapper & operator=( wrapper && other) = default; - boost::context::execution_context< data_t * > - operator()( boost::context::execution_context< data_t * > && ctx, data_t * dp) { + boost::context::continuation + operator()( boost::context::continuation && c) { return boost::context::detail::invoke( std::move( fn1_), fn2_, tpl_, - std::forward< boost::context::execution_context< data_t * > >( ctx), - dp); + std::forward< boost::context::continuation >( c) ); } }; diff --git a/include/boost/fiber/scheduler.hpp b/include/boost/fiber/scheduler.hpp index 4503bc0b..b0e4d4a6 100644 --- a/include/boost/fiber/scheduler.hpp +++ b/include/boost/fiber/scheduler.hpp @@ -13,7 +13,11 @@ #include #include -#include +#if (BOOST_EXECUTION_CONTEXT==1) +# include +#else +# include +#endif #include #include #include @@ -116,9 +120,9 @@ public: void set_terminated( context *) noexcept; #else - boost::context::execution_context< detail::data_t * > dispatch() noexcept; + boost::context::continuation dispatch() noexcept; - boost::context::execution_context< detail::data_t * > set_terminated( context *) noexcept; + boost::context::continuation set_terminated( context *) noexcept; #endif void yield( context *) noexcept; diff --git a/include/boost/fiber/type.hpp b/include/boost/fiber/type.hpp index 93462658..065d22fc 100644 --- a/include/boost/fiber/type.hpp +++ b/include/boost/fiber/type.hpp @@ -18,7 +18,6 @@ #include #include #include -#include #include #include #include diff --git a/src/context.cpp b/src/context.cpp index 2a131e99..041c7b6f 100644 --- a/src/context.cpp +++ b/src/context.cpp @@ -162,10 +162,10 @@ context::resume_( detail::data_t & d) noexcept { #else void context::resume_( detail::data_t & d) noexcept { - auto result = ctx_( & d); - detail::data_t * dp( std::get< 1 >( result) ); + boost::context::continuation c = boost::context::resume( std::move( c_), & d); + detail::data_t * dp = boost::context::transfer_data< detail::data_t * >( c); if ( nullptr != dp) { - dp->from->ctx_ = std::move( std::get< 0 >( result) ); + dp->from->c_ = std::move( c); if ( nullptr != dp->lk) { dp->lk->unlock(); } else if ( nullptr != dp->ctx) { @@ -188,7 +188,7 @@ context::context( main_context_t) noexcept : #if (BOOST_EXECUTION_CONTEXT==1) ctx_{ boost::context::execution_context::current() } { #else - ctx_{} { + c_{} { #endif } @@ -211,21 +211,28 @@ 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"); }} -#else - ctx_{ std::allocator_arg, palloc, salloc, - [this,sched](boost::context::execution_context< detail::data_t * > ctx, detail::data_t * dp) noexcept { - // update execution_context of calling fiber - dp->from->ctx_ = std::move( ctx); - if ( nullptr != dp->lk) { - dp->lk->unlock(); - } else if ( nullptr != dp->ctx) { - context_initializer::active_->set_ready_( dp->ctx); - } - // execute scheduler::dispatch() - return sched->dispatch(); - }} -#endif {} +#else + c_{} +{ + c_ = boost::context::callcc( + std::allocator_arg, palloc, salloc, + [this,sched](boost::context::continuation && c) noexcept { + c = boost::context::resume( std::move( c) ); + detail::data_t * dp = boost::context::transfer_data< detail::data_t * >( c); + // update continuation of calling fiber + dp->from->c_ = std::move( c); + if ( nullptr != dp->lk) { + dp->lk->unlock(); + } else if ( nullptr != dp->ctx) { + context_initializer::active_->set_ready_( dp->ctx); + } + // execute scheduler::dispatch() + return sched->dispatch(); + }); + +} +#endif context::~context() { BOOST_ASSERT( wait_queue_.empty() ); @@ -320,24 +327,8 @@ context::yield() noexcept { get_scheduler()->yield( context::active() ); } -#if (BOOST_EXECUTION_CONTEXT>1) -boost::context::execution_context< detail::data_t * > -context::suspend_with_cc() noexcept { - context * prev = this; - // context_initializer::active_ will point to `this` - // prev will point to previous active context - std::swap( context_initializer::active_, prev); - detail::data_t d{ prev }; - // context switch - return std::move( std::get< 0 >( ctx_( & d) ) ); -} -#endif - #if (BOOST_EXECUTION_CONTEXT==1) void -#else -boost::context::execution_context< detail::data_t * > -#endif context::set_terminated() noexcept { // protect for concurrent access std::unique_lock< detail::spinlock > lk( splk_); @@ -358,9 +349,41 @@ context::set_terminated() noexcept { } fss_data_.clear(); // switch to another context -#if (BOOST_EXECUTION_CONTEXT==1) get_scheduler()->set_terminated( this); +} #else +boost::context::continuation +context::suspend_with_cc() noexcept { + context * prev = this; + // context_initializer::active_ will point to `this` + // prev will point to previous active context + std::swap( context_initializer::active_, prev); + detail::data_t d{ prev }; + // context switch + return boost::context::resume( std::move( c_), & d); +} + +boost::context::continuation +context::set_terminated() noexcept { + // protect for concurrent access + std::unique_lock< detail::spinlock > lk( splk_); + // mark as terminated + flags_ |= flag_terminated; + // notify all waiting fibers + while ( ! wait_queue_.empty() ) { + context * ctx = & wait_queue_.front(); + // remove fiber from wait-queue + wait_queue_.pop_front(); + // notify scheduler + set_ready( ctx); + } + lk.unlock(); + // release fiber-specific-data + for ( fss_data_t::value_type & data : fss_data_) { + data.second.do_cleanup(); + } + fss_data_.clear(); + // switch to another context return get_scheduler()->set_terminated( this); #endif } diff --git a/src/scheduler.cpp b/src/scheduler.cpp index 88433012..95578075 100644 --- a/src/scheduler.cpp +++ b/src/scheduler.cpp @@ -127,10 +127,6 @@ scheduler::~scheduler() { #if (BOOST_EXECUTION_CONTEXT==1) void scheduler::dispatch() noexcept { -#else -boost::context::execution_context< detail::data_t * > -scheduler::dispatch() noexcept { -#endif BOOST_ASSERT( context::active() == dispatcher_ctx_); for (;;) { bool no_worker = worker_queue_.empty(); @@ -173,12 +169,56 @@ scheduler::dispatch() noexcept { // release termianted context' release_terminated_(); // return to main-context -#if (BOOST_EXECUTION_CONTEXT==1) main_ctx_->resume(); -#else - return main_ctx_->suspend_with_cc(); -#endif } +#else +boost::context::continuation +scheduler::dispatch() noexcept { + BOOST_ASSERT( context::active() == dispatcher_ctx_); + for (;;) { + bool no_worker = worker_queue_.empty(); + if ( shutdown_) { + // notify sched-algorithm about termination + algo_->notify(); + if ( no_worker) { + break; + } + } + // release terminated context' + release_terminated_(); +#if ! defined(BOOST_FIBERS_NO_ATOMICS) + // get context' from remote ready-queue + remote_ready2ready_(); +#endif + // get sleeping context' + sleep2ready_(); + // get next ready context + context * ctx = get_next_(); + if ( nullptr != ctx) { + // push dispatcher-context to ready-queue + // so that ready-queue never becomes empty + ctx->resume( dispatcher_ctx_.get() ); + BOOST_ASSERT( context::active() == dispatcher_ctx_.get() ); + } else { + // no ready context, wait till signaled + // set deadline to highest value + std::chrono::steady_clock::time_point suspend_time = + (std::chrono::steady_clock::time_point::max)(); + // get lowest deadline from sleep-queue + sleep_queue_t::iterator i = sleep_queue_.begin(); + if ( sleep_queue_.end() != i) { + suspend_time = i->tp_; + } + // no ready context, wait till signaled + algo_->suspend_until( suspend_time); + } + } + // release termianted context' + release_terminated_(); + // return to main-context + return main_ctx_->suspend_with_cc(); +} +#endif void scheduler::set_ready( context * ctx) noexcept { @@ -222,10 +262,6 @@ scheduler::set_remote_ready( context * ctx) noexcept { #if (BOOST_EXECUTION_CONTEXT==1) void scheduler::set_terminated( context * active_ctx) noexcept { -#else -boost::context::execution_context< detail::data_t * > -scheduler::set_terminated( context * active_ctx) noexcept { -#endif BOOST_ASSERT( nullptr != active_ctx); BOOST_ASSERT( context::active() == active_ctx); BOOST_ASSERT( ! active_ctx->is_context( type::main_context) ); @@ -240,12 +276,28 @@ scheduler::set_terminated( context * active_ctx) noexcept { // intrusive_ptr_release( ctx); active_ctx->terminated_link( terminated_queue_); // resume another fiber -#if (BOOST_EXECUTION_CONTEXT==1) get_next_()->resume(); -#else - return get_next_()->suspend_with_cc(); -#endif } +#else +boost::context::continuation +scheduler::set_terminated( context * active_ctx) noexcept { + BOOST_ASSERT( nullptr != active_ctx); + BOOST_ASSERT( context::active() == active_ctx); + BOOST_ASSERT( ! active_ctx->is_context( type::main_context) ); + BOOST_ASSERT( ! active_ctx->is_context( type::dispatcher_context) ); + //BOOST_ASSERT( active_ctx->worker_is_linked() ); + BOOST_ASSERT( active_ctx->is_terminated() ); + BOOST_ASSERT( ! active_ctx->ready_is_linked() ); + BOOST_ASSERT( ! active_ctx->sleep_is_linked() ); + BOOST_ASSERT( ! active_ctx->wait_is_linked() ); + // store the terminated fiber in the terminated-queue + // the dispatcher-context will call + // intrusive_ptr_release( ctx); + active_ctx->terminated_link( terminated_queue_); + // resume another fiber + return get_next_()->suspend_with_cc(); +} +#endif void scheduler::yield( context * active_ctx) noexcept {