]> git.proxmox.com Git - ceph.git/blob - ceph/src/boost/boost/context/execution_context_v2.hpp
update sources to v12.2.3
[ceph.git] / ceph / src / boost / 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 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( record * p) noexcept {
80 typename std::decay< StackAlloc >::type salloc = std::move( 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 && 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 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 std::forward< StackAlloc >( 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, std::forward< StackAlloc >( 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, std::forward< StackAlloc >( 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, std::forward< StackAlloc >( 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