Imported Upstream version 1.71.0
[platform/upstream/boost.git] / boost / context / execution_context_v2_void.ipp
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 namespace detail {
8
9 template< typename Ctx, typename Fn >
10 transfer_t ecv2_context_ontop_void( transfer_t);
11
12 template< typename Ctx, typename StackAlloc, typename Fn, typename ... Params >
13 fcontext_t ecv2_context_create_void( StackAlloc &&, Fn &&, Params && ...);
14
15 template< typename Ctx, typename StackAlloc, typename Fn, typename ... Params >
16 fcontext_t ecv2_context_create_void( preallocated, StackAlloc &&, Fn &&, Params && ...);
17
18 template< typename Ctx, typename StackAlloc, typename Fn, typename ... Params >
19 class ecv2_record_void {
20 private:
21     typename std::decay< StackAlloc >::type             salloc_;
22     stack_context                                       sctx_;
23     typename std::decay< Fn >::type                     fn_;
24     std::tuple< typename std::decay< Params >::type ... > params_;
25
26     static void destroy( ecv2_record_void * p) noexcept {
27         typename std::decay< StackAlloc >::type salloc = std::move( p->salloc_);
28         stack_context sctx = p->sctx_;
29         // deallocate record
30         p->~ecv2_record_void();
31         // destroy stack with stack allocator
32         salloc.deallocate( sctx);
33     }
34
35 public:
36     ecv2_record_void( stack_context sctx, StackAlloc && salloc,
37             Fn && fn, Params && ... params) noexcept :
38         salloc_( std::forward< StackAlloc >( salloc) ),
39         sctx_( sctx),
40         fn_( std::forward< Fn >( fn) ),
41         params_( std::forward< Params >( params) ... ) {
42     }
43
44     ecv2_record_void( ecv2_record_void const&) = delete;
45     ecv2_record_void & operator=( ecv2_record_void const&) = delete;
46
47     void deallocate() noexcept {
48         destroy( this);
49     }
50
51     transfer_t run( transfer_t t) {
52         Ctx from{ t.fctx };
53         // invoke context-function
54 #if defined(BOOST_NO_CXX17_STD_APPLY)
55         Ctx cc = boost::context::detail::apply( fn_, std::tuple_cat( params_, std::forward_as_tuple( std::move( from) ) ) );
56 #else
57         Ctx cc = std::apply( fn_, std::tuple_cat( params_, std::forward_as_tuple( std::move( from) ) ) );
58 #endif
59         return { exchange( cc.fctx_, nullptr), nullptr };
60     }
61 };
62
63 }
64
65 inline namespace v2 {
66
67 template<>
68 class execution_context< void > {
69 private:
70     friend class ontop_error;
71
72     template< typename Ctx, typename StackAlloc, typename Fn, typename ... Params >
73     friend class detail::ecv2_record_void;
74
75     template< typename Ctx, typename Fn >
76     friend detail::transfer_t detail::ecv2_context_ontop_void( detail::transfer_t);
77
78     detail::fcontext_t  fctx_{ nullptr };
79
80     execution_context( detail::fcontext_t fctx) noexcept :
81         fctx_( fctx) {
82     }
83
84 public:
85     execution_context() noexcept = default;
86
87 #if defined(BOOST_USE_SEGMENTED_STACKS)
88     // segmented-stack requires to preserve the segments of the `current` context
89     // which is not possible (no global pointer to current context)
90     template< typename Fn, typename ... Params >
91     execution_context( std::allocator_arg_t, segmented_stack, Fn &&, Params && ...) = delete;
92
93     template< typename Fn, typename ... Params >
94     execution_context( std::allocator_arg_t, preallocated, segmented_stack, Fn &&, Params && ...) = delete;
95 #else
96     template< typename Fn,
97               typename ... Params,
98               typename = detail::disable_overload< execution_context, Fn >
99     >
100     execution_context( Fn && fn, Params && ... params) :
101         // deferred execution of fn and its arguments
102         // arguments are stored in std::tuple<>
103         // non-type template parameter pack via std::index_sequence_for<>
104         // preserves the number of arguments
105         // used to extract the function arguments from std::tuple<>
106         fctx_( detail::ecv2_context_create_void< execution_context >(
107                     fixedsize_stack(),
108                     std::forward< Fn >( fn),
109                     std::forward< Params >( params) ... ) ) {
110     }
111
112     template< typename StackAlloc,
113               typename Fn,
114               typename ... Params
115     >
116     execution_context( std::allocator_arg_t, StackAlloc && salloc, Fn && fn, Params && ... params) :
117         // deferred execution of fn and its arguments
118         // arguments are stored in std::tuple<>
119         // non-type template parameter pack via std::index_sequence_for<>
120         // preserves the number of arguments
121         // used to extract the function arguments from std::tuple<>
122         fctx_( detail::ecv2_context_create_void< execution_context >(
123                     std::forward< StackAlloc >( salloc),
124                     std::forward< Fn >( fn),
125                     std::forward< Params >( params) ... ) ) {
126     }
127
128     template< typename StackAlloc,
129               typename Fn,
130               typename ... Params
131     >
132     execution_context( std::allocator_arg_t, preallocated palloc, StackAlloc && salloc, Fn && fn, Params && ... params) :
133         // deferred execution of fn and its arguments
134         // arguments are stored in std::tuple<>
135         // non-type template parameter pack via std::index_sequence_for<>
136         // preserves the number of arguments
137         // used to extract the function arguments from std::tuple<>
138         fctx_( detail::ecv2_context_create_void< execution_context >(
139                     palloc, std::forward< StackAlloc >( salloc),
140                     std::forward< Fn >( fn),
141                     std::forward< Params >( params) ... ) ) {
142     }
143 #endif
144
145     ~execution_context() {
146         if ( nullptr != fctx_) {
147             detail::ontop_fcontext( detail::exchange( fctx_, nullptr), nullptr, detail::ecv2_context_unwind);
148         }
149     }
150
151     execution_context( execution_context && other) noexcept :
152         fctx_( other.fctx_) {
153         other.fctx_ = nullptr;
154     }
155
156     execution_context & operator=( execution_context && other) noexcept {
157         if ( this != & other) {
158             execution_context tmp = std::move( other);
159             swap( tmp);
160         }
161         return * this;
162     }
163
164     execution_context( execution_context const& other) noexcept = delete;
165     execution_context & operator=( execution_context const& other) noexcept = delete;
166
167     execution_context operator()() {
168         BOOST_ASSERT( nullptr != fctx_);
169         detail::transfer_t t = detail::jump_fcontext( detail::exchange( fctx_, nullptr), nullptr);
170         if ( nullptr != t.data) {
171             std::exception_ptr * eptr = static_cast< std::exception_ptr * >( t.data);
172             try {
173                 std::rethrow_exception( * eptr);
174             } catch (...) {
175                 std::throw_with_nested( ontop_error{ t.fctx } );
176             }
177         }
178         return execution_context( t.fctx);
179     }
180
181     template< typename Fn >
182     execution_context operator()( exec_ontop_arg_t, Fn && fn) {
183         BOOST_ASSERT( nullptr != fctx_);
184         auto p = std::make_tuple( fn, std::exception_ptr{} );
185         detail::transfer_t t = detail::ontop_fcontext(
186                 detail::exchange( fctx_, nullptr),
187                 & p,
188                 detail::ecv2_context_ontop_void< execution_context, Fn >);
189         if ( nullptr != t.data) {
190             std::exception_ptr * eptr = static_cast< std::exception_ptr * >( t.data);
191             try {
192                 std::rethrow_exception( * eptr);
193             } catch (...) {
194                 std::throw_with_nested( ontop_error{ t.fctx } );
195             }
196         }
197         return execution_context( t.fctx);
198     }
199
200     explicit operator bool() const noexcept {
201         return nullptr != fctx_;
202     }
203
204     bool operator!() const noexcept {
205         return nullptr == fctx_;
206     }
207
208     bool operator<( execution_context const& other) const noexcept {
209         return fctx_ < other.fctx_;
210     }
211
212     template< typename charT, class traitsT >
213     friend std::basic_ostream< charT, traitsT > &
214     operator<<( std::basic_ostream< charT, traitsT > & os, execution_context const& other) {
215         if ( nullptr != other.fctx_) {
216             return os << other.fctx_;
217         } else {
218             return os << "{not-a-context}";
219         }
220     }
221
222     void swap( execution_context & other) noexcept {
223         std::swap( fctx_, other.fctx_);
224     }
225 };
226
227 }
228
229 namespace detail {
230
231 template< typename Ctx, typename Fn >
232 transfer_t ecv2_context_ontop_void( transfer_t t) {
233     auto p = static_cast< std::tuple< Fn, std::exception_ptr > * >( t.data);
234     BOOST_ASSERT( nullptr != p);
235     typename std::decay< Fn >::type fn = std::forward< Fn >( std::get< 0 >( * p) );
236     try {
237         // execute function
238         fn();
239 #if defined( BOOST_CONTEXT_HAS_CXXABI_H )
240     } catch ( abi::__forced_unwind const&) {
241         throw;
242 #endif
243     } catch (...) {
244         std::get< 1 >( * p) = std::current_exception();
245         return { t.fctx, & std::get< 1 >( * p ) };
246     }
247     return { exchange( t.fctx, nullptr), nullptr };
248 }
249
250 template< typename Ctx, typename StackAlloc, typename Fn, typename ... Params >
251 fcontext_t ecv2_context_create_void( StackAlloc && salloc, Fn && fn, Params && ... params) {
252     typedef ecv2_record_void< Ctx, StackAlloc, Fn, Params ... >  record_t;
253
254     auto sctx = salloc.allocate();
255     // reserve space for control structure
256 #if defined(BOOST_NO_CXX11_CONSTEXPR) || defined(BOOST_NO_CXX11_STD_ALIGN)
257     const std::size_t size = sctx.size - sizeof( record_t);
258     void * sp = static_cast< char * >( sctx.sp) - sizeof( record_t);
259 #else
260     constexpr std::size_t func_alignment = 64; // alignof( record_t);
261     constexpr std::size_t func_size = sizeof( record_t);
262     // reserve space on stack
263     void * sp = static_cast< char * >( sctx.sp) - func_size - func_alignment;
264     // align sp pointer
265     std::size_t space = func_size + func_alignment;
266     sp = std::align( func_alignment, func_size, sp, space);
267     BOOST_ASSERT( nullptr != sp);
268     // calculate remaining size
269     const std::size_t size = sctx.size - ( static_cast< char * >( sctx.sp) - static_cast< char * >( sp) );
270 #endif
271     // create fast-context
272     const fcontext_t fctx = make_fcontext( sp, size, & ecv2_context_etry< record_t >);
273     BOOST_ASSERT( nullptr != fctx);
274     // placment new for control structure on context-stack
275     auto rec = ::new ( sp) record_t{
276             sctx, std::forward< StackAlloc >( salloc), std::forward< Fn >( fn), std::forward< Params >( params) ... };
277     // transfer control structure to context-stack
278     return jump_fcontext( fctx, rec).fctx;
279 }
280
281 template< typename Ctx, typename StackAlloc, typename Fn, typename ... Params >
282 fcontext_t ecv2_context_create_void( preallocated palloc, StackAlloc && salloc, Fn && fn, Params && ... params) {
283     typedef ecv2_record_void< Ctx, StackAlloc, Fn, Params ... >  record_t;
284
285     // reserve space for control structure
286 #if defined(BOOST_NO_CXX11_CONSTEXPR) || defined(BOOST_NO_CXX11_STD_ALIGN)
287     const std::size_t size = palloc.size - sizeof( record_t);
288     void * sp = static_cast< char * >( palloc.sp) - sizeof( record_t);
289 #else
290     constexpr std::size_t func_alignment = 64; // alignof( record_t);
291     constexpr std::size_t func_size = sizeof( record_t);
292     // reserve space on stack
293     void * sp = static_cast< char * >( palloc.sp) - func_size - func_alignment;
294     // align sp pointer
295     std::size_t space = func_size + func_alignment;
296     sp = std::align( func_alignment, func_size, sp, space);
297     BOOST_ASSERT( nullptr != sp);
298     // calculate remaining size
299     const std::size_t size = palloc.size - ( static_cast< char * >( palloc.sp) - static_cast< char * >( sp) );
300 #endif
301     // create fast-context
302     const fcontext_t fctx = make_fcontext( sp, size, & ecv2_context_etry< record_t >);
303     BOOST_ASSERT( nullptr != fctx);
304     // placment new for control structure on context-stack
305     auto rec = ::new ( sp) record_t{
306             palloc.sctx, std::forward< StackAlloc >( salloc), std::forward< Fn >( fn), std::forward< Params >( params) ... };
307     // transfer control structure to context-stack
308     return jump_fcontext( fctx, rec).fctx;
309 }
310
311 }