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