diff options
Diffstat (limited to 'boost/context')
-rw-r--r-- | boost/context/all.hpp | 2 | ||||
-rw-r--r-- | boost/context/continuation_fcontext.hpp | 25 | ||||
-rw-r--r-- | boost/context/continuation_ucontext.hpp | 34 | ||||
-rw-r--r-- | boost/context/continuation_winfib.hpp | 19 | ||||
-rw-r--r-- | boost/context/detail/apply.hpp | 4 | ||||
-rw-r--r-- | boost/context/detail/externc.hpp | 23 | ||||
-rw-r--r-- | boost/context/execution_context.hpp | 13 | ||||
-rw-r--r-- | boost/context/execution_context_v1.hpp | 110 | ||||
-rw-r--r-- | boost/context/execution_context_v2.hpp | 102 | ||||
-rw-r--r-- | boost/context/execution_context_v2_void.ipp | 52 | ||||
-rw-r--r-- | boost/context/fiber.hpp | 13 | ||||
-rw-r--r-- | boost/context/fiber_fcontext.hpp | 365 | ||||
-rw-r--r-- | boost/context/fiber_ucontext.hpp | 515 | ||||
-rw-r--r-- | boost/context/fiber_winfib.hpp | 453 |
14 files changed, 1557 insertions, 173 deletions
diff --git a/boost/context/all.hpp b/boost/context/all.hpp index 193eefb937..efee26026a 100644 --- a/boost/context/all.hpp +++ b/boost/context/all.hpp @@ -5,7 +5,9 @@ // http://www.boost.org/LICENSE_1_0.txt) #include <boost/context/continuation.hpp> +#include <boost/context/fiber.hpp> #include <boost/context/fixedsize_stack.hpp> +#include <boost/context/execution_context.hpp> #include <boost/context/pooled_fixedsize_stack.hpp> #include <boost/context/protected_fixedsize_stack.hpp> #include <boost/context/segmented_stack.hpp> diff --git a/boost/context/continuation_fcontext.hpp b/boost/context/continuation_fcontext.hpp index 461933fda5..03f8f6a644 100644 --- a/boost/context/continuation_fcontext.hpp +++ b/boost/context/continuation_fcontext.hpp @@ -138,7 +138,7 @@ public: Ctx c{ fctx }; // invoke context-function #if defined(BOOST_NO_CXX17_STD_INVOKE) - c = invoke( fn_, std::move( c) ); + c = boost::context::detail::invoke( fn_, std::move( c) ); #else c = std::invoke( fn_, std::move( c) ); #endif @@ -238,7 +238,7 @@ public: } continuation( continuation && other) noexcept { - std::swap( fctx_, other.fctx_); + swap( other); } continuation & operator=( continuation && other) noexcept { @@ -252,29 +252,38 @@ public: continuation( continuation const& other) noexcept = delete; continuation & operator=( continuation const& other) noexcept = delete; - continuation resume() { + continuation resume() & { + return std::move( * this).resume(); + } + + continuation resume() && { BOOST_ASSERT( nullptr != fctx_); - return detail::jump_fcontext( + return { detail::jump_fcontext( #if defined(BOOST_NO_CXX14_STD_EXCHANGE) detail::exchange( fctx_, nullptr), #else std::exchange( fctx_, nullptr), #endif - nullptr).fctx; + nullptr).fctx }; + } + + template< typename Fn > + continuation resume_with( Fn && fn) & { + return std::move( * this).resume_with( std::forward< Fn >( fn) ); } template< typename Fn > - continuation resume_with( Fn && fn) { + continuation resume_with( Fn && fn) && { BOOST_ASSERT( nullptr != fctx_); auto p = std::make_tuple( std::forward< Fn >( fn) ); - return detail::ontop_fcontext( + return { detail::ontop_fcontext( #if defined(BOOST_NO_CXX14_STD_EXCHANGE) detail::exchange( fctx_, nullptr), #else std::exchange( fctx_, nullptr), #endif & p, - detail::context_ontop< continuation, Fn >).fctx; + detail::context_ontop< continuation, Fn >).fctx }; } explicit operator bool() const noexcept { diff --git a/boost/context/continuation_ucontext.hpp b/boost/context/continuation_ucontext.hpp index 030d3e9ed6..b2682cf47f 100644 --- a/boost/context/continuation_ucontext.hpp +++ b/boost/context/continuation_ucontext.hpp @@ -37,6 +37,7 @@ extern "C" { #if defined(BOOST_NO_CXX14_STD_EXCHANGE) #include <boost/context/detail/exchange.hpp> #endif +#include <boost/context/detail/externc.hpp> #if defined(BOOST_NO_CXX17_STD_INVOKE) #include <boost/context/detail/invoke.hpp> #endif @@ -52,20 +53,6 @@ extern "C" { # include BOOST_ABI_PREFIX #endif -#if defined(BOOST_USE_ASAN) -extern "C" { -void __sanitizer_start_switch_fiber( void **, const void *, size_t); -void __sanitizer_finish_switch_fiber( void *, const void **, size_t *); -} -#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 { @@ -266,7 +253,7 @@ public: try { // invoke context-function #if defined(BOOST_NO_CXX17_STD_INVOKE) - c = invoke( fn_, std::move( c) ); + c = boost::context::detail::invoke( fn_, std::move( c) ); #else c = std::invoke( fn_, std::move( c) ); #endif @@ -408,7 +395,11 @@ public: return * this; } - continuation resume() { + continuation resume() & { + return std::move( * this).resume(); + } + + continuation resume() && { #if defined(BOOST_NO_CXX14_STD_EXCHANGE) detail::activation_record * ptr = detail::exchange( ptr_, nullptr)->resume(); #else @@ -420,11 +411,16 @@ public: ptr = detail::activation_record::current()->ontop( ptr); detail::activation_record::current()->ontop = nullptr; } - return continuation{ ptr }; + return { ptr }; + } + + template< typename Fn > + continuation resume_with( Fn && fn) & { + return std::move( * this).resume_with( std::forward< Fn >( fn) ); } template< typename Fn > - continuation resume_with( Fn && fn) { + continuation resume_with( Fn && fn) && { #if defined(BOOST_NO_CXX14_STD_EXCHANGE) detail::activation_record * ptr = detail::exchange( ptr_, nullptr)->resume_with< continuation >( std::forward< Fn >( fn) ); @@ -438,7 +434,7 @@ public: ptr = detail::activation_record::current()->ontop( ptr); detail::activation_record::current()->ontop = nullptr; } - return continuation{ ptr }; + return { ptr }; } explicit operator bool() const noexcept { diff --git a/boost/context/continuation_winfib.hpp b/boost/context/continuation_winfib.hpp index 5d24bbf95e..a92463f326 100644 --- a/boost/context/continuation_winfib.hpp +++ b/boost/context/continuation_winfib.hpp @@ -224,7 +224,7 @@ public: try { // invoke context-function #if defined(BOOST_NO_CXX17_STD_INVOKE) - c = invoke( fn_, std::move( c) ); + c = boost::context::detail::invoke( fn_, std::move( c) ); #else c = std::invoke( fn_, std::move( c) ); #endif @@ -334,7 +334,11 @@ public: return * this; } - continuation resume() { + continuation resume() & { + return std::move( * this).resume(); + } + + continuation resume() && { #if defined(BOOST_NO_CXX14_STD_EXCHANGE) detail::activation_record * ptr = detail::exchange( ptr_, nullptr)->resume(); #else @@ -346,11 +350,16 @@ public: ptr = detail::activation_record::current()->ontop( ptr); detail::activation_record::current()->ontop = nullptr; } - return continuation{ ptr }; + return { ptr }; + } + + template< typename Fn > + continuation resume_with( Fn && fn) & { + return std::move( * this).resume_with( std::forward< Fn >( fn) ); } template< typename Fn > - continuation resume_with( Fn && fn) { + continuation resume_with( Fn && fn) && { #if defined(BOOST_NO_CXX14_STD_EXCHANGE) detail::activation_record * ptr = detail::exchange( ptr_, nullptr)->resume_with< continuation >( std::forward< Fn >( fn) ); @@ -364,7 +373,7 @@ public: ptr = detail::activation_record::current()->ontop( ptr); detail::activation_record::current()->ontop = nullptr; } - return continuation{ ptr }; + return { ptr }; } explicit operator bool() const noexcept { diff --git a/boost/context/detail/apply.hpp b/boost/context/detail/apply.hpp index e1dd40a4db..fbf0ca6176 100644 --- a/boost/context/detail/apply.hpp +++ b/boost/context/detail/apply.hpp @@ -37,13 +37,13 @@ template< typename Fn, typename Tpl, std::size_t ... I > auto apply_impl( Fn && fn, Tpl && tpl, index_sequence< I ... >) #if defined(BOOST_NO_CXX17_STD_INVOKE) - -> decltype( invoke( std::forward< Fn >( fn), std::get< I >( std::forward< Tpl >( tpl) ) ... ) ) + -> decltype( boost::context::detail::invoke( std::forward< Fn >( fn), std::get< I >( std::forward< Tpl >( tpl) ) ... ) ) #else -> decltype( std::invoke( std::forward< Fn >( fn), std::get< I >( std::forward< Tpl >( tpl) ) ... ) ) #endif { #if defined(BOOST_NO_CXX17_STD_INVOKE) - return invoke( std::forward< Fn >( fn), std::get< I >( std::forward< Tpl >( tpl) ) ... ); + return boost::context::detail::invoke( std::forward< Fn >( fn), std::get< I >( std::forward< Tpl >( tpl) ) ... ); #else return std::invoke( std::forward< Fn >( fn), std::get< I >( std::forward< Tpl >( tpl) ) ... ); #endif diff --git a/boost/context/detail/externc.hpp b/boost/context/detail/externc.hpp new file mode 100644 index 0000000000..850bc1a3d7 --- /dev/null +++ b/boost/context/detail/externc.hpp @@ -0,0 +1,23 @@ + +// 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) +// + +#include <boost/config.hpp> +#include <boost/context/detail/config.hpp> + +#if defined(BOOST_USE_ASAN) +extern "C" { +void __sanitizer_start_switch_fiber( void **, const void *, size_t); +void __sanitizer_finish_switch_fiber( void *, const void **, size_t *); +} +#endif + +#if defined(BOOST_USE_SEGMENTED_STACKS) +extern "C" { +void __splitstack_getcontext( void * [BOOST_CONTEXT_SEGMENTS]); +void __splitstack_setcontext( void * [BOOST_CONTEXT_SEGMENTS]); +} +#endif diff --git a/boost/context/execution_context.hpp b/boost/context/execution_context.hpp index 2eb02e12ee..5808e027de 100644 --- a/boost/context/execution_context.hpp +++ b/boost/context/execution_context.hpp @@ -6,14 +6,7 @@ #include <boost/context/detail/config.hpp> -#if ! defined(BOOST_CONTEXT_NO_CXX11) -# if (defined(BOOST_EXECUTION_CONTEXT) && (BOOST_EXECUTION_CONTEXT == 1)) -# if !defined(BOOST_NO_CXX11_THREAD_LOCAL) -# include <boost/context/execution_context_v1.hpp> -# else -# error "keyword thread_local not supported" -# endif -# else -# include <boost/context/execution_context_v2.hpp> -# endif +#if !defined(BOOST_NO_CXX11_THREAD_LOCAL) +# include <boost/context/execution_context_v1.hpp> #endif +#include <boost/context/execution_context_v2.hpp> diff --git a/boost/context/execution_context_v1.hpp b/boost/context/execution_context_v1.hpp index 5cf0a91d2a..73008e63ab 100644 --- a/boost/context/execution_context_v1.hpp +++ b/boost/context/execution_context_v1.hpp @@ -4,8 +4,8 @@ // (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 +#ifndef BOOST_CONTEXT_EXECUTION_CONTEXT_V1_H +#define BOOST_CONTEXT_EXECUTION_CONTEXT_V1_H #include <boost/context/detail/config.hpp> @@ -28,6 +28,7 @@ #include <boost/context/detail/apply.hpp> #endif #include <boost/context/detail/disable_overload.hpp> +#include <boost/context/detail/externc.hpp> #include <boost/context/detail/fcontext.hpp> #include <boost/context/fixedsize_stack.hpp> #include <boost/context/flags.hpp> @@ -39,31 +40,24 @@ # 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 { template< typename Fn > -transfer_t context_ontop( transfer_t); +transfer_t ecv1_context_ontop( transfer_t); -struct activation_record; +struct ecv1_activation_record; -struct data_t { - activation_record * from; - void * data; +struct ecv1_data_t { + ecv1_activation_record * from; + void * data; }; -struct BOOST_CONTEXT_DECL activation_record { - typedef boost::intrusive_ptr< activation_record > ptr_t; +struct BOOST_CONTEXT_DECL ecv1_activation_record { + typedef boost::intrusive_ptr< ecv1_activation_record > ptr_t; - thread_local static ptr_t current_rec; + static ptr_t & current() noexcept; std::atomic< std::size_t > use_count{ 0 }; fcontext_t fctx{ nullptr }; @@ -72,15 +66,15 @@ struct BOOST_CONTEXT_DECL activation_record { // used for toplevel-context // (e.g. main context, thread-entry context) - activation_record() = default; + ecv1_activation_record() = default; - activation_record( fcontext_t fctx_, stack_context sctx_) noexcept : + ecv1_activation_record( fcontext_t fctx_, stack_context sctx_) noexcept : 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() = default; + virtual ~ecv1_activation_record() = default; bool is_main_context() const noexcept { return main_ctx; @@ -88,20 +82,20 @@ struct BOOST_CONTEXT_DECL activation_record { void * resume( void * vp) { // store current activation record in local variable - auto from = current_rec.get(); + auto from = current().get(); // store `this` in static, thread local pointer // `this` will become the active (running) context // returned by execution_context::current() - current_rec = this; + current() = this; #if defined(BOOST_USE_SEGMENTED_STACKS) // adjust segmented stack properties __splitstack_getcontext( from->sctx.segments_ctx); __splitstack_setcontext( sctx.segments_ctx); #endif - data_t d = { from, vp }; + ecv1_data_t d = { from, vp }; // context switch from parent context to `this`-context transfer_t t = jump_fcontext( fctx, & d); - data_t * dp = reinterpret_cast< data_t * >( t.data); + ecv1_data_t * dp = reinterpret_cast< ecv1_data_t * >( t.data); dp->from->fctx = t.fctx; // parent context resumed return dp->data; @@ -110,22 +104,22 @@ struct BOOST_CONTEXT_DECL activation_record { template< typename Fn > void * resume_ontop( void * data, Fn && fn) { // store current activation record in local variable - activation_record * from = current_rec.get(); + ecv1_activation_record * from = current().get(); // store `this` in static, thread local pointer // `this` will become the active (running) context // returned by execution_context::current() - current_rec = this; + current() = this; #if defined(BOOST_USE_SEGMENTED_STACKS) // adjust segmented stack properties __splitstack_getcontext( from->sctx.segments_ctx); __splitstack_setcontext( sctx.segments_ctx); #endif std::tuple< void *, Fn > p = std::forward_as_tuple( data, fn); - data_t d = { from, & p }; + ecv1_data_t d = { from, & p }; // context switch from parent context to `this`-context // execute Fn( Tpl) on top of `this` - transfer_t t = ontop_fcontext( fctx, & d, context_ontop< Fn >); - data_t * dp = reinterpret_cast< data_t * >( t.data); + transfer_t t = ontop_fcontext( fctx, & d, ecv1_context_ontop< Fn >); + ecv1_data_t * dp = reinterpret_cast< ecv1_data_t * >( t.data); dp->from->fctx = t.fctx; // parent context resumed return dp->data; @@ -134,11 +128,11 @@ struct BOOST_CONTEXT_DECL activation_record { virtual void deallocate() noexcept { } - friend void intrusive_ptr_add_ref( activation_record * ar) noexcept { + friend void intrusive_ptr_add_ref( ecv1_activation_record * ar) noexcept { ++ar->use_count; } - friend void intrusive_ptr_release( activation_record * ar) noexcept { + friend void intrusive_ptr_release( ecv1_activation_record * ar) noexcept { BOOST_ASSERT( nullptr != ar); if ( 0 == --ar->use_count) { ar->deallocate(); @@ -146,21 +140,21 @@ struct BOOST_CONTEXT_DECL activation_record { } }; -struct BOOST_CONTEXT_DECL activation_record_initializer { - activation_record_initializer() noexcept; - ~activation_record_initializer(); +struct BOOST_CONTEXT_DECL ecv1_activation_record_initializer { + ecv1_activation_record_initializer() noexcept; + ~ecv1_activation_record_initializer(); }; template< typename Fn > -transfer_t context_ontop( transfer_t t) { - data_t * dp = reinterpret_cast< data_t * >( t.data); +transfer_t ecv1_context_ontop( transfer_t t) { + ecv1_data_t * dp = reinterpret_cast< ecv1_data_t * >( t.data); dp->from->fctx = t.fctx; auto tpl = reinterpret_cast< std::tuple< void *, Fn > * >( dp->data); BOOST_ASSERT( nullptr != tpl); auto data = std::get< 0 >( * tpl); typename std::decay< Fn >::type fn = std::forward< Fn >( std::get< 1 >( * tpl) ); #if defined(BOOST_NO_CXX17_STD_APPLY) - dp->data = apply( fn, std::tie( data) ); + dp->data = boost::context::detail::apply( fn, std::tie( data) ); #else dp->data = std::apply( fn, std::tie( data) ); #endif @@ -168,28 +162,28 @@ transfer_t context_ontop( transfer_t t) { } template< typename StackAlloc, typename Fn, typename ... Args > -class capture_record : public activation_record { +class ecv1_capture_record : public ecv1_activation_record { private: typename std::decay< StackAlloc >::type salloc_; typename std::decay< Fn >::type fn_; std::tuple< typename std::decay< Args >::type ... > args_; - activation_record * caller_; + ecv1_activation_record * caller_; - static void destroy( capture_record * p) noexcept { + static void destroy( ecv1_capture_record * p) noexcept { typename std::decay< StackAlloc >::type salloc = std::move( p->salloc_); stack_context sctx = p->sctx; // deallocate activation record - p->~capture_record(); + p->~ecv1_capture_record(); // destroy stack with stack allocator salloc.deallocate( sctx); } public: - capture_record( stack_context sctx, StackAlloc && salloc, + ecv1_capture_record( stack_context sctx, StackAlloc && salloc, fcontext_t fctx, - activation_record * caller, + ecv1_activation_record * caller, Fn && fn, Args && ... args) noexcept : - activation_record{ fctx, sctx }, + ecv1_activation_record{ fctx, sctx }, salloc_{ std::forward< StackAlloc >( salloc) }, fn_( std::forward< Fn >( fn) ), args_( std::forward< Args >( args) ... ), @@ -203,7 +197,7 @@ public: void run() { auto data = caller_->resume( nullptr); #if defined(BOOST_NO_CXX17_STD_APPLY) - apply( fn_, std::tuple_cat( args_, std::tie( data) ) ); + boost::context::detail::apply( fn_, std::tuple_cat( args_, std::tie( data) ) ); #else std::apply( fn_, std::tuple_cat( args_, std::tie( data) ) ); #endif @@ -213,6 +207,8 @@ public: } +namespace v1 { + class BOOST_CONTEXT_DECL execution_context { private: // tampoline function @@ -220,7 +216,7 @@ private: // is resumed for the first time template< typename AR > static void entry_func( detail::transfer_t t) noexcept { - detail::data_t * dp = reinterpret_cast< detail::data_t * >( t.data); + detail::ecv1_data_t * dp = reinterpret_cast< detail::ecv1_data_t * >( t.data); AR * ar = static_cast< AR * >( dp->data); BOOST_ASSERT( nullptr != ar); dp->from->fctx = t.fctx; @@ -228,14 +224,14 @@ private: ar->run(); } - typedef boost::intrusive_ptr< detail::activation_record > ptr_t; + typedef boost::intrusive_ptr< detail::ecv1_activation_record > ptr_t; ptr_t ptr_; template< typename StackAlloc, typename Fn, typename ... Args > - static detail::activation_record * create_context( StackAlloc && salloc, + static detail::ecv1_activation_record * create_context( StackAlloc && salloc, Fn && fn, Args && ... args) { - typedef detail::capture_record< + typedef detail::ecv1_capture_record< StackAlloc, Fn, Args ... > capture_t; @@ -267,9 +263,9 @@ private: } template< typename StackAlloc, typename Fn, typename ... Args > - static detail::activation_record * create_context( preallocated palloc, StackAlloc && salloc, + static detail::ecv1_activation_record * create_context( preallocated palloc, StackAlloc && salloc, Fn && fn, Args && ... args) { - typedef detail::capture_record< + typedef detail::ecv1_capture_record< StackAlloc, Fn, Args ... > capture_t; @@ -300,8 +296,8 @@ private: } execution_context() noexcept : - // default constructed with current activation_record - ptr_{ detail::activation_record::current_rec } { + // default constructed with current ecv1_activation_record + ptr_{ detail::ecv1_activation_record::current() } { } public: @@ -333,7 +329,7 @@ public: // 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( std::forward< StackAlloc >( salloc), + ptr_{ create_context( salloc, std::forward< Fn >( fn), std::forward< Args >( args) ...) } { ptr_->resume( ptr_.get() ); @@ -348,7 +344,7 @@ public: // 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, std::forward< StackAlloc >( salloc), + ptr_{ create_context( palloc, salloc, std::forward< Fn >( fn), std::forward< Args >( args) ...) } { ptr_->resume( ptr_.get() ); @@ -488,10 +484,10 @@ 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_EXECUTION_CONTEXT_H +#endif // BOOST_CONTEXT_EXECUTION_CONTEXT_V1_H diff --git a/boost/context/execution_context_v2.hpp b/boost/context/execution_context_v2.hpp index 86c7ecf001..22a5502603 100644 --- a/boost/context/execution_context_v2.hpp +++ b/boost/context/execution_context_v2.hpp @@ -4,8 +4,8 @@ // (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 +#ifndef BOOST_CONTEXT_EXECUTION_CONTEXT_V2_H +#define BOOST_CONTEXT_EXECUTION_CONTEXT_V2_H #include <boost/context/detail/config.hpp> @@ -51,42 +51,42 @@ namespace boost { namespace context { namespace detail { -transfer_t context_unwind( transfer_t); +transfer_t ecv2_context_unwind( transfer_t); template< typename Rec > -transfer_t context_exit( transfer_t) noexcept; +transfer_t ecv2_context_exit( transfer_t) noexcept; template< typename Rec > -void context_entry( transfer_t) noexcept; +void ecv2_context_etry( transfer_t) noexcept; template< typename Ctx, typename Fn, typename ... Args > -transfer_t context_ontop( transfer_t); +transfer_t ecv2_context_ontop( transfer_t); template< typename Ctx, typename StackAlloc, typename Fn, typename ... Params > -fcontext_t context_create( StackAlloc &&, Fn &&, Params && ...); +fcontext_t ecv2_context_create( StackAlloc &&, Fn &&, Params && ...); template< typename Ctx, typename StackAlloc, typename Fn, typename ... Params > -fcontext_t context_create( preallocated, StackAlloc &&, Fn &&, Params && ...); +fcontext_t ecv2_context_create( preallocated, StackAlloc &&, Fn &&, Params && ...); template< typename Ctx, typename StackAlloc, typename Fn, typename ... Params > -class record { +class ecv2_record { private: typename std::decay< StackAlloc >::type salloc_; stack_context sctx_; typename std::decay< Fn >::type fn_; std::tuple< typename std::decay< Params >::type ... > params_; - static void destroy( record * p) noexcept { + static void destroy( ecv2_record * p) noexcept { typename std::decay< StackAlloc >::type salloc = std::move( p->salloc_); stack_context sctx = p->sctx_; - // deallocate record - p->~record(); + // deallocate ecv2_record + p->~ecv2_record(); // destroy stack with stack allocator salloc.deallocate( sctx); } public: - record( stack_context sctx, StackAlloc && salloc, + ecv2_record( stack_context sctx, StackAlloc && salloc, Fn && fn, Params && ... params) noexcept : salloc_( std::forward< StackAlloc >( salloc)), sctx_( sctx), @@ -94,8 +94,8 @@ public: params_( std::forward< Params >( params) ... ) { } - record( record const&) = delete; - record & operator=( record const&) = delete; + ecv2_record( ecv2_record const&) = delete; + ecv2_record & operator=( ecv2_record const&) = delete; void deallocate() noexcept { destroy( this); @@ -110,7 +110,7 @@ public: std::move( args) ); // invoke context-function #if defined(BOOST_NO_CXX17_STD_APPLY) - Ctx cc = apply( std::move( fn_), std::move( tpl) ); + Ctx cc = boost::context::detail::apply( std::move( fn_), std::move( tpl) ); #else Ctx cc = std::apply( std::move( fn_), std::move( tpl) ); #endif @@ -120,6 +120,8 @@ public: } +inline namespace v2 { + template< typename ... Args > class execution_context { private: @@ -129,10 +131,10 @@ private: typedef std::tuple< execution_context, typename std::decay< Args >::type ... > ret_tpl_t; template< typename Ctx, typename StackAlloc, typename Fn, typename ... Params > - friend class detail::record; + friend class detail::ecv2_record; template< typename Ctx, typename Fn, typename ... ArgsT > - friend detail::transfer_t detail::context_ontop( detail::transfer_t); + friend detail::transfer_t detail::ecv2_context_ontop( detail::transfer_t); detail::fcontext_t fctx_{ nullptr }; @@ -162,7 +164,7 @@ public: // non-type template parameter pack via std::index_sequence_for<> // preserves the number of arguments // used to extract the function arguments from std::tuple<> - fctx_( detail::context_create< execution_context >( + fctx_( detail::ecv2_context_create< execution_context >( fixedsize_stack(), std::forward< Fn >( fn), std::forward< Params >( params) ... ) ) { @@ -178,7 +180,7 @@ public: // non-type template parameter pack via std::index_sequence_for<> // preserves the number of arguments // used to extract the function arguments from std::tuple<> - fctx_( detail::context_create< execution_context >( + fctx_( detail::ecv2_context_create< execution_context >( std::forward< StackAlloc >( salloc), std::forward< Fn >( fn), std::forward< Params >( params) ... ) ) { @@ -194,7 +196,7 @@ public: // non-type template parameter pack via std::index_sequence_for<> // preserves the number of arguments // used to extract the function arguments from std::tuple<> - fctx_( detail::context_create< execution_context >( + fctx_( detail::ecv2_context_create< execution_context >( palloc, std::forward< StackAlloc >( salloc), std::forward< Fn >( fn), std::forward< Params >( params) ... ) ) { @@ -203,7 +205,7 @@ public: ~execution_context() { if ( nullptr != fctx_) { - detail::ontop_fcontext( detail::exchange( fctx_, nullptr), nullptr, detail::context_unwind); + detail::ontop_fcontext( detail::exchange( fctx_, nullptr), nullptr, detail::ecv2_context_unwind); } } @@ -322,7 +324,7 @@ execution_context< Args ... >::operator()( exec_ontop_arg_t, Fn && fn, Args ... detail::transfer_t t = detail::ontop_fcontext( detail::exchange( fctx_, nullptr), & p, - detail::context_ontop< execution_context, Fn, Args ... >); + detail::ecv2_context_ontop< execution_context, Fn, Args ... >); if ( nullptr != t.data) { auto p = static_cast< std::tuple< std::exception_ptr, args_tpl_t > * >( t.data); std::exception_ptr eptr = std::get< 0 >( * p); @@ -338,6 +340,8 @@ execution_context< Args ... >::operator()( exec_ontop_arg_t, Fn && fn, Args ... return std::tuple_cat( std::forward_as_tuple( execution_context( t.fctx) ), std::move( data) ); } +} + namespace detail { template< int N > @@ -357,13 +361,13 @@ struct helper< 1 > { }; inline -transfer_t context_unwind( transfer_t t) { +transfer_t ecv2_context_unwind( transfer_t t) { throw forced_unwind( t.fctx); return { nullptr, nullptr }; } template< typename Rec > -transfer_t context_exit( transfer_t t) noexcept { +transfer_t ecv2_context_exit( transfer_t t) noexcept { Rec * rec = static_cast< Rec * >( t.data); // destroy context stack rec->deallocate(); @@ -371,13 +375,13 @@ transfer_t context_exit( transfer_t t) noexcept { } template< typename Rec > -void context_entry( transfer_t t_) noexcept { +void ecv2_context_etry( transfer_t t_) noexcept { // transfer control structure to the context-stack Rec * rec = static_cast< Rec * >( t_.data); BOOST_ASSERT( nullptr != rec); transfer_t t = { nullptr, nullptr }; try { - // jump back to `context_create()` + // jump back to `ecv2_context_create()` t = jump_fcontext( t_.fctx, nullptr); // start executing t = rec->run( t); @@ -386,12 +390,12 @@ void context_entry( transfer_t t_) noexcept { } BOOST_ASSERT( nullptr != t.fctx); // destroy context-stack of `this`context on next context - ontop_fcontext( t.fctx, rec, context_exit< Rec >); + ontop_fcontext( t.fctx, rec, ecv2_context_exit< Rec >); BOOST_ASSERT_MSG( false, "context already terminated"); } template< typename Ctx, typename Fn, typename ... Args > -transfer_t context_ontop( transfer_t t) { +transfer_t ecv2_context_ontop( transfer_t t) { auto p = static_cast< std::tuple< Fn, std::tuple< std::exception_ptr, std::tuple< Args ... > > > * >( t.data); BOOST_ASSERT( nullptr != p); typename std::decay< Fn >::type fn = std::forward< Fn >( std::get< 0 >( * p) ); @@ -399,7 +403,7 @@ transfer_t context_ontop( transfer_t t) { try { // execute function #if defined(BOOST_NO_CXX17_STD_APPLY) - std::get< 1 >( std::get< 1 >( * p) ) = helper< sizeof ... (Args) >::convert( apply( fn, std::move( args) ) ); + std::get< 1 >( std::get< 1 >( * p) ) = helper< sizeof ... (Args) >::convert( boost::context::detail::apply( fn, std::move( args) ) ); #else std::get< 1 >( std::get< 1 >( * p) ) = helper< sizeof ... (Args) >::convert( std::apply( fn, std::move( args) ) ); #endif @@ -411,17 +415,17 @@ transfer_t context_ontop( transfer_t t) { } template< typename Ctx, typename StackAlloc, typename Fn, typename ... Params > -fcontext_t context_create( StackAlloc && salloc, Fn && fn, Params && ... params) { - typedef record< Ctx, StackAlloc, Fn, Params ... > record_t; +fcontext_t ecv2_context_create( StackAlloc && salloc, Fn && fn, Params && ... params) { + typedef ecv2_record< Ctx, StackAlloc, Fn, Params ... > ecv2_record_t; auto sctx = salloc.allocate(); // reserve space for control structure #if defined(BOOST_NO_CXX11_CONSTEXPR) || defined(BOOST_NO_CXX11_STD_ALIGN) - const std::size_t size = sctx.size - sizeof( record_t); - void * sp = static_cast< char * >( sctx.sp) - sizeof( record_t); + const std::size_t size = sctx.size - sizeof( ecv2_record_t); + void * sp = static_cast< char * >( sctx.sp) - sizeof( ecv2_record_t); #else - constexpr std::size_t func_alignment = 64; // alignof( record_t); - constexpr std::size_t func_size = sizeof( record_t); + constexpr std::size_t func_alignment = 64; // alignof( ecv2_record_t); + constexpr std::size_t func_size = sizeof( ecv2_record_t); // reserve space on stack void * sp = static_cast< char * >( sctx.sp) - func_size - func_alignment; // align sp pointer @@ -432,26 +436,26 @@ fcontext_t context_create( StackAlloc && salloc, Fn && fn, Params && ... params) const std::size_t size = sctx.size - ( static_cast< char * >( sctx.sp) - static_cast< char * >( sp) ); #endif // create fast-context - const fcontext_t fctx = make_fcontext( sp, size, & context_entry< record_t >); + const fcontext_t fctx = make_fcontext( sp, size, & ecv2_context_etry< ecv2_record_t >); BOOST_ASSERT( nullptr != fctx); // placment new for control structure on context-stack - auto rec = ::new ( sp) record_t{ + auto rec = ::new ( sp) ecv2_record_t{ sctx, std::forward< StackAlloc >( salloc), std::forward< Fn >( fn), std::forward< Params >( params) ... }; // transfer control structure to context-stack return jump_fcontext( fctx, rec).fctx; } template< typename Ctx, typename StackAlloc, typename Fn, typename ... Params > -fcontext_t context_create( preallocated palloc, StackAlloc && salloc, Fn && fn, Params && ... params) { - typedef record< Ctx, StackAlloc, Fn, Params ... > record_t; +fcontext_t ecv2_context_create( preallocated palloc, StackAlloc && salloc, Fn && fn, Params && ... params) { + typedef ecv2_record< Ctx, StackAlloc, Fn, Params ... > ecv2_record_t; // reserve space for control structure #if defined(BOOST_NO_CXX11_CONSTEXPR) || defined(BOOST_NO_CXX11_STD_ALIGN) - const std::size_t size = palloc.size - sizeof( record_t); - void * sp = static_cast< char * >( palloc.sp) - sizeof( record_t); + const std::size_t size = palloc.size - sizeof( ecv2_record_t); + void * sp = static_cast< char * >( palloc.sp) - sizeof( ecv2_record_t); #else - constexpr std::size_t func_alignment = 64; // alignof( record_t); - constexpr std::size_t func_size = sizeof( record_t); + constexpr std::size_t func_alignment = 64; // alignof( ecv2_record_t); + constexpr std::size_t func_size = sizeof( ecv2_record_t); // reserve space on stack void * sp = static_cast< char * >( palloc.sp) - func_size - func_alignment; // align sp pointer @@ -462,10 +466,10 @@ fcontext_t context_create( preallocated palloc, StackAlloc && salloc, Fn && fn, const std::size_t size = palloc.size - ( static_cast< char * >( palloc.sp) - static_cast< char * >( sp) ); #endif // create fast-context - const fcontext_t fctx = make_fcontext( sp, size, & context_entry< record_t >); + const fcontext_t fctx = make_fcontext( sp, size, & ecv2_context_etry< ecv2_record_t >); BOOST_ASSERT( nullptr != fctx); // placment new for control structure on context-stack - auto rec = ::new ( sp) record_t{ + auto rec = ::new ( sp) ecv2_record_t{ palloc.sctx, std::forward< StackAlloc >( salloc), std::forward< Fn >( fn), std::forward< Params >( params) ... }; // transfer control structure to context-stack return jump_fcontext( fctx, rec).fctx; @@ -475,12 +479,14 @@ fcontext_t context_create( preallocated palloc, StackAlloc && salloc, Fn && fn, #include <boost/context/execution_context_v2_void.ipp> +inline namespace v2 { + template< typename ... Args > void swap( execution_context< Args ... > & l, execution_context< Args ... > & r) noexcept { l.swap( r); } -}} +}}} #if defined(BOOST_MSVC) # pragma warning(pop) @@ -490,4 +496,4 @@ void swap( execution_context< Args ... > & l, execution_context< Args ... > & r) # include BOOST_ABI_SUFFIX #endif -#endif // BOOST_CONTEXT_EXECUTION_CONTEXT_H +#endif // BOOST_CONTEXT_EXECUTION_CONTEXT_V2_H diff --git a/boost/context/execution_context_v2_void.ipp b/boost/context/execution_context_v2_void.ipp index bab713639a..f262a0c9c9 100644 --- a/boost/context/execution_context_v2_void.ipp +++ b/boost/context/execution_context_v2_void.ipp @@ -7,33 +7,33 @@ namespace detail { template< typename Ctx, typename Fn > -transfer_t context_ontop_void( transfer_t); +transfer_t ecv2_context_ontop_void( transfer_t); template< typename Ctx, typename StackAlloc, typename Fn, typename ... Params > -fcontext_t context_create_void( StackAlloc &&, Fn &&, Params && ...); +fcontext_t ecv2_context_create_void( StackAlloc &&, Fn &&, Params && ...); template< typename Ctx, typename StackAlloc, typename Fn, typename ... Params > -fcontext_t context_create_void( preallocated, StackAlloc &&, Fn &&, Params && ...); +fcontext_t ecv2_context_create_void( preallocated, StackAlloc &&, Fn &&, Params && ...); template< typename Ctx, typename StackAlloc, typename Fn, typename ... Params > -class record_void { +class ecv2_record_void { private: typename std::decay< StackAlloc >::type salloc_; stack_context sctx_; typename std::decay< Fn >::type fn_; std::tuple< typename std::decay< Params >::type ... > params_; - static void destroy( record_void * p) noexcept { + static void destroy( ecv2_record_void * p) noexcept { typename std::decay< StackAlloc >::type salloc = std::move( p->salloc_); stack_context sctx = p->sctx_; // deallocate record - p->~record_void(); + p->~ecv2_record_void(); // destroy stack with stack allocator salloc.deallocate( sctx); } public: - record_void( stack_context sctx, StackAlloc && salloc, + ecv2_record_void( stack_context sctx, StackAlloc && salloc, Fn && fn, Params && ... params) noexcept : salloc_( std::forward< StackAlloc >( salloc) ), sctx_( sctx), @@ -41,8 +41,8 @@ public: params_( std::forward< Params >( params) ... ) { } - record_void( record_void const&) = delete; - record_void & operator=( record_void const&) = delete; + ecv2_record_void( ecv2_record_void const&) = delete; + ecv2_record_void & operator=( ecv2_record_void const&) = delete; void deallocate() noexcept { destroy( this); @@ -52,7 +52,7 @@ public: Ctx from{ t.fctx }; // invoke context-function #if defined(BOOST_NO_CXX17_STD_APPLY) - Ctx cc = apply( fn_, std::tuple_cat( params_, std::forward_as_tuple( std::move( from) ) ) ); + Ctx cc = boost::context::detail::apply( fn_, std::tuple_cat( params_, std::forward_as_tuple( std::move( from) ) ) ); #else Ctx cc = std::apply( fn_, std::tuple_cat( params_, std::forward_as_tuple( std::move( from) ) ) ); #endif @@ -62,16 +62,18 @@ public: } +inline namespace v2 { + template<> class execution_context< void > { private: friend class ontop_error; template< typename Ctx, typename StackAlloc, typename Fn, typename ... Params > - friend class detail::record_void; + friend class detail::ecv2_record_void; template< typename Ctx, typename Fn > - friend detail::transfer_t detail::context_ontop_void( detail::transfer_t); + friend detail::transfer_t detail::ecv2_context_ontop_void( detail::transfer_t); detail::fcontext_t fctx_{ nullptr }; @@ -101,7 +103,7 @@ public: // non-type template parameter pack via std::index_sequence_for<> // preserves the number of arguments // used to extract the function arguments from std::tuple<> - fctx_( detail::context_create_void< execution_context >( + fctx_( detail::ecv2_context_create_void< execution_context >( fixedsize_stack(), std::forward< Fn >( fn), std::forward< Params >( params) ... ) ) { @@ -117,7 +119,7 @@ public: // non-type template parameter pack via std::index_sequence_for<> // preserves the number of arguments // used to extract the function arguments from std::tuple<> - fctx_( detail::context_create_void< execution_context >( + fctx_( detail::ecv2_context_create_void< execution_context >( std::forward< StackAlloc >( salloc), std::forward< Fn >( fn), std::forward< Params >( params) ... ) ) { @@ -133,7 +135,7 @@ public: // non-type template parameter pack via std::index_sequence_for<> // preserves the number of arguments // used to extract the function arguments from std::tuple<> - fctx_( detail::context_create_void< execution_context >( + fctx_( detail::ecv2_context_create_void< execution_context >( palloc, std::forward< StackAlloc >( salloc), std::forward< Fn >( fn), std::forward< Params >( params) ... ) ) { @@ -142,7 +144,7 @@ public: ~execution_context() { if ( nullptr != fctx_) { - detail::ontop_fcontext( detail::exchange( fctx_, nullptr), nullptr, detail::context_unwind); + detail::ontop_fcontext( detail::exchange( fctx_, nullptr), nullptr, detail::ecv2_context_unwind); } } @@ -183,7 +185,7 @@ public: detail::transfer_t t = detail::ontop_fcontext( detail::exchange( fctx_, nullptr), & p, - detail::context_ontop_void< execution_context, Fn >); + detail::ecv2_context_ontop_void< execution_context, Fn >); if ( nullptr != t.data) { std::exception_ptr * eptr = static_cast< std::exception_ptr * >( t.data); try { @@ -242,10 +244,12 @@ public: } }; +} + namespace detail { template< typename Ctx, typename Fn > -transfer_t context_ontop_void( transfer_t t) { +transfer_t ecv2_context_ontop_void( transfer_t t) { auto p = static_cast< std::tuple< Fn, std::exception_ptr > * >( t.data); BOOST_ASSERT( nullptr != p); typename std::decay< Fn >::type fn = std::forward< Fn >( std::get< 0 >( * p) ); @@ -260,8 +264,8 @@ transfer_t context_ontop_void( transfer_t t) { } template< typename Ctx, typename StackAlloc, typename Fn, typename ... Params > -fcontext_t context_create_void( StackAlloc && salloc, Fn && fn, Params && ... params) { - typedef record_void< Ctx, StackAlloc, Fn, Params ... > record_t; +fcontext_t ecv2_context_create_void( StackAlloc && salloc, Fn && fn, Params && ... params) { + typedef ecv2_record_void< Ctx, StackAlloc, Fn, Params ... > record_t; auto sctx = salloc.allocate(); // reserve space for control structure @@ -281,7 +285,7 @@ fcontext_t context_create_void( StackAlloc && salloc, Fn && fn, Params && ... pa const std::size_t size = sctx.size - ( static_cast< char * >( sctx.sp) - static_cast< char * >( sp) ); #endif // create fast-context - const fcontext_t fctx = make_fcontext( sp, size, & context_entry< record_t >); + const fcontext_t fctx = make_fcontext( sp, size, & ecv2_context_etry< record_t >); BOOST_ASSERT( nullptr != fctx); // placment new for control structure on context-stack auto rec = ::new ( sp) record_t{ @@ -291,8 +295,8 @@ fcontext_t context_create_void( StackAlloc && salloc, Fn && fn, Params && ... pa } template< typename Ctx, typename StackAlloc, typename Fn, typename ... Params > -fcontext_t context_create_void( preallocated palloc, StackAlloc && salloc, Fn && fn, Params && ... params) { - typedef record_void< Ctx, StackAlloc, Fn, Params ... > record_t; +fcontext_t ecv2_context_create_void( preallocated palloc, StackAlloc && salloc, Fn && fn, Params && ... params) { + typedef ecv2_record_void< Ctx, StackAlloc, Fn, Params ... > record_t; // reserve space for control structure #if defined(BOOST_NO_CXX11_CONSTEXPR) || defined(BOOST_NO_CXX11_STD_ALIGN) @@ -311,7 +315,7 @@ fcontext_t context_create_void( preallocated palloc, StackAlloc && salloc, Fn && const std::size_t size = palloc.size - ( static_cast< char * >( palloc.sp) - static_cast< char * >( sp) ); #endif // create fast-context - const fcontext_t fctx = make_fcontext( sp, size, & context_entry< record_t >); + const fcontext_t fctx = make_fcontext( sp, size, & ecv2_context_etry< record_t >); BOOST_ASSERT( nullptr != fctx); // placment new for control structure on context-stack auto rec = ::new ( sp) record_t{ diff --git a/boost/context/fiber.hpp b/boost/context/fiber.hpp new file mode 100644 index 0000000000..ff1b79e2da --- /dev/null +++ b/boost/context/fiber.hpp @@ -0,0 +1,13 @@ + +// Copyright Oliver Kowalke 2017. +// 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) + +#if defined(BOOST_USE_UCONTEXT) +#include <boost/context/fiber_ucontext.hpp> +#elif defined(BOOST_USE_WINFIB) +#include <boost/context/fiber_winfib.hpp> +#else +#include <boost/context/fiber_fcontext.hpp> +#endif diff --git a/boost/context/fiber_fcontext.hpp b/boost/context/fiber_fcontext.hpp new file mode 100644 index 0000000000..9b792bc96b --- /dev/null +++ b/boost/context/fiber_fcontext.hpp @@ -0,0 +1,365 @@ + +// Copyright Oliver Kowalke 2017. +// 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_FIBER_H +#define BOOST_CONTEXT_FIBER_H + +#include <boost/context/detail/config.hpp> + +#include <algorithm> +#include <cstddef> +#include <cstdint> +#include <cstdlib> +#include <exception> +#include <functional> +#include <memory> +#include <ostream> +#include <tuple> +#include <utility> + +#include <boost/assert.hpp> +#include <boost/config.hpp> +#include <boost/intrusive_ptr.hpp> + +#if defined(BOOST_NO_CXX14_STD_EXCHANGE) +#include <boost/context/detail/exchange.hpp> +#endif +#if defined(BOOST_NO_CXX17_STD_INVOKE) +#include <boost/context/detail/invoke.hpp> +#endif +#include <boost/context/detail/disable_overload.hpp> +#include <boost/context/detail/exception.hpp> +#include <boost/context/detail/fcontext.hpp> +#include <boost/context/detail/tuple.hpp> +#include <boost/context/fixedsize_stack.hpp> +#include <boost/context/flags.hpp> +#include <boost/context/preallocated.hpp> +#include <boost/context/segmented_stack.hpp> +#include <boost/context/stack_context.hpp> + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_PREFIX +#endif + +#if defined(BOOST_MSVC) +# pragma warning(push) +# pragma warning(disable: 4702) +#endif + +namespace boost { +namespace context { +namespace detail { + +inline +transfer_t fiber_unwind( transfer_t t) { + throw forced_unwind( t.fctx); + return { nullptr, nullptr }; +} + +template< typename Rec > +transfer_t fiber_exit( transfer_t t) noexcept { + Rec * rec = static_cast< Rec * >( t.data); + // destroy context stack + rec->deallocate(); + return { nullptr, nullptr }; +} + +template< typename Rec > +void fiber_entry( transfer_t t) noexcept { + // transfer control structure to the context-stack + Rec * rec = static_cast< Rec * >( t.data); + BOOST_ASSERT( nullptr != t.fctx); + BOOST_ASSERT( nullptr != rec); + try { + // jump back to `create_context()` + t = jump_fcontext( t.fctx, nullptr); + // start executing + t.fctx = rec->run( t.fctx); + } catch ( forced_unwind const& e) { + t = { e.fctx, nullptr }; + } + BOOST_ASSERT( nullptr != t.fctx); + // destroy context-stack of `this`context on next context + ontop_fcontext( t.fctx, rec, fiber_exit< Rec >); + BOOST_ASSERT_MSG( false, "context already terminated"); +} + +template< typename Ctx, typename Fn > +transfer_t fiber_ontop( transfer_t t) { + auto p = static_cast< std::tuple< Fn > * >( t.data); + BOOST_ASSERT( nullptr != p); + typename std::decay< Fn >::type fn = std::get< 0 >( * p); + t.data = nullptr; + // execute function, pass fiber via reference + Ctx c = fn( Ctx{ t.fctx } ); +#if defined(BOOST_NO_CXX14_STD_EXCHANGE) + return { exchange( c.fctx_, nullptr), nullptr }; +#else + return { std::exchange( c.fctx_, nullptr), nullptr }; +#endif +} + +template< typename Ctx, typename StackAlloc, typename Fn > +class fiber_record { +private: + stack_context sctx_; + typename std::decay< StackAlloc >::type salloc_; + typename std::decay< Fn >::type fn_; + + static void destroy( fiber_record * p) noexcept { + typename std::decay< StackAlloc >::type salloc = std::move( p->salloc_); + stack_context sctx = p->sctx_; + // deallocate fiber_record + p->~fiber_record(); + // destroy stack with stack allocator + salloc.deallocate( sctx); + } + +public: + fiber_record( stack_context sctx, StackAlloc && salloc, + Fn && fn) noexcept : + sctx_( sctx), + salloc_( std::forward< StackAlloc >( salloc)), + fn_( std::forward< Fn >( fn) ) { + } + + fiber_record( fiber_record const&) = delete; + fiber_record & operator=( fiber_record const&) = delete; + + void deallocate() noexcept { + destroy( this); + } + + fcontext_t run( fcontext_t fctx) { + // invoke context-function +#if defined(BOOST_NO_CXX17_STD_INVOKE) + Ctx c = boost::context::detail::invoke( fn_, Ctx{ fctx } ); +#else + Ctx c = std::invoke( fn_, Ctx{ fctx } ); +#endif +#if defined(BOOST_NO_CXX14_STD_EXCHANGE) + return exchange( c.fctx_, nullptr); +#else + return std::exchange( c.fctx_, nullptr); +#endif + } +}; + +template< typename Record, typename StackAlloc, typename Fn > +fcontext_t create_fiber1( StackAlloc && salloc, Fn && fn) { + auto sctx = salloc.allocate(); + // reserve space for control structure + void * storage = reinterpret_cast< void * >( + ( reinterpret_cast< uintptr_t >( sctx.sp) - static_cast< uintptr_t >( sizeof( Record) ) ) + & ~static_cast< uintptr_t >( 0xff) ); + // placment new for control structure on context stack + Record * record = new ( storage) Record{ + sctx, std::forward< StackAlloc >( salloc), std::forward< Fn >( fn) }; + // 64byte gab between control structure and stack top + // should be 16byte aligned + void * stack_top = reinterpret_cast< void * >( + reinterpret_cast< uintptr_t >( storage) - static_cast< uintptr_t >( 64) ); + void * stack_bottom = reinterpret_cast< void * >( + reinterpret_cast< uintptr_t >( sctx.sp) - static_cast< uintptr_t >( sctx.size) ); + // create fast-context + const std::size_t size = reinterpret_cast< uintptr_t >( stack_top) - reinterpret_cast< uintptr_t >( stack_bottom); + const fcontext_t fctx = make_fcontext( stack_top, size, & fiber_entry< Record >); + BOOST_ASSERT( nullptr != fctx); + // transfer control structure to context-stack + return jump_fcontext( fctx, record).fctx; +} + +template< typename Record, typename StackAlloc, typename Fn > +fcontext_t create_fiber2( preallocated palloc, StackAlloc && salloc, Fn && fn) { + // reserve space for control structure + void * storage = reinterpret_cast< void * >( + ( reinterpret_cast< uintptr_t >( palloc.sp) - static_cast< uintptr_t >( sizeof( Record) ) ) + & ~ static_cast< uintptr_t >( 0xff) ); + // placment new for control structure on context-stack + Record * record = new ( storage) Record{ + palloc.sctx, std::forward< StackAlloc >( salloc), std::forward< Fn >( fn) }; + // 64byte gab between control structure and stack top + void * stack_top = reinterpret_cast< void * >( + reinterpret_cast< uintptr_t >( storage) - static_cast< uintptr_t >( 64) ); + void * stack_bottom = reinterpret_cast< void * >( + reinterpret_cast< uintptr_t >( palloc.sctx.sp) - static_cast< uintptr_t >( palloc.sctx.size) ); + // create fast-context + const std::size_t size = reinterpret_cast< uintptr_t >( stack_top) - reinterpret_cast< uintptr_t >( stack_bottom); + const fcontext_t fctx = make_fcontext( stack_top, size, & fiber_entry< Record >); + BOOST_ASSERT( nullptr != fctx); + // transfer control structure to context-stack + return jump_fcontext( fctx, record).fctx; +} + +} + +class fiber { +private: + template< typename Ctx, typename StackAlloc, typename Fn > + friend class detail::fiber_record; + + template< typename Ctx, typename Fn > + friend detail::transfer_t + detail::fiber_ontop( detail::transfer_t); + + template< typename StackAlloc, typename Fn > + friend fiber + callcc( std::allocator_arg_t, StackAlloc &&, Fn &&); + + template< typename StackAlloc, typename Fn > + friend fiber + callcc( std::allocator_arg_t, preallocated, StackAlloc &&, Fn &&); + + detail::fcontext_t fctx_{ nullptr }; + + fiber( detail::fcontext_t fctx) noexcept : + fctx_{ fctx } { + } + +public: + fiber() noexcept = default; + + template< typename Fn, typename = detail::disable_overload< fiber, Fn > > + fiber( Fn && fn) : + fiber{ std::allocator_arg, fixedsize_stack(), std::forward< Fn >( fn) } { + } + + template< typename StackAlloc, typename Fn > + fiber( std::allocator_arg_t, StackAlloc && salloc, Fn && fn) : + fctx_{ detail::create_fiber1< detail::fiber_record< fiber, StackAlloc, Fn > >( + std::forward< StackAlloc >( salloc), std::forward< Fn >( fn) ) } { + } + + template< typename StackAlloc, typename Fn > + fiber( std::allocator_arg_t, preallocated palloc, StackAlloc && salloc, Fn && fn) : + fctx_{ detail::create_fiber2< detail::fiber_record< fiber, StackAlloc, Fn > >( + palloc, std::forward< StackAlloc >( salloc), std::forward< Fn >( fn) ) } { + } + +#if defined(BOOST_USE_SEGMENTED_STACKS) + template< typename Fn > + fiber( std::allocator_arg_t, segmented_stack, Fn &&); + + template< typename StackAlloc, typename Fn > + fiber( std::allocator_arg_t, preallocated, segmented_stack, Fn &&); +#endif + + ~fiber() { + if ( BOOST_UNLIKELY( nullptr != fctx_) ) { + detail::ontop_fcontext( +#if defined(BOOST_NO_CXX14_STD_EXCHANGE) + detail::exchange( fctx_, nullptr), +#else + std::exchange( fctx_, nullptr), +#endif + nullptr, + detail::fiber_unwind); + } + } + + fiber( fiber && other) noexcept { + swap( other); + } + + fiber & operator=( fiber && other) noexcept { + if ( BOOST_LIKELY( this != & other) ) { + fiber tmp = std::move( other); + swap( tmp); + } + return * this; + } + + fiber( fiber const& other) noexcept = delete; + fiber & operator=( fiber const& other) noexcept = delete; + + fiber resume() && { + BOOST_ASSERT( nullptr != fctx_); + return { detail::jump_fcontext( +#if defined(BOOST_NO_CXX14_STD_EXCHANGE) + detail::exchange( fctx_, nullptr), +#else + std::exchange( fctx_, nullptr), +#endif + nullptr).fctx }; + } + + template< typename Fn > + fiber resume_with( Fn && fn) && { + BOOST_ASSERT( nullptr != fctx_); + auto p = std::make_tuple( std::forward< Fn >( fn) ); + return { detail::ontop_fcontext( +#if defined(BOOST_NO_CXX14_STD_EXCHANGE) + detail::exchange( fctx_, nullptr), +#else + std::exchange( fctx_, nullptr), +#endif + & p, + detail::fiber_ontop< fiber, Fn >).fctx }; + } + + explicit operator bool() const noexcept { + return nullptr != fctx_; + } + + bool operator!() const noexcept { + return nullptr == fctx_; + } + + bool operator==( fiber const& other) const noexcept { + return fctx_ == other.fctx_; + } + + bool operator!=( fiber const& other) const noexcept { + return fctx_ != other.fctx_; + } + + bool operator<( fiber const& other) const noexcept { + return fctx_ < other.fctx_; + } + + bool operator>( fiber const& other) const noexcept { + return other.fctx_ < fctx_; + } + + bool operator<=( fiber const& other) const noexcept { + return ! ( * this > other); + } + + bool operator>=( fiber 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, fiber const& other) { + if ( nullptr != other.fctx_) { + return os << other.fctx_; + } else { + return os << "{not-a-context}"; + } + } + + void swap( fiber & other) noexcept { + std::swap( fctx_, other.fctx_); + } +}; + +inline +void swap( fiber & l, fiber & r) noexcept { + l.swap( r); +} + +}} + +#if defined(BOOST_MSVC) +# pragma warning(pop) +#endif + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_SUFFIX +#endif + +#endif // BOOST_CONTEXT_FIBER_H diff --git a/boost/context/fiber_ucontext.hpp b/boost/context/fiber_ucontext.hpp new file mode 100644 index 0000000000..ae62389526 --- /dev/null +++ b/boost/context/fiber_ucontext.hpp @@ -0,0 +1,515 @@ + +// Copyright Oliver Kowalke 2017. +// 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_FIBER_H +#define BOOST_CONTEXT_FIBER_H + +#include <boost/predef.h> +#if BOOST_OS_MACOS +#define _XOPEN_SOURCE 600 +#endif + +extern "C" { +#include <ucontext.h> +} + +#include <boost/context/detail/config.hpp> + +#include <algorithm> +#include <cstddef> +#include <cstdint> +#include <cstdlib> +#include <cstring> +#include <functional> +#include <memory> +#include <ostream> +#include <system_error> +#include <tuple> +#include <utility> + +#include <boost/assert.hpp> +#include <boost/config.hpp> + +#include <boost/context/detail/disable_overload.hpp> +#if defined(BOOST_NO_CXX14_STD_EXCHANGE) +#include <boost/context/detail/exchange.hpp> +#endif +#include <boost/context/detail/externc.hpp> +#if defined(BOOST_NO_CXX17_STD_INVOKE) +#include <boost/context/detail/invoke.hpp> +#endif +#include <boost/context/fixedsize_stack.hpp> +#include <boost/context/flags.hpp> +#include <boost/context/preallocated.hpp> +#if defined(BOOST_USE_SEGMENTED_STACKS) +#include <boost/context/segmented_stack.hpp> +#endif +#include <boost/context/stack_context.hpp> + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_PREFIX +#endif + +namespace boost { +namespace context { +namespace detail { + +// tampoline function +// entered if the execution context +// is resumed for the first time +template< typename Record > +static void fiber_entry_func( void * data) noexcept { + Record * record = static_cast< Record * >( data); + BOOST_ASSERT( nullptr != record); + // start execution of toplevel context-function + record->run(); +} + +struct BOOST_CONTEXT_DECL fiber_activation_record { + ucontext_t uctx{}; + stack_context sctx{}; + bool main_ctx{ true }; + fiber_activation_record * from{ nullptr }; + std::function< fiber_activation_record*(fiber_activation_record*&) > ontop{}; + bool terminated{ false }; + bool force_unwind{ false }; +#if defined(BOOST_USE_ASAN) + void * fake_stack{ nullptr }; + void * stack_bottom{ nullptr }; + std::size_t stack_size{ 0 }; +#endif + + static fiber_activation_record *& current() noexcept; + + // used for toplevel-context + // (e.g. main context, thread-entry context) + fiber_activation_record() { + if ( BOOST_UNLIKELY( 0 != ::getcontext( & uctx) ) ) { + throw std::system_error( + std::error_code( errno, std::system_category() ), + "getcontext() failed"); + } + } + + fiber_activation_record( stack_context sctx_) noexcept : + sctx( sctx_ ), + main_ctx( false ) { + } + + virtual ~fiber_activation_record() { + } + + fiber_activation_record( fiber_activation_record const&) = delete; + fiber_activation_record & operator=( fiber_activation_record const&) = delete; + + bool is_main_context() const noexcept { + return main_ctx; + } + + fiber_activation_record * resume() { + from = current(); + // store `this` in static, thread local pointer + // `this` will become the active (running) context + current() = this; +#if defined(BOOST_USE_SEGMENTED_STACKS) + // adjust segmented stack properties + __splitstack_getcontext( from->sctx.segments_ctx); + __splitstack_setcontext( sctx.segments_ctx); +#endif +#if defined(BOOST_USE_ASAN) + if ( terminated) { + __sanitizer_start_switch_fiber( nullptr, stack_bottom, stack_size); + } else { + __sanitizer_start_switch_fiber( & from->fake_stack, stack_bottom, stack_size); + } +#endif + // context switch from parent context to `this`-context + ::swapcontext( & from->uctx, & uctx); +#if defined(BOOST_USE_ASAN) + __sanitizer_finish_switch_fiber( current()->fake_stack, + (const void **) & current()->from->stack_bottom, + & current()->from->stack_size); +#endif +#if defined(BOOST_NO_CXX14_STD_EXCHANGE) + return exchange( current()->from, nullptr); +#else + return std::exchange( current()->from, nullptr); +#endif + } + + template< typename Ctx, typename Fn > + fiber_activation_record * resume_with( Fn && fn) { + from = current(); + // store `this` in static, thread local pointer + // `this` will become the active (running) context + // returned by fiber::current() + current() = this; +#if defined(BOOST_NO_CXX14_GENERIC_LAMBDAS) + current()->ontop = std::bind( + [](typename std::decay< Fn >::type & fn, fiber_activation_record *& ptr){ + Ctx c{ ptr }; + c = fn( std::move( c) ); + if ( ! c) { + ptr = nullptr; + } +#if defined(BOOST_NO_CXX14_STD_EXCHANGE) + return exchange( c.ptr_, nullptr); +#else + return std::exchange( c.ptr_, nullptr); +#endif + }, + std::forward< Fn >( fn), + std::placeholders::_1); +#else + current()->ontop = [fn=std::forward<Fn>(fn)](fiber_activation_record *& ptr){ + Ctx c{ ptr }; + c = fn( std::move( c) ); + if ( ! c) { + ptr = nullptr; + } +#if defined(BOOST_NO_CXX14_STD_EXCHANGE) + return exchange( c.ptr_, nullptr); +#else + return std::exchange( c.ptr_, nullptr); +#endif + }; +#endif +#if defined(BOOST_USE_SEGMENTED_STACKS) + // adjust segmented stack properties + __splitstack_getcontext( from->sctx.segments_ctx); + __splitstack_setcontext( sctx.segments_ctx); +#endif +#if defined(BOOST_USE_ASAN) + __sanitizer_start_switch_fiber( & from->fake_stack, stack_bottom, stack_size); +#endif + // context switch from parent context to `this`-context + ::swapcontext( & from->uctx, & uctx); +#if defined(BOOST_USE_ASAN) + __sanitizer_finish_switch_fiber( current()->fake_stack, + (const void **) & current()->from->stack_bottom, + & current()->from->stack_size); +#endif +#if defined(BOOST_NO_CXX14_STD_EXCHANGE) + return exchange( current()->from, nullptr); +#else + return std::exchange( current()->from, nullptr); +#endif + } + + virtual void deallocate() noexcept { + } +}; + +struct BOOST_CONTEXT_DECL fiber_activation_record_initializer { + fiber_activation_record_initializer() noexcept; + ~fiber_activation_record_initializer(); +}; + +struct forced_unwind { + fiber_activation_record * from{ nullptr }; + + forced_unwind( fiber_activation_record * from_) noexcept : + from{ from_ } { + } +}; + +template< typename Ctx, typename StackAlloc, typename Fn > +class fiber_capture_record : public fiber_activation_record { +private: + typename std::decay< StackAlloc >::type salloc_; + typename std::decay< Fn >::type fn_; + + static void destroy( fiber_capture_record * p) noexcept { + typename std::decay< StackAlloc >::type salloc = std::move( p->salloc_); + stack_context sctx = p->sctx; + // deallocate activation record + p->~fiber_capture_record(); + // destroy stack with stack allocator + salloc.deallocate( sctx); + } + +public: + fiber_capture_record( stack_context sctx, StackAlloc && salloc, Fn && fn) noexcept : + fiber_activation_record{ sctx }, + salloc_{ std::forward< StackAlloc >( salloc) }, + fn_( std::forward< Fn >( fn) ) { + } + + void deallocate() noexcept override final { + BOOST_ASSERT( main_ctx || ( ! main_ctx && terminated) ); + destroy( this); + } + + void run() { +#if defined(BOOST_USE_ASAN) + __sanitizer_finish_switch_fiber( fake_stack, + (const void **) & from->stack_bottom, + & from->stack_size); +#endif + Ctx c{ from }; + try { + // invoke context-function +#if defined(BOOST_NO_CXX17_STD_INVOKE) + c = boost::context::detail::invoke( fn_, std::move( c) ); +#else + c = std::invoke( fn_, std::move( c) ); +#endif + } catch ( forced_unwind const& ex) { + c = Ctx{ ex.from }; + } + // this context has finished its task + from = nullptr; + ontop = nullptr; + terminated = true; + force_unwind = false; + std::move( c).resume(); + BOOST_ASSERT_MSG( false, "fiber already terminated"); + } +}; + +template< typename Ctx, typename StackAlloc, typename Fn > +static fiber_activation_record * create_fiber1( StackAlloc && salloc, Fn && fn) { + typedef fiber_capture_record< Ctx, StackAlloc, Fn > capture_t; + + auto sctx = salloc.allocate(); + // reserve space for control structure + void * storage = reinterpret_cast< void * >( + ( reinterpret_cast< uintptr_t >( sctx.sp) - static_cast< uintptr_t >( sizeof( capture_t) ) ) + & ~ static_cast< uintptr_t >( 0xff) ); + // placment new for control structure on context stack + capture_t * record = new ( storage) capture_t{ + sctx, std::forward< StackAlloc >( salloc), std::forward< Fn >( fn) }; + // stack bottom + void * stack_bottom = reinterpret_cast< void * >( + reinterpret_cast< uintptr_t >( sctx.sp) - static_cast< uintptr_t >( sctx.size) ); + // create user-context + if ( BOOST_UNLIKELY( 0 != ::getcontext( & record->uctx) ) ) { + throw std::system_error( + std::error_code( errno, std::system_category() ), + "getcontext() failed"); + } + record->uctx.uc_stack.ss_sp = stack_bottom; + // 64byte gap between control structure and stack top + record->uctx.uc_stack.ss_size = reinterpret_cast< uintptr_t >( storage) - + reinterpret_cast< uintptr_t >( stack_bottom) - static_cast< uintptr_t >( 64); + record->uctx.uc_link = nullptr; + ::makecontext( & record->uctx, ( void (*)() ) & fiber_entry_func< capture_t >, 1, record); +#if defined(BOOST_USE_ASAN) + record->stack_bottom = record->uctx.uc_stack.ss_sp; + record->stack_size = record->uctx.uc_stack.ss_size; +#endif + return record; +} + +template< typename Ctx, typename StackAlloc, typename Fn > +static fiber_activation_record * create_fiber2( preallocated palloc, StackAlloc && salloc, Fn && fn) { + typedef fiber_capture_record< Ctx, StackAlloc, Fn > capture_t; + + // reserve space for control structure + void * storage = reinterpret_cast< void * >( + ( reinterpret_cast< uintptr_t >( palloc.sp) - static_cast< uintptr_t >( sizeof( capture_t) ) ) + & ~ static_cast< uintptr_t >( 0xff) ); + // placment new for control structure on context stack + capture_t * record = new ( storage) capture_t{ + palloc.sctx, std::forward< StackAlloc >( salloc), std::forward< Fn >( fn) }; + // stack bottom + void * stack_bottom = reinterpret_cast< void * >( + reinterpret_cast< uintptr_t >( palloc.sctx.sp) - static_cast< uintptr_t >( palloc.sctx.size) ); + // create user-context + if ( BOOST_UNLIKELY( 0 != ::getcontext( & record->uctx) ) ) { + throw std::system_error( + std::error_code( errno, std::system_category() ), + "getcontext() failed"); + } + record->uctx.uc_stack.ss_sp = stack_bottom; + // 64byte gap between control structure and stack top + record->uctx.uc_stack.ss_size = reinterpret_cast< uintptr_t >( storage) - + reinterpret_cast< uintptr_t >( stack_bottom) - static_cast< uintptr_t >( 64); + record->uctx.uc_link = nullptr; + ::makecontext( & record->uctx, ( void (*)() ) & fiber_entry_func< capture_t >, 1, record); +#if defined(BOOST_USE_ASAN) + record->stack_bottom = record->uctx.uc_stack.ss_sp; + record->stack_size = record->uctx.uc_stack.ss_size; +#endif + return record; +} + +} + +class BOOST_CONTEXT_DECL fiber { +private: + friend struct detail::fiber_activation_record; + + template< typename Ctx, typename StackAlloc, typename Fn > + friend class detail::fiber_capture_record; + + template< typename Ctx, typename StackAlloc, typename Fn > + friend detail::fiber_activation_record * detail::create_fiber1( StackAlloc &&, Fn &&); + + template< typename Ctx, typename StackAlloc, typename Fn > + friend detail::fiber_activation_record * detail::create_fiber2( preallocated, StackAlloc &&, Fn &&); + + template< typename StackAlloc, typename Fn > + friend fiber + callcc( std::allocator_arg_t, StackAlloc &&, Fn &&); + + template< typename StackAlloc, typename Fn > + friend fiber + callcc( std::allocator_arg_t, preallocated, StackAlloc &&, Fn &&); + + detail::fiber_activation_record * ptr_{ nullptr }; + + fiber( detail::fiber_activation_record * ptr) noexcept : + ptr_{ ptr } { + } + +public: + fiber() = default; + + template< typename Fn, typename = detail::disable_overload< fiber, Fn > > + fiber( Fn && fn) : + fiber{ + std::allocator_arg, +#if defined(BOOST_USE_SEGMENTED_STACKS) + segmented_stack(), +#else + fixedsize_stack(), +#endif + std::forward< Fn >( fn) } { + } + + template< typename StackAlloc, typename Fn > + fiber( std::allocator_arg_t, StackAlloc && salloc, Fn && fn) : + ptr_{ detail::create_fiber1< fiber >( + std::forward< StackAlloc >( salloc), std::forward< Fn >( fn) ) } { + } + + template< typename StackAlloc, typename Fn > + fiber( std::allocator_arg_t, preallocated palloc, StackAlloc && salloc, Fn && fn) : + ptr_{ detail::create_fiber2< fiber >( + palloc, std::forward< StackAlloc >( salloc), std::forward< Fn >( fn) ) } { + } + + ~fiber() { + if ( BOOST_UNLIKELY( nullptr != ptr_) && ! ptr_->main_ctx) { + if ( BOOST_LIKELY( ! ptr_->terminated) ) { + ptr_->force_unwind = true; + ptr_->resume(); + BOOST_ASSERT( ptr_->terminated); + } + ptr_->deallocate(); + } + } + + fiber( fiber const&) = delete; + fiber & operator=( fiber const&) = delete; + + fiber( fiber && other) noexcept { + swap( other); + } + + fiber & operator=( fiber && other) noexcept { + if ( BOOST_LIKELY( this != & other) ) { + fiber tmp = std::move( other); + swap( tmp); + } + return * this; + } + + fiber resume() && { + BOOST_ASSERT( nullptr != ptr_); +#if defined(BOOST_NO_CXX14_STD_EXCHANGE) + detail::fiber_activation_record * ptr = detail::exchange( ptr_, nullptr)->resume(); +#else + detail::fiber_activation_record * ptr = std::exchange( ptr_, nullptr)->resume(); +#endif + if ( BOOST_UNLIKELY( detail::fiber_activation_record::current()->force_unwind) ) { + throw detail::forced_unwind{ ptr}; + } else if ( BOOST_UNLIKELY( nullptr != detail::fiber_activation_record::current()->ontop) ) { + ptr = detail::fiber_activation_record::current()->ontop( ptr); + detail::fiber_activation_record::current()->ontop = nullptr; + } + return { ptr }; + } + + template< typename Fn > + fiber resume_with( Fn && fn) && { + BOOST_ASSERT( nullptr != ptr_); +#if defined(BOOST_NO_CXX14_STD_EXCHANGE) + detail::fiber_activation_record * ptr = + detail::exchange( ptr_, nullptr)->resume_with< fiber >( std::forward< Fn >( fn) ); +#else + detail::fiber_activation_record * ptr = + std::exchange( ptr_, nullptr)->resume_with< fiber >( std::forward< Fn >( fn) ); +#endif + if ( BOOST_UNLIKELY( detail::fiber_activation_record::current()->force_unwind) ) { + throw detail::forced_unwind{ ptr}; + } else if ( BOOST_UNLIKELY( nullptr != detail::fiber_activation_record::current()->ontop) ) { + ptr = detail::fiber_activation_record::current()->ontop( ptr); + detail::fiber_activation_record::current()->ontop = nullptr; + } + return { ptr }; + } + + explicit operator bool() const noexcept { + return nullptr != ptr_ && ! ptr_->terminated; + } + + bool operator!() const noexcept { + return nullptr == ptr_ || ptr_->terminated; + } + + bool operator==( fiber const& other) const noexcept { + return ptr_ == other.ptr_; + } + + bool operator!=( fiber const& other) const noexcept { + return ptr_ != other.ptr_; + } + + bool operator<( fiber const& other) const noexcept { + return ptr_ < other.ptr_; + } + + bool operator>( fiber const& other) const noexcept { + return other.ptr_ < ptr_; + } + + bool operator<=( fiber const& other) const noexcept { + return ! ( * this > other); + } + + bool operator>=( fiber 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, fiber const& other) { + if ( nullptr != other.ptr_) { + return os << other.ptr_; + } else { + return os << "{not-a-context}"; + } + } + + void swap( fiber & other) noexcept { + std::swap( ptr_, other.ptr_); + } +}; + +inline +void swap( fiber & l, fiber & r) noexcept { + l.swap( r); +} + +}} + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_SUFFIX +#endif + +#endif // BOOST_CONTEXT_FIBER_H diff --git a/boost/context/fiber_winfib.hpp b/boost/context/fiber_winfib.hpp new file mode 100644 index 0000000000..cfd570a2e7 --- /dev/null +++ b/boost/context/fiber_winfib.hpp @@ -0,0 +1,453 @@ + +// Copyright Oliver Kowalke 2017. +// 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_FIBER_H +#define BOOST_CONTEXT_FIBER_H + +#include <windows.h> + +#include <boost/context/detail/config.hpp> + +#include <algorithm> +#include <cstddef> +#include <cstdint> +#include <cstdlib> +#include <cstring> +#include <functional> +#include <memory> +#include <ostream> +#include <system_error> +#include <tuple> +#include <utility> + +#include <boost/assert.hpp> +#include <boost/config.hpp> + +#include <boost/context/detail/disable_overload.hpp> +#if defined(BOOST_NO_CXX14_STD_EXCHANGE) +#include <boost/context/detail/exchange.hpp> +#endif +#if defined(BOOST_NO_CXX17_STD_INVOKE) +#include <boost/context/detail/invoke.hpp> +#endif +#include <boost/context/fixedsize_stack.hpp> +#include <boost/context/flags.hpp> +#include <boost/context/preallocated.hpp> +#include <boost/context/stack_context.hpp> + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_PREFIX +#endif + +#if defined(BOOST_MSVC) +# pragma warning(push) +# pragma warning(disable: 4702) +#endif + +namespace boost { +namespace context { +namespace detail { + +// tampoline function +// entered if the execution context +// is resumed for the first time +template< typename Record > +static VOID WINAPI fiber_entry_func( LPVOID data) noexcept { + Record * record = static_cast< Record * >( data); + BOOST_ASSERT( nullptr != record); + // start execution of toplevel context-function + record->run(); +} + +struct BOOST_CONTEXT_DECL fiber_activation_record { + LPVOID fiber{ nullptr }; + stack_context sctx{}; + bool main_ctx{ true }; + fiber_activation_record * from{ nullptr }; + std::function< fiber_activation_record*(fiber_activation_record*&) > ontop{}; + bool terminated{ false }; + bool force_unwind{ false }; + + static fiber_activation_record *& current() noexcept; + + // used for toplevel-context + // (e.g. main context, thread-entry context) + fiber_activation_record() noexcept { +#if ( _WIN32_WINNT > 0x0600) + if ( ::IsThreadAFiber() ) { + fiber = ::GetCurrentFiber(); + } else { + fiber = ::ConvertThreadToFiber( nullptr); + } +#else + fiber = ::ConvertThreadToFiber( nullptr); + if ( BOOST_UNLIKELY( nullptr == fiber) ) { + DWORD err = ::GetLastError(); + BOOST_ASSERT( ERROR_ALREADY_FIBER == err); + fiber = ::GetCurrentFiber(); + BOOST_ASSERT( nullptr != fiber); + BOOST_ASSERT( reinterpret_cast< LPVOID >( 0x1E00) != fiber); + } +#endif + } + + fiber_activation_record( stack_context sctx_) noexcept : + sctx{ sctx_ }, + main_ctx{ false } { + } + + virtual ~fiber_activation_record() { + if ( BOOST_UNLIKELY( main_ctx) ) { + ::ConvertFiberToThread(); + } else { + ::DeleteFiber( fiber); + } + } + + fiber_activation_record( fiber_activation_record const&) = delete; + fiber_activation_record & operator=( fiber_activation_record const&) = delete; + + bool is_main_context() const noexcept { + return main_ctx; + } + + fiber_activation_record * resume() { + from = current(); + // store `this` in static, thread local pointer + // `this` will become the active (running) context + current() = this; + // context switch from parent context to `this`-context + // context switch + ::SwitchToFiber( fiber); +#if defined(BOOST_NO_CXX14_STD_EXCHANGE) + return detail::exchange( current()->from, nullptr); +#else + return std::exchange( current()->from, nullptr); +#endif + } + + template< typename Ctx, typename Fn > + fiber_activation_record * resume_with( Fn && fn) { + from = current(); + // store `this` in static, thread local pointer + // `this` will become the active (running) context + // returned by fiber::current() + current() = this; +#if defined(BOOST_NO_CXX14_GENERIC_LAMBDAS) + current()->ontop = std::bind( + [](typename std::decay< Fn >::type & fn, fiber_activation_record *& ptr){ + Ctx c{ ptr }; + c = fn( std::move( c) ); + if ( ! c) { + ptr = nullptr; + } +#if defined(BOOST_NO_CXX14_STD_EXCHANGE) + return exchange( c.ptr_, nullptr); +#else + return std::exchange( c.ptr_, nullptr); +#endif + }, + std::forward< Fn >( fn), + std::placeholders::_1); +#else + current()->ontop = [fn=std::forward<Fn>(fn)](fiber_activation_record *& ptr){ + Ctx c{ ptr }; + c = fn( std::move( c) ); + if ( ! c) { + ptr = nullptr; + } +#if defined(BOOST_NO_CXX14_STD_EXCHANGE) + return exchange( c.ptr_, nullptr); +#else + return std::exchange( c.ptr_, nullptr); +#endif + }; +#endif + // context switch + ::SwitchToFiber( fiber); +#if defined(BOOST_NO_CXX14_STD_EXCHANGE) + return detail::exchange( current()->from, nullptr); +#else + return std::exchange( current()->from, nullptr); +#endif + } + + virtual void deallocate() noexcept { + } +}; + +struct BOOST_CONTEXT_DECL fiber_activation_record_initializer { + fiber_activation_record_initializer() noexcept; + ~fiber_activation_record_initializer(); +}; + +struct forced_unwind { + fiber_activation_record * from{ nullptr }; + + explicit forced_unwind( fiber_activation_record * from_) : + from{ from_ } { + } +}; + +template< typename Ctx, typename StackAlloc, typename Fn > +class fiber_capture_record : public fiber_activation_record { +private: + typename std::decay< StackAlloc >::type salloc_; + typename std::decay< Fn >::type fn_; + + static void destroy( fiber_capture_record * p) noexcept { + typename std::decay< StackAlloc >::type salloc = std::move( p->salloc_); + stack_context sctx = p->sctx; + // deallocate activation record + p->~fiber_capture_record(); + // destroy stack with stack allocator + salloc.deallocate( sctx); + } + +public: + fiber_capture_record( stack_context sctx, StackAlloc && salloc, Fn && fn) noexcept : + fiber_activation_record( sctx), + salloc_( std::forward< StackAlloc >( salloc)), + fn_( std::forward< Fn >( fn) ) { + } + + void deallocate() noexcept override final { + BOOST_ASSERT( main_ctx || ( ! main_ctx && terminated) ); + destroy( this); + } + + void run() { + Ctx c{ from }; + try { + // invoke context-function +#if defined(BOOST_NO_CXX17_STD_INVOKE) + c = boost::context::detail::invoke( fn_, std::move( c) ); +#else + c = std::invoke( fn_, std::move( c) ); +#endif + } catch ( forced_unwind const& ex) { + c = Ctx{ ex.from }; + } + // this context has finished its task + from = nullptr; + ontop = nullptr; + terminated = true; + force_unwind = false; + std::move( c).resume(); + BOOST_ASSERT_MSG( false, "fiber already terminated"); + } +}; + +template< typename Ctx, typename StackAlloc, typename Fn > +static fiber_activation_record * create_fiber1( StackAlloc && salloc, Fn && fn) { + typedef fiber_capture_record< Ctx, StackAlloc, Fn > capture_t; + + auto sctx = salloc.allocate(); + BOOST_ASSERT( ( sizeof( capture_t) ) < sctx.size); + // reserve space for control structure + void * storage = reinterpret_cast< void * >( + ( reinterpret_cast< uintptr_t >( sctx.sp) - static_cast< uintptr_t >( sizeof( capture_t) ) ) + & ~ static_cast< uintptr_t >( 0xff) ); + // placment new for control structure on context stack + capture_t * record = new ( storage) capture_t{ + sctx, std::forward< StackAlloc >( salloc), std::forward< Fn >( fn) }; + // create user-context + record->fiber = ::CreateFiber( sctx.size, & detail::fiber_entry_func< capture_t >, record); + return record; +} + +template< typename Ctx, typename StackAlloc, typename Fn > +static fiber_activation_record * create_fiber2( preallocated palloc, StackAlloc && salloc, Fn && fn) { + typedef fiber_capture_record< Ctx, StackAlloc, Fn > capture_t; + + BOOST_ASSERT( ( sizeof( capture_t) ) < palloc.size); + // reserve space for control structure + void * storage = reinterpret_cast< void * >( + ( reinterpret_cast< uintptr_t >( palloc.sp) - static_cast< uintptr_t >( sizeof( capture_t) ) ) + & ~ static_cast< uintptr_t >( 0xff) ); + // placment new for control structure on context stack + capture_t * record = new ( storage) capture_t{ + palloc.sctx, std::forward< StackAlloc >( salloc), std::forward< Fn >( fn) }; + // create user-context + record->fiber = ::CreateFiber( palloc.sctx.size, & detail::fiber_entry_func< capture_t >, record); + return record; +} + +} + +class BOOST_CONTEXT_DECL fiber { +private: + friend struct detail::fiber_activation_record; + + template< typename Ctx, typename StackAlloc, typename Fn > + friend class detail::fiber_capture_record; + + template< typename Ctx, typename StackAlloc, typename Fn > + friend detail::fiber_activation_record * detail::create_fiber1( StackAlloc &&, Fn &&); + + template< typename Ctx, typename StackAlloc, typename Fn > + friend detail::fiber_activation_record * detail::create_fiber2( preallocated, StackAlloc &&, Fn &&); + + template< typename StackAlloc, typename Fn > + friend fiber + callcc( std::allocator_arg_t, StackAlloc &&, Fn &&); + + template< typename StackAlloc, typename Fn > + friend fiber + callcc( std::allocator_arg_t, preallocated, StackAlloc &&, Fn &&); + + detail::fiber_activation_record * ptr_{ nullptr }; + + fiber( detail::fiber_activation_record * ptr) noexcept : + ptr_{ ptr } { + } + +public: + fiber() = default; + + template< typename Fn, typename = detail::disable_overload< fiber, Fn > > + fiber( Fn && fn) : + fiber{ std::allocator_arg, + fixedsize_stack(), + std::forward< Fn >( fn) } { + } + + template< typename StackAlloc, typename Fn > + fiber( std::allocator_arg_t, StackAlloc && salloc, Fn && fn) : + ptr_{ detail::create_fiber1< fiber >( + std::forward< StackAlloc >( salloc), std::forward< Fn >( fn) ) } {; + } + + template< typename StackAlloc, typename Fn > + fiber( std::allocator_arg_t, preallocated palloc, StackAlloc && salloc, Fn && fn) : + ptr_{ detail::create_fiber2< fiber >( + palloc, std::forward< StackAlloc >( salloc), std::forward< Fn >( fn) ) } { + } + + ~fiber() { + if ( BOOST_UNLIKELY( nullptr != ptr_) && ! ptr_->main_ctx) { + if ( BOOST_LIKELY( ! ptr_->terminated) ) { + ptr_->force_unwind = true; + ptr_->resume(); + BOOST_ASSERT( ptr_->terminated); + } + ptr_->deallocate(); + } + } + + fiber( fiber const&) = delete; + fiber & operator=( fiber const&) = delete; + + fiber( fiber && other) noexcept { + swap( other); + } + + fiber & operator=( fiber && other) noexcept { + if ( BOOST_LIKELY( this != & other) ) { + fiber tmp = std::move( other); + swap( tmp); + } + return * this; + } + + fiber resume() && { + BOOST_ASSERT( nullptr != ptr_); +#if defined(BOOST_NO_CXX14_STD_EXCHANGE) + detail::fiber_activation_record * ptr = detail::exchange( ptr_, nullptr)->resume(); +#else + detail::fiber_activation_record * ptr = std::exchange( ptr_, nullptr)->resume(); +#endif + if ( BOOST_UNLIKELY( detail::fiber_activation_record::current()->force_unwind) ) { + throw detail::forced_unwind{ ptr}; + } else if ( BOOST_UNLIKELY( nullptr != detail::fiber_activation_record::current()->ontop) ) { + ptr = detail::fiber_activation_record::current()->ontop( ptr); + detail::fiber_activation_record::current()->ontop = nullptr; + } + return { ptr }; + } + + template< typename Fn > + fiber resume_with( Fn && fn) && { + BOOST_ASSERT( nullptr != ptr_); +#if defined(BOOST_NO_CXX14_STD_EXCHANGE) + detail::fiber_activation_record * ptr = + detail::exchange( ptr_, nullptr)->resume_with< fiber >( std::forward< Fn >( fn) ); +#else + detail::fiber_activation_record * ptr = + std::exchange( ptr_, nullptr)->resume_with< fiber >( std::forward< Fn >( fn) ); +#endif + if ( BOOST_UNLIKELY( detail::fiber_activation_record::current()->force_unwind) ) { + throw detail::forced_unwind{ ptr}; + } else if ( BOOST_UNLIKELY( nullptr != detail::fiber_activation_record::current()->ontop) ) { + ptr = detail::fiber_activation_record::current()->ontop( ptr); + detail::fiber_activation_record::current()->ontop = nullptr; + } + return { ptr }; + } + + explicit operator bool() const noexcept { + return nullptr != ptr_ && ! ptr_->terminated; + } + + bool operator!() const noexcept { + return nullptr == ptr_ || ptr_->terminated; + } + + bool operator==( fiber const& other) const noexcept { + return ptr_ == other.ptr_; + } + + bool operator!=( fiber const& other) const noexcept { + return ptr_ != other.ptr_; + } + + bool operator<( fiber const& other) const noexcept { + return ptr_ < other.ptr_; + } + + bool operator>( fiber const& other) const noexcept { + return other.ptr_ < ptr_; + } + + bool operator<=( fiber const& other) const noexcept { + return ! ( * this > other); + } + + bool operator>=( fiber 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, fiber const& other) { + if ( nullptr != other.ptr_) { + return os << other.ptr_; + } else { + return os << "{not-a-context}"; + } + } + + void swap( fiber & other) noexcept { + std::swap( ptr_, other.ptr_); + } +}; + +inline +void swap( fiber & l, fiber & r) noexcept { + l.swap( r); +} + +}} + +#if defined(BOOST_MSVC) +# pragma warning(pop) +#endif + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_SUFFIX +#endif + +#endif // BOOST_CONTEXT_FIBER_H |