diff --git a/build/Jamfile.v2 b/build/Jamfile.v2 index 2e374402..3f2c1657 100644 --- a/build/Jamfile.v2 +++ b/build/Jamfile.v2 @@ -25,7 +25,8 @@ project boost/fiber ; lib boost_fiber - : barrier.cpp + : algorithm.cpp + barrier.cpp condition.cpp detail/fifo.cpp detail/spinlock.cpp @@ -36,6 +37,7 @@ lib boost_fiber future.cpp interruption.cpp mutex.cpp + properties.cpp recursive_mutex.cpp recursive_timed_mutex.cpp round_robin.cpp diff --git a/doc/scheduling.qbk b/doc/scheduling.qbk index 97ba6902..9ff7fec2 100644 --- a/doc/scheduling.qbk +++ b/doc/scheduling.qbk @@ -61,6 +61,65 @@ fiber which is to be resumed next.]] ] +To prevent the library from heap-allocating a default scheduler for a given +thread, that thread must call [function_link set_scheduling_algorithm] before +any other __boost_fiber__ entry point. + + void thread_fn() + { + my_fiber_scheduler mfs; + boost::fibers::set_scheduling_algorithm( & mfs); + ... + } + +A fiber-scheduler must implement interface __algo__. __boost_fiber__ provides one +scheduler: [class_link round_robin]. + +You are explicitly permitted to code your own __algo__ subclass, and to pass +it to [function_link set_scheduling_algorithm]. + + +[class_heading sched_algorithm] + + #include + + struct sched_algorithm + { + virtual ~sched_algorithm() {} + + virtual void awakened( detail::worker_fiber *) = 0; + + virtual detail::worker_fiber * pick_next() = 0; + + virtual void priority( detail::worker_fiber *, int) noexcept = 0; + }; + +[member_heading sched_algorithm..awakened] + + virtual void awakened( detail::worker_fiber * f) = 0; + +[variablelist +[[Effects:] [Marks fiber `f`, to be ready to run.]] +] + +[member_heading sched_algorithm..pick_next] + + virtual detail::worker_fiber * pick_next() = 0; + +[variablelist +[[Effects:] [Depending on the scheduling algorithm, this function returns the +fiber which is to be resumed next.]] +] + +[member_heading sched_algorithm..priority] + + virtual void priority( detail::worker_fiber *, int) noexcept = 0; + +[variablelist +[[Effects:] [Resets the priority of fiber `f`.]] +] + + [class_heading round_robin] This class implements __algo__ and schedules fibers in round-robin fashion diff --git a/examples/Jamfile.v2 b/examples/Jamfile.v2 index 83384118..671c3578 100644 --- a/examples/Jamfile.v2 +++ b/examples/Jamfile.v2 @@ -31,6 +31,7 @@ exe futures_mt : futures_mt.cpp ; exe interrupt : interrupt.cpp ; exe join : join.cpp ; exe ping_pong : ping_pong.cpp ; +exe priority : priority.cpp ; exe segmented_stack : segmented_stack.cpp ; exe simple : simple.cpp ; diff --git a/examples/priority.cpp b/examples/priority.cpp new file mode 100644 index 00000000..890bfd73 --- /dev/null +++ b/examples/priority.cpp @@ -0,0 +1,292 @@ +// Copyright Nat Goodspeed 2014. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include +#include +#include +#include + +class priority_props: public boost::fibers::fiber_properties +{ +public: + priority_props(boost::fibers::fiber_properties::back_ptr p): + fiber_properties(p), + priority_(0) + {} + + int get_priority() const { return priority_; } + + // Call this method to alter priority, because we must notify + // priority_scheduler of any change. + void set_priority(int p) + { + // Of course, it's only worth reshuffling the queue and all if we're + // actually changing the priority. + if (p != priority_) + { + priority_ = p; + notify(); + } + } + + // The fiber name of course is solely for purposes of this example + // program; it has nothing to do with implementing scheduler priority. + // This is a public data member -- not requiring set/get access methods -- + // because we need not inform the scheduler of any change. + std::string name; + +private: + int priority_; +}; + +class priority_scheduler: + public boost::fibers::sched_algorithm_with_properties +{ +private: + // Much as we would like, we don't use std::priority_queue because it + // doesn't appear to provide any way to alter the priority (and hence + // queue position) of a particular item. + boost::fibers::fiber_context* head_; + +public: + priority_scheduler(): + head_(nullptr) + {} + + // For a subclass of sched_algorithm_with_properties<>, it's important to + // override awakened_props(), NOT awakened(). + virtual void awakened_props(boost::fibers::fiber_context* f) + { + int f_priority = properties(f).get_priority(); + // With this scheduler, fibers with higher priority values are + // preferred over fibers with lower priority values. But fibers with + // equal priority values are processed in round-robin fashion. So when + // we're handed a new fiber_base, put it at the end of the fibers with + // that same priority. In other words: search for the first fiber in + // the queue with LOWER priority, and insert before that one. + boost::fibers::fiber_context** fp = &head_; + for ( ; *fp; fp = &(*fp)->nxt) + if (properties(*fp).get_priority() < f_priority) + break; + // It doesn't matter whether we hit the end of the list or found + // another fiber with lower priority. Either way, insert f here. + f->nxt = *fp; + *fp = f; + + std::cout << "awakened(" << properties(f).name << "): "; + describe_ready_queue(); + } + + virtual boost::fibers::fiber_context* pick_next() + { + // if ready queue is empty, just tell caller + if (! head_) + return nullptr; + // Here we have at least one ready fiber. Unlink and return that. + boost::fibers::fiber_context* f = head_; + head_ = f->nxt; + f->nxt = nullptr; + + std::cout << "pick_next() resuming " << properties(f).name << ": "; + describe_ready_queue(); + return f; + } + + virtual void property_change(boost::fibers::fiber_context* f, priority_props& props) + { + // Although our priority_props class defines multiple properties, only + // one of them (priority) actually calls notify() when changed. The + // point of a property_change() override is to reshuffle the ready + // queue according to the updated priority value. + std::cout << "property_change(" << props.name << '(' << props.get_priority() + << ")): "; + + // Despite the added complexity of the loop body, make a single pass + // over the queue to find both the existing item and the new desired + // insertion point. + bool found = false; + boost::fibers::fiber_context **insert = nullptr, **fp = &head_; + for ( ; *fp; fp = &(*fp)->nxt) + { + if (*fp == f) + { + // found the passed fiber in our list -- unlink it + found = true; + *fp = (*fp)->nxt; + f->nxt = nullptr; + // If that was the last item in the list, stop. + if (! *fp) + break; + // If we've already found the new insertion point, no need to + // continue looping. + if (insert) + break; + } + // As in awakened(), we're looking for the first fiber in the + // queue with priority lower than the passed fiber. + if (properties(*fp).get_priority() < props.get_priority()) + { + insert = fp; + // If we've already found and unlinked the passed fiber, no + // need to continue looping. + if (found) + break; + } + } + // property_change() should only be called if f->is_ready(). However, + // a waiting fiber can change state to is_ready() while still on the + // fiber_manager's waiting queue. Every such fiber will be swept onto + // our ready queue before the next pick_next() call, but still it's + // possible to get a property_change() call for a fiber that + // is_ready() but is not yet on our ready queue. If it's not there, no + // action required: we'll handle it next time it hits awakened(). + if (! found) + { + // hopefully user will distinguish this case by noticing that + // the fiber with which we were called does not appear in the + // ready queue at all + describe_ready_queue(); + return; + } + // There might not be any ready fibers with lower priority. In that + // case, append to the end of the queue. + std::string where; + if (insert) + where = std::string("before ") + properties(*insert).name; + else + { + insert = fp; + where = "to end"; + } + // Insert f at the new insertion point in the queue. + f->nxt = *insert; + *insert = f; + + std::cout << "moving " << where << ": "; + describe_ready_queue(); + } + + void describe_ready_queue() + { + if (! head_) + std::cout << "[empty]"; + else + { + const char* delim = ""; + for (boost::fibers::fiber_context *f = head_; f; f = f->nxt) + { + priority_props& props(properties(f)); + std::cout << delim << props.name << '(' << props.get_priority() << ')'; + delim = ", "; + } + } + std::cout << std::endl; + } +}; + +void init(const std::string& name, int priority) +{ + priority_props& props(boost::this_fiber::properties()); + props.name = name; + props.set_priority(priority); +} + +void yield_fn(const std::string& name, int priority) +{ + init(name, priority); + + for (int i = 0; i < 3; ++i) + { + std::cout << "fiber " << name << " running" << std::endl; + boost::this_fiber::yield(); + } +} + +void barrier_fn(const std::string& name, int priority, boost::fibers::barrier& barrier) +{ + init(name, priority); + + std::cout << "fiber " << name << " waiting on barrier" << std::endl; + barrier.wait(); + std::cout << "fiber " << name << " yielding" << std::endl; + boost::this_fiber::yield(); + std::cout << "fiber " << name << " done" << std::endl; +} + +void change_fn(const std::string& name, int priority, + boost::fibers::fiber& other, int other_priority, + boost::fibers::barrier& barrier) +{ + init(name, priority); + + std::cout << "fiber " << name << " waiting on barrier" << std::endl; + barrier.wait(); + // We assume a couple things about 'other': + // - that it was also waiting on the same barrier + // - that it has lower priority than this fiber. + // If both are true, 'other' is now ready to run but is sitting in + // priority_scheduler's ready queue. Change its priority. + priority_props& other_props(other.properties()); + std::cout << "fiber " << name << " changing priority of " << other_props.name + << " to " << other_priority << std::endl; + other_props.set_priority(other_priority); + std::cout << "fiber " << name << " done" << std::endl; +} + +int main(int argc, char *argv[]) +{ + // make sure we use our priority_scheduler rather than default round_robin + priority_scheduler psched; + boost::fibers::set_scheduling_algorithm(&psched); + + { + // verify that high-priority fiber always gets scheduled first + boost::fibers::fiber low(boost::bind(yield_fn, "low", 1)); + boost::fibers::fiber med(boost::bind(yield_fn, "medium", 2)); + boost::fibers::fiber hi(boost::bind(yield_fn, "high", 3)); + hi.join(); + med.join(); + low.join(); + std::cout << std::endl; + } + + { + // fibers of same priority are scheduled in round-robin order + boost::fibers::fiber a(boost::bind(yield_fn, "a", 0)); + boost::fibers::fiber b(boost::bind(yield_fn, "b", 0)); + boost::fibers::fiber c(boost::bind(yield_fn, "c", 0)); + a.join(); + b.join(); + c.join(); + std::cout << std::endl; + } + + { + // using a barrier wakes up all waiting fibers at the same time + boost::fibers::barrier barrier(3); + boost::fibers::fiber low(boost::bind(barrier_fn, "low", 1, boost::ref(barrier))); + boost::fibers::fiber med(boost::bind(barrier_fn, "medium", 2, boost::ref(barrier))); + boost::fibers::fiber hi(boost::bind(barrier_fn, "high", 3, boost::ref(barrier))); + low.join(); + med.join(); + hi.join(); + std::cout << std::endl; + } + + { + // change priority of a fiber in priority_scheduler's ready queue + boost::fibers::barrier barrier(3); + boost::fibers::fiber c(boost::bind(barrier_fn, "c", 1, boost::ref(barrier))); + boost::fibers::fiber a(boost::bind(change_fn, "a", 3, + boost::ref(c), 3, boost::ref(barrier))); + boost::fibers::fiber b(boost::bind(barrier_fn, "b", 2, boost::ref(barrier))); + a.join(); + b.join(); + c.join(); + std::cout << std::endl; + } + + return 0; +} diff --git a/include/boost/fiber/algorithm.hpp b/include/boost/fiber/algorithm.hpp index 75fd125f..bf1188e7 100644 --- a/include/boost/fiber/algorithm.hpp +++ b/include/boost/fiber/algorithm.hpp @@ -8,6 +8,7 @@ #include +#include #include #ifdef BOOST_HAS_ABI_HEADERS @@ -27,6 +28,65 @@ struct BOOST_FIBERS_DECL sched_algorithm { virtual fiber_context * pick_next() = 0; }; +class BOOST_FIBERS_DECL sched_algorithm_with_properties_base: public sched_algorithm +{ +public: + // called by fiber_properties::notify() -- don't directly call + virtual void property_change_( fiber_context *f, fiber_properties* props ) = 0; + +protected: + static fiber_properties* get_properties(fiber_context* f) noexcept; + static void set_properties(fiber_context* f, fiber_properties* p) noexcept; +}; + +template +struct sched_algorithm_with_properties: + public sched_algorithm_with_properties_base +{ + typedef sched_algorithm_with_properties_base super; + + // Mark this override 'final': sched_algorithm_with_properties subclasses + // must override awakened_props() instead. Otherwise you'd have to + // remember to start every subclass awakened() override with: + // sched_algorithm_with_properties::awakened(fb); + virtual void awakened( fiber_context *f) final + { + fiber_properties* props = super::get_properties(f); + if (! props) + { + // TODO: would be great if PROPS could be allocated on the new + // fiber's stack somehow + props = new PROPS(f); + super::set_properties(f, props); + } + // Set sched_algo_ again every time this fiber becomes READY. That + // handles the case of a fiber migrating to a new thread with a new + // sched_algorithm subclass instance. + props->set_sched_algorithm(this); + + // Okay, now forward the call to subclass override. + awakened_props(f); + } + + // subclasses override this method instead of the original awakened() + virtual void awakened_props( fiber_context *) = 0; + + // used for all internal calls + PROPS& properties(fiber_context* f) + { + return static_cast(*super::get_properties(f)); + } + + // override this to be notified by PROPS::notify() + virtual void property_change( fiber_context* f, PROPS& props) {} + + // implementation for sched_algorithm_with_properties_base method + void property_change_( fiber_context *f, fiber_properties* props ) final + { + property_change(f, *static_cast(props)); + } +}; + }} #ifdef BOOST_HAS_ABI_HEADERS diff --git a/include/boost/fiber/detail/fifo.hpp b/include/boost/fiber/detail/fifo.hpp index 4024d3d7..94974082 100644 --- a/include/boost/fiber/detail/fifo.hpp +++ b/include/boost/fiber/detail/fifo.hpp @@ -50,6 +50,10 @@ public: private: fiber_context * head_; + // tail_ points to the nxt field that contains the null that marks the end + // of the fifo. When the fifo is empty, tail_ points to head_. tail_ must + // never be null: it always points to a real fiber_context*. However, in + // normal use, (*tail_) is always null. fiber_context ** tail_; }; diff --git a/include/boost/fiber/fiber.hpp b/include/boost/fiber/fiber.hpp index 9cecef21..c4f78635 100644 --- a/include/boost/fiber/fiber.hpp +++ b/include/boost/fiber/fiber.hpp @@ -20,6 +20,7 @@ #include #include #include +#include #ifdef BOOST_HAS_ABI_HEADERS # include BOOST_ABI_PREFIX @@ -27,11 +28,6 @@ namespace boost { namespace fibers { -namespace detail { - -struct scheduler; - -} class fiber_context; @@ -51,7 +47,7 @@ private: // reserve space for control structure std::size_t size = sctx.size - sizeof( fiber_context); void * sp = static_cast< char * >( sctx.sp) - sizeof( fiber_context); - // placement new of worker_fiber on top of fiber's stack + // placement new of fiber_context on top of fiber's stack return ptr_t( new ( sp) fiber_context( context::preallocated( sp, size, sctx), salloc, std::forward< Fn >( fn), std::forward< Args >( args) ... ) ); @@ -120,15 +116,17 @@ public: return impl_ ? impl_->get_id() : id(); } - bool thread_affinity() const noexcept; - - void thread_affinity( bool) noexcept; - void detach() noexcept; void join(); void interrupt() noexcept; + + template + PROPS& properties() + { + return dynamic_cast(*impl_->get_properties()); + } }; inline diff --git a/include/boost/fiber/fiber_context.hpp b/include/boost/fiber/fiber_context.hpp index 0e8a8596..4ed3b47a 100644 --- a/include/boost/fiber/fiber_context.hpp +++ b/include/boost/fiber/fiber_context.hpp @@ -36,6 +36,8 @@ namespace boost { namespace fibers { +class fiber_properties; + class BOOST_FIBERS_DECL fiber_context { private: enum class fiber_status { @@ -49,8 +51,7 @@ private: flag_main_fiber = 1 << 1, flag_interruption_blocked = 1 << 2, flag_interruption_requested = 1 << 3, - flag_thread_affinity = 1 << 4, - flag_detached = 1 << 5 + flag_detached = 1 << 4 }; struct BOOST_FIBERS_DECL fss_data { @@ -85,6 +86,7 @@ private: std::vector< fiber_context * > waiting_; std::exception_ptr except_; std::chrono::high_resolution_clock::time_point tp_; + fiber_properties * properties_; // main fiber fiber_context() : @@ -92,10 +94,11 @@ private: ctx_( context::execution_context::current() ), fss_data_(), state_( fiber_status::running), - flags_( flag_main_fiber | flag_thread_affinity), + flags_( flag_main_fiber), waiting_(), except_(), tp_( (std::chrono::high_resolution_clock::time_point::max)() ), + properties_( nullptr), nxt() { } @@ -137,6 +140,7 @@ private: waiting_(), except_(), tp_( (std::chrono::high_resolution_clock::time_point::max)() ), + properties_( nullptr), nxt( nullptr) { } @@ -215,9 +219,7 @@ public: std::index_sequence_for< Args ... >() ) { } - virtual ~fiber_context() { - BOOST_ASSERT( waiting_.empty() ); - } + virtual ~fiber_context(); id get_id() const noexcept { return id( const_cast< fiber_context * >( this) ); @@ -237,12 +239,6 @@ public: void request_interruption( bool req) noexcept; - bool thread_affinity() const noexcept { - return 0 != ( flags_.load() & flag_thread_affinity); - } - - void thread_affinity( bool req) noexcept; - bool is_terminated() const noexcept { return fiber_status::terminated == state_; } @@ -302,7 +298,9 @@ public: void resume() { BOOST_ASSERT( is_running() ); // set by the scheduler-algorithm - ctx_(); + // FIXME: post-1.58 boost::context::execution_context has an + // operator()() method. This statement originally called that. + ctx_.resume(); } std::chrono::high_resolution_clock::time_point const& time_point() const noexcept { @@ -317,6 +315,13 @@ public: tp_ = (std::chrono::high_resolution_clock::time_point::max)(); } + void set_properties( fiber_properties* props); + + fiber_properties* get_properties() const + { + return properties_; + } + void release(); friend void intrusive_ptr_add_ref( fiber_context * f) { diff --git a/include/boost/fiber/fiber_manager.hpp b/include/boost/fiber/fiber_manager.hpp index ca836387..bcba2597 100644 --- a/include/boost/fiber/fiber_manager.hpp +++ b/include/boost/fiber/fiber_manager.hpp @@ -81,6 +81,8 @@ public: fiber_context * active() noexcept; + sched_algorithm* get_sched_algo_(); + void set_sched_algo( sched_algorithm *); void wait_interval( std::chrono::high_resolution_clock::duration const&) noexcept; @@ -92,8 +94,7 @@ public: std::chrono::high_resolution_clock::duration wait_interval() noexcept; - bool preserve_fpu(); - + bool preserve_fpu() const; void preserve_fpu( bool); }; diff --git a/include/boost/fiber/operations.hpp b/include/boost/fiber/operations.hpp index 93674b4f..95f0e73b 100644 --- a/include/boost/fiber/operations.hpp +++ b/include/boost/fiber/operations.hpp @@ -49,17 +49,13 @@ void sleep_for( std::chrono::duration< Rep, Period > const& timeout_duration) { sleep_until( std::chrono::high_resolution_clock::now() + timeout_duration); } -inline -bool thread_affinity() noexcept { - return fibers::detail::scheduler::instance()->active()->thread_affinity(); +template < class PROPS > +PROPS& properties() +{ + return dynamic_cast(*fibers::detail::scheduler::instance()->active()->get_properties()); } -inline -void thread_affinity( bool req) noexcept { - fibers::detail::scheduler::instance()->active()->thread_affinity( req); -} - -} +} // this_fiber namespace fibers { diff --git a/include/boost/fiber/properties.hpp b/include/boost/fiber/properties.hpp new file mode 100644 index 00000000..c1e556e0 --- /dev/null +++ b/include/boost/fiber/properties.hpp @@ -0,0 +1,77 @@ +// Copyright Nat Goodspeed 2014. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +// Define fiber_properties, a base class from which a library consumer can +// derive a subclass with specific properties important to a user-coded +// scheduler. + +#ifndef BOOST_FIBERS_PROPERTIES_HPP +#define BOOST_FIBERS_PROPERTIES_HPP + +#include + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_PREFIX +#endif + +# if defined(BOOST_MSVC) +# pragma warning(push) +# pragma warning(disable:4275) +# endif + +namespace boost { +namespace fibers { + +struct sched_algorithm; +class fiber_context; + +class fiber_properties +{ +protected: + // initialized by constructor + fiber_context* fiber_; + // set every time this fiber becomes READY + sched_algorithm* sched_algo_; + + // Inform the relevant sched_algorithm instance that something important + // has changed, so it can (presumably) adjust its data structures + // accordingly. + void notify(); + +public: + // fiber_properties, and by implication every subclass, must accept a back + // pointer to its fiber_context. + typedef fiber_context* back_ptr; + // Any specific property setter method, after updating the relevant + // instance variable, can/should call notify(). + fiber_properties(back_ptr f): + fiber_(f), + sched_algo_(nullptr) + {} + + // We need a virtual destructor (hence a vtable) because fiber_properties + // is stored polymorphically (as fiber_properties*) in fiber_context, and + // destroyed via that pointer. + virtual ~fiber_properties() {} + + // not really intended for public use, but sched_algorithm_with_properties + // must be able to call this + void set_sched_algorithm(sched_algorithm* algo) + { + sched_algo_ = algo; + } +}; + +}} // namespace boost::fibers + +# if defined(BOOST_MSVC) +# pragma warning(pop) +# endif + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_SUFFIX +#endif + +#endif // BOOST_FIBERS_PROPERTIES_HPP diff --git a/src/algorithm.cpp b/src/algorithm.cpp new file mode 100644 index 00000000..609dcc6d --- /dev/null +++ b/src/algorithm.cpp @@ -0,0 +1,38 @@ + +// Copyright Oliver Kowalke / Nat Goodspeed 2015. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include "boost/fiber/algorithm.hpp" + +#include + +#include + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_PREFIX +#endif + +namespace boost { +namespace fibers { + +//static +fiber_properties* +sched_algorithm_with_properties_base::get_properties(fiber_context* f) noexcept +{ + return f->get_properties(); +} + +//static +void +sched_algorithm_with_properties_base::set_properties(fiber_context* f, fiber_properties* props) noexcept +{ + f->set_properties(props); +} + +}} + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_SUFFIX +#endif diff --git a/src/detail/waiting_queue.cpp b/src/detail/waiting_queue.cpp index 5bd4ac38..67081ea9 100644 --- a/src/detail/waiting_queue.cpp +++ b/src/detail/waiting_queue.cpp @@ -31,18 +31,18 @@ waiting_queue::push( fiber_context * item) noexcept { BOOST_ASSERT( nullptr != item); BOOST_ASSERT( nullptr == item->nxt); - // Skip past any worker_fibers in the queue whose time_point() is less - // than item->time_point(), looking for the first worker_fiber in the + // Skip past any fiber_contexts in the queue whose time_point() is less + // than item->time_point(), looking for the first fiber_context in the // queue whose time_point() is at least item->time_point(). Insert // item before that. In other words, insert item so as to preserve - // ascending order of time_point() values. (Recall that a worker_fiber + // ascending order of time_point() values. (Recall that a fiber_context // waiting with no timeout uses the maximum time_point value.) // We do this by walking the linked list of nxt fields with a - // worker_fiber**. In other words, first we point to &head_, then to - // &head_->nxt then to &head_->nxt->nxt and so forth. When we find + // fiber_context**. In other words, first we point to &head_, then to + // &head_->nxt, then to &head_->nxt->nxt and so forth. When we find // the item with the right time_point(), we're already pointing to the - // worker_fiber* that links it into the list. Insert item right there. + // fiber_context* that links it into the list. Insert item right there. fiber_context ** f( & head_); for ( ; nullptr != * f; f = & ( * f)->nxt) { @@ -65,30 +65,30 @@ waiting_queue::move_to( sched_algorithm * sched_algo) { std::chrono::high_resolution_clock::time_point now( std::chrono::high_resolution_clock::now() ); - // Search the queue for every worker_fiber 'f' for which fn(f, now) - // returns true. Each time we find such a worker_fiber, unlink it from - // the queue and pass it to sched_algo->awakened(). + // Search the queue for every fiber_context 'f' which is_ready(). Each + // time we find a ready fiber_context, unlink it from the queue and pass + // it to sched_algo->awakened(). - // Search using a worker_fiber**, starting at &head_. + // Search using a fiber_context**, starting at &head_. for ( fiber_context ** fp( & head_); nullptr != * fp;) { fiber_context * f( * fp); BOOST_ASSERT( ! f->is_running() ); BOOST_ASSERT( ! f->is_terminated() ); - // set fiber to state_ready if dead-line was reached + // set fiber to state_ready if deadline was reached // set fiber to state_ready if interruption was requested if ( f->time_point() <= now || f->interruption_requested() ) { f->set_ready(); } if ( ! f->is_ready() ) { - // If f does NOT meet caller's criteria, skip fp past it. + // If f is NOT ready, skip fp past it. fp = & ( * fp)->nxt; } else { - // Here f satisfies our caller. Unlink it from the list. + // Here f is ready. Unlink it from the list. * fp = ( * fp)->nxt; f->nxt = nullptr; - // Pass the newly-unlinked worker_fiber* to sched_algo. + // Pass the newly-unlinked fiber_context* to sched_algo. f->time_point_reset(); sched_algo->awakened( f); } diff --git a/src/fiber.cpp b/src/fiber.cpp index e51457cd..dcc23ef6 100644 --- a/src/fiber.cpp +++ b/src/fiber.cpp @@ -28,27 +28,13 @@ fiber::start_() { detail::scheduler::instance()->spawn( impl_.get() ); } -bool -fiber::thread_affinity() const noexcept { - BOOST_ASSERT( impl_); - - return impl_->thread_affinity(); -} - -void -fiber::thread_affinity( bool req) noexcept { - BOOST_ASSERT( impl_); - - impl_->thread_affinity( req); -} - void fiber::join() { BOOST_ASSERT( impl_); if ( boost::this_fiber::get_id() == get_id() ) { throw fiber_resource_error( static_cast< int >( std::errc::resource_deadlock_would_occur), - "boost fiber: trying joining itself"); + "boost fiber: trying to join itself"); } if ( ! joinable() ) { diff --git a/src/fiber_context.cpp b/src/fiber_context.cpp index 9d2288ed..42d329ff 100644 --- a/src/fiber_context.cpp +++ b/src/fiber_context.cpp @@ -10,6 +10,7 @@ #include "boost/fiber/detail/scheduler.hpp" #include "boost/fiber/exceptions.hpp" +#include "boost/fiber/properties.hpp" #ifdef BOOST_HAS_ABI_HEADERS # include BOOST_ABI_PREFIX @@ -18,6 +19,11 @@ namespace boost { namespace fibers { +fiber_context::~fiber_context() { + BOOST_ASSERT( waiting_.empty() ); + delete properties_; +} + fiber_context * fiber_context::main_fiber() { static thread_local fiber_context mf; @@ -79,18 +85,6 @@ fiber_context::request_interruption( bool req) noexcept { } } -void -fiber_context::thread_affinity( bool req) noexcept { - // BOOST_ASSERT( 0 == ( flags_.load() & flag_main_fiber) ); - if ( 0 == ( flags_.load() & flag_main_fiber) ) { - if ( req) { - flags_ |= flag_thread_affinity; - } else { - flags_ &= ~flag_thread_affinity; - } - } -} - void * fiber_context::get_fss_data( void const * vp) const { uintptr_t key( reinterpret_cast< uintptr_t >( vp) ); @@ -130,6 +124,13 @@ fiber_context::set_fss_data( void const * vp, } } +void +fiber_context::set_properties( fiber_properties* props) +{ + delete properties_; + properties_ = props; +} + }} #ifdef BOOST_HAS_ABI_HEADERS diff --git a/src/fiber_manager.cpp b/src/fiber_manager.cpp index 39e91d18..47dffed9 100644 --- a/src/fiber_manager.cpp +++ b/src/fiber_manager.cpp @@ -101,7 +101,7 @@ fiber_manager::spawn( fiber_context * f) { void fiber_manager::run() { for (;;) { - // move all fibers witch are ready (state_ready) + // move all fibers which are ready (state_ready) // from waiting-queue to the runnable-queue wqueue_.move_to( sched_algo_); @@ -222,7 +222,7 @@ fiber_manager::wait_interval() noexcept { } bool -fiber_manager::preserve_fpu() { +fiber_manager::preserve_fpu() const { return preserve_fpu_; } diff --git a/src/properties.cpp b/src/properties.cpp new file mode 100644 index 00000000..e40b1ecc --- /dev/null +++ b/src/properties.cpp @@ -0,0 +1,39 @@ +// Copyright Oliver Kowalke 2013. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include +#include "boost/fiber/properties.hpp" +#include "boost/fiber/algorithm.hpp" +#include "boost/fiber/fiber_manager.hpp" +#include "boost/fiber/fiber_context.hpp" + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_PREFIX +#endif + +namespace boost { +namespace fibers { + +void fiber_properties::notify() +{ + BOOST_ASSERT(sched_algo_); + // Application code might change an important property for any fiber at + // any time. The fiber in question might be ready, running or waiting. + // Significantly, only a fiber which is ready but not actually running is + // in the sched_algorithm's ready queue. Don't bother the sched_algorithm + // with a change to a fiber it's not currently tracking: it will do the + // right thing next time the fiber is passed to its awakened() method. + if (fiber_->is_ready()) + { + static_cast(sched_algo_)-> + property_change_(fiber_, this); + } +} + +}} // boost::fibers + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_SUFFIX +#endif