]> git.proxmox.com Git - ceph.git/blame - ceph/src/boost/boost/context/continuation_ucontext.hpp
import new upstream nautilus stable release 14.2.8
[ceph.git] / ceph / src / boost / boost / context / continuation_ucontext.hpp
CommitLineData
b32b8144
FG
1
2// Copyright Oliver Kowalke 2017.
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_CONTINUATION_H
8#define BOOST_CONTEXT_CONTINUATION_H
9
10#include <boost/predef.h>
11#if BOOST_OS_MACOS
12#define _XOPEN_SOURCE 600
13#endif
14
15extern "C" {
16#include <ucontext.h>
17}
18
19#include <boost/context/detail/config.hpp>
20
21#include <algorithm>
22#include <cstddef>
23#include <cstdint>
24#include <cstdlib>
25#include <cstring>
26#include <functional>
27#include <memory>
28#include <ostream>
29#include <system_error>
30#include <tuple>
31#include <utility>
32
33#include <boost/assert.hpp>
34#include <boost/config.hpp>
35
36#include <boost/context/detail/disable_overload.hpp>
37#if defined(BOOST_NO_CXX14_STD_EXCHANGE)
38#include <boost/context/detail/exchange.hpp>
39#endif
11fdf7f2 40#include <boost/context/detail/externc.hpp>
b32b8144
FG
41#if defined(BOOST_NO_CXX17_STD_INVOKE)
42#include <boost/context/detail/invoke.hpp>
43#endif
44#include <boost/context/fixedsize_stack.hpp>
45#include <boost/context/flags.hpp>
46#include <boost/context/preallocated.hpp>
47#if defined(BOOST_USE_SEGMENTED_STACKS)
48#include <boost/context/segmented_stack.hpp>
49#endif
50#include <boost/context/stack_context.hpp>
51
52#ifdef BOOST_HAS_ABI_HEADERS
53# include BOOST_ABI_PREFIX
54#endif
55
b32b8144
FG
56namespace boost {
57namespace context {
58namespace detail {
59
60// tampoline function
61// entered if the execution context
62// is resumed for the first time
63template< typename Record >
64static void entry_func( void * data) noexcept {
65 Record * record = static_cast< Record * >( data);
66 BOOST_ASSERT( nullptr != record);
67 // start execution of toplevel context-function
68 record->run();
69}
70
71struct BOOST_CONTEXT_DECL activation_record {
72 ucontext_t uctx{};
73 stack_context sctx{};
74 bool main_ctx{ true };
75 activation_record * from{ nullptr };
76 std::function< activation_record*(activation_record*&) > ontop{};
77 bool terminated{ false };
78 bool force_unwind{ false };
79#if defined(BOOST_USE_ASAN)
80 void * fake_stack{ nullptr };
81 void * stack_bottom{ nullptr };
82 std::size_t stack_size{ 0 };
83#endif
84
85 static activation_record *& current() noexcept;
86
87 // used for toplevel-context
88 // (e.g. main context, thread-entry context)
89 activation_record() {
90 if ( BOOST_UNLIKELY( 0 != ::getcontext( & uctx) ) ) {
91 throw std::system_error(
92 std::error_code( errno, std::system_category() ),
93 "getcontext() failed");
94 }
95 }
96
97 activation_record( stack_context sctx_) noexcept :
98 sctx( sctx_ ),
99 main_ctx( false ) {
100 }
101
102 virtual ~activation_record() {
103 }
104
105 activation_record( activation_record const&) = delete;
106 activation_record & operator=( activation_record const&) = delete;
107
108 bool is_main_context() const noexcept {
109 return main_ctx;
110 }
111
112 activation_record * resume() {
113 from = current();
114 // store `this` in static, thread local pointer
115 // `this` will become the active (running) context
116 current() = this;
117#if defined(BOOST_USE_SEGMENTED_STACKS)
118 // adjust segmented stack properties
119 __splitstack_getcontext( from->sctx.segments_ctx);
120 __splitstack_setcontext( sctx.segments_ctx);
121#endif
122#if defined(BOOST_USE_ASAN)
123 if ( terminated) {
124 __sanitizer_start_switch_fiber( nullptr, stack_bottom, stack_size);
125 } else {
126 __sanitizer_start_switch_fiber( & from->fake_stack, stack_bottom, stack_size);
127 }
128#endif
129 // context switch from parent context to `this`-context
130 ::swapcontext( & from->uctx, & uctx);
131#if defined(BOOST_USE_ASAN)
132 __sanitizer_finish_switch_fiber( current()->fake_stack,
133 (const void **) & current()->from->stack_bottom,
134 & current()->from->stack_size);
135#endif
136#if defined(BOOST_NO_CXX14_STD_EXCHANGE)
137 return exchange( current()->from, nullptr);
138#else
139 return std::exchange( current()->from, nullptr);
140#endif
141 }
142
143 template< typename Ctx, typename Fn >
144 activation_record * resume_with( Fn && fn) {
145 from = current();
146 // store `this` in static, thread local pointer
147 // `this` will become the active (running) context
148 // returned by continuation::current()
149 current() = this;
150#if defined(BOOST_NO_CXX14_GENERIC_LAMBDAS)
151 current()->ontop = std::bind(
152 [](typename std::decay< Fn >::type & fn, activation_record *& ptr){
153 Ctx c{ ptr };
154 c = fn( std::move( c) );
155 if ( ! c) {
156 ptr = nullptr;
157 }
158#if defined(BOOST_NO_CXX14_STD_EXCHANGE)
159 return exchange( c.ptr_, nullptr);
160#else
161 return std::exchange( c.ptr_, nullptr);
162#endif
163 },
164 std::forward< Fn >( fn),
165 std::placeholders::_1);
166#else
167 current()->ontop = [fn=std::forward<Fn>(fn)](activation_record *& ptr){
168 Ctx c{ ptr };
169 c = fn( std::move( c) );
170 if ( ! c) {
171 ptr = nullptr;
172 }
173#if defined(BOOST_NO_CXX14_STD_EXCHANGE)
174 return exchange( c.ptr_, nullptr);
175#else
176 return std::exchange( c.ptr_, nullptr);
177#endif
178 };
179#endif
180#if defined(BOOST_USE_SEGMENTED_STACKS)
181 // adjust segmented stack properties
182 __splitstack_getcontext( from->sctx.segments_ctx);
183 __splitstack_setcontext( sctx.segments_ctx);
184#endif
185#if defined(BOOST_USE_ASAN)
186 __sanitizer_start_switch_fiber( & from->fake_stack, stack_bottom, stack_size);
187#endif
188 // context switch from parent context to `this`-context
189 ::swapcontext( & from->uctx, & uctx);
190#if defined(BOOST_USE_ASAN)
191 __sanitizer_finish_switch_fiber( current()->fake_stack,
192 (const void **) & current()->from->stack_bottom,
193 & current()->from->stack_size);
194#endif
195#if defined(BOOST_NO_CXX14_STD_EXCHANGE)
196 return exchange( current()->from, nullptr);
197#else
198 return std::exchange( current()->from, nullptr);
199#endif
200 }
201
202 virtual void deallocate() noexcept {
203 }
204};
205
206struct BOOST_CONTEXT_DECL activation_record_initializer {
207 activation_record_initializer() noexcept;
208 ~activation_record_initializer();
209};
210
211struct forced_unwind {
92f5a8d4
TL
212 activation_record * from{ nullptr };
213#ifndef BOOST_ASSERT_IS_VOID
214 bool caught{ false };
215#endif
b32b8144
FG
216
217 forced_unwind( activation_record * from_) noexcept :
218 from{ from_ } {
219 }
92f5a8d4
TL
220
221#ifndef BOOST_ASSERT_IS_VOID
222 ~forced_unwind() {
223 BOOST_ASSERT( caught);
224 }
225#endif
b32b8144
FG
226};
227
228template< typename Ctx, typename StackAlloc, typename Fn >
229class capture_record : public activation_record {
230private:
231 typename std::decay< StackAlloc >::type salloc_;
232 typename std::decay< Fn >::type fn_;
233
234 static void destroy( capture_record * p) noexcept {
235 typename std::decay< StackAlloc >::type salloc = std::move( p->salloc_);
236 stack_context sctx = p->sctx;
237 // deallocate activation record
238 p->~capture_record();
239 // destroy stack with stack allocator
240 salloc.deallocate( sctx);
241 }
242
243public:
244 capture_record( stack_context sctx, StackAlloc && salloc, Fn && fn) noexcept :
245 activation_record{ sctx },
246 salloc_{ std::forward< StackAlloc >( salloc) },
247 fn_( std::forward< Fn >( fn) ) {
248 }
249
250 void deallocate() noexcept override final {
251 BOOST_ASSERT( main_ctx || ( ! main_ctx && terminated) );
252 destroy( this);
253 }
254
255 void run() {
256#if defined(BOOST_USE_ASAN)
257 __sanitizer_finish_switch_fiber( fake_stack,
258 (const void **) & from->stack_bottom,
259 & from->stack_size);
260#endif
261 Ctx c{ from };
262 try {
263 // invoke context-function
264#if defined(BOOST_NO_CXX17_STD_INVOKE)
11fdf7f2 265 c = boost::context::detail::invoke( fn_, std::move( c) );
b32b8144
FG
266#else
267 c = std::invoke( fn_, std::move( c) );
268#endif
269 } catch ( forced_unwind const& ex) {
270 c = Ctx{ ex.from };
92f5a8d4
TL
271#ifndef BOOST_ASSERT_IS_VOID
272 const_cast< forced_unwind & >( ex).caught = true;
273#endif
b32b8144
FG
274 }
275 // this context has finished its task
276 from = nullptr;
277 ontop = nullptr;
278 terminated = true;
279 force_unwind = false;
280 c.resume();
281 BOOST_ASSERT_MSG( false, "continuation already terminated");
282 }
283};
284
285template< typename Ctx, typename StackAlloc, typename Fn >
286static activation_record * create_context1( StackAlloc && salloc, Fn && fn) {
287 typedef capture_record< Ctx, StackAlloc, Fn > capture_t;
288
289 auto sctx = salloc.allocate();
290 // reserve space for control structure
291 void * storage = reinterpret_cast< void * >(
292 ( reinterpret_cast< uintptr_t >( sctx.sp) - static_cast< uintptr_t >( sizeof( capture_t) ) )
293 & ~ static_cast< uintptr_t >( 0xff) );
294 // placment new for control structure on context stack
295 capture_t * record = new ( storage) capture_t{
296 sctx, std::forward< StackAlloc >( salloc), std::forward< Fn >( fn) };
297 // stack bottom
298 void * stack_bottom = reinterpret_cast< void * >(
299 reinterpret_cast< uintptr_t >( sctx.sp) - static_cast< uintptr_t >( sctx.size) );
300 // create user-context
301 if ( BOOST_UNLIKELY( 0 != ::getcontext( & record->uctx) ) ) {
92f5a8d4
TL
302 record->~capture_t();
303 salloc.deallocate( sctx);
b32b8144
FG
304 throw std::system_error(
305 std::error_code( errno, std::system_category() ),
306 "getcontext() failed");
307 }
308 record->uctx.uc_stack.ss_sp = stack_bottom;
309 // 64byte gap between control structure and stack top
310 record->uctx.uc_stack.ss_size = reinterpret_cast< uintptr_t >( storage) -
311 reinterpret_cast< uintptr_t >( stack_bottom) - static_cast< uintptr_t >( 64);
312 record->uctx.uc_link = nullptr;
313 ::makecontext( & record->uctx, ( void (*)() ) & entry_func< capture_t >, 1, record);
314#if defined(BOOST_USE_ASAN)
315 record->stack_bottom = record->uctx.uc_stack.ss_sp;
316 record->stack_size = record->uctx.uc_stack.ss_size;
317#endif
318 return record;
319}
320
321template< typename Ctx, typename StackAlloc, typename Fn >
322static activation_record * create_context2( preallocated palloc, StackAlloc && salloc, Fn && fn) {
323 typedef capture_record< Ctx, StackAlloc, Fn > capture_t;
324
325 // reserve space for control structure
326 void * storage = reinterpret_cast< void * >(
327 ( reinterpret_cast< uintptr_t >( palloc.sp) - static_cast< uintptr_t >( sizeof( capture_t) ) )
328 & ~ static_cast< uintptr_t >( 0xff) );
329 // placment new for control structure on context stack
330 capture_t * record = new ( storage) capture_t{
331 palloc.sctx, std::forward< StackAlloc >( salloc), std::forward< Fn >( fn) };
332 // stack bottom
333 void * stack_bottom = reinterpret_cast< void * >(
334 reinterpret_cast< uintptr_t >( palloc.sctx.sp) - static_cast< uintptr_t >( palloc.sctx.size) );
335 // create user-context
336 if ( BOOST_UNLIKELY( 0 != ::getcontext( & record->uctx) ) ) {
92f5a8d4
TL
337 record->~capture_t();
338 salloc.deallocate( palloc.sctx);
b32b8144
FG
339 throw std::system_error(
340 std::error_code( errno, std::system_category() ),
341 "getcontext() failed");
342 }
343 record->uctx.uc_stack.ss_sp = stack_bottom;
344 // 64byte gap between control structure and stack top
345 record->uctx.uc_stack.ss_size = reinterpret_cast< uintptr_t >( storage) -
346 reinterpret_cast< uintptr_t >( stack_bottom) - static_cast< uintptr_t >( 64);
347 record->uctx.uc_link = nullptr;
348 ::makecontext( & record->uctx, ( void (*)() ) & entry_func< capture_t >, 1, record);
349#if defined(BOOST_USE_ASAN)
350 record->stack_bottom = record->uctx.uc_stack.ss_sp;
351 record->stack_size = record->uctx.uc_stack.ss_size;
352#endif
353 return record;
354}
355
356}
357
358class BOOST_CONTEXT_DECL continuation {
359private:
360 friend struct detail::activation_record;
361
362 template< typename Ctx, typename StackAlloc, typename Fn >
363 friend class detail::capture_record;
364
365 template< typename Ctx, typename StackAlloc, typename Fn >
366 friend detail::activation_record * detail::create_context1( StackAlloc &&, Fn &&);
367
368 template< typename Ctx, typename StackAlloc, typename Fn >
369 friend detail::activation_record * detail::create_context2( preallocated, StackAlloc &&, Fn &&);
370
371 template< typename StackAlloc, typename Fn >
372 friend continuation
373 callcc( std::allocator_arg_t, StackAlloc &&, Fn &&);
374
375 template< typename StackAlloc, typename Fn >
376 friend continuation
377 callcc( std::allocator_arg_t, preallocated, StackAlloc &&, Fn &&);
378
379 detail::activation_record * ptr_{ nullptr };
380
381 continuation( detail::activation_record * ptr) noexcept :
382 ptr_{ ptr } {
383 }
384
385public:
386 continuation() = default;
387
388 ~continuation() {
389 if ( BOOST_UNLIKELY( nullptr != ptr_) && ! ptr_->main_ctx) {
390 if ( BOOST_LIKELY( ! ptr_->terminated) ) {
391 ptr_->force_unwind = true;
392 ptr_->resume();
393 BOOST_ASSERT( ptr_->terminated);
394 }
395 ptr_->deallocate();
396 }
397 }
398
399 continuation( continuation const&) = delete;
400 continuation & operator=( continuation const&) = delete;
401
402 continuation( continuation && other) noexcept {
403 swap( other);
404 }
405
406 continuation & operator=( continuation && other) noexcept {
407 if ( BOOST_LIKELY( this != & other) ) {
408 continuation tmp = std::move( other);
409 swap( tmp);
410 }
411 return * this;
412 }
413
11fdf7f2
TL
414 continuation resume() & {
415 return std::move( * this).resume();
416 }
417
418 continuation resume() && {
b32b8144
FG
419#if defined(BOOST_NO_CXX14_STD_EXCHANGE)
420 detail::activation_record * ptr = detail::exchange( ptr_, nullptr)->resume();
421#else
422 detail::activation_record * ptr = std::exchange( ptr_, nullptr)->resume();
423#endif
424 if ( BOOST_UNLIKELY( detail::activation_record::current()->force_unwind) ) {
425 throw detail::forced_unwind{ ptr};
426 } else if ( BOOST_UNLIKELY( nullptr != detail::activation_record::current()->ontop) ) {
427 ptr = detail::activation_record::current()->ontop( ptr);
428 detail::activation_record::current()->ontop = nullptr;
429 }
11fdf7f2
TL
430 return { ptr };
431 }
432
433 template< typename Fn >
434 continuation resume_with( Fn && fn) & {
435 return std::move( * this).resume_with( std::forward< Fn >( fn) );
b32b8144
FG
436 }
437
438 template< typename Fn >
11fdf7f2 439 continuation resume_with( Fn && fn) && {
b32b8144
FG
440#if defined(BOOST_NO_CXX14_STD_EXCHANGE)
441 detail::activation_record * ptr =
442 detail::exchange( ptr_, nullptr)->resume_with< continuation >( std::forward< Fn >( fn) );
443#else
444 detail::activation_record * ptr =
445 std::exchange( ptr_, nullptr)->resume_with< continuation >( std::forward< Fn >( fn) );
446#endif
447 if ( BOOST_UNLIKELY( detail::activation_record::current()->force_unwind) ) {
448 throw detail::forced_unwind{ ptr};
449 } else if ( BOOST_UNLIKELY( nullptr != detail::activation_record::current()->ontop) ) {
450 ptr = detail::activation_record::current()->ontop( ptr);
451 detail::activation_record::current()->ontop = nullptr;
452 }
11fdf7f2 453 return { ptr };
b32b8144
FG
454 }
455
456 explicit operator bool() const noexcept {
457 return nullptr != ptr_ && ! ptr_->terminated;
458 }
459
460 bool operator!() const noexcept {
461 return nullptr == ptr_ || ptr_->terminated;
462 }
463
b32b8144
FG
464 bool operator<( continuation const& other) const noexcept {
465 return ptr_ < other.ptr_;
466 }
467
b32b8144
FG
468 template< typename charT, class traitsT >
469 friend std::basic_ostream< charT, traitsT > &
470 operator<<( std::basic_ostream< charT, traitsT > & os, continuation const& other) {
471 if ( nullptr != other.ptr_) {
472 return os << other.ptr_;
473 } else {
474 return os << "{not-a-context}";
475 }
476 }
477
478 void swap( continuation & other) noexcept {
479 std::swap( ptr_, other.ptr_);
480 }
481};
482
483template<
484 typename Fn,
485 typename = detail::disable_overload< continuation, Fn >
486>
487continuation
488callcc( Fn && fn) {
489 return callcc(
490 std::allocator_arg,
491#if defined(BOOST_USE_SEGMENTED_STACKS)
492 segmented_stack(),
493#else
494 fixedsize_stack(),
495#endif
496 std::forward< Fn >( fn) );
497}
498
499template< typename StackAlloc, typename Fn >
500continuation
501callcc( std::allocator_arg_t, StackAlloc && salloc, Fn && fn) {
502 return continuation{
503 detail::create_context1< continuation >(
504 std::forward< StackAlloc >( salloc), std::forward< Fn >( fn) ) }.resume();
505}
506
507template< typename StackAlloc, typename Fn >
508continuation
509callcc( std::allocator_arg_t, preallocated palloc, StackAlloc && salloc, Fn && fn) {
510 return continuation{
511 detail::create_context2< continuation >(
512 palloc, std::forward< StackAlloc >( salloc), std::forward< Fn >( fn) ) }.resume();
513}
514
515inline
516void swap( continuation & l, continuation & r) noexcept {
517 l.swap( r);
518}
519
520}}
521
522#ifdef BOOST_HAS_ABI_HEADERS
523# include BOOST_ABI_SUFFIX
524#endif
525
526#endif // BOOST_CONTEXT_CONTINUATION_H