summaryrefslogtreecommitdiff
path: root/boost/context/execution_context.ipp
diff options
context:
space:
mode:
Diffstat (limited to 'boost/context/execution_context.ipp')
-rw-r--r--boost/context/execution_context.ipp374
1 files changed, 374 insertions, 0 deletions
diff --git a/boost/context/execution_context.ipp b/boost/context/execution_context.ipp
new file mode 100644
index 0000000000..8832d75c20
--- /dev/null
+++ b/boost/context/execution_context.ipp
@@ -0,0 +1,374 @@
+
+// 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 <algorithm>
+# include <cstddef>
+# include <cstdint>
+# include <cstdlib>
+# include <functional>
+# 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/detail/invoke.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 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;
+ }
+# 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
+# endif
+ }
+
+ virtual void deallocate() {}
+
+ friend void intrusive_ptr_add_ref( activation_record * ar) {
+ ++ar->use_count;
+ }
+
+ friend void intrusive_ptr_release( activation_record * ar) {
+ BOOST_ASSERT( nullptr != ar);
+
+ 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);
+ }
+
+ 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) ) {
+ }
+
+ void deallocate() override final {
+ destroy( this);
+ }
+
+ void run() noexcept {
+ try {
+ fn_();
+ } catch (...) {
+ std::terminate();
+ }
+ BOOST_ASSERT( 0 == (flags & flag_main_ctx) );
+ }
+ };
+
+ // tampoline function
+ // entered if the execution context
+ // is resumed for the first time
+ template< typename AR >
+ static void entry_func( intptr_t p) noexcept {
+ BOOST_ASSERT( 0 != p);
+
+ AR * ar( reinterpret_cast< AR * >( p) );
+ BOOST_ASSERT( nullptr != ar);
+
+ // start execution of toplevel context-function
+ ar->run();
+ }
+
+ typedef boost::intrusive_ptr< 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;
+
+ stack_context sctx( salloc.allocate() );
+ // reserve space for control structure
+#if defined(BOOST_NO_CXX14_CONSTEXPR) || defined(BOOST_NO_CXX11_STD_ALIGN)
+ std::size_t size = sctx.size - sizeof( capture_t);
+ void * sp = static_cast< char * >( sctx.sp) - sizeof( capture_t);
+#else
+ constexpr std::size_t func_alignment = 64; // alignof( capture_t);
+ constexpr std::size_t func_size = sizeof( capture_t);
+ // reserve space on stack
+ void * sp = static_cast< char * >( sctx.sp) - func_size - func_alignment;
+ // align sp pointer
+ std::size_t space = func_size + func_alignment;
+ sp = std::align( func_alignment, func_size, sp, space);
+ 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< capture_t >);
+ BOOST_ASSERT( nullptr != fctx);
+ // placment new for control structure on fast-context stack
+ return new ( sp) capture_t( sctx, salloc, fctx, std::forward< Fn >( fn), use_segmented_stack);
+ }
+
+ 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;
+
+ // reserve space for control structure
+#if defined(BOOST_NO_CXX14_CONSTEXPR) || defined(BOOST_NO_CXX11_STD_ALIGN)
+ std::size_t size = palloc.size - sizeof( capture_t);
+ void * sp = static_cast< char * >( palloc.sp) - sizeof( capture_t);
+#else
+ constexpr std::size_t func_alignment = 64; // alignof( capture_t);
+ constexpr std::size_t func_size = sizeof( capture_t);
+ // reserve space on stack
+ void * sp = static_cast< char * >( palloc.sp) - func_size - func_alignment;
+ // align sp pointer
+ std::size_t space = func_size + func_alignment;
+ sp = std::align( func_alignment, func_size, sp, space);
+ 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< capture_t >);
+ BOOST_ASSERT( nullptr != fctx);
+ // 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);
+ }
+
+ execution_context() :
+ // default constructed with current activation_record
+ ptr_( activation_record::current_rec) {
+ }
+
+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) :
+ // 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) ) {
+ }
+
+ template< typename Fn, typename ... Args >
+ explicit execution_context( 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_capture_record( palloc, salloc,
+ std::forward< Fn >( fn),
+ std::make_tuple( std::forward< Args >( args) ... ),
+ std::index_sequence_for< Args ... >(), true) ) {
+ }
+# endif
+
+ template< typename StackAlloc, typename Fn, typename ... Args >
+ explicit execution_context( 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) ) {
+ }
+
+ template< typename StackAlloc, typename Fn, typename ... Args >
+ explicit execution_context( 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) ) {
+ }
+
+ void operator()( bool preserve_fpu = false) noexcept {
+ ptr_->resume( preserve_fpu);
+ }
+};
+
+}}
+
+# ifdef BOOST_HAS_ABI_HEADERS
+# include BOOST_ABI_SUFFIX
+# endif
+
+#endif
+
+#endif // BOOST_CONTEXT_EXECUTION_CONTEXT_H