2 // Copyright Oliver Kowalke 2013.
3 // Distributed under the Boost Software License, Version 1.0.
4 // (See accompanying file LICENSE_1_0.txt or copy at
5 // http://www.boost.org/LICENSE_1_0.txt)
7 #ifndef BOOST_FIBERS_CONTEXT_H
8 #define BOOST_FIBERS_CONTEXT_H
19 #include <type_traits>
22 #include <boost/assert.hpp>
23 #include <boost/config.hpp>
24 #include <boost/core/ignore_unused.hpp>
25 #if defined(BOOST_NO_CXX17_STD_APPLY)
26 #include <boost/context/detail/apply.hpp>
28 #include <boost/context/fiber.hpp>
29 #include <boost/context/stack_context.hpp>
30 #include <boost/intrusive/list.hpp>
31 #include <boost/intrusive/parent_from_member.hpp>
32 #include <boost/intrusive_ptr.hpp>
33 #include <boost/intrusive/set.hpp>
34 #include <boost/intrusive/slist.hpp>
36 #include <boost/fiber/detail/config.hpp>
37 #include <boost/fiber/detail/data.hpp>
38 #include <boost/fiber/detail/decay_copy.hpp>
39 #include <boost/fiber/detail/fss.hpp>
40 #include <boost/fiber/detail/spinlock.hpp>
41 #include <boost/fiber/exceptions.hpp>
42 #include <boost/fiber/fixedsize_stack.hpp>
43 #include <boost/fiber/policy.hpp>
44 #include <boost/fiber/properties.hpp>
45 #include <boost/fiber/segmented_stack.hpp>
46 #include <boost/fiber/type.hpp>
47 #include <boost/fiber/waker.hpp>
49 #ifdef BOOST_HAS_ABI_HEADERS
50 # include BOOST_ABI_PREFIX
54 # pragma warning(push)
55 # pragma warning(disable:4251)
68 typedef intrusive::list_member_hook<
69 intrusive::tag< ready_tag >,
71 intrusive::auto_unlink
76 typedef intrusive::set_member_hook<
77 intrusive::tag< sleep_tag >,
79 intrusive::auto_unlink
84 typedef intrusive::list_member_hook<
85 intrusive::tag< worker_tag >,
87 intrusive::auto_unlink
91 struct terminated_tag;
92 typedef intrusive::slist_member_hook<
93 intrusive::tag< terminated_tag >,
99 struct remote_ready_tag;
100 typedef intrusive::slist_member_hook<
101 intrusive::tag< remote_ready_tag >,
102 intrusive::link_mode<
109 class BOOST_FIBERS_DECL context {
111 friend class dispatcher_context;
112 friend class main_context;
113 template< typename Fn, typename ... Arg > friend class worker_context;
114 friend class scheduler;
117 void * vp{ nullptr };
118 detail::fss_cleanup_function::ptr_t cleanup_function{};
120 fss_data() noexcept = default;
122 fss_data( void * vp_,
123 detail::fss_cleanup_function::ptr_t fn) noexcept :
125 cleanup_function(std::move( fn)) {
126 BOOST_ASSERT( cleanup_function);
130 ( * cleanup_function)( vp);
134 typedef std::map< uintptr_t, fss_data > fss_data_t;
136 #if ! defined(BOOST_FIBERS_NO_ATOMICS)
137 std::atomic< std::size_t > use_count_;
139 std::size_t use_count_;
141 #if ! defined(BOOST_FIBERS_NO_ATOMICS)
142 detail::remote_ready_hook remote_ready_hook_{};
144 detail::spinlock splk_{};
145 bool terminated_{ false };
146 wait_queue wait_queue_{};
148 #if ! defined(BOOST_FIBERS_NO_ATOMICS)
149 std::atomic<size_t> waker_epoch_{ 0 };
152 scheduler * scheduler_{ nullptr };
153 fss_data_t fss_data_{};
154 detail::sleep_hook sleep_hook_{};
155 waker sleep_waker_{};
156 detail::ready_hook ready_hook_{};
157 detail::terminated_hook terminated_hook_{};
158 detail::worker_hook worker_hook_{};
159 fiber_properties * properties_{ nullptr };
160 boost::context::fiber c_{};
161 std::chrono::steady_clock::time_point tp_;
165 context( std::size_t initial_count, type t, launch policy) noexcept :
166 use_count_{ initial_count },
167 tp_{ (std::chrono::steady_clock::time_point::max)() },
175 context * impl_{ nullptr };
180 explicit id( context * impl) noexcept :
184 bool operator==( id const& other) const noexcept {
185 return impl_ == other.impl_;
188 bool operator!=( id const& other) const noexcept {
189 return impl_ != other.impl_;
192 bool operator<( id const& other) const noexcept {
193 return impl_ < other.impl_;
196 bool operator>( id const& other) const noexcept {
197 return other.impl_ < impl_;
200 bool operator<=( id const& other) const noexcept {
201 return ! ( * this > other);
204 bool operator>=( id const& other) const noexcept {
205 return ! ( * this < other);
208 template< typename charT, class traitsT >
209 friend std::basic_ostream< charT, traitsT > &
210 operator<<( std::basic_ostream< charT, traitsT > & os, id const& other) {
211 if ( nullptr != other.impl_) {
212 return os << other.impl_;
214 return os << "{not-valid}";
217 explicit operator bool() const noexcept {
218 return nullptr != impl_;
221 bool operator!() const noexcept {
222 return nullptr == impl_;
226 static context * active() noexcept;
228 static void reset_active() noexcept;
230 context( context const&) = delete;
231 context( context &&) = delete;
232 context & operator=( context const&) = delete;
233 context & operator=( context &&) = delete;
235 #if !defined(BOOST_EMBTC)
238 operator==( context const& lhs, context const& rhs) noexcept {
239 return & lhs == & rhs;
245 operator==( context const& lhs, context const& rhs) noexcept;
251 scheduler * get_scheduler() const noexcept {
255 id get_id() const noexcept;
257 bool is_resumable() const noexcept {
258 return static_cast<bool>(c_);
261 void resume() noexcept;
262 void resume( detail::spinlock_lock &) noexcept;
263 void resume( context *) noexcept;
265 void suspend() noexcept;
266 void suspend( detail::spinlock_lock &) noexcept;
268 boost::context::fiber suspend_with_cc() noexcept;
269 boost::context::fiber terminate() noexcept;
273 void yield() noexcept;
275 bool wait_until( std::chrono::steady_clock::time_point const&) noexcept;
276 bool wait_until( std::chrono::steady_clock::time_point const&,
277 detail::spinlock_lock &,
280 bool wake(const size_t) noexcept;
282 waker create_waker() noexcept {
283 // this operation makes all previously created wakers to be outdated
284 return { this, ++waker_epoch_ };
287 void schedule( context *) noexcept;
289 bool is_context( type t) const noexcept {
290 return type::none != ( type_ & t);
293 void * get_fss_data( void const * vp) const;
297 detail::fss_cleanup_function::ptr_t const& cleanup_fn,
299 bool cleanup_existing);
301 void set_properties( fiber_properties * props) noexcept;
303 fiber_properties * get_properties() const noexcept {
307 launch get_policy() const noexcept {
311 bool worker_is_linked() const noexcept;
313 bool ready_is_linked() const noexcept;
315 bool remote_ready_is_linked() const noexcept;
317 bool sleep_is_linked() const noexcept;
319 bool terminated_is_linked() const noexcept;
321 template< typename List >
322 void worker_link( List & lst) noexcept {
323 static_assert( std::is_same< typename List::value_traits::hook_type, detail::worker_hook >::value, "not a worker-queue");
324 BOOST_ASSERT( ! worker_is_linked() );
325 lst.push_back( * this);
328 template< typename List >
329 void ready_link( List & lst) noexcept {
330 static_assert( std::is_same< typename List::value_traits::hook_type, detail::ready_hook >::value, "not a ready-queue");
331 BOOST_ASSERT( ! ready_is_linked() );
332 lst.push_back( * this);
335 template< typename List >
336 void remote_ready_link( List & lst) noexcept {
337 static_assert( std::is_same< typename List::value_traits::hook_type, detail::remote_ready_hook >::value, "not a remote-ready-queue");
338 BOOST_ASSERT( ! remote_ready_is_linked() );
339 lst.push_back( * this);
342 template< typename Set >
343 void sleep_link( Set & set) noexcept {
344 static_assert( std::is_same< typename Set::value_traits::hook_type,detail::sleep_hook >::value, "not a sleep-queue");
345 BOOST_ASSERT( ! sleep_is_linked() );
349 template< typename List >
350 void terminated_link( List & lst) noexcept {
351 static_assert( std::is_same< typename List::value_traits::hook_type, detail::terminated_hook >::value, "not a terminated-queue");
352 BOOST_ASSERT( ! terminated_is_linked() );
353 lst.push_back( * this);
356 void worker_unlink() noexcept;
358 void ready_unlink() noexcept;
360 void sleep_unlink() noexcept;
362 void detach() noexcept;
364 void attach( context *) noexcept;
366 #if !defined(BOOST_EMBTC)
368 friend void intrusive_ptr_add_ref( context * ctx) noexcept {
369 BOOST_ASSERT( nullptr != ctx);
370 ctx->use_count_.fetch_add( 1, std::memory_order_relaxed);
373 friend void intrusive_ptr_release( context * ctx) noexcept {
374 BOOST_ASSERT( nullptr != ctx);
375 if ( 1 == ctx->use_count_.fetch_sub( 1, std::memory_order_release) ) {
376 std::atomic_thread_fence( std::memory_order_acquire);
377 boost::context::fiber c = std::move( ctx->c_);
381 std::move( c).resume();
387 friend void intrusive_ptr_add_ref( context * ctx) noexcept;
388 friend void intrusive_ptr_release( context * ctx) noexcept;
394 #if defined(BOOST_EMBTC)
397 operator==( context const& lhs, context const& rhs) noexcept {
398 return & lhs == & rhs;
401 inline void intrusive_ptr_add_ref( context * ctx) noexcept {
402 BOOST_ASSERT( nullptr != ctx);
403 ctx->use_count_.fetch_add( 1, std::memory_order_relaxed);
406 inline void intrusive_ptr_release( context * ctx) noexcept {
407 BOOST_ASSERT( nullptr != ctx);
408 if ( 1 == ctx->use_count_.fetch_sub( 1, std::memory_order_release) ) {
409 std::atomic_thread_fence( std::memory_order_acquire);
410 boost::context::fiber c = std::move( ctx->c_);
414 std::move( c).resume();
421 bool operator<( context const& l, context const& r) noexcept {
422 return l.get_id() < r.get_id();
425 template< typename Fn, typename ... Arg >
426 class worker_context final : public context {
428 typename std::decay< Fn >::type fn_;
429 std::tuple< Arg ... > arg_;
431 boost::context::fiber
432 run_( boost::context::fiber && c) {
434 // fn and tpl must be destroyed before calling terminate()
435 auto fn = std::move( fn_);
436 auto arg = std::move( arg_);
437 #if (defined(BOOST_USE_UCONTEXT)||defined(BOOST_USE_WINFIB))
438 std::move( c).resume();
440 boost::ignore_unused(c);
442 #if defined(BOOST_NO_CXX17_STD_APPLY)
443 boost::context::detail::apply( std::move( fn), std::move( arg) );
445 std::apply( std::move( fn), std::move( arg) );
453 template< typename StackAlloc >
454 worker_context( launch policy,
455 boost::context::preallocated const& palloc, StackAlloc && salloc,
456 Fn && fn, Arg ... arg) :
457 context{ 1, type::worker_context, policy },
458 fn_( std::forward< Fn >( fn) ),
459 arg_( std::forward< Arg >( arg) ... ) {
460 c_ = boost::context::fiber{ std::allocator_arg, palloc, std::forward< StackAlloc >( salloc),
461 std::bind( & worker_context::run_, this, std::placeholders::_1) };
462 #if (defined(BOOST_USE_UCONTEXT)||defined(BOOST_USE_WINFIB))
463 c_ = std::move( c_).resume();
469 template< typename StackAlloc, typename Fn, typename ... Arg >
470 static intrusive_ptr< context > make_worker_context( launch policy,
471 StackAlloc && salloc,
472 Fn && fn, Arg ... arg) {
473 typedef worker_context< Fn, Arg ... > context_t;
475 auto sctx = salloc.allocate();
476 // reserve space for control structure
477 void * storage = reinterpret_cast< void * >(
478 ( reinterpret_cast< uintptr_t >( sctx.sp) - static_cast< uintptr_t >( sizeof( context_t) ) )
479 & ~ static_cast< uintptr_t >( 0xff) );
480 void * stack_bottom = reinterpret_cast< void * >(
481 reinterpret_cast< uintptr_t >( sctx.sp) - static_cast< uintptr_t >( sctx.size) );
482 const std::size_t size = reinterpret_cast< uintptr_t >( storage) - reinterpret_cast< uintptr_t >( stack_bottom);
483 // placement new of context on top of fiber's stack
484 return intrusive_ptr< context >{
485 new ( storage) context_t{
487 boost::context::preallocated{ storage, size, sctx },
488 std::forward< StackAlloc >( salloc),
489 std::forward< Fn >( fn),
490 std::forward< Arg >( arg) ... } };
496 # pragma warning(pop)
499 #ifdef BOOST_HAS_ABI_HEADERS
500 # include BOOST_ABI_SUFFIX
503 #endif // BOOST_FIBERS_CONTEXT_H