File indexing completed on 2025-09-17 08:25:22
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 #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
0073 struct manage_exception_state {};
0074
0075 }
0076 }
0077 }
0078
0079 #else
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 }
0100
0101 namespace boost {
0102 namespace context {
0103 namespace detail {
0104
0105 using __cxxabiv1::manage_exception_state;
0106
0107 }
0108 }
0109 }
0110 #endif
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
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
0132 rec->deallocate();
0133 return { nullptr, nullptr };
0134 }
0135
0136 template< typename Rec >
0137 void fiber_entry( transfer_t t) noexcept {
0138
0139 Rec * rec = static_cast< Rec * >( t.data);
0140 BOOST_ASSERT( nullptr != t.fctx);
0141 BOOST_ASSERT( nullptr != rec);
0142 try {
0143
0144 t = jump_fcontext( t.fctx, nullptr);
0145
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
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
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
0181 p->~fiber_record();
0182
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
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
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
0224 Record * record = new ( storage) Record{
0225 sctx, std::forward< StackAlloc >( salloc), std::forward< Fn >( fn) };
0226
0227
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
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
0238 ss_size = (ss_size + 7) & ~7;
0239
0240 ss_size = (ss_size > 4096) ? size : 4096;
0241
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
0246
0247
0248 *((unsigned long*)(reinterpret_cast< uintptr_t >( stack_top)- 8)) = ss_sp;
0249
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
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
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
0266 Record * record = new ( storage) Record{
0267 palloc.sctx, std::forward< StackAlloc >( salloc), std::forward< Fn >( fn) };
0268
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
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
0279 ss_size = (ss_size + 7) & ~7;
0280
0281 ss_size = (ss_size > 4096) ? size : 4096;
0282
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
0287
0288
0289 *((unsigned long*)(reinterpret_cast< uintptr_t >( stack_top)- 8)) = ss_sp;
0290
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
0297 return jump_fcontext( fctx, record).fctx;
0298 }
0299
0300 }
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