File indexing completed on 2025-01-18 09:30:24
0001
0002
0003
0004
0005
0006
0007 #ifndef BOOST_CONTEXT_CONTINUATION_H
0008 #define BOOST_CONTEXT_CONTINUATION_H
0009
0010 #include <boost/predef/os.h>
0011 #if BOOST_OS_MACOS
0012 #define _XOPEN_SOURCE 600
0013 #endif
0014
0015 extern "C" {
0016 #include <ucontext.h>
0017 }
0018
0019 #include <boost/predef.h>
0020 #include <boost/context/detail/config.hpp>
0021
0022 #include <algorithm>
0023 #include <cstddef>
0024 #include <cstdint>
0025 #include <cstdlib>
0026 #include <cstring>
0027 #include <functional>
0028 #include <memory>
0029 #include <ostream>
0030 #include <system_error>
0031 #include <tuple>
0032 #include <utility>
0033
0034 #include <boost/assert.hpp>
0035 #include <boost/config.hpp>
0036
0037 #include <boost/context/detail/disable_overload.hpp>
0038 #if defined(BOOST_NO_CXX14_STD_EXCHANGE)
0039 #include <boost/context/detail/exchange.hpp>
0040 #endif
0041 #include <boost/context/detail/externc.hpp>
0042 #if defined(BOOST_NO_CXX17_STD_INVOKE)
0043 #include <boost/context/detail/invoke.hpp>
0044 #endif
0045 #include <boost/context/fixedsize_stack.hpp>
0046 #include <boost/context/flags.hpp>
0047 #include <boost/context/preallocated.hpp>
0048 #if defined(BOOST_USE_SEGMENTED_STACKS)
0049 #include <boost/context/segmented_stack.hpp>
0050 #endif
0051 #include <boost/context/stack_context.hpp>
0052
0053 #ifdef BOOST_HAS_ABI_HEADERS
0054 # include BOOST_ABI_PREFIX
0055 #endif
0056
0057 namespace boost {
0058 namespace context {
0059 namespace detail {
0060
0061
0062
0063
0064 template <typename Record>
0065 #if BOOST_OS_MACOS
0066 static void entry_func(std::uint32_t data_high,
0067 std::uint32_t data_low) noexcept {
0068 auto data =
0069 reinterpret_cast<void *>(std::uint64_t(data_high) << 32 | data_low);
0070 #else
0071 static void entry_func(void *data) noexcept {
0072 #endif
0073 Record *record = static_cast<Record *>(data);
0074 BOOST_ASSERT(nullptr != record);
0075
0076 record->run();
0077 }
0078
0079 struct BOOST_CONTEXT_DECL activation_record {
0080 ucontext_t uctx{};
0081 stack_context sctx{};
0082 bool main_ctx{ true };
0083 activation_record * from{ nullptr };
0084 std::function< activation_record*(activation_record*&) > ontop{};
0085 bool terminated{ false };
0086 bool force_unwind{ false };
0087 #if defined(BOOST_USE_ASAN)
0088 void * fake_stack{ nullptr };
0089 void * stack_bottom{ nullptr };
0090 std::size_t stack_size{ 0 };
0091 #endif
0092
0093 static activation_record *& current() noexcept;
0094
0095
0096
0097 activation_record() {
0098 if ( BOOST_UNLIKELY( 0 != ::getcontext( & uctx) ) ) {
0099 throw std::system_error(
0100 std::error_code( errno, std::system_category() ),
0101 "getcontext() failed");
0102 }
0103 }
0104
0105 activation_record( stack_context sctx_) noexcept :
0106 sctx( sctx_ ),
0107 main_ctx( false ) {
0108 }
0109
0110 virtual ~activation_record() {
0111 }
0112
0113 activation_record( activation_record const&) = delete;
0114 activation_record & operator=( activation_record const&) = delete;
0115
0116 bool is_main_context() const noexcept {
0117 return main_ctx;
0118 }
0119
0120 activation_record * resume() {
0121 from = current();
0122
0123
0124 current() = this;
0125 #if defined(BOOST_USE_SEGMENTED_STACKS)
0126
0127 __splitstack_getcontext( from->sctx.segments_ctx);
0128 __splitstack_setcontext( sctx.segments_ctx);
0129 #endif
0130 #if defined(BOOST_USE_ASAN)
0131 if ( terminated) {
0132 __sanitizer_start_switch_fiber( nullptr, stack_bottom, stack_size);
0133 } else {
0134 __sanitizer_start_switch_fiber( & from->fake_stack, stack_bottom, stack_size);
0135 }
0136 #endif
0137
0138 ::swapcontext( & from->uctx, & uctx);
0139 #if defined(BOOST_USE_ASAN)
0140 __sanitizer_finish_switch_fiber( current()->fake_stack,
0141 (const void **) & current()->from->stack_bottom,
0142 & current()->from->stack_size);
0143 #endif
0144 #if defined(BOOST_NO_CXX14_STD_EXCHANGE)
0145 return exchange( current()->from, nullptr);
0146 #else
0147 return std::exchange( current()->from, nullptr);
0148 #endif
0149 }
0150
0151 template< typename Ctx, typename Fn >
0152 activation_record * resume_with( Fn && fn) {
0153 from = current();
0154
0155
0156
0157 current() = this;
0158 #if defined(BOOST_NO_CXX14_GENERIC_LAMBDAS)
0159 current()->ontop = std::bind(
0160 [](typename std::decay< Fn >::type & fn, activation_record *& ptr){
0161 Ctx c{ ptr };
0162 c = fn( std::move( c) );
0163 if ( ! c) {
0164 ptr = nullptr;
0165 }
0166 #if defined(BOOST_NO_CXX14_STD_EXCHANGE)
0167 return exchange( c.ptr_, nullptr);
0168 #else
0169 return std::exchange( c.ptr_, nullptr);
0170 #endif
0171 },
0172 std::forward< Fn >( fn),
0173 std::placeholders::_1);
0174 #else
0175 current()->ontop = [fn=std::forward<Fn>(fn)](activation_record *& ptr){
0176 Ctx c{ ptr };
0177 c = fn( std::move( c) );
0178 if ( ! c) {
0179 ptr = nullptr;
0180 }
0181 #if defined(BOOST_NO_CXX14_STD_EXCHANGE)
0182 return exchange( c.ptr_, nullptr);
0183 #else
0184 return std::exchange( c.ptr_, nullptr);
0185 #endif
0186 };
0187 #endif
0188 #if defined(BOOST_USE_SEGMENTED_STACKS)
0189
0190 __splitstack_getcontext( from->sctx.segments_ctx);
0191 __splitstack_setcontext( sctx.segments_ctx);
0192 #endif
0193 #if defined(BOOST_USE_ASAN)
0194 __sanitizer_start_switch_fiber( & from->fake_stack, stack_bottom, stack_size);
0195 #endif
0196
0197 ::swapcontext( & from->uctx, & uctx);
0198 #if defined(BOOST_USE_ASAN)
0199 __sanitizer_finish_switch_fiber( current()->fake_stack,
0200 (const void **) & current()->from->stack_bottom,
0201 & current()->from->stack_size);
0202 #endif
0203 #if defined(BOOST_NO_CXX14_STD_EXCHANGE)
0204 return exchange( current()->from, nullptr);
0205 #else
0206 return std::exchange( current()->from, nullptr);
0207 #endif
0208 }
0209
0210 virtual void deallocate() noexcept {
0211 }
0212 };
0213
0214 struct BOOST_CONTEXT_DECL activation_record_initializer {
0215 activation_record_initializer() noexcept;
0216 ~activation_record_initializer();
0217 };
0218
0219 struct forced_unwind {
0220 activation_record * from{ nullptr };
0221
0222 forced_unwind( activation_record * from_) noexcept :
0223 from{ from_ } {
0224 }
0225 };
0226
0227 template< typename Ctx, typename StackAlloc, typename Fn >
0228 class capture_record : public activation_record {
0229 private:
0230 typename std::decay< StackAlloc >::type salloc_;
0231 typename std::decay< Fn >::type fn_;
0232
0233 static void destroy( capture_record * p) noexcept {
0234 typename std::decay< StackAlloc >::type salloc = std::move( p->salloc_);
0235 stack_context sctx = p->sctx;
0236
0237 p->~capture_record();
0238
0239 salloc.deallocate( sctx);
0240 }
0241
0242 public:
0243 capture_record( stack_context sctx, StackAlloc && salloc, Fn && fn) noexcept :
0244 activation_record{ sctx },
0245 salloc_{ std::forward< StackAlloc >( salloc) },
0246 fn_( std::forward< Fn >( fn) ) {
0247 }
0248
0249 void deallocate() noexcept override final {
0250 BOOST_ASSERT( main_ctx || ( ! main_ctx && terminated) );
0251 destroy( this);
0252 }
0253
0254 void run() {
0255 #if defined(BOOST_USE_ASAN)
0256 __sanitizer_finish_switch_fiber( fake_stack,
0257 (const void **) & from->stack_bottom,
0258 & from->stack_size);
0259 #endif
0260 Ctx c{ from };
0261 try {
0262
0263 #if defined(BOOST_NO_CXX17_STD_INVOKE)
0264 c = boost::context::detail::invoke( fn_, std::move( c) );
0265 #else
0266 c = std::invoke( fn_, std::move( c) );
0267 #endif
0268 } catch ( forced_unwind const& ex) {
0269 c = Ctx{ ex.from };
0270 }
0271
0272 from = nullptr;
0273 ontop = nullptr;
0274 terminated = true;
0275 force_unwind = false;
0276 c.resume();
0277 BOOST_ASSERT_MSG( false, "continuation already terminated");
0278 }
0279 };
0280
0281 template< typename Ctx, typename StackAlloc, typename Fn >
0282 static activation_record * create_context1( StackAlloc && salloc, Fn && fn) {
0283 typedef capture_record< Ctx, StackAlloc, Fn > capture_t;
0284
0285 auto sctx = salloc.allocate();
0286
0287 void * storage = reinterpret_cast< void * >(
0288 ( reinterpret_cast< uintptr_t >( sctx.sp) - static_cast< uintptr_t >( sizeof( capture_t) ) )
0289 & ~ static_cast< uintptr_t >( 0xff) );
0290
0291 capture_t * record = new ( storage) capture_t{
0292 sctx, std::forward< StackAlloc >( salloc), std::forward< Fn >( fn) };
0293
0294 void * stack_bottom = reinterpret_cast< void * >(
0295 reinterpret_cast< uintptr_t >( sctx.sp) - static_cast< uintptr_t >( sctx.size) );
0296
0297 if ( BOOST_UNLIKELY( 0 != ::getcontext( & record->uctx) ) ) {
0298 record->~capture_t();
0299 salloc.deallocate( sctx);
0300 throw std::system_error(
0301 std::error_code( errno, std::system_category() ),
0302 "getcontext() failed");
0303 }
0304 record->uctx.uc_stack.ss_sp = stack_bottom;
0305
0306 record->uctx.uc_stack.ss_size = reinterpret_cast< uintptr_t >( storage) -
0307 reinterpret_cast< uintptr_t >( stack_bottom) - static_cast< uintptr_t >( 64);
0308 record->uctx.uc_link = nullptr;
0309 #if BOOST_OS_MACOS
0310 const auto integer = std::uint64_t(record);
0311 ::makecontext(&record->uctx, (void (*)()) & entry_func<capture_t>, 2,
0312 std::uint32_t((integer >> 32) & 0xFFFFFFFF),
0313 std::uint32_t(integer));
0314 #else
0315 ::makecontext(&record->uctx, (void (*)()) & entry_func<capture_t>, 1,
0316 record);
0317 #endif
0318 #if defined(BOOST_USE_ASAN)
0319 record->stack_bottom = record->uctx.uc_stack.ss_sp;
0320 record->stack_size = record->uctx.uc_stack.ss_size;
0321 #endif
0322 return record;
0323 }
0324
0325 template< typename Ctx, typename StackAlloc, typename Fn >
0326 static activation_record * create_context2( preallocated palloc, StackAlloc && salloc, Fn && fn) {
0327 typedef capture_record< Ctx, StackAlloc, Fn > capture_t;
0328
0329
0330 void * storage = reinterpret_cast< void * >(
0331 ( reinterpret_cast< uintptr_t >( palloc.sp) - static_cast< uintptr_t >( sizeof( capture_t) ) )
0332 & ~ static_cast< uintptr_t >( 0xff) );
0333
0334 capture_t * record = new ( storage) capture_t{
0335 palloc.sctx, std::forward< StackAlloc >( salloc), std::forward< Fn >( fn) };
0336
0337 void * stack_bottom = reinterpret_cast< void * >(
0338 reinterpret_cast< uintptr_t >( palloc.sctx.sp) - static_cast< uintptr_t >( palloc.sctx.size) );
0339
0340 if ( BOOST_UNLIKELY( 0 != ::getcontext( & record->uctx) ) ) {
0341 record->~capture_t();
0342 salloc.deallocate( palloc.sctx);
0343 throw std::system_error(
0344 std::error_code( errno, std::system_category() ),
0345 "getcontext() failed");
0346 }
0347 record->uctx.uc_stack.ss_sp = stack_bottom;
0348
0349 record->uctx.uc_stack.ss_size = reinterpret_cast< uintptr_t >( storage) -
0350 reinterpret_cast< uintptr_t >( stack_bottom) - static_cast< uintptr_t >( 64);
0351 record->uctx.uc_link = nullptr;
0352 #if BOOST_OS_MACOS
0353 const auto integer = std::uint64_t(record);
0354 ::makecontext(&record->uctx, (void (*)()) & entry_func<capture_t>, 2,
0355 std::uint32_t((integer >> 32) & 0xFFFFFFFF),
0356 std::uint32_t(integer));
0357 #else
0358 ::makecontext(&record->uctx, (void (*)()) & entry_func<capture_t>, 1,
0359 record);
0360 #endif
0361 #if defined(BOOST_USE_ASAN)
0362 record->stack_bottom = record->uctx.uc_stack.ss_sp;
0363 record->stack_size = record->uctx.uc_stack.ss_size;
0364 #endif
0365 return record;
0366 }
0367
0368 }
0369
0370 class BOOST_CONTEXT_DECL continuation {
0371 private:
0372 friend struct detail::activation_record;
0373
0374 template< typename Ctx, typename StackAlloc, typename Fn >
0375 friend class detail::capture_record;
0376
0377 template< typename Ctx, typename StackAlloc, typename Fn >
0378 friend detail::activation_record * detail::create_context1( StackAlloc &&, Fn &&);
0379
0380 template< typename Ctx, typename StackAlloc, typename Fn >
0381 friend detail::activation_record * detail::create_context2( preallocated, StackAlloc &&, Fn &&);
0382
0383 template< typename StackAlloc, typename Fn >
0384 friend continuation
0385 callcc( std::allocator_arg_t, StackAlloc &&, Fn &&);
0386
0387 template< typename StackAlloc, typename Fn >
0388 friend continuation
0389 callcc( std::allocator_arg_t, preallocated, StackAlloc &&, Fn &&);
0390
0391 detail::activation_record * ptr_{ nullptr };
0392
0393 continuation( detail::activation_record * ptr) noexcept :
0394 ptr_{ ptr } {
0395 }
0396
0397 public:
0398 continuation() = default;
0399
0400 ~continuation() {
0401 if ( BOOST_UNLIKELY( nullptr != ptr_) && ! ptr_->main_ctx) {
0402 if ( BOOST_LIKELY( ! ptr_->terminated) ) {
0403 ptr_->force_unwind = true;
0404 ptr_->resume();
0405 BOOST_ASSERT( ptr_->terminated);
0406 }
0407 ptr_->deallocate();
0408 }
0409 }
0410
0411 continuation( continuation const&) = delete;
0412 continuation & operator=( continuation const&) = delete;
0413
0414 continuation( continuation && other) noexcept {
0415 swap( other);
0416 }
0417
0418 continuation & operator=( continuation && other) noexcept {
0419 if ( BOOST_LIKELY( this != & other) ) {
0420 continuation tmp = std::move( other);
0421 swap( tmp);
0422 }
0423 return * this;
0424 }
0425
0426 continuation resume() & {
0427 return std::move( * this).resume();
0428 }
0429
0430 continuation resume() && {
0431 #if defined(BOOST_NO_CXX14_STD_EXCHANGE)
0432 detail::activation_record * ptr = detail::exchange( ptr_, nullptr)->resume();
0433 #else
0434 detail::activation_record * ptr = std::exchange( ptr_, nullptr)->resume();
0435 #endif
0436 if ( BOOST_UNLIKELY( detail::activation_record::current()->force_unwind) ) {
0437 throw detail::forced_unwind{ ptr};
0438 } else if ( BOOST_UNLIKELY( nullptr != detail::activation_record::current()->ontop) ) {
0439 ptr = detail::activation_record::current()->ontop( ptr);
0440 detail::activation_record::current()->ontop = nullptr;
0441 }
0442 return { ptr };
0443 }
0444
0445 template< typename Fn >
0446 continuation resume_with( Fn && fn) & {
0447 return std::move( * this).resume_with( std::forward< Fn >( fn) );
0448 }
0449
0450 template< typename Fn >
0451 continuation resume_with( Fn && fn) && {
0452 #if defined(BOOST_NO_CXX14_STD_EXCHANGE)
0453 detail::activation_record * ptr =
0454 detail::exchange( ptr_, nullptr)->resume_with< continuation >( std::forward< Fn >( fn) );
0455 #else
0456 detail::activation_record * ptr =
0457 std::exchange( ptr_, nullptr)->resume_with< continuation >( std::forward< Fn >( fn) );
0458 #endif
0459 if ( BOOST_UNLIKELY( detail::activation_record::current()->force_unwind) ) {
0460 throw detail::forced_unwind{ ptr};
0461 } else if ( BOOST_UNLIKELY( nullptr != detail::activation_record::current()->ontop) ) {
0462 ptr = detail::activation_record::current()->ontop( ptr);
0463 detail::activation_record::current()->ontop = nullptr;
0464 }
0465 return { ptr };
0466 }
0467
0468 explicit operator bool() const noexcept {
0469 return nullptr != ptr_ && ! ptr_->terminated;
0470 }
0471
0472 bool operator!() const noexcept {
0473 return nullptr == ptr_ || ptr_->terminated;
0474 }
0475
0476 bool operator<( continuation const& other) const noexcept {
0477 return ptr_ < other.ptr_;
0478 }
0479
0480 #if !defined(BOOST_EMBTC)
0481
0482 template< typename charT, class traitsT >
0483 friend std::basic_ostream< charT, traitsT > &
0484 operator<<( std::basic_ostream< charT, traitsT > & os, continuation const& other) {
0485 if ( nullptr != other.ptr_) {
0486 return os << other.ptr_;
0487 } else {
0488 return os << "{not-a-context}";
0489 }
0490 }
0491
0492 #else
0493
0494 template< typename charT, class traitsT >
0495 friend std::basic_ostream< charT, traitsT > &
0496 operator<<( std::basic_ostream< charT, traitsT > & os, continuation const& other);
0497
0498 #endif
0499
0500 void swap( continuation & other) noexcept {
0501 std::swap( ptr_, other.ptr_);
0502 }
0503 };
0504
0505 #if defined(BOOST_EMBTC)
0506
0507 template< typename charT, class traitsT >
0508 inline std::basic_ostream< charT, traitsT > &
0509 operator<<( std::basic_ostream< charT, traitsT > & os, continuation const& other) {
0510 if ( nullptr != other.ptr_) {
0511 return os << other.ptr_;
0512 } else {
0513 return os << "{not-a-context}";
0514 }
0515 }
0516
0517 #endif
0518
0519 template<
0520 typename Fn,
0521 typename = detail::disable_overload< continuation, Fn >
0522 >
0523 continuation
0524 callcc( Fn && fn) {
0525 return callcc(
0526 std::allocator_arg,
0527 #if defined(BOOST_USE_SEGMENTED_STACKS)
0528 segmented_stack(),
0529 #else
0530 fixedsize_stack(),
0531 #endif
0532 std::forward< Fn >( fn) );
0533 }
0534
0535 template< typename StackAlloc, typename Fn >
0536 continuation
0537 callcc( std::allocator_arg_t, StackAlloc && salloc, Fn && fn) {
0538 return continuation{
0539 detail::create_context1< continuation >(
0540 std::forward< StackAlloc >( salloc), std::forward< Fn >( fn) ) }.resume();
0541 }
0542
0543 template< typename StackAlloc, typename Fn >
0544 continuation
0545 callcc( std::allocator_arg_t, preallocated palloc, StackAlloc && salloc, Fn && fn) {
0546 return continuation{
0547 detail::create_context2< continuation >(
0548 palloc, std::forward< StackAlloc >( salloc), std::forward< Fn >( fn) ) }.resume();
0549 }
0550
0551 inline
0552 void swap( continuation & l, continuation & r) noexcept {
0553 l.swap( r);
0554 }
0555
0556 }}
0557
0558 #ifdef BOOST_HAS_ABI_HEADERS
0559 # include BOOST_ABI_SUFFIX
0560 #endif
0561
0562 #endif