diff options
Diffstat (limited to 'boost/context/execution_context.ipp')
-rw-r--r-- | boost/context/execution_context.ipp | 470 |
1 files changed, 274 insertions, 196 deletions
diff --git a/boost/context/execution_context.ipp b/boost/context/execution_context.ipp index 8832d75c20..ec5443297f 100644 --- a/boost/context/execution_context.ipp +++ b/boost/context/execution_context.ipp @@ -12,11 +12,13 @@ #if ! defined(BOOST_CONTEXT_NO_EXECUTION_CONTEXT) # include <algorithm> +# include <atomic> # include <cstddef> # include <cstdint> # include <cstdlib> # include <functional> # include <memory> +# include <ostream> # include <tuple> # include <utility> @@ -26,6 +28,7 @@ # include <boost/intrusive_ptr.hpp> # include <boost/context/detail/invoke.hpp> +# include <boost/context/fixedsize_stack.hpp> # include <boost/context/stack_context.hpp> # include <boost/context/segmented_stack.hpp> @@ -45,142 +48,148 @@ void __splitstack_setcontext( void * [BOOST_CONTEXT_SEGMENTS]); namespace boost { namespace context { +namespace detail { -struct preallocated { - void * sp; - std::size_t size; - stack_context sctx; +struct activation_record { + typedef boost::intrusive_ptr< activation_record > ptr_t; - preallocated( void * sp_, std::size_t size_, stack_context sctx_) noexcept : - sp( sp_), size( size_), sctx( sctx_) { - } -}; + enum flag_t { + flag_main_ctx = 1 << 1, + flag_preserve_fpu = 1 << 2 + }; -class BOOST_CONTEXT_DECL execution_context { -private: - struct activation_record { - typedef boost::intrusive_ptr< activation_record > ptr_t; - - enum flag_t { - flag_main_ctx = 1 << 1, - flag_preserve_fpu = 1 << 2, - flag_segmented_stack = 1 << 3 - }; - - thread_local static activation_record toplevel_rec; - thread_local static ptr_t current_rec; - - std::size_t use_count; - fcontext_t fctx; - stack_context sctx; - int flags; - - // used for toplevel-context - // (e.g. main context, thread-entry context) - activation_record() noexcept : - use_count( 1), - fctx( nullptr), - sctx(), - flags( flag_main_ctx) { - } - - activation_record( fcontext_t fctx_, stack_context sctx_, bool use_segmented_stack) noexcept : - use_count( 0), - fctx( fctx_), - sctx( sctx_), - flags( use_segmented_stack ? flag_segmented_stack : 0) { - } - - virtual ~activation_record() noexcept = default; - - void resume( bool fpu = false) noexcept { - // store current activation record in local variable - activation_record * from = current_rec.get(); - // store `this` in static, thread local pointer - // `this` will become the active (running) context - // returned by execution_context::current() - current_rec = this; - // set FPU flag - if (fpu) { - from->flags |= flag_preserve_fpu; - this->flags |= flag_preserve_fpu; - } else { - from->flags &= ~flag_preserve_fpu; - this->flags &= ~flag_preserve_fpu; - } + thread_local static ptr_t current_rec; + + std::atomic< std::size_t > use_count; + fcontext_t fctx; + stack_context sctx; + int flags; + + // used for toplevel-context + // (e.g. main context, thread-entry context) + activation_record() noexcept : + use_count( 0), + fctx( nullptr), + sctx(), + flags( flag_main_ctx) { + } + + activation_record( fcontext_t fctx_, stack_context sctx_) noexcept : + use_count( 0), + fctx( fctx_), + sctx( sctx_), + flags( 0) { + } + + virtual ~activation_record() noexcept = default; + + void * resume( void * vp, bool fpu) noexcept { + // store current activation record in local variable + activation_record * from = current_rec.get(); + // store `this` in static, thread local pointer + // `this` will become the active (running) context + // returned by execution_context::current() + current_rec = this; + // set FPU flag + if (fpu) { + from->flags |= flag_preserve_fpu; + this->flags |= flag_preserve_fpu; + } else { + from->flags &= ~flag_preserve_fpu; + this->flags &= ~flag_preserve_fpu; + } # if defined(BOOST_USE_SEGMENTED_STACKS) - if ( 0 != (flags & flag_segmented_stack) ) { - // adjust segmented stack properties - __splitstack_getcontext( from->sctx.segments_ctx); - __splitstack_setcontext( sctx.segments_ctx); - // context switch from parent context to `this`-context - jump_fcontext( & from->fctx, fctx, reinterpret_cast< intptr_t >( this), fpu); - // parent context resumed - // adjust segmented stack properties - __splitstack_setcontext( from->sctx.segments_ctx); - } else { - // context switch from parent context to `this`-context - jump_fcontext( & from->fctx, fctx, reinterpret_cast< intptr_t >( this), fpu); - // parent context resumed - } -# else - // context switch from parent context to `this`-context - jump_fcontext( & from->fctx, fctx, reinterpret_cast< intptr_t >( this), fpu); - // parent context resumed + // adjust segmented stack properties + __splitstack_getcontext( from->sctx.segments_ctx); + __splitstack_setcontext( sctx.segments_ctx); # endif - } + // context switch from parent context to `this`-context + intptr_t ret = jump_fcontext( & from->fctx, fctx, reinterpret_cast< intptr_t >( vp), fpu); + // parent context resumed + return reinterpret_cast< void * >( ret); + } - virtual void deallocate() {} + virtual void deallocate() { + delete this; + } - friend void intrusive_ptr_add_ref( activation_record * ar) { - ++ar->use_count; - } + friend void intrusive_ptr_add_ref( activation_record * ar) { + ++ar->use_count; + } - friend void intrusive_ptr_release( activation_record * ar) { - BOOST_ASSERT( nullptr != ar); + friend void intrusive_ptr_release( activation_record * ar) { + BOOST_ASSERT( nullptr != ar); - if ( 0 == --ar->use_count) { - ar->deallocate(); - } + if ( 0 == --ar->use_count) { + ar->deallocate(); } - }; + } +}; - template< typename Fn, typename StackAlloc > - class capture_record : public activation_record { - private: - StackAlloc salloc_; - Fn fn_; - - static void destroy( capture_record * p) { - StackAlloc salloc( p->salloc_); - stack_context sctx( p->sctx); - // deallocate activation record - p->~capture_record(); - // destroy stack with stack allocator - salloc.deallocate( sctx); - } +struct activation_record_initializer { + activation_record_initializer(); + ~activation_record_initializer(); +}; - public: - explicit capture_record( stack_context sctx, StackAlloc const& salloc, fcontext_t fctx, Fn && fn, bool use_segmented_stack) noexcept : - activation_record( fctx, sctx, use_segmented_stack), - salloc_( salloc), - fn_( std::forward< Fn >( fn) ) { - } +template< typename Fn, typename Tpl, typename StackAlloc > +class capture_record : public activation_record { +private: + StackAlloc salloc_; + Fn fn_; + Tpl tpl_; + activation_record * caller_; + + static void destroy( capture_record * p) { + StackAlloc salloc( p->salloc_); + stack_context sctx( p->sctx); + // deallocate activation record + p->~capture_record(); + // destroy stack with stack allocator + salloc.deallocate( sctx); + } - void deallocate() override final { - destroy( this); - } +public: + explicit capture_record( + stack_context sctx, StackAlloc const& salloc, + fcontext_t fctx, + Fn && fn, Tpl && tpl, + activation_record * caller) noexcept : + activation_record( fctx, sctx), + salloc_( salloc), + fn_( std::forward< Fn >( fn) ), + tpl_( std::forward< Tpl >( tpl) ), + caller_( caller) { + } + + void deallocate() override final { + destroy( this); + } - void run() noexcept { - try { - fn_(); - } catch (...) { - std::terminate(); - } - BOOST_ASSERT( 0 == (flags & flag_main_ctx) ); + void run() noexcept { + try { + void * vp = caller_->resume( caller_, true); + do_invoke( fn_, std::tuple_cat( tpl_, std::tie( vp) ) ); + } catch (...) { + std::terminate(); } - }; + BOOST_ASSERT( 0 == (flags & flag_main_ctx) ); + } +}; + +} + +struct preallocated { + void * sp; + std::size_t size; + stack_context sctx; + preallocated( void * sp_, std::size_t size_, stack_context sctx_) noexcept : + sp( sp_), size( size_), sctx( sctx_) { + } +}; + +class BOOST_CONTEXT_DECL execution_context { +private: // tampoline function // entered if the execution context // is resumed for the first time @@ -195,13 +204,15 @@ private: ar->run(); } - typedef boost::intrusive_ptr< activation_record > ptr_t; + typedef boost::intrusive_ptr< detail::activation_record > ptr_t; ptr_t ptr_; - template< typename StackAlloc, typename Fn > - static activation_record * create_context( StackAlloc salloc, Fn && fn, bool use_segmented_stack) { - typedef capture_record< Fn, StackAlloc > capture_t; + template< typename StackAlloc, typename Fn ,typename Tpl > + static detail::activation_record * create_context( + StackAlloc salloc, + Fn && fn, Tpl && tpl) { + typedef detail::capture_record< Fn, Tpl, StackAlloc > capture_t; stack_context sctx( salloc.allocate() ); // reserve space for control structure @@ -223,13 +234,18 @@ private: // create fast-context fcontext_t fctx = make_fcontext( sp, size, & execution_context::entry_func< capture_t >); BOOST_ASSERT( nullptr != fctx); + // get current activation record + ptr_t curr = execution_context::current().ptr_; // placment new for control structure on fast-context stack - return new ( sp) capture_t( sctx, salloc, fctx, std::forward< Fn >( fn), use_segmented_stack); + return new ( sp) capture_t( + sctx, salloc, fctx, std::forward< Fn >( fn), std::forward< Tpl >( tpl), curr.get() ); } - template< typename StackAlloc, typename Fn > - static activation_record * create_context( preallocated palloc, StackAlloc salloc, Fn && fn, bool use_segmented_stack) { - typedef capture_record< Fn, StackAlloc > capture_t; + template< typename StackAlloc, typename Fn , typename Tpl > + static detail::activation_record * create_context( + preallocated palloc, StackAlloc salloc, + Fn && fn, Tpl && tpl) { + typedef detail::capture_record< Fn, Tpl, StackAlloc > capture_t; // reserve space for control structure #if defined(BOOST_NO_CXX14_CONSTEXPR) || defined(BOOST_NO_CXX11_STD_ALIGN) @@ -250,119 +266,181 @@ private: // create fast-context fcontext_t fctx = make_fcontext( sp, size, & execution_context::entry_func< capture_t >); BOOST_ASSERT( nullptr != fctx); + // get current activation record + ptr_t curr = execution_context::current().ptr_; // placment new for control structure on fast-context stack - return new ( sp) capture_t( palloc.sctx, salloc, fctx, std::forward< Fn >( fn), use_segmented_stack); - } - - template< typename StackAlloc, typename Fn, typename Tpl, std::size_t ... I > - static activation_record * create_capture_record( StackAlloc salloc, - Fn && fn_, Tpl && tpl_, - std::index_sequence< I ... >, - bool use_segmented_stack) { - return create_context( - salloc, - // lambda, executed in new execution context - [fn=std::forward< Fn >( fn_),tpl=std::forward< Tpl >( tpl_)] () mutable -> decltype( auto) { - detail::invoke( fn, - // non-type template parameter pack used to extract the - // parameters (arguments) from the tuple and pass them to fn - // via parameter pack expansion - // std::tuple_element<> does not perfect forwarding - std::forward< decltype( std::get< I >( std::declval< Tpl >() ) ) >( - std::get< I >( std::forward< Tpl >( tpl) ) ) ... ); - }, - use_segmented_stack); - } - - template< typename StackAlloc, typename Fn, typename Tpl, std::size_t ... I > - static activation_record * create_capture_record( preallocated palloc, StackAlloc salloc, - Fn && fn_, Tpl && tpl_, - std::index_sequence< I ... >, - bool use_segmented_stack) { - return create_context( - palloc, salloc, - // lambda, executed in new execution context - [fn=std::forward< Fn >( fn_),tpl=std::forward< Tpl >( tpl_)] () mutable -> decltype( auto) { - detail::invoke( fn, - // non-type template parameter pack used to extract the - // parameters (arguments) from the tuple and pass them to fn - // via parameter pack expansion - // std::tuple_element<> does not perfect forwarding - std::forward< decltype( std::get< I >( std::declval< Tpl >() ) ) >( - std::get< I >( std::forward< Tpl >( tpl) ) ) ... ); - }, - use_segmented_stack); + return new ( sp) capture_t( + palloc.sctx, salloc, fctx, std::forward< Fn >( fn), std::forward< Tpl >( tpl), curr.get() ); } execution_context() : // default constructed with current activation_record - ptr_( activation_record::current_rec) { + ptr_( detail::activation_record::current_rec) { } public: - static execution_context current() noexcept { - return execution_context(); - } + static execution_context current() noexcept; # if defined(BOOST_USE_SEGMENTED_STACKS) template< typename Fn, typename ... Args > - explicit execution_context( segmented_stack salloc, Fn && fn, Args && ... args) : + explicit execution_context( Fn && fn, Args && ... args) : // deferred execution of fn and its arguments // arguments are stored in std::tuple<> // non-type template parameter pack via std::index_sequence_for<> // preserves the number of arguments // used to extract the function arguments from std::tuple<> - ptr_( create_capture_record( salloc, - std::forward< Fn >( fn), - std::make_tuple( std::forward< Args >( args) ... ), - std::index_sequence_for< Args ... >(), true) ) { + ptr_( create_context( segmented_stack(), + std::forward< Fn >( fn), + std::make_tuple( std::forward< Args >( args) ...) ) ) { + ptr_->resume( ptr_.get(), true); } template< typename Fn, typename ... Args > - explicit execution_context( preallocated palloc, segmented_stack salloc, Fn && fn, Args && ... args) : + explicit execution_context( std::allocator_arg_t, segmented_stack salloc, Fn && fn, Args && ... args) : // deferred execution of fn and its arguments // arguments are stored in std::tuple<> // non-type template parameter pack via std::index_sequence_for<> // preserves the number of arguments // used to extract the function arguments from std::tuple<> - ptr_( create_capture_record( palloc, salloc, - std::forward< Fn >( fn), - std::make_tuple( std::forward< Args >( args) ... ), - std::index_sequence_for< Args ... >(), true) ) { + ptr_( create_context( salloc, + std::forward< Fn >( fn), + std::make_tuple( std::forward< Args >( args) ...) ) ) { + ptr_->resume( ptr_.get(), true); + } + + template< typename Fn, typename ... Args > + explicit execution_context( std::allocator_arg_t, preallocated palloc, segmented_stack salloc, Fn && fn, Args && ... args) : + // deferred execution of fn and its arguments + // arguments are stored in std::tuple<> + // non-type template parameter pack via std::index_sequence_for<> + // preserves the number of arguments + // used to extract the function arguments from std::tuple<> + ptr_( create_context( palloc, salloc, + std::forward< Fn >( fn), + std::make_tuple( std::forward< Args >( args) ...) ) ) { + ptr_->resume( ptr_.get(), true); + } +# else + template< typename Fn, typename ... Args > + explicit execution_context( Fn && fn, Args && ... args) : + // deferred execution of fn and its arguments + // arguments are stored in std::tuple<> + // non-type template parameter pack via std::index_sequence_for<> + // preserves the number of arguments + // used to extract the function arguments from std::tuple<> + ptr_( create_context( fixedsize_stack(), + std::forward< Fn >( fn), + std::make_tuple( std::forward< Args >( args) ...) ) ) { + ptr_->resume( ptr_.get(), true); } -# endif template< typename StackAlloc, typename Fn, typename ... Args > - explicit execution_context( StackAlloc salloc, Fn && fn, Args && ... args) : + explicit execution_context( std::allocator_arg_t, StackAlloc salloc, Fn && fn, Args && ... args) : // deferred execution of fn and its arguments // arguments are stored in std::tuple<> // non-type template parameter pack via std::index_sequence_for<> // preserves the number of arguments // used to extract the function arguments from std::tuple<> - ptr_( create_capture_record( salloc, - std::forward< Fn >( fn), - std::make_tuple( std::forward< Args >( args) ... ), - std::index_sequence_for< Args ... >(), false) ) { + ptr_( create_context( salloc, + std::forward< Fn >( fn), + std::make_tuple( std::forward< Args >( args) ...) ) ) { + ptr_->resume( ptr_.get(), true); } template< typename StackAlloc, typename Fn, typename ... Args > - explicit execution_context( preallocated palloc, StackAlloc salloc, Fn && fn, Args && ... args) : + explicit execution_context( std::allocator_arg_t, preallocated palloc, StackAlloc salloc, Fn && fn, Args && ... args) : // deferred execution of fn and its arguments // arguments are stored in std::tuple<> // non-type template parameter pack via std::index_sequence_for<> // preserves the number of arguments // used to extract the function arguments from std::tuple<> - ptr_( create_capture_record( palloc, salloc, - std::forward< Fn >( fn), - std::make_tuple( std::forward< Args >( args) ... ), - std::index_sequence_for< Args ... >(), false) ) { + ptr_( create_context( palloc, salloc, + std::forward< Fn >( fn), + std::make_tuple( std::forward< Args >( args) ...) ) ) { + ptr_->resume( ptr_.get(), true); + } +# endif + + execution_context( execution_context const& other) noexcept : + ptr_( other.ptr_) { } - void operator()( bool preserve_fpu = false) noexcept { - ptr_->resume( preserve_fpu); + execution_context( execution_context && other) noexcept : + ptr_( other.ptr_) { + other.ptr_.reset(); + } + + execution_context & operator=( execution_context const& other) noexcept { + if ( this != & other) { + ptr_ = other.ptr_; + } + return * this; + } + + execution_context & operator=( execution_context && other) noexcept { + if ( this != & other) { + ptr_ = other.ptr_; + other.ptr_.reset(); + } + return * this; + } + + void * operator()( void * vp = nullptr, bool preserve_fpu = false) noexcept { + return ptr_->resume( vp, preserve_fpu); + } + + explicit operator bool() const noexcept { + return nullptr != ptr_.get(); + } + + bool operator!() const noexcept { + return nullptr == ptr_.get(); + } + + bool operator==( execution_context const& other) const noexcept { + return ptr_ == other.ptr_; + } + + bool operator!=( execution_context const& other) const noexcept { + return ptr_ != other.ptr_; + } + + bool operator<( execution_context const& other) const noexcept { + return ptr_ < other.ptr_; + } + + bool operator>( execution_context const& other) const noexcept { + return other.ptr_ < ptr_; + } + + bool operator<=( execution_context const& other) const noexcept { + return ! ( * this > other); + } + + bool operator>=( execution_context const& other) const noexcept { + return ! ( * this < other); + } + + template< typename charT, class traitsT > + friend std::basic_ostream< charT, traitsT > & + operator<<( std::basic_ostream< charT, traitsT > & os, execution_context const& other) { + if ( nullptr != other.ptr_) { + return os << other.ptr_; + } else { + return os << "{not-valid}"; + } + } + + void swap( execution_context & other) noexcept { + ptr_.swap( other.ptr_); } }; +inline +void swap( execution_context & l, execution_context & r) noexcept { + l.swap( r); +} + }} # ifdef BOOST_HAS_ABI_HEADERS |