1
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)
6
7 #ifndef BOOST_FIBERS_CONTEXT_H
8 #define BOOST_FIBERS_CONTEXT_H
9
10 #include <atomic>
11 #include <chrono>
12 #include <cstdint>
13 #include <exception>
14 #include <functional>
15 #include <iostream>
16 #include <map>
17 #include <memory>
18 #include <tuple>
19 #include <type_traits>
20 #include <utility>
21
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>
27 #endif
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>
35
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
48 #ifdef BOOST_HAS_ABI_HEADERS
49 # include BOOST_ABI_PREFIX
50 #endif
51
52 #ifdef _MSC_VER
53 # pragma warning(push)
54 # pragma warning(disable:4251)
55 #endif
56
57 namespace boost {
58 namespace fibers {
59
60 class context;
61 class fiber;
62 class scheduler;
63
64 namespace detail {
65
66 struct wait_tag;
67 typedef intrusive::list_member_hook<
68 intrusive::tag< wait_tag >,
69 intrusive::link_mode<
70 intrusive::auto_unlink
71 >
72 > wait_hook;
73 // declaration of the functor that converts between
74 // the context class and the wait-hook
75 struct wait_functor {
76 // required types
77 using hook_type = wait_hook;
78 using hook_ptr = hook_type *;
79 using const_hook_ptr = const hook_type *;
80 using value_type = context;
81 using pointer = value_type *;
82 using const_pointer = const value_type *;
83
84 // required static functions
85 static hook_ptr to_hook_ptr( value_type &value);
86 static const_hook_ptr to_hook_ptr( value_type const& value);
87 static pointer to_value_ptr( hook_ptr n);
88 static const_pointer to_value_ptr( const_hook_ptr n);
89 };
90
91 struct ready_tag;
92 typedef intrusive::list_member_hook<
93 intrusive::tag< ready_tag >,
94 intrusive::link_mode<
95 intrusive::auto_unlink
96 >
97 > ready_hook;
98
99 struct sleep_tag;
100 typedef intrusive::set_member_hook<
101 intrusive::tag< sleep_tag >,
102 intrusive::link_mode<
103 intrusive::auto_unlink
104 >
105 > sleep_hook;
106
107 struct worker_tag;
108 typedef intrusive::list_member_hook<
109 intrusive::tag< worker_tag >,
110 intrusive::link_mode<
111 intrusive::auto_unlink
112 >
113 > worker_hook;
114
115 struct terminated_tag;
116 typedef intrusive::slist_member_hook<
117 intrusive::tag< terminated_tag >,
118 intrusive::link_mode<
119 intrusive::safe_link
120 >
121 > terminated_hook;
122
123 struct remote_ready_tag;
124 typedef intrusive::slist_member_hook<
125 intrusive::tag< remote_ready_tag >,
126 intrusive::link_mode<
127 intrusive::safe_link
128 >
129 > remote_ready_hook;
130
131 }
132
133 class BOOST_FIBERS_DECL context {
134 public:
135 typedef intrusive::list<
136 context,
137 intrusive::function_hook< detail::wait_functor >,
138 intrusive::constant_time_size< false >
139 > wait_queue_t;
140
141 private:
142 friend class dispatcher_context;
143 friend class main_context;
144 template< typename Fn, typename ... Arg > friend class worker_context;
145 friend class scheduler;
146
147 struct fss_data {
148 void * vp{ nullptr };
149 detail::fss_cleanup_function::ptr_t cleanup_function{};
150
151 fss_data() noexcept = default;
152
fss_databoost::fibers::context::fss_data153 fss_data( void * vp_,
154 detail::fss_cleanup_function::ptr_t fn) noexcept :
155 vp( vp_),
156 cleanup_function(std::move( fn)) {
157 BOOST_ASSERT( cleanup_function);
158 }
159
do_cleanupboost::fibers::context::fss_data160 void do_cleanup() {
161 ( * cleanup_function)( vp);
162 }
163 };
164
165 typedef std::map< uintptr_t, fss_data > fss_data_t;
166
167 #if ! defined(BOOST_FIBERS_NO_ATOMICS)
168 std::atomic< std::size_t > use_count_;
169 #else
170 std::size_t use_count_;
171 #endif
172 #if ! defined(BOOST_FIBERS_NO_ATOMICS)
173 detail::remote_ready_hook remote_ready_hook_{};
174 #endif
175 detail::spinlock splk_{};
176 bool terminated_{ false };
177 wait_queue_t wait_queue_{};
178 public:
179 detail::wait_hook wait_hook_{};
180 #if ! defined(BOOST_FIBERS_NO_ATOMICS)
181 std::atomic< std::intptr_t > twstatus{ 0 };
182 #endif
183 private:
184 scheduler * scheduler_{ nullptr };
185 fss_data_t fss_data_{};
186 detail::sleep_hook sleep_hook_{};
187 detail::ready_hook ready_hook_{};
188 detail::terminated_hook terminated_hook_{};
189 detail::worker_hook worker_hook_{};
190 fiber_properties * properties_{ nullptr };
191 boost::context::fiber c_{};
192 std::chrono::steady_clock::time_point tp_;
193 type type_;
194 launch policy_;
195
context(std::size_t initial_count,type t,launch policy)196 context( std::size_t initial_count, type t, launch policy) noexcept :
197 use_count_{ initial_count },
198 tp_{ (std::chrono::steady_clock::time_point::max)() },
199 type_{ t },
200 policy_{ policy } {
201 }
202
203 public:
204 class id {
205 private:
206 context * impl_{ nullptr };
207
208 public:
209 id() = default;
210
id(context * impl)211 explicit id( context * impl) noexcept :
212 impl_{ impl } {
213 }
214
operator ==(id const & other) const215 bool operator==( id const& other) const noexcept {
216 return impl_ == other.impl_;
217 }
218
operator !=(id const & other) const219 bool operator!=( id const& other) const noexcept {
220 return impl_ != other.impl_;
221 }
222
operator <(id const & other) const223 bool operator<( id const& other) const noexcept {
224 return impl_ < other.impl_;
225 }
226
operator >(id const & other) const227 bool operator>( id const& other) const noexcept {
228 return other.impl_ < impl_;
229 }
230
operator <=(id const & other) const231 bool operator<=( id const& other) const noexcept {
232 return ! ( * this > other);
233 }
234
operator >=(id const & other) const235 bool operator>=( id const& other) const noexcept {
236 return ! ( * this < other);
237 }
238
239 template< typename charT, class traitsT >
240 friend std::basic_ostream< charT, traitsT > &
operator <<(std::basic_ostream<charT,traitsT> & os,id const & other)241 operator<<( std::basic_ostream< charT, traitsT > & os, id const& other) {
242 if ( nullptr != other.impl_) {
243 return os << other.impl_;
244 }
245 return os << "{not-valid}";
246 }
247
operator bool() const248 explicit operator bool() const noexcept {
249 return nullptr != impl_;
250 }
251
operator !() const252 bool operator!() const noexcept {
253 return nullptr == impl_;
254 }
255 };
256
257 static context * active() noexcept;
258
259 static void reset_active() noexcept;
260
261 context( context const&) = delete;
262 context( context &&) = delete;
263 context & operator=( context const&) = delete;
264 context & operator=( context &&) = delete;
265
266 friend bool
operator ==(context const & lhs,context const & rhs)267 operator==( context const& lhs, context const& rhs) noexcept {
268 return & lhs == & rhs;
269 }
270
271 virtual ~context();
272
get_scheduler() const273 scheduler * get_scheduler() const noexcept {
274 return scheduler_;
275 }
276
277 id get_id() const noexcept;
278
is_resumable() const279 bool is_resumable() const noexcept {
280 return static_cast<bool>(c_);
281 }
282
283 void resume() noexcept;
284 void resume( detail::spinlock_lock &) noexcept;
285 void resume( context *) noexcept;
286
287 void suspend() noexcept;
288 void suspend( detail::spinlock_lock &) noexcept;
289
290 boost::context::fiber suspend_with_cc() noexcept;
291 boost::context::fiber terminate() noexcept;
292
293 void join();
294
295 void yield() noexcept;
296
297 bool wait_until( std::chrono::steady_clock::time_point const&) noexcept;
298 bool wait_until( std::chrono::steady_clock::time_point const&,
299 detail::spinlock_lock &) noexcept;
300
301 void schedule( context *) noexcept;
302
is_context(type t) const303 bool is_context( type t) const noexcept {
304 return type::none != ( type_ & t);
305 }
306
307 void * get_fss_data( void const * vp) const;
308
309 void set_fss_data(
310 void const * vp,
311 detail::fss_cleanup_function::ptr_t const& cleanup_fn,
312 void * data,
313 bool cleanup_existing);
314
315 void set_properties( fiber_properties * props) noexcept;
316
get_properties() const317 fiber_properties * get_properties() const noexcept {
318 return properties_;
319 }
320
get_policy() const321 launch get_policy() const noexcept {
322 return policy_;
323 }
324
325 bool worker_is_linked() const noexcept;
326
327 bool ready_is_linked() const noexcept;
328
329 bool remote_ready_is_linked() const noexcept;
330
331 bool sleep_is_linked() const noexcept;
332
333 bool terminated_is_linked() const noexcept;
334
335 bool wait_is_linked() const noexcept;
336
337 template< typename List >
worker_link(List & lst)338 void worker_link( List & lst) noexcept {
339 static_assert( std::is_same< typename List::value_traits::hook_type, detail::worker_hook >::value, "not a worker-queue");
340 BOOST_ASSERT( ! worker_is_linked() );
341 lst.push_back( * this);
342 }
343
344 template< typename List >
ready_link(List & lst)345 void ready_link( List & lst) noexcept {
346 static_assert( std::is_same< typename List::value_traits::hook_type, detail::ready_hook >::value, "not a ready-queue");
347 BOOST_ASSERT( ! ready_is_linked() );
348 lst.push_back( * this);
349 }
350
351 template< typename List >
remote_ready_link(List & lst)352 void remote_ready_link( List & lst) noexcept {
353 static_assert( std::is_same< typename List::value_traits::hook_type, detail::remote_ready_hook >::value, "not a remote-ready-queue");
354 BOOST_ASSERT( ! remote_ready_is_linked() );
355 lst.push_back( * this);
356 }
357
358 template< typename Set >
sleep_link(Set & set)359 void sleep_link( Set & set) noexcept {
360 static_assert( std::is_same< typename Set::value_traits::hook_type,detail::sleep_hook >::value, "not a sleep-queue");
361 BOOST_ASSERT( ! sleep_is_linked() );
362 set.insert( * this);
363 }
364
365 template< typename List >
terminated_link(List & lst)366 void terminated_link( List & lst) noexcept {
367 static_assert( std::is_same< typename List::value_traits::hook_type, detail::terminated_hook >::value, "not a terminated-queue");
368 BOOST_ASSERT( ! terminated_is_linked() );
369 lst.push_back( * this);
370 }
371
372 template< typename List >
wait_link(List & lst)373 void wait_link( List & lst) noexcept {
374 static_assert( std::is_same< typename List::value_traits::hook_type, detail::wait_hook >::value, "not a wait-queue");
375 BOOST_ASSERT( ! wait_is_linked() );
376 lst.push_back( * this);
377 }
378
379 void worker_unlink() noexcept;
380
381 void ready_unlink() noexcept;
382
383 void sleep_unlink() noexcept;
384
385 void wait_unlink() noexcept;
386
387 void detach() noexcept;
388
389 void attach( context *) noexcept;
390
intrusive_ptr_add_ref(context * ctx)391 friend void intrusive_ptr_add_ref( context * ctx) noexcept {
392 BOOST_ASSERT( nullptr != ctx);
393 ctx->use_count_.fetch_add( 1, std::memory_order_relaxed);
394 }
395
intrusive_ptr_release(context * ctx)396 friend void intrusive_ptr_release( context * ctx) noexcept {
397 BOOST_ASSERT( nullptr != ctx);
398 if ( 1 == ctx->use_count_.fetch_sub( 1, std::memory_order_release) ) {
399 std::atomic_thread_fence( std::memory_order_acquire);
400 boost::context::fiber c = std::move( ctx->c_);
401 // destruct context
402 ctx->~context();
403 // deallocated stack
404 std::move( c).resume();
405 }
406 }
407 };
408
409 inline
operator <(context const & l,context const & r)410 bool operator<( context const& l, context const& r) noexcept {
411 return l.get_id() < r.get_id();
412 }
413
414 template< typename Fn, typename ... Arg >
415 class worker_context final : public context {
416 private:
417 typename std::decay< Fn >::type fn_;
418 std::tuple< Arg ... > arg_;
419
420 boost::context::fiber
run_(boost::context::fiber && c)421 run_( boost::context::fiber && c) {
422 {
423 // fn and tpl must be destroyed before calling terminate()
424 auto fn = std::move( fn_);
425 auto arg = std::move( arg_);
426 #if (defined(BOOST_USE_UCONTEXT)||defined(BOOST_USE_WINFIB))
427 std::move( c).resume();
428 #else
429 boost::ignore_unused(c);
430 #endif
431 #if defined(BOOST_NO_CXX17_STD_APPLY)
432 boost::context::detail::apply( std::move( fn), std::move( arg) );
433 #else
434 std::apply( std::move( fn), std::move( arg) );
435 #endif
436 }
437 // terminate context
438 return terminate();
439 }
440
441 public:
442 template< typename StackAlloc >
worker_context(launch policy,boost::context::preallocated const & palloc,StackAlloc && salloc,Fn && fn,Arg...arg)443 worker_context( launch policy,
444 boost::context::preallocated const& palloc, StackAlloc && salloc,
445 Fn && fn, Arg ... arg) :
446 context{ 1, type::worker_context, policy },
447 fn_( std::forward< Fn >( fn) ),
448 arg_( std::forward< Arg >( arg) ... ) {
449 c_ = boost::context::fiber{ std::allocator_arg, palloc, std::forward< StackAlloc >( salloc),
450 std::bind( & worker_context::run_, this, std::placeholders::_1) };
451 #if (defined(BOOST_USE_UCONTEXT)||defined(BOOST_USE_WINFIB))
452 c_ = std::move( c_).resume();
453 #endif
454 }
455 };
456
457
458 template< typename StackAlloc, typename Fn, typename ... Arg >
make_worker_context(launch policy,StackAlloc && salloc,Fn && fn,Arg...arg)459 static intrusive_ptr< context > make_worker_context( launch policy,
460 StackAlloc && salloc,
461 Fn && fn, Arg ... arg) {
462 typedef worker_context< Fn, Arg ... > context_t;
463
464 auto sctx = salloc.allocate();
465 // reserve space for control structure
466 void * storage = reinterpret_cast< void * >(
467 ( reinterpret_cast< uintptr_t >( sctx.sp) - static_cast< uintptr_t >( sizeof( context_t) ) )
468 & ~ static_cast< uintptr_t >( 0xff) );
469 void * stack_bottom = reinterpret_cast< void * >(
470 reinterpret_cast< uintptr_t >( sctx.sp) - static_cast< uintptr_t >( sctx.size) );
471 const std::size_t size = reinterpret_cast< uintptr_t >( storage) - reinterpret_cast< uintptr_t >( stack_bottom);
472 // placement new of context on top of fiber's stack
473 return intrusive_ptr< context >{
474 new ( storage) context_t{
475 policy,
476 boost::context::preallocated{ storage, size, sctx },
477 std::forward< StackAlloc >( salloc),
478 std::forward< Fn >( fn),
479 std::forward< Arg >( arg) ... } };
480 }
481
482 namespace detail {
483
484 inline
to_hook_ptr(wait_functor::value_type & value)485 wait_functor::hook_ptr wait_functor::to_hook_ptr( wait_functor::value_type & value) {
486 return & value.wait_hook_;
487 }
488
489 inline
to_hook_ptr(wait_functor::value_type const & value)490 wait_functor::const_hook_ptr wait_functor::to_hook_ptr( wait_functor::value_type const& value) {
491 return & value.wait_hook_;
492 }
493
494 inline
to_value_ptr(wait_functor::hook_ptr n)495 wait_functor::pointer wait_functor::to_value_ptr( wait_functor::hook_ptr n) {
496 return intrusive::get_parent_from_member< context >( n, & context::wait_hook_);
497 }
498
499 inline
to_value_ptr(wait_functor::const_hook_ptr n)500 wait_functor::const_pointer wait_functor::to_value_ptr( wait_functor::const_hook_ptr n) {
501 return intrusive::get_parent_from_member< context >( n, & context::wait_hook_);
502 }
503
504 }}}
505
506 #ifdef _MSC_VER
507 # pragma warning(pop)
508 #endif
509
510 #ifdef BOOST_HAS_ABI_HEADERS
511 # include BOOST_ABI_SUFFIX
512 #endif
513
514 #endif // BOOST_FIBERS_CONTEXT_H
515