Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-01-18 09:30:24

0001 
0002 //          Copyright Oliver Kowalke 2017.
0003 // Distributed under the Boost Software License, Version 1.0.
0004 //    (See accompanying file LICENSE_1_0.txt or copy at
0005 //          http://www.boost.org/LICENSE_1_0.txt)
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 // tampoline function
0062 // entered if the execution context
0063 // is resumed for the first time
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   // start execution of toplevel context-function
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     // used for toplevel-context
0096     // (e.g. main context, thread-entry context)
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         // store `this` in static, thread local pointer
0123         // `this` will become the active (running) context
0124         current() = this;
0125 #if defined(BOOST_USE_SEGMENTED_STACKS)
0126         // adjust segmented stack properties
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         // context switch from parent context to `this`-context
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         // store `this` in static, thread local pointer
0155         // `this` will become the active (running) context
0156         // returned by continuation::current()
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         // adjust segmented stack properties
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         // context switch from parent context to `this`-context
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         // deallocate activation record
0237         p->~capture_record();
0238         // destroy stack with stack allocator
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             // invoke context-function
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         // this context has finished its task
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     // reserve space for control structure
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     // placment new for control structure on context stack
0291     capture_t * record = new ( storage) capture_t{
0292             sctx, std::forward< StackAlloc >( salloc), std::forward< Fn >( fn) };
0293     // stack bottom
0294     void * stack_bottom = reinterpret_cast< void * >(
0295             reinterpret_cast< uintptr_t >( sctx.sp) - static_cast< uintptr_t >( sctx.size) );
0296     // create user-context
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     // 64byte gap between control structure and stack top
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     // reserve space for control structure
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     // placment new for control structure on context stack
0334     capture_t * record = new ( storage) capture_t{
0335             palloc.sctx, std::forward< StackAlloc >( salloc), std::forward< Fn >( fn) };
0336     // stack bottom
0337     void * stack_bottom = reinterpret_cast< void * >(
0338             reinterpret_cast< uintptr_t >( palloc.sctx.sp) - static_cast< uintptr_t >( palloc.sctx.size) );
0339     // create user-context
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     // 64byte gap between control structure and stack top
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 // BOOST_CONTEXT_CONTINUATION_H