File indexing completed on 2025-01-30 09:35:09
0001
0002
0003
0004
0005
0006
0007 #ifndef BOOST_CONTEXT_FIBER_H
0008 #define BOOST_CONTEXT_FIBER_H
0009
0010 #include <windows.h>
0011
0012 #include <boost/context/detail/config.hpp>
0013
0014 #include <algorithm>
0015 #include <cstddef>
0016 #include <cstdint>
0017 #include <cstdlib>
0018 #include <cstring>
0019 #include <functional>
0020 #include <memory>
0021 #include <ostream>
0022 #include <system_error>
0023 #include <tuple>
0024 #include <utility>
0025
0026 #include <boost/assert.hpp>
0027 #include <boost/config.hpp>
0028
0029 #include <boost/context/detail/disable_overload.hpp>
0030 #if defined(BOOST_NO_CXX14_STD_EXCHANGE)
0031 #include <boost/context/detail/exchange.hpp>
0032 #endif
0033 #if defined(BOOST_NO_CXX17_STD_INVOKE)
0034 #include <boost/context/detail/invoke.hpp>
0035 #endif
0036 #include <boost/context/fixedsize_stack.hpp>
0037 #include <boost/context/flags.hpp>
0038 #include <boost/context/preallocated.hpp>
0039 #include <boost/context/stack_context.hpp>
0040
0041 #ifdef BOOST_HAS_ABI_HEADERS
0042 # include BOOST_ABI_PREFIX
0043 #endif
0044
0045 #if defined(BOOST_MSVC)
0046 # pragma warning(push)
0047 # pragma warning(disable: 4702)
0048 #endif
0049
0050 namespace boost {
0051 namespace context {
0052 namespace detail {
0053
0054
0055
0056
0057 template< typename Record >
0058 static VOID WINAPI fiber_entry_func( LPVOID data) noexcept {
0059 Record * record = static_cast< Record * >( data);
0060 BOOST_ASSERT( nullptr != record);
0061
0062 record->run();
0063 }
0064
0065 struct BOOST_CONTEXT_DECL fiber_activation_record {
0066 LPVOID fiber{ nullptr };
0067 stack_context sctx{};
0068 bool main_ctx{ true };
0069 fiber_activation_record * from{ nullptr };
0070 std::function< fiber_activation_record*(fiber_activation_record*&) > ontop{};
0071 bool terminated{ false };
0072 bool force_unwind{ false };
0073
0074 static fiber_activation_record *& current() noexcept;
0075
0076
0077
0078 fiber_activation_record() noexcept {
0079 #if ( _WIN32_WINNT > 0x0600)
0080 if ( ::IsThreadAFiber() ) {
0081 fiber = ::GetCurrentFiber();
0082 } else {
0083 fiber = ::ConvertThreadToFiber( nullptr);
0084 }
0085 #else
0086 fiber = ::ConvertThreadToFiber( nullptr);
0087 if ( BOOST_UNLIKELY( nullptr == fiber) ) {
0088 BOOST_ASSERT( ERROR_ALREADY_FIBER == ::GetLastError());
0089 fiber = ::GetCurrentFiber();
0090 BOOST_ASSERT( nullptr != fiber);
0091 BOOST_ASSERT( reinterpret_cast< LPVOID >( 0x1E00) != fiber);
0092 }
0093 #endif
0094 }
0095
0096 fiber_activation_record( stack_context sctx_) noexcept :
0097 sctx{ sctx_ },
0098 main_ctx{ false } {
0099 }
0100
0101 virtual ~fiber_activation_record() {
0102 if ( BOOST_UNLIKELY( main_ctx) ) {
0103 ::ConvertFiberToThread();
0104 } else {
0105 ::DeleteFiber( fiber);
0106 }
0107 }
0108
0109 fiber_activation_record( fiber_activation_record const&) = delete;
0110 fiber_activation_record & operator=( fiber_activation_record const&) = delete;
0111
0112 bool is_main_context() const noexcept {
0113 return main_ctx;
0114 }
0115
0116 fiber_activation_record * resume() {
0117 from = current();
0118
0119
0120 current() = this;
0121
0122
0123 ::SwitchToFiber( fiber);
0124 #if defined(BOOST_NO_CXX14_STD_EXCHANGE)
0125 return detail::exchange( current()->from, nullptr);
0126 #else
0127 return std::exchange( current()->from, nullptr);
0128 #endif
0129 }
0130
0131 template< typename Ctx, typename Fn >
0132 fiber_activation_record * resume_with( Fn && fn) {
0133 from = current();
0134
0135
0136
0137 current() = this;
0138 #if defined(BOOST_NO_CXX14_GENERIC_LAMBDAS)
0139 current()->ontop = std::bind(
0140 [](typename std::decay< Fn >::type & fn, fiber_activation_record *& ptr){
0141 Ctx c{ ptr };
0142 c = fn( std::move( c) );
0143 if ( ! c) {
0144 ptr = nullptr;
0145 }
0146 #if defined(BOOST_NO_CXX14_STD_EXCHANGE)
0147 return exchange( c.ptr_, nullptr);
0148 #else
0149 return std::exchange( c.ptr_, nullptr);
0150 #endif
0151 },
0152 std::forward< Fn >( fn),
0153 std::placeholders::_1);
0154 #else
0155 current()->ontop = [fn=std::forward<Fn>(fn)](fiber_activation_record *& ptr){
0156 Ctx c{ ptr };
0157 c = fn( std::move( c) );
0158 if ( ! c) {
0159 ptr = nullptr;
0160 }
0161 #if defined(BOOST_NO_CXX14_STD_EXCHANGE)
0162 return exchange( c.ptr_, nullptr);
0163 #else
0164 return std::exchange( c.ptr_, nullptr);
0165 #endif
0166 };
0167 #endif
0168
0169 ::SwitchToFiber( fiber);
0170 #if defined(BOOST_NO_CXX14_STD_EXCHANGE)
0171 return detail::exchange( current()->from, nullptr);
0172 #else
0173 return std::exchange( current()->from, nullptr);
0174 #endif
0175 }
0176
0177 virtual void deallocate() noexcept {
0178 }
0179 };
0180
0181 struct BOOST_CONTEXT_DECL fiber_activation_record_initializer {
0182 fiber_activation_record_initializer() noexcept;
0183 ~fiber_activation_record_initializer();
0184 };
0185
0186 struct forced_unwind {
0187 fiber_activation_record * from{ nullptr };
0188
0189 explicit forced_unwind( fiber_activation_record * from_) :
0190 from{ from_ } {
0191 }
0192 };
0193
0194 template< typename Ctx, typename StackAlloc, typename Fn >
0195 class fiber_capture_record : public fiber_activation_record {
0196 private:
0197 typename std::decay< StackAlloc >::type salloc_;
0198 typename std::decay< Fn >::type fn_;
0199
0200 static void destroy( fiber_capture_record * p) noexcept {
0201 typename std::decay< StackAlloc >::type salloc = std::move( p->salloc_);
0202 stack_context sctx = p->sctx;
0203
0204 p->~fiber_capture_record();
0205
0206 salloc.deallocate( sctx);
0207 }
0208
0209 public:
0210 fiber_capture_record( stack_context sctx, StackAlloc && salloc, Fn && fn) noexcept :
0211 fiber_activation_record( sctx),
0212 salloc_( std::forward< StackAlloc >( salloc)),
0213 fn_( std::forward< Fn >( fn) ) {
0214 }
0215
0216 void deallocate() noexcept override final {
0217 BOOST_ASSERT( main_ctx || ( ! main_ctx && terminated) );
0218 destroy( this);
0219 }
0220
0221 void run() {
0222 Ctx c{ from };
0223 try {
0224
0225 #if defined(BOOST_NO_CXX17_STD_INVOKE)
0226 c = boost::context::detail::invoke( fn_, std::move( c) );
0227 #else
0228 c = std::invoke( fn_, std::move( c) );
0229 #endif
0230 } catch ( forced_unwind const& ex) {
0231 c = Ctx{ ex.from };
0232 }
0233
0234 from = nullptr;
0235 ontop = nullptr;
0236 terminated = true;
0237 force_unwind = false;
0238 std::move( c).resume();
0239 BOOST_ASSERT_MSG( false, "fiber already terminated");
0240 }
0241 };
0242
0243 template< typename Ctx, typename StackAlloc, typename Fn >
0244 static fiber_activation_record * create_fiber1( StackAlloc && salloc, Fn && fn) {
0245 typedef fiber_capture_record< Ctx, StackAlloc, Fn > capture_t;
0246
0247 auto sctx = salloc.allocate();
0248 BOOST_ASSERT( ( sizeof( capture_t) ) < sctx.size);
0249
0250 void * storage = reinterpret_cast< void * >(
0251 ( reinterpret_cast< uintptr_t >( sctx.sp) - static_cast< uintptr_t >( sizeof( capture_t) ) )
0252 & ~ static_cast< uintptr_t >( 0xff) );
0253
0254 capture_t * record = new ( storage) capture_t{
0255 sctx, std::forward< StackAlloc >( salloc), std::forward< Fn >( fn) };
0256
0257 record->fiber = ::CreateFiber( sctx.size, & detail::fiber_entry_func< capture_t >, record);
0258 return record;
0259 }
0260
0261 template< typename Ctx, typename StackAlloc, typename Fn >
0262 static fiber_activation_record * create_fiber2( preallocated palloc, StackAlloc && salloc, Fn && fn) {
0263 typedef fiber_capture_record< Ctx, StackAlloc, Fn > capture_t;
0264
0265 BOOST_ASSERT( ( sizeof( capture_t) ) < palloc.size);
0266
0267 void * storage = reinterpret_cast< void * >(
0268 ( reinterpret_cast< uintptr_t >( palloc.sp) - static_cast< uintptr_t >( sizeof( capture_t) ) )
0269 & ~ static_cast< uintptr_t >( 0xff) );
0270
0271 capture_t * record = new ( storage) capture_t{
0272 palloc.sctx, std::forward< StackAlloc >( salloc), std::forward< Fn >( fn) };
0273
0274 record->fiber = ::CreateFiber( palloc.sctx.size, & detail::fiber_entry_func< capture_t >, record);
0275 return record;
0276 }
0277
0278 }
0279
0280 class BOOST_CONTEXT_DECL fiber {
0281 private:
0282 friend struct detail::fiber_activation_record;
0283
0284 template< typename Ctx, typename StackAlloc, typename Fn >
0285 friend class detail::fiber_capture_record;
0286
0287 template< typename Ctx, typename StackAlloc, typename Fn >
0288 friend detail::fiber_activation_record * detail::create_fiber1( StackAlloc &&, Fn &&);
0289
0290 template< typename Ctx, typename StackAlloc, typename Fn >
0291 friend detail::fiber_activation_record * detail::create_fiber2( preallocated, StackAlloc &&, Fn &&);
0292
0293 detail::fiber_activation_record * ptr_{ nullptr };
0294
0295 fiber( detail::fiber_activation_record * ptr) noexcept :
0296 ptr_{ ptr } {
0297 }
0298
0299 public:
0300 fiber() = default;
0301
0302 template< typename Fn, typename = detail::disable_overload< fiber, Fn > >
0303 fiber( Fn && fn) :
0304 fiber{ std::allocator_arg,
0305 fixedsize_stack(),
0306 std::forward< Fn >( fn) } {
0307 }
0308
0309 template< typename StackAlloc, typename Fn >
0310 fiber( std::allocator_arg_t, StackAlloc && salloc, Fn && fn) :
0311 ptr_{ detail::create_fiber1< fiber >(
0312 std::forward< StackAlloc >( salloc), std::forward< Fn >( fn) ) } {;
0313 }
0314
0315 template< typename StackAlloc, typename Fn >
0316 fiber( std::allocator_arg_t, preallocated palloc, StackAlloc && salloc, Fn && fn) :
0317 ptr_{ detail::create_fiber2< fiber >(
0318 palloc, std::forward< StackAlloc >( salloc), std::forward< Fn >( fn) ) } {
0319 }
0320
0321 ~fiber() {
0322 if ( BOOST_UNLIKELY( nullptr != ptr_) && ! ptr_->main_ctx) {
0323 if ( BOOST_LIKELY( ! ptr_->terminated) ) {
0324 ptr_->force_unwind = true;
0325 ptr_->resume();
0326 BOOST_ASSERT( ptr_->terminated);
0327 }
0328 ptr_->deallocate();
0329 }
0330 }
0331
0332 fiber( fiber const&) = delete;
0333 fiber & operator=( fiber const&) = delete;
0334
0335 fiber( fiber && other) noexcept {
0336 swap( other);
0337 }
0338
0339 fiber & operator=( fiber && other) noexcept {
0340 if ( BOOST_LIKELY( this != & other) ) {
0341 fiber tmp = std::move( other);
0342 swap( tmp);
0343 }
0344 return * this;
0345 }
0346
0347 fiber resume() && {
0348 BOOST_ASSERT( nullptr != ptr_);
0349 #if defined(BOOST_NO_CXX14_STD_EXCHANGE)
0350 detail::fiber_activation_record * ptr = detail::exchange( ptr_, nullptr)->resume();
0351 #else
0352 detail::fiber_activation_record * ptr = std::exchange( ptr_, nullptr)->resume();
0353 #endif
0354 if ( BOOST_UNLIKELY( detail::fiber_activation_record::current()->force_unwind) ) {
0355 throw detail::forced_unwind{ ptr};
0356 } else if ( BOOST_UNLIKELY( nullptr != detail::fiber_activation_record::current()->ontop) ) {
0357 ptr = detail::fiber_activation_record::current()->ontop( ptr);
0358 detail::fiber_activation_record::current()->ontop = nullptr;
0359 }
0360 return { ptr };
0361 }
0362
0363 template< typename Fn >
0364 fiber resume_with( Fn && fn) && {
0365 BOOST_ASSERT( nullptr != ptr_);
0366 #if defined(BOOST_NO_CXX14_STD_EXCHANGE)
0367 detail::fiber_activation_record * ptr =
0368 detail::exchange( ptr_, nullptr)->resume_with< fiber >( std::forward< Fn >( fn) );
0369 #else
0370 detail::fiber_activation_record * ptr =
0371 std::exchange( ptr_, nullptr)->resume_with< fiber >( std::forward< Fn >( fn) );
0372 #endif
0373 if ( BOOST_UNLIKELY( detail::fiber_activation_record::current()->force_unwind) ) {
0374 throw detail::forced_unwind{ ptr};
0375 } else if ( BOOST_UNLIKELY( nullptr != detail::fiber_activation_record::current()->ontop) ) {
0376 ptr = detail::fiber_activation_record::current()->ontop( ptr);
0377 detail::fiber_activation_record::current()->ontop = nullptr;
0378 }
0379 return { ptr };
0380 }
0381
0382 explicit operator bool() const noexcept {
0383 return nullptr != ptr_ && ! ptr_->terminated;
0384 }
0385
0386 bool operator!() const noexcept {
0387 return nullptr == ptr_ || ptr_->terminated;
0388 }
0389
0390 bool operator<( fiber const& other) const noexcept {
0391 return ptr_ < other.ptr_;
0392 }
0393
0394 #if !defined(BOOST_EMBTC)
0395
0396 template< typename charT, class traitsT >
0397 friend std::basic_ostream< charT, traitsT > &
0398 operator<<( std::basic_ostream< charT, traitsT > & os, fiber const& other) {
0399 if ( nullptr != other.ptr_) {
0400 return os << other.ptr_;
0401 } else {
0402 return os << "{not-a-context}";
0403 }
0404 }
0405
0406 #else
0407
0408 template< typename charT, class traitsT >
0409 friend std::basic_ostream< charT, traitsT > &
0410 operator<<( std::basic_ostream< charT, traitsT > & os, fiber const& other);
0411
0412 #endif
0413
0414 void swap( fiber & other) noexcept {
0415 std::swap( ptr_, other.ptr_);
0416 }
0417 };
0418
0419 #if defined(BOOST_EMBTC)
0420
0421 template< typename charT, class traitsT >
0422 inline std::basic_ostream< charT, traitsT > &
0423 operator<<( std::basic_ostream< charT, traitsT > & os, fiber const& other) {
0424 if ( nullptr != other.ptr_) {
0425 return os << other.ptr_;
0426 } else {
0427 return os << "{not-a-context}";
0428 }
0429 }
0430
0431 #endif
0432
0433 inline
0434 void swap( fiber & l, fiber & r) noexcept {
0435 l.swap( r);
0436 }
0437
0438 typedef fiber fiber_context;
0439
0440 }}
0441
0442 #if defined(BOOST_MSVC)
0443 # pragma warning(pop)
0444 #endif
0445
0446 #ifdef BOOST_HAS_ABI_HEADERS
0447 # include BOOST_ABI_SUFFIX
0448 #endif
0449
0450 #endif