mirror of
https://github.com/boostorg/fiber.git
synced 2026-02-16 13:22:17 +00:00
Merge pull request #26 from nat-goodspeed/sched-props-dev
Support arbitrary properties for a user-supplied sched_algorithm implementation.
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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 <boost/fiber/algorithm.hpp>
|
||||
|
||||
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
|
||||
|
||||
@@ -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 ;
|
||||
|
||||
|
||||
292
examples/priority.cpp
Normal file
292
examples/priority.cpp
Normal file
@@ -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 <boost/fiber/all.hpp>
|
||||
#include <boost/bind.hpp>
|
||||
#include <boost/ref.hpp>
|
||||
#include <iostream>
|
||||
|
||||
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<priority_props>
|
||||
{
|
||||
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<priority_props>());
|
||||
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<priority_props>());
|
||||
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;
|
||||
}
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
#include <boost/config.hpp>
|
||||
|
||||
#include <boost/fiber/properties.hpp>
|
||||
#include <boost/fiber/detail/config.hpp>
|
||||
|
||||
#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 <class PROPS>
|
||||
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<PROPS>::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<PROPS&>(*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*>(props));
|
||||
}
|
||||
};
|
||||
|
||||
}}
|
||||
|
||||
#ifdef BOOST_HAS_ABI_HEADERS
|
||||
|
||||
@@ -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_;
|
||||
};
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#include <boost/fiber/detail/config.hpp>
|
||||
#include <boost/fiber/fiber_context.hpp>
|
||||
#include <boost/fiber/fixedsize_stack.hpp>
|
||||
#include <boost/fiber/detail/scheduler.hpp>
|
||||
|
||||
#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 <class PROPS>
|
||||
PROPS& properties()
|
||||
{
|
||||
return dynamic_cast<PROPS&>(*impl_->get_properties());
|
||||
}
|
||||
};
|
||||
|
||||
inline
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
};
|
||||
|
||||
|
||||
@@ -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<PROPS&>(*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 {
|
||||
|
||||
|
||||
77
include/boost/fiber/properties.hpp
Normal file
77
include/boost/fiber/properties.hpp
Normal file
@@ -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 <boost/fiber/detail/config.hpp>
|
||||
|
||||
#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
|
||||
38
src/algorithm.cpp
Normal file
38
src/algorithm.cpp
Normal file
@@ -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 <boost/config.hpp>
|
||||
|
||||
#include <boost/fiber/fiber_context.hpp>
|
||||
|
||||
#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
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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() ) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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_;
|
||||
}
|
||||
|
||||
|
||||
39
src/properties.cpp
Normal file
39
src/properties.cpp
Normal file
@@ -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 <boost/assert.hpp>
|
||||
#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_algorithm_with_properties_base*>(sched_algo_)->
|
||||
property_change_(fiber_, this);
|
||||
}
|
||||
}
|
||||
|
||||
}} // boost::fibers
|
||||
|
||||
#ifdef BOOST_HAS_ABI_HEADERS
|
||||
# include BOOST_ABI_SUFFIX
|
||||
#endif
|
||||
Reference in New Issue
Block a user