Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-01-30 09:35:08

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