Chris@102: Chris@102: // Copyright Oliver Kowalke 2014. Chris@102: // Distributed under the Boost Software License, Version 1.0. Chris@102: // (See accompanying file LICENSE_1_0.txt or copy at Chris@102: // http://www.boost.org/LICENSE_1_0.txt) Chris@102: Chris@102: #ifndef BOOST_CONTEXT_EXECUTION_CONTEXT_H Chris@102: #define BOOST_CONTEXT_EXECUTION_CONTEXT_H Chris@102: Chris@102: #include Chris@102: Chris@102: #if ! defined(BOOST_CONTEXT_NO_EXECUTION_CONTEXT) Chris@102: Chris@102: # include Chris@102: # include Chris@102: # include Chris@102: # include Chris@102: # include Chris@102: # include Chris@102: # include Chris@102: Chris@102: # include Chris@102: # include Chris@102: # include Chris@102: # include Chris@102: Chris@102: # include Chris@102: # include Chris@102: Chris@102: # ifdef BOOST_HAS_ABI_HEADERS Chris@102: # include BOOST_ABI_PREFIX Chris@102: # endif Chris@102: Chris@102: # if defined(BOOST_USE_SEGMENTED_STACKS) Chris@102: extern "C" { Chris@102: Chris@102: void __splitstack_getcontext( void * [BOOST_CONTEXT_SEGMENTS]); Chris@102: Chris@102: void __splitstack_setcontext( void * [BOOST_CONTEXT_SEGMENTS]); Chris@102: Chris@102: } Chris@102: # endif Chris@102: Chris@102: namespace boost { Chris@102: namespace context { Chris@102: Chris@102: struct preallocated { Chris@102: void * sp; Chris@102: std::size_t size; Chris@102: stack_context sctx; Chris@102: Chris@102: preallocated( void * sp_, std::size_t size_, stack_context sctx_) noexcept : Chris@102: sp( sp_), size( size_), sctx( sctx_) { Chris@102: } Chris@102: }; Chris@102: Chris@102: class BOOST_CONTEXT_DECL execution_context { Chris@102: private: Chris@102: struct fcontext { Chris@102: std::size_t use_count; Chris@102: fcontext_t fctx; Chris@102: stack_context sctx; Chris@102: Chris@102: // main-context Chris@102: fcontext() noexcept : Chris@102: use_count( 1), Chris@102: fctx( nullptr), Chris@102: sctx() { Chris@102: } Chris@102: Chris@102: // worker-context Chris@102: fcontext( fcontext_t fctx_, stack_context const& sctx_) noexcept : Chris@102: use_count( 0), Chris@102: fctx( fctx_), Chris@102: sctx( sctx_) { Chris@102: } Chris@102: Chris@102: virtual ~fcontext() noexcept { Chris@102: } Chris@102: Chris@102: virtual void deallocate() { Chris@102: } Chris@102: Chris@102: virtual void run() noexcept { Chris@102: } Chris@102: Chris@102: friend void intrusive_ptr_add_ref( fcontext * ctx) { Chris@102: ++ctx->use_count; Chris@102: } Chris@102: Chris@102: friend void intrusive_ptr_release( fcontext * ctx) { Chris@102: BOOST_ASSERT( nullptr != ctx); Chris@102: Chris@102: if ( 0 == --ctx->use_count) { Chris@102: ctx->~fcontext(); Chris@102: } Chris@102: } Chris@102: }; Chris@102: Chris@102: template< typename Fn, typename StackAlloc > Chris@102: class worker_fcontext : public fcontext { Chris@102: private: Chris@102: StackAlloc salloc_; Chris@102: Fn fn_; Chris@102: Chris@102: static void destroy( worker_fcontext * p) { Chris@102: StackAlloc salloc( p->salloc_); Chris@102: stack_context sctx( p->sctx); Chris@102: p->~worker_fcontext(); Chris@102: salloc.deallocate( sctx); Chris@102: } Chris@102: Chris@102: public: Chris@102: explicit worker_fcontext( stack_context sctx, StackAlloc const& salloc, fcontext_t fctx, Fn && fn) noexcept : Chris@102: fcontext( fctx, sctx), Chris@102: salloc_( salloc), Chris@102: fn_( std::forward< Fn >( fn) ) { Chris@102: } Chris@102: Chris@102: void deallocate() override final { Chris@102: destroy( this); Chris@102: } Chris@102: Chris@102: void run() noexcept override final { Chris@102: fn_(); Chris@102: } Chris@102: }; Chris@102: Chris@102: static void entry_func( intptr_t p) noexcept { Chris@102: BOOST_ASSERT( 0 != p); Chris@102: Chris@102: fcontext * bp( reinterpret_cast< fcontext * >( p) ); Chris@102: BOOST_ASSERT( nullptr != bp); Chris@102: Chris@102: bp->run(); Chris@102: } Chris@102: Chris@102: typedef boost::intrusive_ptr< fcontext > ptr_t; Chris@102: Chris@102: thread_local static fcontext main_ctx_; Chris@102: thread_local static ptr_t current_ctx_; Chris@102: Chris@102: boost::intrusive_ptr< fcontext > ptr_; Chris@102: # if defined(BOOST_USE_SEGMENTED_STACKS) Chris@102: bool use_segmented_stack_ = false; Chris@102: # endif Chris@102: Chris@102: template< typename StackAlloc, typename Fn > Chris@102: static fcontext * create_context( StackAlloc salloc, Fn && fn) { Chris@102: typedef worker_fcontext< Fn, StackAlloc > func_t; Chris@102: Chris@102: stack_context sctx( salloc.allocate() ); Chris@102: // reserve space for control structure Chris@102: std::size_t size = sctx.size - sizeof( func_t); Chris@102: void * sp = static_cast< char * >( sctx.sp) - sizeof( func_t); Chris@102: #if 0 Chris@102: constexpr std::size_t func_alignment = 64; // alignof( func_t); Chris@102: constexpr std::size_t func_size = sizeof( func_t); Chris@102: // reserve space on stack Chris@102: void * sp = static_cast< char * >( sctx.sp) - func_size - func_alignment; Chris@102: // align sp pointer Chris@102: sp = std::align( func_alignment, func_size, sp, func_size + func_alignment); Chris@102: BOOST_ASSERT( nullptr != sp); Chris@102: // calculate remaining size Chris@102: std::size_t size = sctx.size - ( static_cast< char * >( sctx.sp) - static_cast< char * >( sp) ); Chris@102: #endif Chris@102: // create fast-context Chris@102: fcontext_t fctx = make_fcontext( sp, size, & execution_context::entry_func); Chris@102: BOOST_ASSERT( nullptr != fctx); Chris@102: // placment new for control structure on fast-context stack Chris@102: return new ( sp) func_t( sctx, salloc, fctx, std::forward< Fn >( fn) ); Chris@102: } Chris@102: Chris@102: template< typename StackAlloc, typename Fn > Chris@102: static fcontext * create_context( preallocated palloc, StackAlloc salloc, Fn && fn) { Chris@102: typedef worker_fcontext< Fn, StackAlloc > func_t; Chris@102: Chris@102: // reserve space for control structure Chris@102: std::size_t size = palloc.size - sizeof( func_t); Chris@102: void * sp = static_cast< char * >( palloc.sp) - sizeof( func_t); Chris@102: #if 0 Chris@102: constexpr std::size_t func_alignment = 64; // alignof( func_t); Chris@102: constexpr std::size_t func_size = sizeof( func_t); Chris@102: // reserve space on stack Chris@102: void * sp = static_cast< char * >( palloc.sp) - func_size - func_alignment; Chris@102: // align sp pointer Chris@102: sp = std::align( func_alignment, func_size, sp, func_size + func_alignment); Chris@102: BOOST_ASSERT( nullptr != sp); Chris@102: // calculate remaining size Chris@102: std::size_t size = palloc.size - ( static_cast< char * >( palloc.sp) - static_cast< char * >( sp) ); Chris@102: #endif Chris@102: // create fast-context Chris@102: fcontext_t fctx = make_fcontext( sp, size, & execution_context::entry_func); Chris@102: BOOST_ASSERT( nullptr != fctx); Chris@102: // placment new for control structure on fast-context stack Chris@102: return new ( sp) func_t( palloc.sctx, salloc, fctx, std::forward< Fn >( fn) ); Chris@102: } Chris@102: Chris@102: template< typename StackAlloc, typename Fn, typename Tpl, std::size_t ... I > Chris@102: static fcontext * create_worker_fcontext( StackAlloc salloc, Chris@102: Fn && fn_, Tpl && tpl_, Chris@102: std::index_sequence< I ... >) { Chris@102: return create_context( salloc, Chris@102: [fn=std::forward< Fn >( fn_),tpl=std::forward< Tpl >( tpl_)] () mutable { Chris@102: try { Chris@102: fn( Chris@102: // std::tuple_element<> does not perfect forwarding Chris@102: std::forward< decltype( std::get< I >( std::declval< Tpl >() ) ) >( Chris@102: std::get< I >( std::forward< Tpl >( tpl) ) ) ... ); Chris@102: } catch (...) { Chris@102: std::terminate(); Chris@102: } Chris@102: }); Chris@102: } Chris@102: Chris@102: template< typename StackAlloc, typename Fn, typename Tpl, std::size_t ... I > Chris@102: static fcontext * create_worker_fcontext( preallocated palloc, StackAlloc salloc, Chris@102: Fn && fn_, Tpl && tpl_, Chris@102: std::index_sequence< I ... >) { Chris@102: return create_context( palloc, salloc, Chris@102: [fn=std::forward< Fn >( fn_),tpl=std::forward< Tpl >( tpl_)] () mutable { Chris@102: try { Chris@102: fn( Chris@102: // std::tuple_element<> does not perfect forwarding Chris@102: std::forward< decltype( std::get< I >( std::declval< Tpl >() ) ) >( Chris@102: std::get< I >( std::forward< Tpl >( tpl) ) ) ... ); Chris@102: } catch (...) { Chris@102: std::terminate(); Chris@102: } Chris@102: }); Chris@102: } Chris@102: Chris@102: execution_context() : Chris@102: ptr_( current_ctx_) { Chris@102: } Chris@102: Chris@102: public: Chris@102: static execution_context current() noexcept { Chris@102: return execution_context(); Chris@102: } Chris@102: Chris@102: # if defined(BOOST_USE_SEGMENTED_STACKS) Chris@102: template< typename Fn, typename ... Args > Chris@102: explicit execution_context( segmented_stack salloc, Fn && fn, Args && ... args) : Chris@102: ptr_( create_worker_fcontext( salloc, Chris@102: std::forward< Fn >( fn), Chris@102: std::make_tuple( std::forward< Args >( args) ... ), Chris@102: std::index_sequence_for< Args ... >() ) ), Chris@102: use_segmented_stack_( true) { Chris@102: } Chris@102: Chris@102: template< typename Fn, typename ... Args > Chris@102: explicit execution_context( preallocated palloc, segmented_stack salloc, Fn && fn, Args && ... args) : Chris@102: ptr_( create_worker_fcontext( palloc, salloc, Chris@102: std::forward< Fn >( fn), Chris@102: std::make_tuple( std::forward< Args >( args) ... ), Chris@102: std::index_sequence_for< Args ... >() ) ), Chris@102: use_segmented_stack_( true) { Chris@102: } Chris@102: # endif Chris@102: Chris@102: template< typename StackAlloc, typename Fn, typename ... Args > Chris@102: explicit execution_context( StackAlloc salloc, Fn && fn, Args && ... args) : Chris@102: ptr_( create_worker_fcontext( salloc, Chris@102: std::forward< Fn >( fn), Chris@102: std::make_tuple( std::forward< Args >( args) ... ), Chris@102: std::index_sequence_for< Args ... >() ) ) { Chris@102: } Chris@102: Chris@102: template< typename StackAlloc, typename Fn, typename ... Args > Chris@102: explicit execution_context( preallocated palloc, StackAlloc salloc, Fn && fn, Args && ... args) : Chris@102: ptr_( create_worker_fcontext( palloc, salloc, Chris@102: std::forward< Fn >( fn), Chris@102: std::make_tuple( std::forward< Args >( args) ... ), Chris@102: std::index_sequence_for< Args ... >() ) ) { Chris@102: } Chris@102: Chris@102: void resume( bool preserve_fpu = false) noexcept { Chris@102: fcontext * old_ctx( current_ctx_.get() ); Chris@102: fcontext * new_ctx( ptr_.get() ); Chris@102: current_ctx_ = ptr_; Chris@102: # if defined(BOOST_USE_SEGMENTED_STACKS) Chris@102: if ( use_segmented_stack_) { Chris@102: __splitstack_getcontext( old_ctx->sctx.segments_ctx); Chris@102: __splitstack_setcontext( new_ctx->sctx.segments_ctx); Chris@102: Chris@102: jump_fcontext( & old_ctx->fctx, new_ctx->fctx, reinterpret_cast< intptr_t >( new_ctx), preserve_fpu); Chris@102: Chris@102: __splitstack_setcontext( old_ctx->sctx.segments_ctx); Chris@102: } else { Chris@102: jump_fcontext( & old_ctx->fctx, new_ctx->fctx, reinterpret_cast< intptr_t >( new_ctx), preserve_fpu); Chris@102: } Chris@102: # else Chris@102: jump_fcontext( & old_ctx->fctx, new_ctx->fctx, reinterpret_cast< intptr_t >( new_ctx), preserve_fpu); Chris@102: # endif Chris@102: } Chris@102: }; Chris@102: Chris@102: }} Chris@102: Chris@102: # ifdef BOOST_HAS_ABI_HEADERS Chris@102: # include BOOST_ABI_SUFFIX Chris@102: # endif Chris@102: Chris@102: #endif Chris@102: Chris@102: #endif // BOOST_CONTEXT_EXECUTION_CONTEXT_H