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