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.ipp470
1 files changed, 274 insertions, 196 deletions
diff --git a/boost/context/execution_context.ipp b/boost/context/execution_context.ipp
index 8832d75c20..ec5443297f 100644
--- a/boost/context/execution_context.ipp
+++ b/boost/context/execution_context.ipp
@@ -12,11 +12,13 @@
#if ! defined(BOOST_CONTEXT_NO_EXECUTION_CONTEXT)
# include <algorithm>
+# include <atomic>
# include <cstddef>
# include <cstdint>
# include <cstdlib>
# include <functional>
# include <memory>
+# include <ostream>
# include <tuple>
# include <utility>
@@ -26,6 +28,7 @@
# include <boost/intrusive_ptr.hpp>
# include <boost/context/detail/invoke.hpp>
+# include <boost/context/fixedsize_stack.hpp>
# include <boost/context/stack_context.hpp>
# include <boost/context/segmented_stack.hpp>
@@ -45,142 +48,148 @@ void __splitstack_setcontext( void * [BOOST_CONTEXT_SEGMENTS]);
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
+ };
-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;
- }
+ thread_local static ptr_t current_rec;
+
+ std::atomic< 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( 0),
+ fctx( nullptr),
+ sctx(),
+ flags( flag_main_ctx) {
+ }
+
+ activation_record( fcontext_t fctx_, stack_context sctx_) noexcept :
+ use_count( 0),
+ fctx( fctx_),
+ sctx( sctx_),
+ flags( 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;
+ // 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
+ // adjust segmented stack properties
+ __splitstack_getcontext( from->sctx.segments_ctx);
+ __splitstack_setcontext( sctx.segments_ctx);
# endif
- }
+ // context switch from parent context to `this`-context
+ intptr_t ret = jump_fcontext( & from->fctx, fctx, reinterpret_cast< intptr_t >( vp), fpu);
+ // parent context resumed
+ return reinterpret_cast< void * >( ret);
+ }
- 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, fcontext_t fctx, Fn && fn, bool use_segmented_stack) noexcept :
- activation_record( fctx, 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,
+ fcontext_t fctx,
+ Fn && fn, Tpl && tpl,
+ activation_record * caller) noexcept :
+ activation_record( fctx, sctx),
+ salloc_( salloc),
+ fn_( std::forward< Fn >( fn) ),
+ tpl_( std::forward< Tpl >( tpl) ),
+ caller_( caller) {
+ }
+
+ void deallocate() override final {
+ destroy( this);
+ }
- void run() noexcept {
- try {
- fn_();
- } catch (...) {
- std::terminate();
- }
- BOOST_ASSERT( 0 == (flags & flag_main_ctx) );
+ 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
@@ -195,13 +204,15 @@ private:
ar->run();
}
- 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) {
+ typedef detail::capture_record< Fn, Tpl, StackAlloc > capture_t;
stack_context sctx( salloc.allocate() );
// reserve space for control structure
@@ -223,13 +234,18 @@ private:
// create fast-context
fcontext_t fctx = make_fcontext( sp, size, & execution_context::entry_func< capture_t >);
BOOST_ASSERT( nullptr != fctx);
+ // get current activation record
+ ptr_t curr = execution_context::current().ptr_;
// placment new for control structure on fast-context stack
- return new ( sp) capture_t( sctx, salloc, fctx, std::forward< Fn >( fn), use_segmented_stack);
+ return new ( sp) capture_t(
+ sctx, salloc, fctx, std::forward< Fn >( fn), std::forward< Tpl >( tpl), curr.get() );
}
- 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) {
+ typedef detail::capture_record< Fn, Tpl, StackAlloc > capture_t;
// reserve space for control structure
#if defined(BOOST_NO_CXX14_CONSTEXPR) || defined(BOOST_NO_CXX11_STD_ALIGN)
@@ -250,119 +266,181 @@ private:
// create fast-context
fcontext_t fctx = make_fcontext( sp, size, & execution_context::entry_func< capture_t >);
BOOST_ASSERT( nullptr != fctx);
+ // get current activation record
+ ptr_t curr = execution_context::current().ptr_;
// 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);
+ return new ( sp) capture_t(
+ palloc.sctx, salloc, fctx, std::forward< Fn >( fn), std::forward< Tpl >( tpl), curr.get() );
}
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;
# if defined(BOOST_USE_SEGMENTED_STACKS)
template< typename Fn, typename ... Args >
- explicit execution_context( segmented_stack salloc, Fn && fn, Args && ... 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_capture_record( salloc,
- std::forward< Fn >( fn),
- std::make_tuple( std::forward< Args >( args) ... ),
- std::index_sequence_for< Args ... >(), true) ) {
+ ptr_( create_context( segmented_stack(),
+ std::forward< Fn >( fn),
+ std::make_tuple( std::forward< Args >( args) ...) ) ) {
+ ptr_->resume( ptr_.get(), true);
}
template< typename Fn, typename ... Args >
- explicit execution_context( preallocated palloc, segmented_stack salloc, Fn && fn, Args && ... args) :
+ explicit execution_context( std::allocator_arg_t, 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) ) {
+ ptr_( create_context( salloc,
+ std::forward< Fn >( fn),
+ std::make_tuple( std::forward< Args >( args) ...) ) ) {
+ ptr_->resume( ptr_.get(), true);
+ }
+
+ template< typename Fn, typename ... Args >
+ explicit execution_context( std::allocator_arg_t, 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_context( palloc, salloc,
+ std::forward< Fn >( fn),
+ std::make_tuple( std::forward< Args >( args) ...) ) ) {
+ ptr_->resume( ptr_.get(), true);
+ }
+# else
+ 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) ...) ) ) {
+ ptr_->resume( ptr_.get(), true);
}
-# endif
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) ...) ) ) {
+ 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) ...) ) ) {
+ ptr_->resume( ptr_.get(), true);
+ }
+# endif
+
+ execution_context( execution_context const& other) noexcept :
+ ptr_( other.ptr_) {
}
- void operator()( bool preserve_fpu = false) noexcept {
- ptr_->resume( preserve_fpu);
+ 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;
+ }
+
+ void * operator()( void * vp = nullptr, bool preserve_fpu = false) noexcept {
+ return ptr_->resume( vp, preserve_fpu);
+ }
+
+ explicit operator bool() const noexcept {
+ return nullptr != ptr_.get();
+ }
+
+ bool operator!() const noexcept {
+ return nullptr == ptr_.get();
+ }
+
+ bool operator==( execution_context const& other) const noexcept {
+ return ptr_ == other.ptr_;
+ }
+
+ bool operator!=( execution_context const& other) const noexcept {
+ return ptr_ != other.ptr_;
+ }
+
+ bool operator<( execution_context const& other) const noexcept {
+ return ptr_ < other.ptr_;
+ }
+
+ bool operator>( execution_context const& other) const noexcept {
+ return other.ptr_ < ptr_;
+ }
+
+ bool operator<=( execution_context const& other) const noexcept {
+ return ! ( * this > other);
+ }
+
+ bool operator>=( execution_context const& other) const noexcept {
+ return ! ( * this < other);
+ }
+
+ template< typename charT, class traitsT >
+ friend std::basic_ostream< charT, traitsT > &
+ operator<<( std::basic_ostream< charT, traitsT > & os, execution_context const& other) {
+ if ( nullptr != other.ptr_) {
+ return os << other.ptr_;
+ } else {
+ return os << "{not-valid}";
+ }
+ }
+
+ void swap( execution_context & other) noexcept {
+ ptr_.swap( other.ptr_);
}
};
+inline
+void swap( execution_context & l, execution_context & r) noexcept {
+ l.swap( r);
+}
+
}}
# ifdef BOOST_HAS_ABI_HEADERS