From d564cccf8268a14e25a8eb5b7fab7460a4ddfdb2 Mon Sep 17 00:00:00 2001 From: Oliver Kowalke Date: Sun, 29 Nov 2015 11:23:14 +0100 Subject: [PATCH] refactor execution-context --- doc/execution_context.qbk | 13 - example/execution_context/fibonacci.cpp | 5 +- example/execution_context/jump.cpp | 2 - example/execution_context/parameter.cpp | 20 +- example/execution_context/parser.cpp | 4 - example/execution_context/segmented.cpp | 18 +- include/boost/context/execution_context.hpp | 415 ++++++++++++++++- include/boost/context/execution_context.ipp | 435 ------------------ .../context/execution_context_winfib.ipp | 361 --------------- include/boost/context/preallocated.hpp | 39 ++ src/execution_context.cpp | 8 +- 11 files changed, 472 insertions(+), 848 deletions(-) delete mode 100644 include/boost/context/execution_context.ipp delete mode 100644 include/boost/context/execution_context_winfib.ipp create mode 100644 include/boost/context/preallocated.hpp diff --git a/doc/execution_context.qbk b/doc/execution_context.qbk index 397a4f0..1d7721c 100644 --- a/doc/execution_context.qbk +++ b/doc/execution_context.qbk @@ -381,18 +381,5 @@ implementation-defined total order of `execution_context` values places `*this` [[Effects:] [Creates an object of preallocated.]] ] -[section:winfibers Using WinFiber-API] - -Because the TIB (thread information block) is not fully described in the MSDN, -it might be possible that not all required TIB-parts are swapped. -With compiler flag `BOOST_USE_WINFIBERS` `execution_context` uses internally the -Windows Fiber API. -[note The first call of `execution_context::operator()` converts the thread into a Windows fiber -by invoking `ConvertThreadToFiber()`. -If desired, `ConvertFiberToThread()` has to be called by the user explicitly in order to -release resources allocated by `ConvertThreadToFiber()` (e.g. after using boost.context).] - -[endsect] - [endsect] diff --git a/example/execution_context/fibonacci.cpp b/example/execution_context/fibonacci.cpp index 22ddead..eba521b 100644 --- a/example/execution_context/fibonacci.cpp +++ b/example/execution_context/fibonacci.cpp @@ -31,7 +31,6 @@ int main() { ctx(); std::cout<( vp); - std::string str = boost::lexical_cast(i); - caller_( & str); - } catch (...) { - excptr_=std::current_exception(); - } - }) + callee_( [this]( void * vp){ + try { + int i = * static_cast< int * >( vp); + std::string str = boost::lexical_cast(i); + caller_( & str); + } catch (...) { + excptr_=std::current_exception(); + } + }) {} std::string operator()(int i){ @@ -48,4 +47,5 @@ int main() { X x; std::cout< 0) - { + if ( i > 0) { access( buf); std::cout << i << ". iteration" << std::endl; bar( i - 1); @@ -36,7 +32,6 @@ void bar( int i) int main() { int count = 384; - #if defined(BOOST_USE_SEGMENTED_STACKS) std::cout << "using segmented_stack stacks: allocates " << count << " * 4kB == " << 4 * count << "kB on stack, "; std::cout << "initial stack size = " << boost::context::segmented_stack::traits_type::default_size() / 1024 << "kB" << std::endl; @@ -46,19 +41,14 @@ int main() { std::cout << "initial stack size = " << boost::context::fixedsize_stack::traits_type::default_size() / 1024 << "kB" << std::endl; std::cout << "application might fail" << std::endl; #endif - boost::context::execution_context main_ctx( boost::context::execution_context::current() ); - boost::context::execution_context bar_ctx( [& main_ctx, count]( void *){ bar( count); main_ctx(); }); - bar_ctx(); - std::cout << "main: done" << std::endl; - - return 0; + return EXIT_SUCCESS; } diff --git a/include/boost/context/execution_context.hpp b/include/boost/context/execution_context.hpp index 97b1d4e..4dea100 100644 --- a/include/boost/context/execution_context.hpp +++ b/include/boost/context/execution_context.hpp @@ -4,8 +4,417 @@ // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) -#if defined(BOOST_USE_WINFIBERS) -#include +#ifndef BOOST_CONTEXT_EXECUTION_CONTEXT_H +#define BOOST_CONTEXT_EXECUTION_CONTEXT_H + +#include + +#if ! defined(BOOST_CONTEXT_NO_CPP14) + +# include +# include +# include +# include +# include +# include +# 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 { +namespace detail { + +struct activation_record { + typedef boost::intrusive_ptr< activation_record > ptr_t; + + thread_local static ptr_t current_rec; + + std::atomic< std::size_t > use_count{ 0 }; + fcontext_t fctx{ nullptr }; + stack_context sctx{}; + bool main_ctx{ true }; + + // used for toplevel-context + // (e.g. main context, thread-entry context) + activation_record() = default; + + activation_record( fcontext_t fctx_, stack_context sctx_) noexcept : + use_count{ 0 }, + fctx{ fctx_ }, + sctx( sctx_ ), // sctx{ sctx_ } - clang-3.6: no viable conversion from 'boost::context::stack_context' to 'std::size_t' + main_ctx{ false } { + } + + virtual ~activation_record() noexcept = default; + + bool is_main_context() const noexcept { + return main_ctx; + } + + void * resume( void * vp) { + // 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; +# if defined(BOOST_USE_SEGMENTED_STACKS) + // adjust segmented stack properties + __splitstack_getcontext( from->sctx.segments_ctx); + __splitstack_setcontext( sctx.segments_ctx); +# endif + // context switch from parent context to `this`-context + return jump_fcontext( & from->fctx, fctx, vp); + } + + virtual void deallocate() noexcept { + } + + friend void intrusive_ptr_add_ref( activation_record * ar) noexcept { + ++ar->use_count; + } + + friend void intrusive_ptr_release( activation_record * ar) noexcept { + BOOST_ASSERT( nullptr != ar); + + if ( 0 == --ar->use_count) { + ar->deallocate(); + } + } +}; + +struct activation_record_initializer { + activation_record_initializer(); + ~activation_record_initializer() noexcept; +}; + +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) noexcept { + StackAlloc salloc = p->salloc_; + stack_context sctx = p->sctx; + // deallocate activation record + p->~capture_record(); + // destroy stack with stack allocator + salloc.deallocate( sctx); + } + +public: + 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) ), // fn_{ std::forward< Fn >( fn) } - msvc-14.0: void(__cdecl&)(double,void*) can not be converted to void(__cdecl&)(double,void*) + tpl_( std::forward< Tpl >( tpl) ),// tpl_{ std::forward< Tpl >( tpl) } - clang-3.6: excess elements in struct initializer + caller_{ caller } { + } + + ~capture_record() noexcept = default; + + void deallocate() noexcept override final { + destroy( this); + } + + void run() { + auto data = caller_->resume( nullptr); + do_invoke( fn_, std::tuple_cat( tpl_, std::tie( data) ) ); + BOOST_ASSERT_MSG( ! main_ctx, "main-context does not execute activation-record::run()"); + } +}; + +} + +class BOOST_CONTEXT_DECL execution_context { +private: + // tampoline function + // entered if the execution context + // is resumed for the first time + template< typename AR > + static void entry_func( void * vp) noexcept { + AR * ar = static_cast< AR * >( vp); + BOOST_ASSERT( nullptr != ar); + // start execution of toplevel context-function + ar->run(); + } + + typedef boost::intrusive_ptr< detail::activation_record > ptr_t; + + ptr_t ptr_; + + 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 +#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 -#include + 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); + // get current activation record + auto 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), std::forward< Tpl >( tpl), curr.get() }; + } + + 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) + 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); + // get current activation record + auto 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), std::forward< Tpl >( tpl), curr.get() }; + } + + execution_context() noexcept : + // default constructed with current activation_record + ptr_{ detail::activation_record::current_rec } { + } + +public: + static execution_context current() noexcept; + +# if defined(BOOST_USE_SEGMENTED_STACKS) + template< typename Fn, typename ... Args > + 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( segmented_stack(), + std::forward< Fn >( fn), + std::make_tuple( std::forward< Args >( args) ...) ) } { + ptr_->resume( ptr_.get() ); + } + + template< typename Fn, typename ... Args > + 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_context( salloc, + std::forward< Fn >( fn), + std::make_tuple( std::forward< Args >( args) ...) ) } { + ptr_->resume( ptr_.get() ); + } + + template< typename Fn, typename ... Args > + 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() ); + } +# else + template< typename Fn, typename ... Args > + 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() ); + } + + template< typename StackAlloc, typename Fn, typename ... Args > + 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_context( salloc, + std::forward< Fn >( fn), + std::make_tuple( std::forward< Args >( args) ...) ) } { + ptr_->resume( ptr_.get() ); + } + + template< typename StackAlloc, typename Fn, typename ... Args > + 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_context( palloc, salloc, + std::forward< Fn >( fn), + std::make_tuple( std::forward< Args >( args) ...) ) } { + ptr_->resume( ptr_.get() ); + } +# endif + + execution_context( execution_context const& other) noexcept : + ptr_{ other.ptr_ } { + } + + execution_context( execution_context && other) noexcept : + ptr_{ other.ptr_ } { + other.ptr_.reset(); + } + + execution_context & operator=( execution_context const& other) noexcept { + // intrusive_ptr<> does not test for self-assignment + if ( this == & other) return * this; + ptr_ = other.ptr_; + return * this; + } + + execution_context & operator=( execution_context && other) noexcept { + if ( this == & other) return * this; + execution_context tmp{ std::move( other) }; + swap( tmp); + return * this; + } + + void * operator()( void * vp = nullptr) { + return ptr_->resume( vp); + } + + 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-a-context}"; + } + } + + 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 +# include BOOST_ABI_SUFFIX +# endif + +#endif // BOOST_CONTEXT_NO_CPP14 + +#endif // BOOST_CONTEXT_EXECUTION_CONTEXT_H diff --git a/include/boost/context/execution_context.ipp b/include/boost/context/execution_context.ipp deleted file mode 100644 index 1f8f603..0000000 --- a/include/boost/context/execution_context.ipp +++ /dev/null @@ -1,435 +0,0 @@ - -// 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 -# 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 { -namespace detail { - -struct activation_record { - typedef boost::intrusive_ptr< activation_record > ptr_t; - - thread_local static ptr_t current_rec; - - std::atomic< std::size_t > use_count; - fcontext_t fctx; - stack_context sctx; - bool main_ctx; - - // used for toplevel-context - // (e.g. main context, thread-entry context) - activation_record() noexcept : - use_count( 0), - fctx( nullptr), - sctx(), - main_ctx( true) { - } - - activation_record( fcontext_t fctx_, stack_context sctx_) noexcept : - use_count( 0), - fctx( fctx_), - sctx( sctx_), - main_ctx( false) { - } - - virtual ~activation_record() noexcept = default; - - void * resume( void * vp) 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; -# if defined(BOOST_USE_SEGMENTED_STACKS) - // adjust segmented stack properties - __splitstack_getcontext( from->sctx.segments_ctx); - __splitstack_setcontext( sctx.segments_ctx); -# endif - // context switch from parent context to `this`-context - return jump_fcontext( & from->fctx, fctx, vp); - } - - virtual void deallocate() { - delete this; - } - - 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(); - } - } -}; - -struct activation_record_initializer { - activation_record_initializer(); - ~activation_record_initializer(); -}; - -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); - } - -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 { - void * vp = caller_->resume( caller_); - do_invoke( fn_, std::tuple_cat( tpl_, std::tie( vp) ) ); - } catch (...) { - std::terminate(); - } - BOOST_ASSERT( ! 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 - template< typename AR > - static void entry_func( void * vp) noexcept { - AR * ar( reinterpret_cast< AR * >( vp) ); - BOOST_ASSERT( nullptr != ar); - - // start execution of toplevel context-function - ar->run(); - } - - typedef boost::intrusive_ptr< detail::activation_record > ptr_t; - - ptr_t ptr_; - - 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 -#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); - // 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), std::forward< Tpl >( tpl), curr.get() ); - } - - 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) - 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); - // 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), std::forward< Tpl >( tpl), curr.get() ); - } - - execution_context() : - // default constructed with current activation_record - ptr_( detail::activation_record::current_rec) { - } - -public: - static execution_context current() noexcept; - -# if defined(BOOST_USE_SEGMENTED_STACKS) - 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( segmented_stack(), - std::forward< Fn >( fn), - std::make_tuple( std::forward< Args >( args) ...) ) ) { - ptr_->resume( ptr_.get() ); - } - - template< typename Fn, typename ... 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_context( salloc, - std::forward< Fn >( fn), - std::make_tuple( std::forward< Args >( args) ...) ) ) { - ptr_->resume( ptr_.get() ); - } - - 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() ); - } -# 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() ); - } - - template< typename StackAlloc, typename Fn, typename ... 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_context( salloc, - std::forward< Fn >( fn), - std::make_tuple( std::forward< Args >( args) ...) ) ) { - ptr_->resume( ptr_.get() ); - } - - template< typename StackAlloc, typename Fn, typename ... 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_context( palloc, salloc, - std::forward< Fn >( fn), - std::make_tuple( std::forward< Args >( args) ...) ) ) { - ptr_->resume( ptr_.get() ); - } -# endif - - execution_context( execution_context const& other) noexcept : - ptr_( other.ptr_) { - } - - 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) noexcept { - return ptr_->resume( vp); - } - - 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 -# include BOOST_ABI_SUFFIX -# endif - -#endif - -#endif // BOOST_CONTEXT_EXECUTION_CONTEXT_H diff --git a/include/boost/context/execution_context_winfib.ipp b/include/boost/context/execution_context_winfib.ipp deleted file mode 100644 index 1b106fa..0000000 --- a/include/boost/context/execution_context_winfib.ipp +++ /dev/null @@ -1,361 +0,0 @@ - -// 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 -#include -#include - -#ifdef BOOST_HAS_ABI_HEADERS -# include BOOST_ABI_PREFIX -#endif - -namespace boost { -namespace context { -namespace detail { - -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 ptr_t current_rec; - - std::atomic< std::size_t > use_count; - LPVOID fiber; - stack_context sctx; - void * data; - int flags; - - // used for toplevel-context - // (e.g. main context, thread-entry context) - activation_record() noexcept : - use_count( 0), - fiber( nullptr), - sctx(), - flags( flag_main_ctx -# if defined(BOOST_USE_SEGMENTED_STACKS) - | flag_segmented_stack -# endif - ) { - } - - activation_record( stack_context sctx_, bool use_segmented_stack) noexcept : - use_count( 0), - fiber( nullptr), - sctx( sctx_), - data( nullptr), - flags( use_segmented_stack ? flag_segmented_stack : 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; - // context switch from parent context to `this`-context -#if ( _WIN32_WINNT > 0x0600) - if ( ::IsThreadAFiber() ) { - from->fiber = ::GetCurrentFiber(); - } else { - from->fiber = ::ConvertThreadToFiber( nullptr); - } -#else - from->fiber = ::ConvertThreadToFiber( nullptr); - if ( nullptr == from->fiber) { - DWORD err = ::GetLastError(); - BOOST_ASSERT( ERROR_ALREADY_FIBER == err); - from->fiber = ::GetCurrentFiber(); - BOOST_ASSERT( nullptr != from->fiber); - BOOST_ASSERT( reinterpret_cast< LPVOID >( 0x1E00) != from->fiber); - } -#endif - // store passed argument (void pointer) - data = vp; - // context switch - ::SwitchToFiber( fiber); - // access the activation-record of the current fiber - activation_record * ar = static_cast< activation_record * >( GetFiberData() ); - return nullptr != ar ? ar->data : nullptr; - } - - virtual void deallocate() { - delete this; - } - - 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(); - } - } -}; - -struct activation_record_initializer { - activation_record_initializer(); - ~activation_record_initializer(); -}; - -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); - } - -public: - explicit capture_record( - stack_context sctx, StackAlloc const& salloc, - Fn && fn, Tpl && tpl, - activation_record * caller, - bool use_segmented_stack) noexcept : - activation_record( sctx, use_segmented_stack), - salloc_( salloc), - fn_( std::forward< Fn >( fn) ), - tpl_( std::forward< Tpl >( tpl) ), - caller_( caller) { - } - - void deallocate() override final { - destroy( this); - } - - 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 - template< typename AR > - static VOID WINAPI entry_func( LPVOID p) { - BOOST_ASSERT( 0 != p); - - AR * ar( reinterpret_cast< AR * >( p) ); - // start execution of toplevel context-function - ar->run(); - //ctx->fn_(ctx->param_); - ::DeleteFiber( ar->fiber); - } - - typedef boost::intrusive_ptr< detail::activation_record > ptr_t; - - ptr_t ptr_; - - template< typename StackAlloc, typename Fn ,typename Tpl > - static detail::activation_record * create_context( - StackAlloc salloc, - Fn && fn, Tpl && tpl, - bool use_segmented_stack) { - typedef detail::capture_record< Fn, Tpl, StackAlloc > capture_t; - - // hackish - std::size_t fsize = salloc.size_; - // protected_fixedsize_stack needs at least 2*page-size - salloc.size_ = ( std::max)( sizeof( capture_t), 2 * stack_traits::page_size() ); - - stack_context sctx( salloc.allocate() ); - // reserve space for control structure - void * sp = static_cast< char * >( sctx.sp) - sizeof( capture_t); - // get current activation record - ptr_t curr = execution_context::current().ptr_; - // placement new for control structure on fast-context stack - capture_t * cr = new ( sp) capture_t( - sctx, salloc, std::forward< Fn >( fn), std::forward< Tpl >( tpl), curr.get(), use_segmented_stack); - // create fiber - // use default stacksize - cr->fiber = ::CreateFiber( fsize, execution_context::entry_func< capture_t >, cr); - BOOST_ASSERT( nullptr != cr->fiber); - return cr; - } - - template< typename StackAlloc, typename Fn , typename Tpl > - static detail::activation_record * create_context( - preallocated palloc, StackAlloc salloc, - Fn && fn, Tpl && tpl, - bool use_segmented_stack) { - typedef detail::capture_record< Fn, Tpl, StackAlloc > capture_t; - - // hackish - std::size_t fsize = salloc.size_; - // protected_fixedsize_stack needs at least 2*page-size - salloc.size_ = ( std::max)( sizeof( capture_t), 2 * stack_traits::page_size() ); - - // reserve space for control structure - void * sp = static_cast< char * >( palloc.sp) - sizeof( capture_t); - // get current activation record - ptr_t curr = execution_context::current().ptr_; - // placement new for control structure on fast-context stack - capture_t * cr = new ( sp) capture_t( - palloc.sctx, salloc, std::forward< Fn >( fn), std::forward< Tpl >( tpl), curr.get(), use_segmented_stack); - // create fiber - // use default stacksize - cr->fiber = ::CreateFiber( fsize, execution_context::entry_func< capture_t >, cr); - BOOST_ASSERT( nullptr != cr->fiber); - return cr; - } - - execution_context() : - // default constructed with current activation_record - ptr_( detail::activation_record::current_rec) { - } - -public: - static execution_context current() noexcept; - - 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) ...), - false) ) { - ptr_->resume( ptr_.get(), true); - } - - template< typename StackAlloc, typename Fn, typename ... 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_context( salloc, - std::forward< Fn >( fn), - std::make_tuple( std::forward< Args >( args) ...), - false) ) { - ptr_->resume( ptr_.get(), true); - } - - template< typename StackAlloc, typename Fn, typename ... 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_context( palloc, salloc, - std::forward< Fn >( fn), - std::make_tuple( std::forward< Args >( args) ...), - false) ) { - ptr_->resume( ptr_.get(), true); - } - - execution_context( execution_context const& other) noexcept : - ptr_( other.ptr_) { - } - - 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; - } - - explicit operator bool() const noexcept { - return nullptr != ptr_.get(); - } - - bool operator!() const noexcept { - return nullptr == ptr_.get(); - } - - void * operator()( void * vp = nullptr, bool preserve_fpu = false) noexcept { - return ptr_->resume( vp, preserve_fpu); - } -}; - -}} - -# ifdef BOOST_HAS_ABI_HEADERS -# include BOOST_ABI_SUFFIX -# endif - -#endif - -#endif // BOOST_CONTEXT_EXECUTION_CONTEXT_H diff --git a/include/boost/context/preallocated.hpp b/include/boost/context/preallocated.hpp new file mode 100644 index 0000000..862a6a5 --- /dev/null +++ b/include/boost/context/preallocated.hpp @@ -0,0 +1,39 @@ + +// 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_PREALLOCATED_H +#define BOOST_CONTEXT_PREALLOCATED_H + +#include + +#include + +#include + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_PREFIX +#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_) { + } +}; + +}} + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_SUFFIX +#endif + +#endif // BOOST_CONTEXT_PREALLOCATED_H diff --git a/src/execution_context.cpp b/src/execution_context.cpp index 80bff36..19605be 100644 --- a/src/execution_context.cpp +++ b/src/execution_context.cpp @@ -6,7 +6,7 @@ #include -#if ! defined(BOOST_CONTEXT_NO_EXECUTION_CONTEXT) +#if ! defined(BOOST_CONTEXT_NO_CPP14) # include "boost/context/execution_context.hpp" @@ -34,9 +34,11 @@ activation_record_initializer::activation_record_initializer() { } } -activation_record_initializer::~activation_record_initializer() { +activation_record_initializer::~activation_record_initializer() noexcept { if ( 0 == --counter) { - activation_record::current_rec.reset(); + BOOST_ASSERT( activation_record::current_rec->is_main_context() ); + delete activation_record::current_rec.detach(); + } }