diff options
Diffstat (limited to 'boost/context/execution_context.hpp')
-rw-r--r-- | boost/context/execution_context.hpp | 307 |
1 files changed, 307 insertions, 0 deletions
diff --git a/boost/context/execution_context.hpp b/boost/context/execution_context.hpp new file mode 100644 index 0000000000..4d809b0c21 --- /dev/null +++ b/boost/context/execution_context.hpp @@ -0,0 +1,307 @@ + +// 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 <boost/context/detail/config.hpp> + +#if ! defined(BOOST_CONTEXT_NO_EXECUTION_CONTEXT) + +# include <cstddef> +# include <cstdint> +# include <cstdlib> +# include <exception> +# include <memory> +# include <tuple> +# include <utility> + +# include <boost/assert.hpp> +# include <boost/config.hpp> +# include <boost/context/fcontext.hpp> +# include <boost/intrusive_ptr.hpp> + +# include <boost/context/stack_context.hpp> +# include <boost/context/segmented_stack.hpp> + +# 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 |