Imported Upstream version 1.64.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_H
8 #define BOOST_CONTEXT_EXECUTION_CONTEXT_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 context_unwind( transfer_t);
55
56 template< typename Rec >
57 transfer_t context_exit( transfer_t) noexcept;
58
59 template< typename Rec >
60 void context_entry( transfer_t) noexcept;
61
62 template< typename Ctx, typename Fn, typename ... Args >
63 transfer_t context_ontop( transfer_t);
64
65 template< typename Ctx, typename StackAlloc, typename Fn, typename ... Params >
66 fcontext_t context_create( StackAlloc, Fn &&, Params && ...);
67
68 template< typename Ctx, typename StackAlloc, typename Fn, typename ... Params >
69 fcontext_t context_create( preallocated, StackAlloc, Fn &&, Params && ...);
70
71 template< typename Ctx, typename StackAlloc, typename Fn, typename ... Params >
72 class record {
73 private:
74     StackAlloc                                          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( record * p) noexcept {
80         StackAlloc salloc = p->salloc_;
81         stack_context sctx = p->sctx_;
82         // deallocate record
83         p->~record();
84         // destroy stack with stack allocator
85         salloc.deallocate( sctx);
86     }
87
88 public:
89     record( stack_context sctx, StackAlloc const& salloc,
90             Fn && fn, Params && ... params) noexcept :
91         salloc_( salloc),
92         sctx_( sctx),
93         fn_( std::forward< Fn >( fn) ),
94         params_( std::forward< Params >( params) ... ) {
95     }
96
97     record( record const&) = delete;
98     record & operator=( 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 = 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 template< typename ... Args >
124 class execution_context {
125 private:
126     friend class ontop_error;
127
128     typedef std::tuple< Args ... >     args_tpl_t;
129     typedef std::tuple< execution_context, typename std::decay< Args >::type ... >               ret_tpl_t;
130
131     template< typename Ctx, typename StackAlloc, typename Fn, typename ... Params >
132     friend class detail::record;
133
134     template< typename Ctx, typename Fn, typename ... ArgsT >
135     friend detail::transfer_t detail::context_ontop( detail::transfer_t);
136
137     detail::fcontext_t  fctx_{ nullptr };
138
139     execution_context( detail::fcontext_t fctx) noexcept :
140         fctx_( fctx) {
141     }
142
143 public:
144     execution_context() noexcept = default;
145
146 #if defined(BOOST_USE_SEGMENTED_STACKS)
147     // segmented-stack requires to preserve the segments of the `current` context
148     // which is not possible (no global pointer to current context)
149     template< typename Fn, typename ... Params >
150     execution_context( std::allocator_arg_t, segmented_stack, Fn &&, Params && ...) = delete;
151
152     template< typename Fn, typename ... Params >
153     execution_context( std::allocator_arg_t, preallocated, segmented_stack, Fn &&, Params && ...) = delete;
154 #else
155     template< typename Fn,
156               typename ... Params,
157               typename = detail::disable_overload< execution_context, Fn >
158     >
159     execution_context( Fn && fn, Params && ... params) :
160         // deferred execution of fn and its arguments
161         // arguments are stored in std::tuple<>
162         // non-type template parameter pack via std::index_sequence_for<>
163         // preserves the number of arguments
164         // used to extract the function arguments from std::tuple<>
165         fctx_( detail::context_create< execution_context >(
166                     fixedsize_stack(),
167                     std::forward< Fn >( fn),
168                     std::forward< Params >( params) ... ) ) {
169     }
170
171     template< typename StackAlloc,
172               typename Fn,
173               typename ... Params
174     >
175     execution_context( std::allocator_arg_t, StackAlloc salloc, Fn && fn, Params && ... params) :
176         // deferred execution of fn and its arguments
177         // arguments are stored in std::tuple<>
178         // non-type template parameter pack via std::index_sequence_for<>
179         // preserves the number of arguments
180         // used to extract the function arguments from std::tuple<>
181         fctx_( detail::context_create< execution_context >(
182                     salloc,
183                     std::forward< Fn >( fn),
184                     std::forward< Params >( params) ... ) ) {
185     }
186
187     template< typename StackAlloc,
188               typename Fn,
189               typename ... Params
190     >
191     execution_context( std::allocator_arg_t, preallocated palloc, StackAlloc salloc, Fn && fn, Params && ... params) :
192         // deferred execution of fn and its arguments
193         // arguments are stored in std::tuple<>
194         // non-type template parameter pack via std::index_sequence_for<>
195         // preserves the number of arguments
196         // used to extract the function arguments from std::tuple<>
197         fctx_( detail::context_create< execution_context >(
198                     palloc, salloc,
199                     std::forward< Fn >( fn),
200                     std::forward< Params >( params) ... ) ) {
201     }
202 #endif
203
204     ~execution_context() {
205         if ( nullptr != fctx_) {
206             detail::ontop_fcontext( detail::exchange( fctx_, nullptr), nullptr, detail::context_unwind);
207         }
208     }
209
210     execution_context( execution_context && other) noexcept :
211         fctx_( other.fctx_) {
212         other.fctx_ = nullptr;
213     }
214
215     execution_context & operator=( execution_context && other) noexcept {
216         if ( this != & other) {
217             execution_context tmp = std::move( other);
218             swap( tmp);
219         }
220         return * this;
221     }
222
223     execution_context( execution_context const& other) noexcept = delete;
224     execution_context & operator=( execution_context const& other) noexcept = delete;
225
226     ret_tpl_t operator()( Args ... args);
227
228     template< typename Fn >
229     ret_tpl_t operator()( exec_ontop_arg_t, Fn && fn, Args ... args);
230
231     explicit operator bool() const noexcept {
232         return nullptr != fctx_;
233     }
234
235     bool operator!() const noexcept {
236         return nullptr == fctx_;
237     }
238
239     bool operator==( execution_context const& other) const noexcept {
240         return fctx_ == other.fctx_;
241     }
242
243     bool operator!=( execution_context const& other) const noexcept {
244         return fctx_ != other.fctx_;
245     }
246
247     bool operator<( execution_context const& other) const noexcept {
248         return fctx_ < other.fctx_;
249     }
250
251     bool operator>( execution_context const& other) const noexcept {
252         return other.fctx_ < fctx_;
253     }
254
255     bool operator<=( execution_context const& other) const noexcept {
256         return ! ( * this > other);
257     }
258
259     bool operator>=( execution_context const& other) const noexcept {
260         return ! ( * this < other);
261     }
262
263     template< typename charT, class traitsT >
264     friend std::basic_ostream< charT, traitsT > &
265     operator<<( std::basic_ostream< charT, traitsT > & os, execution_context const& other) {
266         if ( nullptr != other.fctx_) {
267             return os << other.fctx_;
268         } else {
269             return os << "{not-a-context}";
270         }
271     }
272
273     void swap( execution_context & other) noexcept {
274         std::swap( fctx_, other.fctx_);
275     }
276 };
277
278 class ontop_error : public std::exception {
279 private:
280     detail::fcontext_t  fctx_;
281
282 public:
283     ontop_error( detail::fcontext_t fctx) noexcept :
284         fctx_{ fctx } {
285     }
286
287     template< typename ... Args >
288     execution_context< Args ... > get_context() const noexcept {
289         return execution_context< Args ... >{ fctx_ };
290     }
291 };
292
293 template< typename ... Args >
294 typename execution_context< Args ... >::ret_tpl_t
295 execution_context< Args ... >::operator()( Args ... args) {
296     BOOST_ASSERT( nullptr != fctx_);
297     args_tpl_t data( std::forward< Args >( args) ... );
298     auto p = std::make_tuple( std::exception_ptr{}, std::move( data) );
299     detail::transfer_t t = detail::jump_fcontext( detail::exchange( fctx_, nullptr), & p);
300     if ( nullptr != t.data) {
301         auto p = static_cast< std::tuple< std::exception_ptr, args_tpl_t > * >( t.data);
302         std::exception_ptr eptr = std::get< 0 >( * p);
303         if ( eptr) {
304             try {
305                 std::rethrow_exception( eptr);
306             } catch (...) {
307                 std::throw_with_nested( ontop_error{ t.fctx } );
308             }
309         }
310         data = std::move( std::get< 1 >( * p) );
311     }
312     return std::tuple_cat( std::forward_as_tuple( execution_context( t.fctx) ), std::move( data) );
313 }
314
315 template< typename ... Args >
316 template< typename Fn >
317 typename execution_context< Args ... >::ret_tpl_t
318 execution_context< Args ... >::operator()( exec_ontop_arg_t, Fn && fn, Args ... args) {
319     BOOST_ASSERT( nullptr != fctx_);
320     args_tpl_t data{ std::forward< Args >( args) ... };
321     auto p = std::make_tuple( fn, std::make_tuple( std::exception_ptr{}, std::move( data) ) );
322     detail::transfer_t t = detail::ontop_fcontext(
323             detail::exchange( fctx_, nullptr),
324             & p,
325             detail::context_ontop< execution_context, Fn, Args ... >);
326     if ( nullptr != t.data) {
327         auto p = static_cast< std::tuple< std::exception_ptr, args_tpl_t > * >( t.data);
328         std::exception_ptr eptr = std::get< 0 >( * p);
329         if ( eptr) {
330             try {
331                 std::rethrow_exception( eptr);
332             } catch (...) {
333                 std::throw_with_nested( ontop_error{ t.fctx } );
334             }
335         }
336         data = std::move( std::get< 1 >( * p) );
337     }
338     return std::tuple_cat( std::forward_as_tuple( execution_context( t.fctx) ), std::move( data) );
339 }
340
341 namespace detail {
342
343 template< int N >
344 struct helper {
345     template< typename T >
346     static T convert( T && t) noexcept {
347         return std::forward< T >( t);
348     }
349 };
350
351 template<>
352 struct helper< 1 > {
353     template< typename T >
354     static std::tuple< T > convert( T && t) noexcept {
355         return std::make_tuple( std::forward< T >( t) );
356     }
357 };
358
359 inline
360 transfer_t context_unwind( transfer_t t) {
361     throw forced_unwind( t.fctx);
362     return { nullptr, nullptr };
363 }
364
365 template< typename Rec >
366 transfer_t context_exit( transfer_t t) noexcept {
367     Rec * rec = static_cast< Rec * >( t.data);
368     // destroy context stack
369     rec->deallocate();
370     return { nullptr, nullptr };
371 }
372
373 template< typename Rec >
374 void context_entry( transfer_t t_) noexcept {
375     // transfer control structure to the context-stack
376     Rec * rec = static_cast< Rec * >( t_.data);
377     BOOST_ASSERT( nullptr != rec);
378     transfer_t t = { nullptr, nullptr };
379     try {
380         // jump back to `context_create()`
381         t = jump_fcontext( t_.fctx, nullptr);
382         // start executing
383         t = rec->run( t);
384     } catch ( forced_unwind const& e) {
385         t = { e.fctx, nullptr };
386     }
387     BOOST_ASSERT( nullptr != t.fctx);
388     // destroy context-stack of `this`context on next context
389     ontop_fcontext( t.fctx, rec, context_exit< Rec >);
390     BOOST_ASSERT_MSG( false, "context already terminated");
391 }
392
393 template< typename Ctx, typename Fn, typename ... Args >
394 transfer_t context_ontop( transfer_t t) {
395     auto p = static_cast< std::tuple< Fn, std::tuple< std::exception_ptr, std::tuple< Args ... > > > * >( t.data);
396     BOOST_ASSERT( nullptr != p);
397     typename std::decay< Fn >::type fn = std::forward< Fn >( std::get< 0 >( * p) );
398     auto args = std::move( std::get< 1 >( std::get< 1 >( * p) ) );
399     try {
400         // execute function
401 #if defined(BOOST_NO_CXX17_STD_APPLY)
402         std::get< 1 >( std::get< 1 >( * p) ) = helper< sizeof ... (Args) >::convert( apply( fn, std::move( args) ) );
403 #else
404         std::get< 1 >( std::get< 1 >( * p) ) = helper< sizeof ... (Args) >::convert( std::apply( fn, std::move( args) ) );
405 #endif
406     } catch (...) {
407         std::get< 0 >( std::get< 1 >( * p) ) = std::current_exception();
408     }
409     // apply returned data
410     return { t.fctx, & std::get< 1 >( * p) };
411 }
412
413 template< typename Ctx, typename StackAlloc, typename Fn, typename ... Params >
414 fcontext_t context_create( StackAlloc salloc, Fn && fn, Params && ... params) {
415     typedef record< Ctx, StackAlloc, Fn, Params ... >  record_t;
416
417     auto sctx = salloc.allocate();
418     // reserve space for control structure
419 #if defined(BOOST_NO_CXX11_CONSTEXPR) || defined(BOOST_NO_CXX11_STD_ALIGN)
420     const std::size_t size = sctx.size - sizeof( record_t);
421     void * sp = static_cast< char * >( sctx.sp) - sizeof( record_t);
422 #else
423     constexpr std::size_t func_alignment = 64; // alignof( record_t);
424     constexpr std::size_t func_size = sizeof( record_t);
425     // reserve space on stack
426     void * sp = static_cast< char * >( sctx.sp) - func_size - func_alignment;
427     // align sp pointer
428     std::size_t space = func_size + func_alignment;
429     sp = std::align( func_alignment, func_size, sp, space);
430     BOOST_ASSERT( nullptr != sp);
431     // calculate remaining size
432     const std::size_t size = sctx.size - ( static_cast< char * >( sctx.sp) - static_cast< char * >( sp) );
433 #endif
434     // create fast-context
435     const fcontext_t fctx = make_fcontext( sp, size, & context_entry< record_t >);
436     BOOST_ASSERT( nullptr != fctx);
437     // placment new for control structure on context-stack
438     auto rec = ::new ( sp) record_t{
439             sctx, salloc, std::forward< Fn >( fn), std::forward< Params >( params) ... };
440     // transfer control structure to context-stack
441     return jump_fcontext( fctx, rec).fctx;
442 }
443
444 template< typename Ctx, typename StackAlloc, typename Fn, typename ... Params >
445 fcontext_t context_create( preallocated palloc, StackAlloc salloc, Fn && fn, Params && ... params) {
446     typedef record< Ctx, StackAlloc, Fn, Params ... >  record_t;
447
448     // reserve space for control structure
449 #if defined(BOOST_NO_CXX11_CONSTEXPR) || defined(BOOST_NO_CXX11_STD_ALIGN)
450     const std::size_t size = palloc.size - sizeof( record_t);
451     void * sp = static_cast< char * >( palloc.sp) - sizeof( record_t);
452 #else
453     constexpr std::size_t func_alignment = 64; // alignof( record_t);
454     constexpr std::size_t func_size = sizeof( record_t);
455     // reserve space on stack
456     void * sp = static_cast< char * >( palloc.sp) - func_size - func_alignment;
457     // align sp pointer
458     std::size_t space = func_size + func_alignment;
459     sp = std::align( func_alignment, func_size, sp, space);
460     BOOST_ASSERT( nullptr != sp);
461     // calculate remaining size
462     const std::size_t size = palloc.size - ( static_cast< char * >( palloc.sp) - static_cast< char * >( sp) );
463 #endif
464     // create fast-context
465     const fcontext_t fctx = make_fcontext( sp, size, & context_entry< record_t >);
466     BOOST_ASSERT( nullptr != fctx);
467     // placment new for control structure on context-stack
468     auto rec = ::new ( sp) record_t{
469             palloc.sctx, salloc, std::forward< Fn >( fn), std::forward< Params >( params) ... };
470     // transfer control structure to context-stack
471     return jump_fcontext( fctx, rec).fctx;
472 }
473
474 }
475
476 #include <boost/context/execution_context_v2_void.ipp>
477
478 template< typename ... Args >
479 void swap( execution_context< Args ... > & l, execution_context< Args ... > & r) noexcept {
480     l.swap( r);
481 }
482
483 }}
484
485 #if defined(BOOST_MSVC)
486 # pragma warning(pop)
487 #endif
488
489 #ifdef BOOST_HAS_ABI_HEADERS
490 # include BOOST_ABI_SUFFIX
491 #endif
492
493 #endif // BOOST_CONTEXT_EXECUTION_CONTEXT_H