1 // Copyright Oliver Kowalke 2013. 2 // Distributed under the Boost Software License, Version 1.0. 3 // (See accompanying file LICENSE_1_0.txt or copy at 4 // http://www.boost.org/LICENSE_1_0.txt) 5 6 #ifndef BOOST_FIBERS_ALGO_ALGORITHM_H 7 #define BOOST_FIBERS_ALGO_ALGORITHM_H 8 9 #include <atomic> 10 #include <chrono> 11 #include <cstddef> 12 13 #include <boost/assert.hpp> 14 #include <boost/config.hpp> 15 #include <boost/intrusive_ptr.hpp> 16 17 #include <boost/fiber/properties.hpp> 18 #include <boost/fiber/detail/config.hpp> 19 20 #ifdef BOOST_HAS_ABI_HEADERS 21 # include BOOST_ABI_PREFIX 22 #endif 23 24 namespace boost { 25 namespace fibers { 26 27 class context; 28 29 namespace algo { 30 31 class BOOST_FIBERS_DECL algorithm { 32 private: 33 std::atomic< std::size_t > use_count_{ 0 }; 34 35 public: 36 typedef intrusive_ptr< algorithm > ptr_t; 37 38 virtual ~algorithm() = default; 39 40 virtual void awakened( context *) noexcept = 0; 41 42 virtual context * pick_next() noexcept = 0; 43 44 virtual bool has_ready_fibers() const noexcept = 0; 45 46 virtual void suspend_until( std::chrono::steady_clock::time_point const&) noexcept = 0; 47 48 virtual void notify() noexcept = 0; 49 intrusive_ptr_add_ref(algorithm * algo)50 friend void intrusive_ptr_add_ref( algorithm * algo) noexcept { 51 BOOST_ASSERT( nullptr != algo); 52 algo->use_count_.fetch_add( 1, std::memory_order_relaxed); 53 } 54 intrusive_ptr_release(algorithm * algo)55 friend void intrusive_ptr_release( algorithm * algo) noexcept { 56 BOOST_ASSERT( nullptr != algo); 57 if ( 1 == algo->use_count_.fetch_sub( 1, std::memory_order_release) ) { 58 std::atomic_thread_fence( std::memory_order_acquire); 59 delete algo; 60 } 61 } 62 }; 63 64 class BOOST_FIBERS_DECL algorithm_with_properties_base : public algorithm { 65 public: 66 // called by fiber_properties::notify() -- don't directly call 67 virtual void property_change_( context * ctx, fiber_properties * props) noexcept = 0; 68 69 protected: 70 static fiber_properties* get_properties( context * ctx) noexcept; 71 static void set_properties( context * ctx, fiber_properties * p) noexcept; 72 }; 73 74 template< typename PROPS > 75 struct algorithm_with_properties : public algorithm_with_properties_base { 76 typedef algorithm_with_properties_base super; 77 78 // Mark this override 'final': algorithm_with_properties subclasses 79 // must override awakened() with properties parameter instead. Otherwise 80 // you'd have to remember to start every subclass awakened() override 81 // with: algorithm_with_properties<PROPS>::awakened(fb); awakenedboost::fibers::algo::algorithm_with_properties82 void awakened( context * ctx) noexcept final { 83 fiber_properties * props = super::get_properties( ctx); 84 if ( BOOST_LIKELY( nullptr == props) ) { 85 // TODO: would be great if PROPS could be allocated on the new 86 // fiber's stack somehow 87 props = new_properties( ctx); 88 // It is not good for new_properties() to return 0. 89 BOOST_ASSERT_MSG( props, "new_properties() must return non-NULL"); 90 // new_properties() must return instance of (a subclass of) PROPS 91 BOOST_ASSERT_MSG( dynamic_cast< PROPS * >( props), 92 "new_properties() must return properties class"); 93 super::set_properties( ctx, props); 94 } 95 // Set algo_ again every time this fiber becomes READY. That 96 // handles the case of a fiber migrating to a new thread with a new 97 // algorithm subclass instance. 98 props->set_algorithm( this); 99 100 // Okay, now forward the call to subclass override. 101 awakened( ctx, properties( ctx) ); 102 } 103 104 // subclasses override this method instead of the original awakened() 105 virtual void awakened( context *, PROPS &) noexcept = 0; 106 107 // used for all internal calls propertiesboost::fibers::algo::algorithm_with_properties108 PROPS & properties( context * ctx) noexcept { 109 return static_cast< PROPS & >( * super::get_properties( ctx) ); 110 } 111 112 // override this to be notified by PROPS::notify() property_changeboost::fibers::algo::algorithm_with_properties113 virtual void property_change( context * /* ctx */, PROPS & /* props */) noexcept { 114 } 115 116 // implementation for algorithm_with_properties_base method property_change_boost::fibers::algo::algorithm_with_properties117 void property_change_( context * ctx, fiber_properties * props) noexcept final { 118 property_change( ctx, * static_cast< PROPS * >( props) ); 119 } 120 121 // Override this to customize instantiation of PROPS, e.g. use a different 122 // allocator. Each PROPS instance is associated with a particular 123 // context. new_propertiesboost::fibers::algo::algorithm_with_properties124 virtual fiber_properties * new_properties( context * ctx) { 125 return new PROPS( ctx); 126 } 127 }; 128 129 }}} 130 131 #ifdef BOOST_HAS_ABI_HEADERS 132 # include BOOST_ABI_SUFFIX 133 #endif 134 135 #endif // BOOST_FIBERS_ALGO_ALGORITHM_H 136