File indexing completed on 2025-01-30 09:35:08
0001
0002
0003
0004
0005
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
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
0083 rec->deallocate();
0084 return { nullptr, nullptr };
0085 }
0086
0087 template< typename Rec >
0088 void fiber_entry( transfer_t t) noexcept {
0089
0090 Rec * rec = static_cast< Rec * >( t.data);
0091 BOOST_ASSERT( nullptr != t.fctx);
0092 BOOST_ASSERT( nullptr != rec);
0093 try {
0094
0095 t = jump_fcontext( t.fctx, nullptr);
0096
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
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
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
0132 p->~fiber_record();
0133
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
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
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
0175 Record * record = new ( storage) Record{
0176 sctx, std::forward< StackAlloc >( salloc), std::forward< Fn >( fn) };
0177
0178
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
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
0189 ss_size = (ss_size + 7) & ~7;
0190
0191 ss_size = (ss_size > 4096) ? size : 4096;
0192
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
0197
0198
0199 *((unsigned long*)(reinterpret_cast< uintptr_t >( stack_top)- 8)) = ss_sp;
0200
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
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
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
0217 Record * record = new ( storage) Record{
0218 palloc.sctx, std::forward< StackAlloc >( salloc), std::forward< Fn >( fn) };
0219
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
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
0230 ss_size = (ss_size + 7) & ~7;
0231
0232 ss_size = (ss_size > 4096) ? size : 4096;
0233
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
0238
0239
0240 *((unsigned long*)(reinterpret_cast< uintptr_t >( stack_top)- 8)) = ss_sp;
0241
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
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