Repository URL to install this package:
|
Version:
1.59.0-ubuntu16 ▾
|
// 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