Imported Upstream version 1.71.0
[platform/upstream/boost.git] / boost / context / execution_context_v2.hpp
1
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)
6
7 #ifndef BOOST_CONTEXT_EXECUTION_CONTEXT_V2_H
8 #define BOOST_CONTEXT_EXECUTION_CONTEXT_V2_H
9
10 #include <boost/context/detail/config.hpp>
11
12 #include <algorithm>
13 #include <cstddef>
14 #include <cstdint>
15 #include <cstdlib>
16 #include <exception>
17 #include <functional>
18 #include <memory>
19 #include <ostream>
20 #include <tuple>
21 #include <utility>
22
23 #include <boost/assert.hpp>
24 #include <boost/config.hpp>
25 #include <boost/intrusive_ptr.hpp>
26
27 #if defined(BOOST_NO_CXX17_STD_APPLY)
28 #include <boost/context/detail/apply.hpp>
29 #endif
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>
40
41 #ifdef BOOST_HAS_ABI_HEADERS
42 # include BOOST_ABI_PREFIX
43 #endif
44
45 #if defined(BOOST_MSVC)
46 # pragma warning(push)
47 # pragma warning(disable: 4702)
48 #endif
49
50 namespace boost {
51 namespace context {
52 namespace detail {
53
54 transfer_t ecv2_context_unwind( transfer_t);
55
56 template< typename Rec >
57 transfer_t ecv2_context_exit( transfer_t) noexcept;
58
59 template< typename Rec >
60 void ecv2_context_etry( transfer_t) noexcept;
61
62 template< typename Ctx, typename Fn, typename ... Args >
63 transfer_t ecv2_context_ontop( transfer_t);
64
65 template< typename Ctx, typename StackAlloc, typename Fn, typename ... Params >
66 fcontext_t ecv2_context_create( StackAlloc &&, Fn &&, Params && ...);
67
68 template< typename Ctx, typename StackAlloc, typename Fn, typename ... Params >
69 fcontext_t ecv2_context_create( preallocated, StackAlloc &&, Fn &&, Params && ...);
70
71 template< typename Ctx, typename StackAlloc, typename Fn, typename ... Params >
72 class ecv2_record {
73 private:
74     typename std::decay< StackAlloc >::type             salloc_;
75     stack_context                                       sctx_;
76     typename std::decay< Fn >::type                     fn_;
77     std::tuple< typename std::decay< Params >::type ... > params_;
78
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
83         p->~ecv2_record();
84         // destroy stack with stack allocator
85         salloc.deallocate( sctx);
86     }
87
88 public:
89     ecv2_record( stack_context sctx, StackAlloc && salloc,
90             Fn && fn, Params && ... params) noexcept :
91         salloc_( std::forward< StackAlloc >( salloc)),
92         sctx_( sctx),
93         fn_( std::forward< Fn >( fn) ),
94         params_( std::forward< Params >( params) ... ) {
95     }
96
97     ecv2_record( ecv2_record const&) = delete;
98     ecv2_record & operator=( ecv2_record const&) = delete;
99
100     void deallocate() noexcept {
101         destroy( this);
102     }
103
104     transfer_t run( transfer_t t) {
105         Ctx from{ t.fctx };
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(
108                     params_,
109                     std::forward_as_tuple( std::move( from) ),
110                     std::move( args) );
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) );
114 #else
115         Ctx cc = std::apply( std::move( fn_), std::move( tpl) );
116 #endif
117         return { exchange( cc.fctx_, nullptr), nullptr };
118     }
119 };
120
121 }
122
123 inline namespace v2 {
124
125 template< typename ... Args >
126 class execution_context {
127 private:
128     friend class ontop_error;
129
130     typedef std::tuple< Args ... >     args_tpl_t;
131     typedef std::tuple< execution_context, typename std::decay< Args >::type ... >               ret_tpl_t;
132
133     template< typename Ctx, typename StackAlloc, typename Fn, typename ... Params >
134     friend class detail::ecv2_record;
135
136     template< typename Ctx, typename Fn, typename ... ArgsT >
137     friend detail::transfer_t detail::ecv2_context_ontop( detail::transfer_t);
138
139     detail::fcontext_t  fctx_{ nullptr };
140
141     execution_context( detail::fcontext_t fctx) noexcept :
142         fctx_( fctx) {
143     }
144
145 public:
146     execution_context() noexcept = default;
147
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;
153
154     template< typename Fn, typename ... Params >
155     execution_context( std::allocator_arg_t, preallocated, segmented_stack, Fn &&, Params && ...) = delete;
156 #else
157     template< typename Fn,
158               typename ... Params,
159               typename = detail::disable_overload< execution_context, Fn >
160     >
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 >(
168                     fixedsize_stack(),
169                     std::forward< Fn >( fn),
170                     std::forward< Params >( params) ... ) ) {
171     }
172
173     template< typename StackAlloc,
174               typename Fn,
175               typename ... Params
176     >
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) ... ) ) {
187     }
188
189     template< typename StackAlloc,
190               typename Fn,
191               typename ... Params
192     >
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) ... ) ) {
203     }
204 #endif
205
206     ~execution_context() {
207         if ( nullptr != fctx_) {
208             detail::ontop_fcontext( detail::exchange( fctx_, nullptr), nullptr, detail::ecv2_context_unwind);
209         }
210     }
211
212     execution_context( execution_context && other) noexcept :
213         fctx_( other.fctx_) {
214         other.fctx_ = nullptr;
215     }
216
217     execution_context & operator=( execution_context && other) noexcept {
218         if ( this != & other) {
219             execution_context tmp = std::move( other);
220             swap( tmp);
221         }
222         return * this;
223     }
224
225     execution_context( execution_context const& other) noexcept = delete;
226     execution_context & operator=( execution_context const& other) noexcept = delete;
227
228     ret_tpl_t operator()( Args ... args);
229
230     template< typename Fn >
231     ret_tpl_t operator()( exec_ontop_arg_t, Fn && fn, Args ... args);
232
233     explicit operator bool() const noexcept {
234         return nullptr != fctx_;
235     }
236
237     bool operator!() const noexcept {
238         return nullptr == fctx_;
239     }
240
241     bool operator<( execution_context const& other) const noexcept {
242         return fctx_ < other.fctx_;
243     }
244
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_;
250         } else {
251             return os << "{not-a-context}";
252         }
253     }
254
255     void swap( execution_context & other) noexcept {
256         std::swap( fctx_, other.fctx_);
257     }
258 };
259
260 class ontop_error : public std::exception {
261 private:
262     detail::fcontext_t  fctx_;
263
264 public:
265     ontop_error( detail::fcontext_t fctx) noexcept :
266         fctx_{ fctx } {
267     }
268
269     template< typename ... Args >
270     execution_context< Args ... > get_context() const noexcept {
271         return execution_context< Args ... >{ fctx_ };
272     }
273 };
274
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);
285         if ( eptr) {
286             try {
287                 std::rethrow_exception( eptr);
288             } catch (...) {
289                 std::throw_with_nested( ontop_error{ t.fctx } );
290             }
291         }
292         data = std::move( std::get< 1 >( * p) );
293     }
294     return std::tuple_cat( std::forward_as_tuple( execution_context( t.fctx) ), std::move( data) );
295 }
296
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),
306             & p,
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);
311         if ( eptr) {
312             try {
313                 std::rethrow_exception( eptr);
314             } catch (...) {
315                 std::throw_with_nested( ontop_error{ t.fctx } );
316             }
317         }
318         data = std::move( std::get< 1 >( * p) );
319     }
320     return std::tuple_cat( std::forward_as_tuple( execution_context( t.fctx) ), std::move( data) );
321 }
322
323 }
324
325 namespace detail {
326
327 template< int N >
328 struct helper {
329     template< typename T >
330     static T convert( T && t) noexcept {
331         return std::forward< T >( t);
332     }
333 };
334
335 template<>
336 struct helper< 1 > {
337     template< typename T >
338     static std::tuple< T > convert( T && t) noexcept {
339         return std::make_tuple( std::forward< T >( t) );
340     }
341 };
342
343 inline
344 transfer_t ecv2_context_unwind( transfer_t t) {
345     throw forced_unwind( t.fctx);
346     return { nullptr, nullptr };
347 }
348
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
353     rec->deallocate();
354     return { nullptr, nullptr };
355 }
356
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 };
363     try {
364         // jump back to `ecv2_context_create()`
365         t = jump_fcontext( t_.fctx, nullptr);
366         // start executing
367         t = rec->run( t);
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;
372 #endif
373     }
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");
378 }
379
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) ) );
386     try {
387         // execute function
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) ) );
390 #else
391         std::get< 1 >( std::get< 1 >( * p) ) = helper< sizeof ... (Args) >::convert( std::apply( fn, std::move( args) ) );
392 #endif
393 #if defined( BOOST_CONTEXT_HAS_CXXABI_H )
394     } catch ( abi::__forced_unwind const&) {
395         throw;
396 #endif
397     } catch (...) {
398         std::get< 0 >( std::get< 1 >( * p) ) = std::current_exception();
399     }
400     // apply returned data
401     return { t.fctx, & std::get< 1 >( * p) };
402 }
403
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;
407
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);
413 #else
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;
418     // align sp pointer
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) );
424 #endif
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;
433 }
434
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;
438
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);
443 #else
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;
448     // align sp pointer
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) );
454 #endif
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;
463 }
464
465 }
466
467 #include <boost/context/execution_context_v2_void.ipp>
468
469 inline namespace v2 {
470
471 template< typename ... Args >
472 void swap( execution_context< Args ... > & l, execution_context< Args ... > & r) noexcept {
473     l.swap( r);
474 }
475
476 }}}
477
478 #if defined(BOOST_MSVC)
479 # pragma warning(pop)
480 #endif
481
482 #ifdef BOOST_HAS_ABI_HEADERS
483 # include BOOST_ABI_SUFFIX
484 #endif
485
486 #endif // BOOST_CONTEXT_EXECUTION_CONTEXT_V2_H