2 // Copyright Oliver Kowalke 2014.
3 // Distributed under the Boost Software License, Version 1.0.
4 // (See accompanying file LICENSE_1_0.txt or copy at
5 // http://www.boost.org/LICENSE_1_0.txt)
7 #ifndef BOOST_CONTEXT_EXECUTION_CONTEXT_V2_H
8 #define BOOST_CONTEXT_EXECUTION_CONTEXT_V2_H
10 #include <boost/context/detail/config.hpp>
23 #include <boost/assert.hpp>
24 #include <boost/config.hpp>
25 #include <boost/intrusive_ptr.hpp>
27 #if defined(BOOST_NO_CXX17_STD_APPLY)
28 #include <boost/context/detail/apply.hpp>
30 #include <boost/context/detail/disable_overload.hpp>
31 #include <boost/context/detail/exception.hpp>
32 #include <boost/context/detail/exchange.hpp>
33 #include <boost/context/detail/fcontext.hpp>
34 #include <boost/context/detail/tuple.hpp>
35 #include <boost/context/fixedsize_stack.hpp>
36 #include <boost/context/flags.hpp>
37 #include <boost/context/preallocated.hpp>
38 #include <boost/context/segmented_stack.hpp>
39 #include <boost/context/stack_context.hpp>
41 #ifdef BOOST_HAS_ABI_HEADERS
42 # include BOOST_ABI_PREFIX
45 #if defined(BOOST_MSVC)
46 # pragma warning(push)
47 # pragma warning(disable: 4702)
54 transfer_t ecv2_context_unwind( transfer_t);
56 template< typename Rec >
57 transfer_t ecv2_context_exit( transfer_t) noexcept;
59 template< typename Rec >
60 void ecv2_context_etry( transfer_t) noexcept;
62 template< typename Ctx, typename Fn, typename ... Args >
63 transfer_t ecv2_context_ontop( transfer_t);
65 template< typename Ctx, typename StackAlloc, typename Fn, typename ... Params >
66 fcontext_t ecv2_context_create( StackAlloc &&, Fn &&, Params && ...);
68 template< typename Ctx, typename StackAlloc, typename Fn, typename ... Params >
69 fcontext_t ecv2_context_create( preallocated, StackAlloc &&, Fn &&, Params && ...);
71 template< typename Ctx, typename StackAlloc, typename Fn, typename ... Params >
74 typename std::decay< StackAlloc >::type salloc_;
76 typename std::decay< Fn >::type fn_;
77 std::tuple< typename std::decay< Params >::type ... > params_;
79 static void destroy( ecv2_record * p) noexcept {
80 typename std::decay< StackAlloc >::type salloc = std::move( p->salloc_);
81 stack_context sctx = p->sctx_;
82 // deallocate ecv2_record
84 // destroy stack with stack allocator
85 salloc.deallocate( sctx);
89 ecv2_record( stack_context sctx, StackAlloc && salloc,
90 Fn && fn, Params && ... params) noexcept :
91 salloc_( std::forward< StackAlloc >( salloc)),
93 fn_( std::forward< Fn >( fn) ),
94 params_( std::forward< Params >( params) ... ) {
97 ecv2_record( ecv2_record const&) = delete;
98 ecv2_record & operator=( ecv2_record const&) = delete;
100 void deallocate() noexcept {
104 transfer_t run( transfer_t t) {
106 typename Ctx::args_tpl_t args = std::move( std::get<1>( * static_cast< std::tuple< std::exception_ptr, typename Ctx::args_tpl_t > * >( t.data) ) );
107 auto tpl = std::tuple_cat(
109 std::forward_as_tuple( std::move( from) ),
111 // invoke context-function
112 #if defined(BOOST_NO_CXX17_STD_APPLY)
113 Ctx cc = boost::context::detail::apply( std::move( fn_), std::move( tpl) );
115 Ctx cc = std::apply( std::move( fn_), std::move( tpl) );
117 return { exchange( cc.fctx_, nullptr), nullptr };
123 inline namespace v2 {
125 template< typename ... Args >
126 class execution_context {
128 friend class ontop_error;
130 typedef std::tuple< Args ... > args_tpl_t;
131 typedef std::tuple< execution_context, typename std::decay< Args >::type ... > ret_tpl_t;
133 template< typename Ctx, typename StackAlloc, typename Fn, typename ... Params >
134 friend class detail::ecv2_record;
136 template< typename Ctx, typename Fn, typename ... ArgsT >
137 friend detail::transfer_t detail::ecv2_context_ontop( detail::transfer_t);
139 detail::fcontext_t fctx_{ nullptr };
141 execution_context( detail::fcontext_t fctx) noexcept :
146 execution_context() noexcept = default;
148 #if defined(BOOST_USE_SEGMENTED_STACKS)
149 // segmented-stack requires to preserve the segments of the `current` context
150 // which is not possible (no global pointer to current context)
151 template< typename Fn, typename ... Params >
152 execution_context( std::allocator_arg_t, segmented_stack, Fn &&, Params && ...) = delete;
154 template< typename Fn, typename ... Params >
155 execution_context( std::allocator_arg_t, preallocated, segmented_stack, Fn &&, Params && ...) = delete;
157 template< typename Fn,
159 typename = detail::disable_overload< execution_context, Fn >
161 execution_context( Fn && fn, Params && ... params) :
162 // deferred execution of fn and its arguments
163 // arguments are stored in std::tuple<>
164 // non-type template parameter pack via std::index_sequence_for<>
165 // preserves the number of arguments
166 // used to extract the function arguments from std::tuple<>
167 fctx_( detail::ecv2_context_create< execution_context >(
169 std::forward< Fn >( fn),
170 std::forward< Params >( params) ... ) ) {
173 template< typename StackAlloc,
177 execution_context( std::allocator_arg_t, StackAlloc && salloc, Fn && fn, Params && ... params) :
178 // deferred execution of fn and its arguments
179 // arguments are stored in std::tuple<>
180 // non-type template parameter pack via std::index_sequence_for<>
181 // preserves the number of arguments
182 // used to extract the function arguments from std::tuple<>
183 fctx_( detail::ecv2_context_create< execution_context >(
184 std::forward< StackAlloc >( salloc),
185 std::forward< Fn >( fn),
186 std::forward< Params >( params) ... ) ) {
189 template< typename StackAlloc,
193 execution_context( std::allocator_arg_t, preallocated palloc, StackAlloc && salloc, Fn && fn, Params && ... params) :
194 // deferred execution of fn and its arguments
195 // arguments are stored in std::tuple<>
196 // non-type template parameter pack via std::index_sequence_for<>
197 // preserves the number of arguments
198 // used to extract the function arguments from std::tuple<>
199 fctx_( detail::ecv2_context_create< execution_context >(
200 palloc, std::forward< StackAlloc >( salloc),
201 std::forward< Fn >( fn),
202 std::forward< Params >( params) ... ) ) {
206 ~execution_context() {
207 if ( nullptr != fctx_) {
208 detail::ontop_fcontext( detail::exchange( fctx_, nullptr), nullptr, detail::ecv2_context_unwind);
212 execution_context( execution_context && other) noexcept :
213 fctx_( other.fctx_) {
214 other.fctx_ = nullptr;
217 execution_context & operator=( execution_context && other) noexcept {
218 if ( this != & other) {
219 execution_context tmp = std::move( other);
225 execution_context( execution_context const& other) noexcept = delete;
226 execution_context & operator=( execution_context const& other) noexcept = delete;
228 ret_tpl_t operator()( Args ... args);
230 template< typename Fn >
231 ret_tpl_t operator()( exec_ontop_arg_t, Fn && fn, Args ... args);
233 explicit operator bool() const noexcept {
234 return nullptr != fctx_;
237 bool operator!() const noexcept {
238 return nullptr == fctx_;
241 bool operator==( execution_context const& other) const noexcept {
242 return fctx_ == other.fctx_;
245 bool operator!=( execution_context const& other) const noexcept {
246 return fctx_ != other.fctx_;
249 bool operator<( execution_context const& other) const noexcept {
250 return fctx_ < other.fctx_;
253 bool operator>( execution_context const& other) const noexcept {
254 return other.fctx_ < fctx_;
257 bool operator<=( execution_context const& other) const noexcept {
258 return ! ( * this > other);
261 bool operator>=( execution_context const& other) const noexcept {
262 return ! ( * this < other);
265 template< typename charT, class traitsT >
266 friend std::basic_ostream< charT, traitsT > &
267 operator<<( std::basic_ostream< charT, traitsT > & os, execution_context const& other) {
268 if ( nullptr != other.fctx_) {
269 return os << other.fctx_;
271 return os << "{not-a-context}";
275 void swap( execution_context & other) noexcept {
276 std::swap( fctx_, other.fctx_);
280 class ontop_error : public std::exception {
282 detail::fcontext_t fctx_;
285 ontop_error( detail::fcontext_t fctx) noexcept :
289 template< typename ... Args >
290 execution_context< Args ... > get_context() const noexcept {
291 return execution_context< Args ... >{ fctx_ };
295 template< typename ... Args >
296 typename execution_context< Args ... >::ret_tpl_t
297 execution_context< Args ... >::operator()( Args ... args) {
298 BOOST_ASSERT( nullptr != fctx_);
299 args_tpl_t data( std::forward< Args >( args) ... );
300 auto p = std::make_tuple( std::exception_ptr{}, std::move( data) );
301 detail::transfer_t t = detail::jump_fcontext( detail::exchange( fctx_, nullptr), & p);
302 if ( nullptr != t.data) {
303 auto p = static_cast< std::tuple< std::exception_ptr, args_tpl_t > * >( t.data);
304 std::exception_ptr eptr = std::get< 0 >( * p);
307 std::rethrow_exception( eptr);
309 std::throw_with_nested( ontop_error{ t.fctx } );
312 data = std::move( std::get< 1 >( * p) );
314 return std::tuple_cat( std::forward_as_tuple( execution_context( t.fctx) ), std::move( data) );
317 template< typename ... Args >
318 template< typename Fn >
319 typename execution_context< Args ... >::ret_tpl_t
320 execution_context< Args ... >::operator()( exec_ontop_arg_t, Fn && fn, Args ... args) {
321 BOOST_ASSERT( nullptr != fctx_);
322 args_tpl_t data{ std::forward< Args >( args) ... };
323 auto p = std::make_tuple( fn, std::make_tuple( std::exception_ptr{}, std::move( data) ) );
324 detail::transfer_t t = detail::ontop_fcontext(
325 detail::exchange( fctx_, nullptr),
327 detail::ecv2_context_ontop< execution_context, Fn, Args ... >);
328 if ( nullptr != t.data) {
329 auto p = static_cast< std::tuple< std::exception_ptr, args_tpl_t > * >( t.data);
330 std::exception_ptr eptr = std::get< 0 >( * p);
333 std::rethrow_exception( eptr);
335 std::throw_with_nested( ontop_error{ t.fctx } );
338 data = std::move( std::get< 1 >( * p) );
340 return std::tuple_cat( std::forward_as_tuple( execution_context( t.fctx) ), std::move( data) );
349 template< typename T >
350 static T convert( T && t) noexcept {
351 return std::forward< T >( t);
357 template< typename T >
358 static std::tuple< T > convert( T && t) noexcept {
359 return std::make_tuple( std::forward< T >( t) );
364 transfer_t ecv2_context_unwind( transfer_t t) {
365 throw forced_unwind( t.fctx);
366 return { nullptr, nullptr };
369 template< typename Rec >
370 transfer_t ecv2_context_exit( transfer_t t) noexcept {
371 Rec * rec = static_cast< Rec * >( t.data);
372 // destroy context stack
374 return { nullptr, nullptr };
377 template< typename Rec >
378 void ecv2_context_etry( transfer_t t_) noexcept {
379 // transfer control structure to the context-stack
380 Rec * rec = static_cast< Rec * >( t_.data);
381 BOOST_ASSERT( nullptr != rec);
382 transfer_t t = { nullptr, nullptr };
384 // jump back to `ecv2_context_create()`
385 t = jump_fcontext( t_.fctx, nullptr);
388 } catch ( forced_unwind const& e) {
389 t = { e.fctx, nullptr };
391 BOOST_ASSERT( nullptr != t.fctx);
392 // destroy context-stack of `this`context on next context
393 ontop_fcontext( t.fctx, rec, ecv2_context_exit< Rec >);
394 BOOST_ASSERT_MSG( false, "context already terminated");
397 template< typename Ctx, typename Fn, typename ... Args >
398 transfer_t ecv2_context_ontop( transfer_t t) {
399 auto p = static_cast< std::tuple< Fn, std::tuple< std::exception_ptr, std::tuple< Args ... > > > * >( t.data);
400 BOOST_ASSERT( nullptr != p);
401 typename std::decay< Fn >::type fn = std::forward< Fn >( std::get< 0 >( * p) );
402 auto args = std::move( std::get< 1 >( std::get< 1 >( * p) ) );
405 #if defined(BOOST_NO_CXX17_STD_APPLY)
406 std::get< 1 >( std::get< 1 >( * p) ) = helper< sizeof ... (Args) >::convert( boost::context::detail::apply( fn, std::move( args) ) );
408 std::get< 1 >( std::get< 1 >( * p) ) = helper< sizeof ... (Args) >::convert( std::apply( fn, std::move( args) ) );
411 std::get< 0 >( std::get< 1 >( * p) ) = std::current_exception();
413 // apply returned data
414 return { t.fctx, & std::get< 1 >( * p) };
417 template< typename Ctx, typename StackAlloc, typename Fn, typename ... Params >
418 fcontext_t ecv2_context_create( StackAlloc && salloc, Fn && fn, Params && ... params) {
419 typedef ecv2_record< Ctx, StackAlloc, Fn, Params ... > ecv2_record_t;
421 auto sctx = salloc.allocate();
422 // reserve space for control structure
423 #if defined(BOOST_NO_CXX11_CONSTEXPR) || defined(BOOST_NO_CXX11_STD_ALIGN)
424 const std::size_t size = sctx.size - sizeof( ecv2_record_t);
425 void * sp = static_cast< char * >( sctx.sp) - sizeof( ecv2_record_t);
427 constexpr std::size_t func_alignment = 64; // alignof( ecv2_record_t);
428 constexpr std::size_t func_size = sizeof( ecv2_record_t);
429 // reserve space on stack
430 void * sp = static_cast< char * >( sctx.sp) - func_size - func_alignment;
432 std::size_t space = func_size + func_alignment;
433 sp = std::align( func_alignment, func_size, sp, space);
434 BOOST_ASSERT( nullptr != sp);
435 // calculate remaining size
436 const std::size_t size = sctx.size - ( static_cast< char * >( sctx.sp) - static_cast< char * >( sp) );
438 // create fast-context
439 const fcontext_t fctx = make_fcontext( sp, size, & ecv2_context_etry< ecv2_record_t >);
440 BOOST_ASSERT( nullptr != fctx);
441 // placment new for control structure on context-stack
442 auto rec = ::new ( sp) ecv2_record_t{
443 sctx, std::forward< StackAlloc >( salloc), std::forward< Fn >( fn), std::forward< Params >( params) ... };
444 // transfer control structure to context-stack
445 return jump_fcontext( fctx, rec).fctx;
448 template< typename Ctx, typename StackAlloc, typename Fn, typename ... Params >
449 fcontext_t ecv2_context_create( preallocated palloc, StackAlloc && salloc, Fn && fn, Params && ... params) {
450 typedef ecv2_record< Ctx, StackAlloc, Fn, Params ... > ecv2_record_t;
452 // reserve space for control structure
453 #if defined(BOOST_NO_CXX11_CONSTEXPR) || defined(BOOST_NO_CXX11_STD_ALIGN)
454 const std::size_t size = palloc.size - sizeof( ecv2_record_t);
455 void * sp = static_cast< char * >( palloc.sp) - sizeof( ecv2_record_t);
457 constexpr std::size_t func_alignment = 64; // alignof( ecv2_record_t);
458 constexpr std::size_t func_size = sizeof( ecv2_record_t);
459 // reserve space on stack
460 void * sp = static_cast< char * >( palloc.sp) - func_size - func_alignment;
462 std::size_t space = func_size + func_alignment;
463 sp = std::align( func_alignment, func_size, sp, space);
464 BOOST_ASSERT( nullptr != sp);
465 // calculate remaining size
466 const std::size_t size = palloc.size - ( static_cast< char * >( palloc.sp) - static_cast< char * >( sp) );
468 // create fast-context
469 const fcontext_t fctx = make_fcontext( sp, size, & ecv2_context_etry< ecv2_record_t >);
470 BOOST_ASSERT( nullptr != fctx);
471 // placment new for control structure on context-stack
472 auto rec = ::new ( sp) ecv2_record_t{
473 palloc.sctx, std::forward< StackAlloc >( salloc), std::forward< Fn >( fn), std::forward< Params >( params) ... };
474 // transfer control structure to context-stack
475 return jump_fcontext( fctx, rec).fctx;
480 #include <boost/context/execution_context_v2_void.ipp>
482 inline namespace v2 {
484 template< typename ... Args >
485 void swap( execution_context< Args ... > & l, execution_context< Args ... > & r) noexcept {
491 #if defined(BOOST_MSVC)
492 # pragma warning(pop)
495 #ifdef BOOST_HAS_ABI_HEADERS
496 # include BOOST_ABI_SUFFIX
499 #endif // BOOST_CONTEXT_EXECUTION_CONTEXT_V2_H