diff options
Diffstat (limited to 'boost/fiber/context.hpp')
-rw-r--r-- | boost/fiber/context.hpp | 267 |
1 files changed, 156 insertions, 111 deletions
diff --git a/boost/fiber/context.hpp b/boost/fiber/context.hpp index d873343538..773528e3a1 100644 --- a/boost/fiber/context.hpp +++ b/boost/fiber/context.hpp @@ -7,23 +7,32 @@ #ifndef BOOST_FIBERS_CONTEXT_H #define BOOST_FIBERS_CONTEXT_H +#include <iostream> #include <atomic> #include <chrono> #include <exception> #include <functional> #include <map> #include <memory> +#include <tuple> #include <type_traits> #include <boost/assert.hpp> #include <boost/config.hpp> +#if defined(BOOST_NO_CXX17_STD_APPLY) #include <boost/context/detail/apply.hpp> -#include <boost/context/execution_context.hpp> +#endif +#if (BOOST_EXECUTION_CONTEXT==1) +# include <boost/context/execution_context.hpp> +#else +# include <boost/context/continuation.hpp> +#endif #include <boost/context/stack_context.hpp> #include <boost/intrusive/list.hpp> #include <boost/intrusive/parent_from_member.hpp> #include <boost/intrusive_ptr.hpp> #include <boost/intrusive/set.hpp> +#include <boost/intrusive/slist.hpp> #include <boost/fiber/detail/config.hpp> #include <boost/fiber/detail/data.hpp> @@ -57,10 +66,10 @@ class scheduler; namespace detail { struct wait_tag; -typedef intrusive::list_member_hook< +typedef intrusive::slist_member_hook< intrusive::tag< wait_tag >, intrusive::link_mode< - intrusive::auto_unlink + intrusive::safe_link > > wait_hook; // declaration of the functor that converts between @@ -97,21 +106,29 @@ typedef intrusive::set_member_hook< > > sleep_hook; -struct terminated_tag; +struct worker_tag; typedef intrusive::list_member_hook< - intrusive::tag< terminated_tag >, + intrusive::tag< worker_tag >, intrusive::link_mode< intrusive::auto_unlink > +> worker_hook; + +struct terminated_tag; +typedef intrusive::slist_member_hook< + intrusive::tag< terminated_tag >, + intrusive::link_mode< + intrusive::safe_link + > > terminated_hook; -struct worker_tag; -typedef intrusive::list_member_hook< - intrusive::tag< worker_tag >, +struct remote_ready_tag; +typedef intrusive::slist_member_hook< + intrusive::tag< remote_ready_tag >, intrusive::link_mode< - intrusive::auto_unlink + intrusive::safe_link > -> worker_hook; +> remote_ready_hook; } @@ -125,13 +142,17 @@ struct worker_context_t {}; const worker_context_t worker_context{}; class BOOST_FIBERS_DECL context { +public: + typedef intrusive::slist< + context, + intrusive::function_hook< detail::wait_functor >, + intrusive::linear< true >, + intrusive::cache_last< true > + > wait_queue_t; + private: friend class scheduler; - enum flag_t { - flag_terminated = 1 << 1 - }; - struct fss_data { void * vp{ nullptr }; detail::fss_cleanup_function::ptr_t cleanup_function{}; @@ -151,100 +172,102 @@ private: } }; - typedef std::map< uintptr_t, fss_data > fss_data_t; + typedef std::map< uintptr_t, fss_data > fss_data_t; #if ! defined(BOOST_FIBERS_NO_ATOMICS) - std::atomic< std::size_t > use_count_{ 0 }; - std::atomic< unsigned int > flags_; - std::atomic< type > type_; - std::atomic< scheduler * > scheduler_{ nullptr }; + alignas(cache_alignment) std::atomic< std::size_t > use_count_{ 0 }; #else - std::size_t use_count_{ 0 }; - unsigned int flags_; - type type_; - scheduler * scheduler_{ nullptr }; + alignas(cache_alignment) std::size_t use_count_{ 0 }; +#endif +#if ! defined(BOOST_FIBERS_NO_ATOMICS) + alignas(cache_alignment) detail::remote_ready_hook remote_ready_hook_{}; + std::atomic< context * > remote_nxt_{ nullptr }; #endif - launch policy_{ launch::post }; + alignas(cache_alignment) detail::spinlock splk_{}; + bool terminated_{ false }; + wait_queue_t wait_queue_{}; +public: + detail::wait_hook wait_hook_{}; +private: + alignas(cache_alignment) scheduler * scheduler_{ nullptr }; + fss_data_t fss_data_{}; + detail::sleep_hook sleep_hook_{}; + detail::ready_hook ready_hook_{}; + detail::terminated_hook terminated_hook_{}; + detail::worker_hook worker_hook_{}; #if (BOOST_EXECUTION_CONTEXT==1) - boost::context::execution_context ctx_; + boost::context::execution_context ctx_; #else - boost::context::execution_context< detail::data_t * > ctx_; + boost::context::continuation c_; #endif + fiber_properties * properties_{ nullptr }; + std::chrono::steady_clock::time_point tp_{ (std::chrono::steady_clock::time_point::max)() }; + type type_; + launch policy_; void resume_( detail::data_t &) noexcept; - void set_ready_( context *) noexcept; + void schedule_( context *) noexcept; #if (BOOST_EXECUTION_CONTEXT==1) template< typename Fn, typename Tpl > void run_( Fn && fn_, Tpl && tpl_, detail::data_t * dp) noexcept { { - // fn and tpl must be destroyed before calling set_terminated() + // fn and tpl must be destroyed before calling terminate() typename std::decay< Fn >::type fn = std::forward< Fn >( fn_); typename std::decay< Tpl >::type tpl = std::forward< Tpl >( tpl_); if ( nullptr != dp->lk) { dp->lk->unlock(); } else if ( nullptr != dp->ctx) { - active()->set_ready_( dp->ctx); + active()->schedule_( dp->ctx); } +#if defined(BOOST_NO_CXX17_STD_APPLY) boost::context::detail::apply( std::move( fn), std::move( tpl) ); +#else + std::apply( std::move( fn), std::move( tpl) ); +#endif } // terminate context - set_terminated(); + terminate(); BOOST_ASSERT_MSG( false, "fiber already terminated"); } #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() + // fn and tpl must be destroyed before calling terminate() 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 = c.resume(); + detail::data_t * dp = c.get_data< detail::data_t * >(); + // 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); + active()->schedule_( dp->ctx); } +#if defined(BOOST_NO_CXX17_STD_APPLY) boost::context::detail::apply( std::move( fn), std::move( tpl) ); +#else + std::apply( std::move( fn), std::move( tpl) ); +#endif } // terminate context - return set_terminated(); + return terminate(); } #endif public: - detail::ready_hook ready_hook_{}; - detail::sleep_hook sleep_hook_{}; - detail::terminated_hook terminated_hook_{}; - detail::wait_hook wait_hook_{}; - detail::worker_hook worker_hook_{}; - std::atomic< context * > remote_nxt_{ nullptr }; - std::chrono::steady_clock::time_point tp_{ (std::chrono::steady_clock::time_point::max)() }; - - typedef intrusive::list< - context, - intrusive::function_hook< detail::wait_functor >, - intrusive::constant_time_size< false > > wait_queue_t; - -private: - fss_data_t fss_data_{}; - wait_queue_t wait_queue_{}; - detail::spinlock splk_{}; - fiber_properties * properties_{ nullptr }; - -public: class id { private: context * impl_{ nullptr }; public: - id() noexcept { - } + id() = default; explicit id( context * impl) noexcept : - impl_( impl) { + impl_{ impl } { } bool operator==( id const& other) const noexcept { @@ -311,9 +334,6 @@ public: boost::context::preallocated palloc, StackAlloc salloc, Fn && fn, Tpl && tpl) : use_count_{ 1 }, // fiber instance or scheduler owner - flags_{ 0 }, - type_{ type::worker_context }, - policy_{ policy }, #if (BOOST_EXECUTION_CONTEXT==1) # if defined(BOOST_NO_CXX14_GENERIC_LAMBDAS) ctx_{ std::allocator_arg, palloc, salloc, @@ -325,50 +345,66 @@ public: std::forward< Fn >( fn), std::forward< Tpl >( tpl), boost::context::execution_context::current() ) - } + }, + type_{ type::worker_context }, + policy_{ policy } # else ctx_{ std::allocator_arg, palloc, salloc, [this,fn=detail::decay_copy( std::forward< Fn >( fn) ),tpl=std::forward< Tpl >( tpl), ctx=boost::context::execution_context::current()] (void * vp) mutable noexcept { run_( std::move( fn), std::move( tpl), static_cast< detail::data_t * >( vp) ); - }} + }}, + type_{ type::worker_context }, + policy_{ policy } # endif + {} #else + c_{}, + type_{ type::worker_context }, + policy_{ policy } + { # 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; + friend bool + operator==( context const& lhs, context const& rhs) noexcept { + return & lhs == & rhs; + } + virtual ~context(); scheduler * get_scheduler() const noexcept { -#if ! defined(BOOST_FIBERS_NO_ATOMICS) - return scheduler_.load( std::memory_order_relaxed); -#else return scheduler_; -#endif } id get_id() const noexcept; + bool is_resumable() const noexcept { + if ( c_) return true; + else return false; + } + void resume() noexcept; void resume( detail::spinlock_lock &) noexcept; void resume( context *) noexcept; @@ -377,10 +413,10 @@ public: void suspend( detail::spinlock_lock &) noexcept; #if (BOOST_EXECUTION_CONTEXT==1) - void set_terminated() noexcept; + void terminate() 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 terminate() noexcept; #endif void join(); @@ -390,16 +426,12 @@ public: bool wait_until( std::chrono::steady_clock::time_point const&, detail::spinlock_lock &) noexcept; - void set_ready( context *) noexcept; + void schedule( context *) noexcept; bool is_context( type t) const noexcept { return type::none != ( type_ & t); } - bool is_terminated() const noexcept { - return 0 != ( flags_ & flag_terminated); - } - void * get_fss_data( void const * vp) const; void set_fss_data( @@ -418,77 +450,90 @@ public: return policy_; } + bool worker_is_linked() const noexcept; + bool ready_is_linked() const noexcept; + bool remote_ready_is_linked() const noexcept; + bool sleep_is_linked() const noexcept; bool terminated_is_linked() const noexcept; bool wait_is_linked() const noexcept; - bool worker_is_linked() const noexcept; + template< typename List > + void worker_link( List & lst) noexcept { + static_assert( std::is_same< typename List::value_traits::hook_type, detail::worker_hook >::value, "not a worker-queue"); + BOOST_ASSERT( ! worker_is_linked() ); + lst.push_back( * this); + } template< typename List > void ready_link( List & lst) noexcept { static_assert( std::is_same< typename List::value_traits::hook_type, detail::ready_hook >::value, "not a ready-queue"); + BOOST_ASSERT( ! ready_is_linked() ); + lst.push_back( * this); + } + + template< typename List > + void remote_ready_link( List & lst) noexcept { + static_assert( std::is_same< typename List::value_traits::hook_type, detail::remote_ready_hook >::value, "not a remote-ready-queue"); + BOOST_ASSERT( ! remote_ready_is_linked() ); lst.push_back( * this); } template< typename Set > void sleep_link( Set & set) noexcept { static_assert( std::is_same< typename Set::value_traits::hook_type,detail::sleep_hook >::value, "not a sleep-queue"); + BOOST_ASSERT( ! sleep_is_linked() ); set.insert( * this); } template< typename List > void terminated_link( List & lst) noexcept { static_assert( std::is_same< typename List::value_traits::hook_type, detail::terminated_hook >::value, "not a terminated-queue"); + BOOST_ASSERT( ! terminated_is_linked() ); lst.push_back( * this); } template< typename List > void wait_link( List & lst) noexcept { static_assert( std::is_same< typename List::value_traits::hook_type, detail::wait_hook >::value, "not a wait-queue"); + BOOST_ASSERT( ! wait_is_linked() ); lst.push_back( * this); } - template< typename List > - void worker_link( List & lst) noexcept { - static_assert( std::is_same< typename List::value_traits::hook_type, detail::worker_hook >::value, "not a worker-queue"); - lst.push_back( * this); - } + void worker_unlink() noexcept; void ready_unlink() noexcept; void sleep_unlink() noexcept; - void wait_unlink() noexcept; - - void worker_unlink() noexcept; - void detach() noexcept; void attach( context *) noexcept; friend void intrusive_ptr_add_ref( context * ctx) noexcept { BOOST_ASSERT( nullptr != ctx); - ++ctx->use_count_; + ctx->use_count_.fetch_add( 1, std::memory_order_relaxed); } friend void intrusive_ptr_release( context * ctx) noexcept { BOOST_ASSERT( nullptr != ctx); - if ( 0 == --ctx->use_count_) { + if ( 1 == ctx->use_count_.fetch_sub( 1, std::memory_order_release) ) { + std::atomic_thread_fence( std::memory_order_acquire); #if (BOOST_EXECUTION_CONTEXT==1) - boost::context::execution_context ec( ctx->ctx_); + boost::context::execution_context ec = ctx->ctx_; // destruct context // 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 c = std::move( ctx->c_); // destruct context ctx->~context(); // deallocated stack - cc( nullptr); + c.resume( nullptr); #endif } } @@ -521,14 +566,14 @@ static intrusive_ptr< context > make_worker_context( launch policy, const std::size_t size = sctx.size - ( static_cast< char * >( sctx.sp) - static_cast< char * >( sp) ); #endif // placement new of context on top of fiber's stack - return intrusive_ptr< context >( - ::new ( sp) context( + return intrusive_ptr< context >{ + ::new ( sp) context{ worker_context, policy, - boost::context::preallocated( sp, size, sctx), + boost::context::preallocated{ sp, size, sctx }, salloc, std::forward< Fn >( fn), - std::make_tuple( std::forward< Args >( args) ... ) ) ); + std::make_tuple( std::forward< Args >( args) ... ) } }; } namespace detail { |