File indexing completed on 2024-11-15 09:05:26
0001
0002
0003
0004
0005
0006
0007 #ifndef BOOST_FIBERS_CONTEXT_H
0008 #define BOOST_FIBERS_CONTEXT_H
0009
0010 #include <atomic>
0011 #include <chrono>
0012 #include <cstdint>
0013 #include <exception>
0014 #include <functional>
0015 #include <iostream>
0016 #include <map>
0017 #include <memory>
0018 #include <tuple>
0019 #include <type_traits>
0020 #include <utility>
0021
0022 #include <boost/assert.hpp>
0023 #include <boost/config.hpp>
0024 #include <boost/core/ignore_unused.hpp>
0025 #if defined(BOOST_NO_CXX17_STD_APPLY)
0026 #include <boost/context/detail/apply.hpp>
0027 #endif
0028 #include <boost/context/fiber.hpp>
0029 #include <boost/context/stack_context.hpp>
0030 #include <boost/intrusive/list.hpp>
0031 #include <boost/intrusive/parent_from_member.hpp>
0032 #include <boost/intrusive_ptr.hpp>
0033 #include <boost/intrusive/set.hpp>
0034 #include <boost/intrusive/slist.hpp>
0035
0036 #include <boost/fiber/detail/config.hpp>
0037 #include <boost/fiber/detail/data.hpp>
0038 #include <boost/fiber/detail/decay_copy.hpp>
0039 #include <boost/fiber/detail/fss.hpp>
0040 #include <boost/fiber/detail/spinlock.hpp>
0041 #include <boost/fiber/exceptions.hpp>
0042 #include <boost/fiber/fixedsize_stack.hpp>
0043 #include <boost/fiber/policy.hpp>
0044 #include <boost/fiber/properties.hpp>
0045 #include <boost/fiber/segmented_stack.hpp>
0046 #include <boost/fiber/type.hpp>
0047 #include <boost/fiber/waker.hpp>
0048 #include <boost/fiber/stack_allocator_wrapper.hpp>
0049 #include <boost/fiber/algo/algorithm.hpp>
0050
0051 #ifdef BOOST_HAS_ABI_HEADERS
0052 # include BOOST_ABI_PREFIX
0053 #endif
0054
0055 #ifdef _MSC_VER
0056 # pragma warning(push)
0057 # pragma warning(disable:4251)
0058 #endif
0059
0060 namespace boost {
0061 namespace fibers {
0062
0063 class context;
0064 class fiber;
0065 class scheduler;
0066
0067 namespace detail {
0068
0069 struct ready_tag;
0070 typedef intrusive::list_member_hook<
0071 intrusive::tag< ready_tag >,
0072 intrusive::link_mode<
0073 intrusive::auto_unlink
0074 >
0075 > ready_hook;
0076
0077 struct sleep_tag;
0078 typedef intrusive::set_member_hook<
0079 intrusive::tag< sleep_tag >,
0080 intrusive::link_mode<
0081 intrusive::auto_unlink
0082 >
0083 > sleep_hook;
0084
0085 struct worker_tag;
0086 typedef intrusive::list_member_hook<
0087 intrusive::tag< worker_tag >,
0088 intrusive::link_mode<
0089 intrusive::auto_unlink
0090 >
0091 > worker_hook;
0092
0093 struct terminated_tag;
0094 typedef intrusive::slist_member_hook<
0095 intrusive::tag< terminated_tag >,
0096 intrusive::link_mode<
0097 intrusive::safe_link
0098 >
0099 > terminated_hook;
0100
0101 struct remote_ready_tag;
0102 typedef intrusive::slist_member_hook<
0103 intrusive::tag< remote_ready_tag >,
0104 intrusive::link_mode<
0105 intrusive::safe_link
0106 >
0107 > remote_ready_hook;
0108
0109 }
0110
0111 class BOOST_FIBERS_DECL context {
0112 private:
0113 friend class dispatcher_context;
0114 friend class main_context;
0115 template< typename Fn, typename ... Arg > friend class worker_context;
0116 friend class scheduler;
0117
0118 struct fss_data {
0119 void * vp{ nullptr };
0120 detail::fss_cleanup_function::ptr_t cleanup_function{};
0121
0122 fss_data() = default;
0123
0124 fss_data( void * vp_,
0125 detail::fss_cleanup_function::ptr_t fn) noexcept :
0126 vp( vp_),
0127 cleanup_function(std::move( fn)) {
0128 BOOST_ASSERT( cleanup_function);
0129 }
0130
0131 void do_cleanup() {
0132 ( * cleanup_function)( vp);
0133 }
0134 };
0135
0136 typedef std::map< uintptr_t, fss_data > fss_data_t;
0137
0138 #if ! defined(BOOST_FIBERS_NO_ATOMICS)
0139 std::atomic< std::size_t > use_count_;
0140 #else
0141 std::size_t use_count_;
0142 #endif
0143 #if ! defined(BOOST_FIBERS_NO_ATOMICS)
0144 detail::remote_ready_hook remote_ready_hook_{};
0145 #endif
0146 detail::spinlock splk_{};
0147 bool terminated_{ false };
0148 wait_queue wait_queue_{};
0149 public:
0150 #if ! defined(BOOST_FIBERS_NO_ATOMICS)
0151 std::atomic<size_t> waker_epoch_{ 0 };
0152 #endif
0153 private:
0154 scheduler * scheduler_{ nullptr };
0155 fss_data_t fss_data_{};
0156 detail::sleep_hook sleep_hook_{};
0157 waker sleep_waker_{};
0158 detail::ready_hook ready_hook_{};
0159 detail::terminated_hook terminated_hook_{};
0160 detail::worker_hook worker_hook_{};
0161 fiber_properties * properties_{ nullptr };
0162 boost::context::fiber c_{};
0163 std::chrono::steady_clock::time_point tp_;
0164 type type_;
0165 launch policy_;
0166
0167 context( std::size_t initial_count, type t, launch policy) noexcept :
0168 use_count_{ initial_count },
0169 tp_{ (std::chrono::steady_clock::time_point::max)() },
0170 type_{ t },
0171 policy_{ policy } {
0172 }
0173
0174 public:
0175 class id {
0176 private:
0177 context * impl_{ nullptr };
0178
0179 public:
0180 id() = default;
0181
0182 explicit id( context * impl) noexcept :
0183 impl_{ impl } {
0184 }
0185
0186 bool operator==( id const& other) const noexcept {
0187 return impl_ == other.impl_;
0188 }
0189
0190 bool operator!=( id const& other) const noexcept {
0191 return impl_ != other.impl_;
0192 }
0193
0194 bool operator<( id const& other) const noexcept {
0195 return impl_ < other.impl_;
0196 }
0197
0198 bool operator>( id const& other) const noexcept {
0199 return other.impl_ < impl_;
0200 }
0201
0202 bool operator<=( id const& other) const noexcept {
0203 return ! ( * this > other);
0204 }
0205
0206 bool operator>=( id const& other) const noexcept {
0207 return ! ( * this < other);
0208 }
0209
0210 template< typename charT, class traitsT >
0211 friend std::basic_ostream< charT, traitsT > &
0212 operator<<( std::basic_ostream< charT, traitsT > & os, id const& other) {
0213 if ( nullptr != other.impl_) {
0214 return os << other.impl_;
0215 }
0216 return os << "{not-valid}";
0217 }
0218
0219 explicit operator bool() const noexcept {
0220 return nullptr != impl_;
0221 }
0222
0223 bool operator!() const noexcept {
0224 return nullptr == impl_;
0225 }
0226 };
0227
0228
0229 static bool initialize_thread(algo::algorithm::ptr_t algo, stack_allocator_wrapper&& salloc) noexcept;
0230
0231 static context * active() noexcept;
0232
0233 static void reset_active() noexcept;
0234
0235 context( context const&) = delete;
0236 context( context &&) = delete;
0237 context & operator=( context const&) = delete;
0238 context & operator=( context &&) = delete;
0239
0240 #if !defined(BOOST_EMBTC)
0241
0242 friend bool
0243 operator==( context const& lhs, context const& rhs) noexcept {
0244 return & lhs == & rhs;
0245 }
0246
0247 #else
0248
0249 friend bool
0250 operator==( context const& lhs, context const& rhs) noexcept;
0251
0252 #endif
0253
0254 virtual ~context();
0255
0256 scheduler * get_scheduler() const noexcept {
0257 return scheduler_;
0258 }
0259
0260 id get_id() const noexcept;
0261
0262 bool is_resumable() const noexcept {
0263 return static_cast<bool>(c_);
0264 }
0265
0266 void resume() noexcept;
0267 void resume( detail::spinlock_lock &) noexcept;
0268 void resume( context *) noexcept;
0269
0270 void suspend() noexcept;
0271 void suspend( detail::spinlock_lock &) noexcept;
0272
0273 boost::context::fiber suspend_with_cc() noexcept;
0274 boost::context::fiber terminate() noexcept;
0275
0276 void join();
0277
0278 void yield() noexcept;
0279
0280 bool wait_until( std::chrono::steady_clock::time_point const&) noexcept;
0281 bool wait_until( std::chrono::steady_clock::time_point const&,
0282 detail::spinlock_lock &,
0283 waker &&) noexcept;
0284
0285 bool wake(const size_t) noexcept;
0286
0287 waker create_waker() noexcept {
0288
0289 return { this, ++waker_epoch_ };
0290 }
0291
0292 void schedule( context *) noexcept;
0293
0294 bool is_context( type t) const noexcept {
0295 return type::none != ( type_ & t);
0296 }
0297
0298 void * get_fss_data( void const * vp) const;
0299
0300 void set_fss_data(
0301 void const * vp,
0302 detail::fss_cleanup_function::ptr_t const& cleanup_fn,
0303 void * data,
0304 bool cleanup_existing);
0305
0306 void set_properties( fiber_properties * props) noexcept;
0307
0308 fiber_properties * get_properties() const noexcept {
0309 return properties_;
0310 }
0311
0312 launch get_policy() const noexcept {
0313 return policy_;
0314 }
0315
0316 bool worker_is_linked() const noexcept;
0317
0318 bool ready_is_linked() const noexcept;
0319
0320 bool remote_ready_is_linked() const noexcept;
0321
0322 bool sleep_is_linked() const noexcept;
0323
0324 bool terminated_is_linked() const noexcept;
0325
0326 template< typename List >
0327 void worker_link( List & lst) noexcept {
0328 static_assert( std::is_same< typename List::value_traits::hook_type, detail::worker_hook >::value, "not a worker-queue");
0329 BOOST_ASSERT( ! worker_is_linked() );
0330 lst.push_back( * this);
0331 }
0332
0333 template< typename List >
0334 void ready_link( List & lst) noexcept {
0335 static_assert( std::is_same< typename List::value_traits::hook_type, detail::ready_hook >::value, "not a ready-queue");
0336 BOOST_ASSERT( ! ready_is_linked() );
0337 lst.push_back( * this);
0338 }
0339
0340 template< typename List >
0341 void remote_ready_link( List & lst) noexcept {
0342 static_assert( std::is_same< typename List::value_traits::hook_type, detail::remote_ready_hook >::value, "not a remote-ready-queue");
0343 BOOST_ASSERT( ! remote_ready_is_linked() );
0344 lst.push_back( * this);
0345 }
0346
0347 template< typename Set >
0348 void sleep_link( Set & set) noexcept {
0349 static_assert( std::is_same< typename Set::value_traits::hook_type,detail::sleep_hook >::value, "not a sleep-queue");
0350 BOOST_ASSERT( ! sleep_is_linked() );
0351 set.insert( * this);
0352 }
0353
0354 template< typename List >
0355 void terminated_link( List & lst) noexcept {
0356 static_assert( std::is_same< typename List::value_traits::hook_type, detail::terminated_hook >::value, "not a terminated-queue");
0357 BOOST_ASSERT( ! terminated_is_linked() );
0358 lst.push_back( * this);
0359 }
0360
0361 void worker_unlink() noexcept;
0362
0363 void ready_unlink() noexcept;
0364
0365 void sleep_unlink() noexcept;
0366
0367 void detach() noexcept;
0368
0369 void attach( context *) noexcept;
0370
0371 #if !defined(BOOST_EMBTC)
0372
0373 friend void intrusive_ptr_add_ref( context * ctx) noexcept {
0374 BOOST_ASSERT( nullptr != ctx);
0375 ctx->use_count_.fetch_add( 1, std::memory_order_relaxed);
0376 }
0377
0378 friend void intrusive_ptr_release( context * ctx) noexcept {
0379 BOOST_ASSERT( nullptr != ctx);
0380 if ( 1 == ctx->use_count_.fetch_sub( 1, std::memory_order_release) ) {
0381 std::atomic_thread_fence( std::memory_order_acquire);
0382 boost::context::fiber c = std::move( ctx->c_);
0383
0384 ctx->~context();
0385
0386 std::move( c).resume();
0387 }
0388 }
0389
0390 #else
0391
0392 friend void intrusive_ptr_add_ref( context * ctx) noexcept;
0393 friend void intrusive_ptr_release( context * ctx) noexcept;
0394
0395 #endif
0396
0397 };
0398
0399 #if defined(BOOST_EMBTC)
0400
0401 inline bool
0402 operator==( context const& lhs, context const& rhs) noexcept {
0403 return & lhs == & rhs;
0404 }
0405
0406 inline void intrusive_ptr_add_ref( context * ctx) noexcept {
0407 BOOST_ASSERT( nullptr != ctx);
0408 ctx->use_count_.fetch_add( 1, std::memory_order_relaxed);
0409 }
0410
0411 inline void intrusive_ptr_release( context * ctx) noexcept {
0412 BOOST_ASSERT( nullptr != ctx);
0413 if ( 1 == ctx->use_count_.fetch_sub( 1, std::memory_order_release) ) {
0414 std::atomic_thread_fence( std::memory_order_acquire);
0415 boost::context::fiber c = std::move( ctx->c_);
0416
0417 ctx->~context();
0418
0419 std::move( c).resume();
0420 }
0421 }
0422
0423 #endif
0424
0425 inline
0426 bool operator<( context const& l, context const& r) noexcept {
0427 return l.get_id() < r.get_id();
0428 }
0429
0430 template< typename Fn, typename ... Arg >
0431 class worker_context final : public context {
0432 private:
0433 typename std::decay< Fn >::type fn_;
0434 std::tuple< Arg ... > arg_;
0435
0436 boost::context::fiber
0437 run_( boost::context::fiber && c) {
0438 {
0439
0440 auto fn = std::move( fn_);
0441 auto arg = std::move( arg_);
0442 #if (defined(BOOST_USE_UCONTEXT)||defined(BOOST_USE_WINFIB))
0443 std::move( c).resume();
0444 #else
0445 boost::ignore_unused(c);
0446 #endif
0447 #if defined(BOOST_NO_CXX17_STD_APPLY)
0448 boost::context::detail::apply( std::move( fn), std::move( arg) );
0449 #else
0450 std::apply( std::move( fn), std::move( arg) );
0451 #endif
0452 }
0453
0454 return terminate();
0455 }
0456
0457 public:
0458 template< typename StackAlloc >
0459 worker_context( launch policy,
0460 fiber_properties* properties,
0461 boost::context::preallocated const& palloc, StackAlloc && salloc,
0462 Fn && fn, Arg ... arg) :
0463 context{ 1, type::worker_context, policy },
0464 fn_( std::forward< Fn >( fn) ),
0465 arg_( std::forward< Arg >( arg) ... ) {
0466 if ( properties != nullptr ) {
0467 set_properties(properties);
0468 properties->set_context(this);
0469 }
0470 c_ = boost::context::fiber{ std::allocator_arg, palloc, std::forward< StackAlloc >( salloc),
0471 std::bind( & worker_context::run_, this, std::placeholders::_1) };
0472 #if (defined(BOOST_USE_UCONTEXT)||defined(BOOST_USE_WINFIB))
0473 c_ = std::move( c_).resume();
0474 #endif
0475 }
0476
0477 template< typename StackAlloc >
0478 worker_context( launch policy,
0479 boost::context::preallocated const& palloc, StackAlloc && salloc,
0480 Fn && fn, Arg ... arg) :
0481 worker_context( policy, palloc, salloc, nullptr, std::forward<Fn>( fn ), std::forward<Arg>( arg ) ... ){
0482 }
0483 };
0484
0485
0486 template< typename StackAlloc, typename Fn, typename ... Arg >
0487 static intrusive_ptr< context > make_worker_context_with_properties( launch policy,
0488 fiber_properties* properties,
0489 StackAlloc && salloc,
0490 Fn && fn, Arg ... arg) {
0491 typedef worker_context< Fn, Arg ... > context_t;
0492
0493 auto sctx = salloc.allocate();
0494
0495 void * storage = reinterpret_cast< void * >(
0496 ( reinterpret_cast< uintptr_t >( sctx.sp) - static_cast< uintptr_t >( sizeof( context_t) ) )
0497 & ~ static_cast< uintptr_t >( 0xff) );
0498 void * stack_bottom = reinterpret_cast< void * >(
0499 reinterpret_cast< uintptr_t >( sctx.sp) - static_cast< uintptr_t >( sctx.size) );
0500 const std::size_t size = reinterpret_cast< uintptr_t >( storage) - reinterpret_cast< uintptr_t >( stack_bottom);
0501
0502 return intrusive_ptr< context >{
0503 new ( storage) context_t{
0504 policy,
0505 properties,
0506 boost::context::preallocated{ storage, size, sctx },
0507 std::forward< StackAlloc >( salloc),
0508 std::forward< Fn >( fn),
0509 std::forward< Arg >( arg) ... } };
0510 }
0511
0512 template< typename StackAlloc, typename Fn, typename ... Arg >
0513 static intrusive_ptr< context > make_worker_context( launch policy,
0514 StackAlloc && salloc,
0515 Fn && fn, Arg ... arg){
0516 return make_worker_context_with_properties( policy, nullptr, std::forward<StackAlloc>(salloc),
0517 std::forward<Fn>( fn ), std::forward<Arg>( arg ) ... );
0518 }
0519
0520
0521 }}
0522
0523 #ifdef _MSC_VER
0524 # pragma warning(pop)
0525 #endif
0526
0527 #ifdef BOOST_HAS_ABI_HEADERS
0528 # include BOOST_ABI_SUFFIX
0529 #endif
0530
0531 #endif