• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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