diff options
Diffstat (limited to 'boost/context/execution_context_winfib.ipp')
-rw-r--r-- | boost/context/execution_context_winfib.ipp | 407 |
1 files changed, 230 insertions, 177 deletions
diff --git a/boost/context/execution_context_winfib.ipp b/boost/context/execution_context_winfib.ipp index 7b0a92b550..1b106fa245 100644 --- a/boost/context/execution_context_winfib.ipp +++ b/boost/context/execution_context_winfib.ipp @@ -13,6 +13,8 @@ #include <windows.h> +#include <algorithm> +#include <atomic> #include <cstddef> #include <cstdint> #include <cstdlib> @@ -25,6 +27,7 @@ #include <boost/intrusive_ptr.hpp> #include <boost/context/detail/invoke.hpp> +#include <boost/context/fixedsize_stack.hpp> #include <boost/context/stack_context.hpp> #ifdef BOOST_HAS_ABI_HEADERS @@ -33,132 +36,162 @@ 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, + flag_segmented_stack = 1 << 3 + }; -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; - LPVOID fiber; - stack_context sctx; - int flags; - - // used for toplevel-context - // (e.g. main context, thread-entry context) - activation_record() noexcept : - use_count( 1), - fiber( nullptr), - sctx(), - flags( flag_main_ctx) { - } - - activation_record( stack_context sctx_, bool use_segmented_stack) noexcept : - use_count( 0), - fiber( nullptr), - sctx( sctx_), - flags( use_segmented_stack ? flag_segmented_stack : 0) { - } - - virtual ~activation_record() noexcept = default; - - void resume() 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; - // context switch from parent context to `this`-context + thread_local static ptr_t current_rec; + + std::atomic< std::size_t > use_count; + LPVOID fiber; + stack_context sctx; + void * data; + int flags; + + // used for toplevel-context + // (e.g. main context, thread-entry context) + activation_record() noexcept : + use_count( 0), + fiber( nullptr), + sctx(), + flags( flag_main_ctx +# if defined(BOOST_USE_SEGMENTED_STACKS) + | flag_segmented_stack +# endif + ) { + } + + activation_record( stack_context sctx_, bool use_segmented_stack) noexcept : + use_count( 0), + fiber( nullptr), + sctx( sctx_), + data( nullptr), + flags( use_segmented_stack ? flag_segmented_stack : 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; + // context switch from parent context to `this`-context #if ( _WIN32_WINNT > 0x0600) - if ( ::IsThreadAFiber() ) { - from->fiber = ::GetCurrentFiber(); - } else { - from->fiber = ::ConvertThreadToFiber( nullptr); - } -#else + if ( ::IsThreadAFiber() ) { + from->fiber = ::GetCurrentFiber(); + } else { from->fiber = ::ConvertThreadToFiber( nullptr); - if ( nullptr == from->fiber) { - DWORD err = ::GetLastError(); - BOOST_ASSERT( ERROR_ALREADY_FIBER == err); - from->fiber = ::GetCurrentFiber(); - BOOST_ASSERT( nullptr != from->fiber); - BOOST_ASSERT( reinterpret_cast< LPVOID >( 0x1E00) != from->fiber); - } -#endif - ::SwitchToFiber( fiber); } +#else + from->fiber = ::ConvertThreadToFiber( nullptr); + if ( nullptr == from->fiber) { + DWORD err = ::GetLastError(); + BOOST_ASSERT( ERROR_ALREADY_FIBER == err); + from->fiber = ::GetCurrentFiber(); + BOOST_ASSERT( nullptr != from->fiber); + BOOST_ASSERT( reinterpret_cast< LPVOID >( 0x1E00) != from->fiber); + } +#endif + // store passed argument (void pointer) + data = vp; + // context switch + ::SwitchToFiber( fiber); + // access the activation-record of the current fiber + activation_record * ar = static_cast< activation_record * >( GetFiberData() ); + return nullptr != ar ? ar->data : nullptr; + } - 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, Fn && fn, bool use_segmented_stack) noexcept : - activation_record( 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, + Fn && fn, Tpl && tpl, + activation_record * caller, + bool use_segmented_stack) noexcept : + activation_record( sctx, use_segmented_stack), + salloc_( salloc), + fn_( std::forward< Fn >( fn) ), + tpl_( std::forward< Tpl >( tpl) ), + caller_( caller) { + } - void run() noexcept { - try { - fn_(); - } catch (...) { - std::terminate(); - } - BOOST_ASSERT( 0 == (flags & flag_main_ctx) ); + void deallocate() override final { + destroy( this); + } + + 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 @@ -173,23 +206,30 @@ private: ::DeleteFiber( ar->fiber); } - 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, + bool use_segmented_stack) { + typedef detail::capture_record< Fn, Tpl, StackAlloc > capture_t; // hackish std::size_t fsize = salloc.size_; - salloc.size_ = sizeof( capture_t); + // protected_fixedsize_stack needs at least 2*page-size + salloc.size_ = ( std::max)( sizeof( capture_t), 2 * stack_traits::page_size() ); stack_context sctx( salloc.allocate() ); // reserve space for control structure void * sp = static_cast< char * >( sctx.sp) - sizeof( capture_t); - // placment new for control structure on fast-context stack - capture_t * cr = new ( sp) capture_t( sctx, salloc, std::forward< Fn >( fn), use_segmented_stack); + // get current activation record + ptr_t curr = execution_context::current().ptr_; + // placement new for control structure on fast-context stack + capture_t * cr = new ( sp) capture_t( + sctx, salloc, std::forward< Fn >( fn), std::forward< Tpl >( tpl), curr.get(), use_segmented_stack); // create fiber // use default stacksize cr->fiber = ::CreateFiber( fsize, execution_context::entry_func< capture_t >, cr); @@ -197,18 +237,25 @@ private: return cr; } - 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, + bool use_segmented_stack) { + typedef detail::capture_record< Fn, Tpl, StackAlloc > capture_t; // hackish std::size_t fsize = salloc.size_; - salloc.size_ = sizeof( capture_t); + // protected_fixedsize_stack needs at least 2*page-size + salloc.size_ = ( std::max)( sizeof( capture_t), 2 * stack_traits::page_size() ); // reserve space for control structure void * sp = static_cast< char * >( palloc.sp) - sizeof( capture_t); - // placment new for control structure on fast-context stack - capture_t * cr = new ( sp) capture_t( palloc.sctx, salloc, std::forward< Fn >( fn), use_segmented_stack); + // get current activation record + ptr_t curr = execution_context::current().ptr_; + // placement new for control structure on fast-context stack + capture_t * cr = new ( sp) capture_t( + palloc.sctx, salloc, std::forward< Fn >( fn), std::forward< Tpl >( tpl), curr.get(), use_segmented_stack); // create fiber // use default stacksize cr->fiber = ::CreateFiber( fsize, execution_context::entry_func< capture_t >, cr); @@ -216,84 +263,90 @@ private: return cr; } - 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); - } - 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; + + 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) ...), + false) ) { + ptr_->resume( ptr_.get(), true); } 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) ...), + false) ) { + 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) ...), + false) ) { + ptr_->resume( ptr_.get(), true); + } + + execution_context( execution_context const& other) noexcept : + ptr_( other.ptr_) { + } + + 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; + } + + explicit operator bool() const noexcept { + return nullptr != ptr_.get(); + } + + bool operator!() const noexcept { + return nullptr == ptr_.get(); } - void operator()( bool preserve_fpu = false) noexcept { - ptr_->resume(); + void * operator()( void * vp = nullptr, bool preserve_fpu = false) noexcept { + return ptr_->resume( vp, preserve_fpu); } }; |