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 template< typename charT, class traitsT >
246 friend std::basic_ostream< charT, traitsT > &
247 operator<<( std::basic_ostream< charT, traitsT > & os, execution_context const& other) {
248 if ( nullptr != other.fctx_) {
249 return os << other.fctx_;
251 return os << "{not-a-context}";
255 void swap( execution_context & other) noexcept {
256 std::swap( fctx_, other.fctx_);
260 class ontop_error : public std::exception {
262 detail::fcontext_t fctx_;
265 ontop_error( detail::fcontext_t fctx) noexcept :
269 template< typename ... Args >
270 execution_context< Args ... > get_context() const noexcept {
271 return execution_context< Args ... >{ fctx_ };
275 template< typename ... Args >
276 typename execution_context< Args ... >::ret_tpl_t
277 execution_context< Args ... >::operator()( Args ... args) {
278 BOOST_ASSERT( nullptr != fctx_);
279 args_tpl_t data( std::forward< Args >( args) ... );
280 auto p = std::make_tuple( std::exception_ptr{}, std::move( data) );
281 detail::transfer_t t = detail::jump_fcontext( detail::exchange( fctx_, nullptr), & p);
282 if ( nullptr != t.data) {
283 auto p = static_cast< std::tuple< std::exception_ptr, args_tpl_t > * >( t.data);
284 std::exception_ptr eptr = std::get< 0 >( * p);
287 std::rethrow_exception( eptr);
289 std::throw_with_nested( ontop_error{ t.fctx } );
292 data = std::move( std::get< 1 >( * p) );
294 return std::tuple_cat( std::forward_as_tuple( execution_context( t.fctx) ), std::move( data) );
297 template< typename ... Args >
298 template< typename Fn >
299 typename execution_context< Args ... >::ret_tpl_t
300 execution_context< Args ... >::operator()( exec_ontop_arg_t, Fn && fn, Args ... args) {
301 BOOST_ASSERT( nullptr != fctx_);
302 args_tpl_t data{ std::forward< Args >( args) ... };
303 auto p = std::make_tuple( fn, std::make_tuple( std::exception_ptr{}, std::move( data) ) );
304 detail::transfer_t t = detail::ontop_fcontext(
305 detail::exchange( fctx_, nullptr),
307 detail::ecv2_context_ontop< execution_context, Fn, Args ... >);
308 if ( nullptr != t.data) {
309 auto p = static_cast< std::tuple< std::exception_ptr, args_tpl_t > * >( t.data);
310 std::exception_ptr eptr = std::get< 0 >( * p);
313 std::rethrow_exception( eptr);
315 std::throw_with_nested( ontop_error{ t.fctx } );
318 data = std::move( std::get< 1 >( * p) );
320 return std::tuple_cat( std::forward_as_tuple( execution_context( t.fctx) ), std::move( data) );
329 template< typename T >
330 static T convert( T && t) noexcept {
331 return std::forward< T >( t);
337 template< typename T >
338 static std::tuple< T > convert( T && t) noexcept {
339 return std::make_tuple( std::forward< T >( t) );
344 transfer_t ecv2_context_unwind( transfer_t t) {
345 throw forced_unwind( t.fctx);
346 return { nullptr, nullptr };
349 template< typename Rec >
350 transfer_t ecv2_context_exit( transfer_t t) noexcept {
351 Rec * rec = static_cast< Rec * >( t.data);
352 // destroy context stack
354 return { nullptr, nullptr };
357 template< typename Rec >
358 void ecv2_context_etry( transfer_t t_) noexcept {
359 // transfer control structure to the context-stack
360 Rec * rec = static_cast< Rec * >( t_.data);
361 BOOST_ASSERT( nullptr != rec);
362 transfer_t t = { nullptr, nullptr };
364 // jump back to `ecv2_context_create()`
365 t = jump_fcontext( t_.fctx, nullptr);
368 } catch ( forced_unwind const& ex) {
369 t = { ex.fctx, nullptr };
370 #ifndef BOOST_ASSERT_IS_VOID
371 const_cast< forced_unwind & >( ex).caught = true;
374 BOOST_ASSERT( nullptr != t.fctx);
375 // destroy context-stack of `this`context on next context
376 ontop_fcontext( t.fctx, rec, ecv2_context_exit< Rec >);
377 BOOST_ASSERT_MSG( false, "context already terminated");
380 template< typename Ctx, typename Fn, typename ... Args >
381 transfer_t ecv2_context_ontop( transfer_t t) {
382 auto p = static_cast< std::tuple< Fn, std::tuple< std::exception_ptr, std::tuple< Args ... > > > * >( t.data);
383 BOOST_ASSERT( nullptr != p);
384 typename std::decay< Fn >::type fn = std::forward< Fn >( std::get< 0 >( * p) );
385 auto args = std::move( std::get< 1 >( std::get< 1 >( * p) ) );
388 #if defined(BOOST_NO_CXX17_STD_APPLY)
389 std::get< 1 >( std::get< 1 >( * p) ) = helper< sizeof ... (Args) >::convert( boost::context::detail::apply( fn, std::move( args) ) );
391 std::get< 1 >( std::get< 1 >( * p) ) = helper< sizeof ... (Args) >::convert( std::apply( fn, std::move( args) ) );
393 #if defined( BOOST_CONTEXT_HAS_CXXABI_H )
394 } catch ( abi::__forced_unwind const&) {
398 std::get< 0 >( std::get< 1 >( * p) ) = std::current_exception();
400 // apply returned data
401 return { t.fctx, & std::get< 1 >( * p) };
404 template< typename Ctx, typename StackAlloc, typename Fn, typename ... Params >
405 fcontext_t ecv2_context_create( StackAlloc && salloc, Fn && fn, Params && ... params) {
406 typedef ecv2_record< Ctx, StackAlloc, Fn, Params ... > ecv2_record_t;
408 auto sctx = salloc.allocate();
409 // reserve space for control structure
410 #if defined(BOOST_NO_CXX11_CONSTEXPR) || defined(BOOST_NO_CXX11_STD_ALIGN)
411 const std::size_t size = sctx.size - sizeof( ecv2_record_t);
412 void * sp = static_cast< char * >( sctx.sp) - sizeof( ecv2_record_t);
414 constexpr std::size_t func_alignment = 64; // alignof( ecv2_record_t);
415 constexpr std::size_t func_size = sizeof( ecv2_record_t);
416 // reserve space on stack
417 void * sp = static_cast< char * >( sctx.sp) - func_size - func_alignment;
419 std::size_t space = func_size + func_alignment;
420 sp = std::align( func_alignment, func_size, sp, space);
421 BOOST_ASSERT( nullptr != sp);
422 // calculate remaining size
423 const std::size_t size = sctx.size - ( static_cast< char * >( sctx.sp) - static_cast< char * >( sp) );
425 // create fast-context
426 const fcontext_t fctx = make_fcontext( sp, size, & ecv2_context_etry< ecv2_record_t >);
427 BOOST_ASSERT( nullptr != fctx);
428 // placment new for control structure on context-stack
429 auto rec = ::new ( sp) ecv2_record_t{
430 sctx, std::forward< StackAlloc >( salloc), std::forward< Fn >( fn), std::forward< Params >( params) ... };
431 // transfer control structure to context-stack
432 return jump_fcontext( fctx, rec).fctx;
435 template< typename Ctx, typename StackAlloc, typename Fn, typename ... Params >
436 fcontext_t ecv2_context_create( preallocated palloc, StackAlloc && salloc, Fn && fn, Params && ... params) {
437 typedef ecv2_record< Ctx, StackAlloc, Fn, Params ... > ecv2_record_t;
439 // reserve space for control structure
440 #if defined(BOOST_NO_CXX11_CONSTEXPR) || defined(BOOST_NO_CXX11_STD_ALIGN)
441 const std::size_t size = palloc.size - sizeof( ecv2_record_t);
442 void * sp = static_cast< char * >( palloc.sp) - sizeof( ecv2_record_t);
444 constexpr std::size_t func_alignment = 64; // alignof( ecv2_record_t);
445 constexpr std::size_t func_size = sizeof( ecv2_record_t);
446 // reserve space on stack
447 void * sp = static_cast< char * >( palloc.sp) - func_size - func_alignment;
449 std::size_t space = func_size + func_alignment;
450 sp = std::align( func_alignment, func_size, sp, space);
451 BOOST_ASSERT( nullptr != sp);
452 // calculate remaining size
453 const std::size_t size = palloc.size - ( static_cast< char * >( palloc.sp) - static_cast< char * >( sp) );
455 // create fast-context
456 const fcontext_t fctx = make_fcontext( sp, size, & ecv2_context_etry< ecv2_record_t >);
457 BOOST_ASSERT( nullptr != fctx);
458 // placment new for control structure on context-stack
459 auto rec = ::new ( sp) ecv2_record_t{
460 palloc.sctx, std::forward< StackAlloc >( salloc), std::forward< Fn >( fn), std::forward< Params >( params) ... };
461 // transfer control structure to context-stack
462 return jump_fcontext( fctx, rec).fctx;
467 #include <boost/context/execution_context_v2_void.ipp>
469 inline namespace v2 {
471 template< typename ... Args >
472 void swap( execution_context< Args ... > & l, execution_context< Args ... > & r) noexcept {
478 #if defined(BOOST_MSVC)
479 # pragma warning(pop)
482 #ifdef BOOST_HAS_ABI_HEADERS
483 # include BOOST_ABI_SUFFIX
486 #endif // BOOST_CONTEXT_EXECUTION_CONTEXT_V2_H