Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-09-17 08:25:22

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_FIBER_H
0008 #define BOOST_CONTEXT_FIBER_H
0009 
0010 #include <boost/context/detail/config.hpp>
0011 
0012 #include <algorithm>
0013 #include <cstddef>
0014 #include <cstdint>
0015 #include <cstdlib>
0016 #include <exception>
0017 #include <functional>
0018 #include <memory>
0019 #include <ostream>
0020 #include <tuple>
0021 #include <utility>
0022 
0023 #include <boost/assert.hpp>
0024 #include <boost/config.hpp>
0025 #include <boost/intrusive_ptr.hpp>
0026 #include <boost/core/ignore_unused.hpp>
0027 
0028 #if defined(BOOST_NO_CXX14_STD_EXCHANGE)
0029 #include <boost/context/detail/exchange.hpp>
0030 #endif
0031 #if defined(BOOST_NO_CXX17_STD_INVOKE)
0032 #include <boost/context/detail/invoke.hpp>
0033 #endif
0034 #include <boost/context/detail/disable_overload.hpp>
0035 #include <boost/context/detail/exception.hpp>
0036 #include <boost/context/detail/fcontext.hpp>
0037 #include <boost/context/detail/tuple.hpp>
0038 #include <boost/context/fixedsize_stack.hpp>
0039 #include <boost/context/flags.hpp>
0040 #include <boost/context/preallocated.hpp>
0041 #include <boost/context/segmented_stack.hpp>
0042 #include <boost/context/stack_context.hpp>
0043 
0044 #ifdef BOOST_HAS_ABI_HEADERS
0045 # include BOOST_ABI_PREFIX
0046 #endif
0047 
0048 #if defined(__CET__) && defined(__unix__)
0049 #  include <cet.h>
0050 #  include <sys/mman.h>
0051 #  include <unistd.h>
0052 #  define SHSTK_ENABLED (__CET__ & 0x2)
0053 #  define BOOST_CONTEXT_SHADOW_STACK (SHSTK_ENABLED && SHADOW_STACK_SYSCALL)
0054 #  if !defined(__NR_map_shadow_stack)
0055 #    define __NR_map_shadow_stack 453
0056 #  endif
0057 #ifndef SHADOW_STACK_SET_TOKEN
0058 #  define SHADOW_STACK_SET_TOKEN 0x1
0059 #endif
0060 #endif
0061 
0062 #if defined(BOOST_MSVC)
0063 # pragma warning(push)
0064 # pragma warning(disable: 4702)
0065 #endif
0066 
0067 #if ! (defined(__GLIBCPP__) || defined(__GLIBCXX__))
0068 namespace boost {
0069 namespace context {
0070 namespace detail {
0071 
0072 // manage_exception_state is a dummy struct unless we have specific support
0073 struct manage_exception_state {};
0074 
0075 } // namespace detail
0076 } // namespace context
0077 } // namespace boost
0078 
0079 #else // libstdc++
0080 #include <cxxabi.h>
0081 
0082 namespace __cxxabiv1 {
0083 struct __cxa_eh_globals {
0084     void *       caughtExceptions;
0085     unsigned int uncaughtExceptions;
0086 };
0087 
0088 class manage_exception_state {
0089 public:
0090     manage_exception_state() {
0091         exception_state_ = *__cxa_get_globals();
0092     }
0093     ~manage_exception_state() {
0094         *__cxa_get_globals() = exception_state_;
0095     }
0096 private:
0097     __cxa_eh_globals exception_state_;
0098 };
0099 } // namespace __cxxabiv1
0100 
0101 namespace boost {
0102 namespace context {
0103 namespace detail {
0104 
0105 using __cxxabiv1::manage_exception_state;
0106 
0107 } // namespace detail
0108 } // namespace context
0109 } // namespace boost
0110 #endif // __GLIBCPP__ or __GLIBCXX__
0111 
0112 namespace boost {
0113 namespace context {
0114 namespace detail {
0115 
0116 inline
0117 transfer_t fiber_unwind( transfer_t t) {
0118     throw forced_unwind( t.fctx);
0119     return { nullptr, nullptr };
0120 }
0121 
0122 template< typename Rec >
0123 transfer_t fiber_exit( transfer_t t) noexcept {
0124     Rec * rec = static_cast< Rec * >( t.data);
0125 #if BOOST_CONTEXT_SHADOW_STACK
0126     // destory shadow stack
0127     std::size_t ss_size = *((unsigned long*)(reinterpret_cast< uintptr_t >( rec)- 16));
0128     long unsigned int ss_base = *((unsigned long*)(reinterpret_cast< uintptr_t >( rec)- 8));
0129     munmap((void *)ss_base, ss_size);
0130 #endif
0131     // destroy context stack
0132     rec->deallocate();
0133     return { nullptr, nullptr };
0134 }
0135 
0136 template< typename Rec >
0137 void fiber_entry( transfer_t t) noexcept {
0138     // transfer control structure to the context-stack
0139     Rec * rec = static_cast< Rec * >( t.data);
0140     BOOST_ASSERT( nullptr != t.fctx);
0141     BOOST_ASSERT( nullptr != rec);
0142     try {
0143         // jump back to `create_context()`
0144         t = jump_fcontext( t.fctx, nullptr);
0145         // start executing
0146         t.fctx = rec->run( t.fctx);
0147     } catch ( forced_unwind const& ex) {
0148         t = { ex.fctx, nullptr };
0149     }
0150     BOOST_ASSERT( nullptr != t.fctx);
0151     // destroy context-stack of `this`context on next context
0152     ontop_fcontext( t.fctx, rec, fiber_exit< Rec >);
0153     BOOST_ASSERT_MSG( false, "context already terminated");
0154 }
0155 
0156 template< typename Ctx, typename Fn >
0157 transfer_t fiber_ontop( transfer_t t) {
0158     BOOST_ASSERT( nullptr != t.data);
0159     auto p = *static_cast< Fn * >( t.data);
0160     t.data = nullptr;
0161     // execute function, pass fiber via reference
0162     Ctx c = p( Ctx{ t.fctx } );
0163 #if defined(BOOST_NO_CXX14_STD_EXCHANGE)
0164     return { exchange( c.fctx_, nullptr), nullptr };
0165 #else
0166     return { std::exchange( c.fctx_, nullptr), nullptr };
0167 #endif
0168 }
0169 
0170 template< typename Ctx, typename StackAlloc, typename Fn >
0171 class fiber_record {
0172 private:
0173     stack_context                                       sctx_;
0174     typename std::decay< StackAlloc >::type             salloc_;
0175     typename std::decay< Fn >::type                     fn_;
0176 
0177     static void destroy( fiber_record * p) noexcept {
0178         typename std::decay< StackAlloc >::type salloc = std::move( p->salloc_);
0179         stack_context sctx = p->sctx_;
0180         // deallocate fiber_record
0181         p->~fiber_record();
0182         // destroy stack with stack allocator
0183         salloc.deallocate( sctx);
0184     }
0185 
0186 public:
0187     fiber_record( stack_context sctx, StackAlloc && salloc,
0188             Fn && fn) noexcept :
0189         sctx_( sctx),
0190         salloc_( std::forward< StackAlloc >( salloc)),
0191         fn_( std::forward< Fn >( fn) ) {
0192     }
0193 
0194     fiber_record( fiber_record const&) = delete;
0195     fiber_record & operator=( fiber_record const&) = delete;
0196 
0197     void deallocate() noexcept {
0198         destroy( this);
0199     }
0200 
0201     fcontext_t run( fcontext_t fctx) {
0202         // invoke context-function
0203 #if defined(BOOST_NO_CXX17_STD_INVOKE)
0204         Ctx c = boost::context::detail::invoke( fn_, Ctx{ fctx } );
0205 #else
0206         Ctx c = std::invoke( fn_, Ctx{ fctx } );
0207 #endif
0208 #if defined(BOOST_NO_CXX14_STD_EXCHANGE)
0209         return exchange( c.fctx_, nullptr);
0210 #else
0211         return std::exchange( c.fctx_, nullptr);
0212 #endif
0213     }
0214 };
0215 
0216 template< typename Record, typename StackAlloc, typename Fn >
0217 fcontext_t create_fiber1( StackAlloc && salloc, Fn && fn) {
0218     auto sctx = salloc.allocate();
0219     // reserve space for control structure
0220     void * storage = reinterpret_cast< void * >(
0221             ( reinterpret_cast< uintptr_t >( sctx.sp) - static_cast< uintptr_t >( sizeof( Record) ) )
0222             & ~static_cast< uintptr_t >( 0xff) );
0223     // placment new for control structure on context stack
0224     Record * record = new ( storage) Record{
0225             sctx, std::forward< StackAlloc >( salloc), std::forward< Fn >( fn) };
0226     // 64byte gab between control structure and stack top
0227     // should be 16byte aligned
0228     void * stack_top = reinterpret_cast< void * >(
0229             reinterpret_cast< uintptr_t >( storage) - static_cast< uintptr_t >( 64) );
0230     void * stack_bottom = reinterpret_cast< void * >(
0231             reinterpret_cast< uintptr_t >( sctx.sp) - static_cast< uintptr_t >( sctx.size) );
0232     // create fast-context
0233     const std::size_t size = reinterpret_cast< uintptr_t >( stack_top) - reinterpret_cast< uintptr_t >( stack_bottom);
0234 
0235 #if BOOST_CONTEXT_SHADOW_STACK
0236     std::size_t ss_size = size >> 5;
0237     // align shadow stack to 8 bytes.
0238     ss_size = (ss_size + 7) & ~7;
0239     // Todo: shadow stack occupies at least 4KB
0240     ss_size = (ss_size > 4096) ? size : 4096;
0241     // create shadow stack
0242     void *ss_base = (void *)syscall(__NR_map_shadow_stack, 0, ss_size, SHADOW_STACK_SET_TOKEN);
0243     BOOST_ASSERT(ss_base != -1);
0244     unsigned long ss_sp = (unsigned long)ss_base + ss_size;
0245     /* pass the shadow stack pointer to make_fcontext
0246        i.e., link the new shadow stack with the new fcontext
0247        TODO should be a better way? */
0248     *((unsigned long*)(reinterpret_cast< uintptr_t >( stack_top)- 8)) = ss_sp;
0249     /* Todo: place shadow stack info in 64byte gap */
0250     *((unsigned long*)(reinterpret_cast< uintptr_t >( storage)- 8)) = (unsigned long) ss_base;
0251     *((unsigned long*)(reinterpret_cast< uintptr_t >( storage)- 16)) = ss_size;
0252 #endif
0253     const fcontext_t fctx = make_fcontext( stack_top, size, & fiber_entry< Record >);
0254     BOOST_ASSERT( nullptr != fctx);
0255     // transfer control structure to context-stack
0256     return jump_fcontext( fctx, record).fctx;
0257 }
0258 
0259 template< typename Record, typename StackAlloc, typename Fn >
0260 fcontext_t create_fiber2( preallocated palloc, StackAlloc && salloc, Fn && fn) {
0261     // reserve space for control structure
0262     void * storage = reinterpret_cast< void * >(
0263             ( reinterpret_cast< uintptr_t >( palloc.sp) - static_cast< uintptr_t >( sizeof( Record) ) )
0264             & ~ static_cast< uintptr_t >( 0xff) );
0265     // placment new for control structure on context-stack
0266     Record * record = new ( storage) Record{
0267             palloc.sctx, std::forward< StackAlloc >( salloc), std::forward< Fn >( fn) };
0268     // 64byte gab between control structure and stack top
0269     void * stack_top = reinterpret_cast< void * >(
0270             reinterpret_cast< uintptr_t >( storage) - static_cast< uintptr_t >( 64) );
0271     void * stack_bottom = reinterpret_cast< void * >(
0272             reinterpret_cast< uintptr_t >( palloc.sctx.sp) - static_cast< uintptr_t >( palloc.sctx.size) );
0273     // create fast-context
0274     const std::size_t size = reinterpret_cast< uintptr_t >( stack_top) - reinterpret_cast< uintptr_t >( stack_bottom);
0275 
0276 #if BOOST_CONTEXT_SHADOW_STACK
0277     std::size_t ss_size = size >> 5;
0278     // align shadow stack to 8 bytes.
0279     ss_size = (ss_size + 7) & ~7;
0280     // Todo: shadow stack occupies at least 4KB
0281     ss_size = (ss_size > 4096) ? size : 4096;
0282     // create shadow stack
0283     void *ss_base = (void *)syscall(__NR_map_shadow_stack, 0, ss_size, SHADOW_STACK_SET_TOKEN);
0284     BOOST_ASSERT(ss_base != -1);
0285     unsigned long ss_sp = (unsigned long)ss_base + ss_size;
0286     /* pass the shadow stack pointer to make_fcontext
0287        i.e., link the new shadow stack with the new fcontext
0288        TODO should be a better way? */
0289     *((unsigned long*)(reinterpret_cast< uintptr_t >( stack_top)- 8)) = ss_sp;
0290     /* Todo: place shadow stack info in 64byte gap */
0291     *((unsigned long*)(reinterpret_cast< uintptr_t >( storage)- 8)) = (unsigned long) ss_base;
0292     *((unsigned long*)(reinterpret_cast< uintptr_t >( storage)- 16)) = ss_size;
0293 #endif
0294     const fcontext_t fctx = make_fcontext( stack_top, size, & fiber_entry< Record >);
0295     BOOST_ASSERT( nullptr != fctx);
0296     // transfer control structure to context-stack
0297     return jump_fcontext( fctx, record).fctx;
0298 }
0299 
0300 } // namespace detail
0301 
0302 class fiber {
0303 private:
0304     template< typename Ctx, typename StackAlloc, typename Fn >
0305     friend class detail::fiber_record;
0306 
0307     template< typename Ctx, typename Fn >
0308     friend detail::transfer_t
0309     detail::fiber_ontop( detail::transfer_t);
0310 
0311     detail::fcontext_t  fctx_{ nullptr };
0312 
0313     fiber( detail::fcontext_t fctx) noexcept :
0314         fctx_{ fctx } {
0315     }
0316 
0317 public:
0318     fiber() noexcept = default;
0319 
0320     template< typename Fn, typename = detail::disable_overload< fiber, Fn > >
0321     fiber( Fn && fn) :
0322         fiber{ std::allocator_arg, fixedsize_stack(), std::forward< Fn >( fn) } {
0323     }
0324 
0325     template< typename StackAlloc, typename Fn >
0326     fiber( std::allocator_arg_t, StackAlloc && salloc, Fn && fn) :
0327         fctx_{ detail::create_fiber1< detail::fiber_record< fiber, StackAlloc, Fn > >(
0328                 std::forward< StackAlloc >( salloc), std::forward< Fn >( fn) ) } {
0329     }
0330 
0331     template< typename StackAlloc, typename Fn >
0332     fiber( std::allocator_arg_t, preallocated palloc, StackAlloc && salloc, Fn && fn) :
0333         fctx_{ detail::create_fiber2< detail::fiber_record< fiber, StackAlloc, Fn > >(
0334                 palloc, std::forward< StackAlloc >( salloc), std::forward< Fn >( fn) ) } {
0335     }
0336 
0337 #if defined(BOOST_USE_SEGMENTED_STACKS)
0338     template< typename Fn >
0339     fiber( std::allocator_arg_t, segmented_stack, Fn &&);
0340 
0341     template< typename StackAlloc, typename Fn >
0342     fiber( std::allocator_arg_t, preallocated, segmented_stack, Fn &&);
0343 #endif
0344 
0345     ~fiber() {
0346         if ( BOOST_UNLIKELY( nullptr != fctx_) ) {
0347             detail::ontop_fcontext(
0348 #if defined(BOOST_NO_CXX14_STD_EXCHANGE)
0349                     detail::exchange( fctx_, nullptr),
0350 #else
0351                     std::exchange( fctx_, nullptr),
0352 #endif
0353                    nullptr,
0354                    detail::fiber_unwind);
0355         }
0356     }
0357 
0358     fiber( fiber && other) noexcept {
0359         swap( other);
0360     }
0361 
0362     fiber & operator=( fiber && other) noexcept {
0363         if ( BOOST_LIKELY( this != & other) ) {
0364             fiber tmp = std::move( other);
0365             swap( tmp);
0366         }
0367         return * this;
0368     }
0369 
0370     fiber( fiber const& other) noexcept = delete;
0371     fiber & operator=( fiber const& other) noexcept = delete;
0372 
0373     fiber resume() && {
0374         BOOST_ASSERT( nullptr != fctx_);
0375         detail::manage_exception_state exstate;
0376         boost::ignore_unused(exstate);
0377         return { detail::jump_fcontext(
0378 #if defined(BOOST_NO_CXX14_STD_EXCHANGE)
0379                     detail::exchange( fctx_, nullptr),
0380 #else
0381                     std::exchange( fctx_, nullptr),
0382 #endif
0383                     nullptr).fctx };
0384     }
0385 
0386     template< typename Fn >
0387     fiber resume_with( Fn && fn) && {
0388         BOOST_ASSERT( nullptr != fctx_);
0389         detail::manage_exception_state exstate;
0390         boost::ignore_unused(exstate);
0391         auto p = std::forward< Fn >( fn);
0392         return { detail::ontop_fcontext(
0393 #if defined(BOOST_NO_CXX14_STD_EXCHANGE)
0394                     detail::exchange( fctx_, nullptr),
0395 #else
0396                     std::exchange( fctx_, nullptr),
0397 #endif
0398                     & p,
0399                     detail::fiber_ontop< fiber, decltype(p) >).fctx };
0400     }
0401 
0402     explicit operator bool() const noexcept {
0403         return nullptr != fctx_;
0404     }
0405 
0406     bool operator!() const noexcept {
0407         return nullptr == fctx_;
0408     }
0409 
0410     bool operator<( fiber const& other) const noexcept {
0411         return fctx_ < other.fctx_;
0412     }
0413 
0414     #if !defined(BOOST_EMBTC)
0415 
0416     template< typename charT, class traitsT >
0417     friend std::basic_ostream< charT, traitsT > &
0418     operator<<( std::basic_ostream< charT, traitsT > & os, fiber const& other) {
0419         if ( nullptr != other.fctx_) {
0420             return os << other.fctx_;
0421         } else {
0422             return os << "{not-a-context}";
0423         }
0424     }
0425 
0426     #else
0427 
0428     template< typename charT, class traitsT >
0429     friend std::basic_ostream< charT, traitsT > &
0430     operator<<( std::basic_ostream< charT, traitsT > & os, fiber const& other);
0431 
0432     #endif
0433 
0434     void swap( fiber & other) noexcept {
0435         std::swap( fctx_, other.fctx_);
0436     }
0437 };
0438 
0439 #if defined(BOOST_EMBTC)
0440 
0441     template< typename charT, class traitsT >
0442     inline std::basic_ostream< charT, traitsT > &
0443     operator<<( std::basic_ostream< charT, traitsT > & os, fiber const& other) {
0444         if ( nullptr != other.fctx_) {
0445             return os << other.fctx_;
0446         } else {
0447             return os << "{not-a-context}";
0448         }
0449     }
0450 
0451 #endif
0452 
0453 inline
0454 void swap( fiber & l, fiber & r) noexcept {
0455     l.swap( r);
0456 }
0457 
0458 typedef fiber fiber_context;
0459 
0460 }}
0461 
0462 #if defined(BOOST_MSVC)
0463 # pragma warning(pop)
0464 #endif
0465 
0466 #ifdef BOOST_HAS_ABI_HEADERS
0467 # include BOOST_ABI_SUFFIX
0468 #endif
0469 
0470 #endif // BOOST_CONTEXT_FIBER_H