move sched_algorithm to algo::algorithm

- new namesapce algo
- shared_round_robin with shared ready queue
This commit is contained in:
Oliver Kowalke
2016-05-13 20:14:48 +02:00
parent f1b43062d5
commit 310f3ce2f2
34 changed files with 637 additions and 408 deletions
+3 -2
View File
@@ -27,7 +27,9 @@ project boost/fiber
;
lib boost_fiber
: algorithm.cpp
: algo/algorithm.cpp
algo/round_robin.cpp
algo/shared_round_robin.cpp
barrier.cpp
condition_variable.cpp
context.cpp
@@ -38,7 +40,6 @@ lib boost_fiber
properties.cpp
recursive_mutex.cpp
recursive_timed_mutex.cpp
round_robin.cpp
timed_mutex.cpp
scheduler.cpp
: <link>shared:<library>../../context/build//boost_context
+13 -8
View File
@@ -48,17 +48,22 @@ members in the barrier object.]
[class_heading barrier]
#include <boost/fiber/barrier.hpp>
#include <boost/fiber/barrier.hpp>
class barrier {
public:
explicit barrier( std::size_t);
namespace boost {
namespace fibers {
barrier( barrier const&) = delete;
barrier & operator=( barrier const&) = delete;
class barrier {
public:
explicit barrier( std::size_t);
bool wait();
};
barrier( barrier const&) = delete;
barrier & operator=( barrier const&) = delete;
bool wait();
};
}}
Instances of __barrier__ are not copyable or movable.
+75 -65
View File
@@ -73,35 +73,40 @@ channel operations return the state of the channel.
[template_heading unbounded_channel]
#include <boost/fiber/unbounded_channel.hpp>
#include <boost/fiber/unbounded_channel.hpp>
template< typename T, typename __Allocator__ = __allocator__ >
class unbounded_channel {
public:
typedef T value_type;
namespace boost {
namespace fibers {
explicit unbounded_channel( __Allocator__ const& alloc = Allocator() ) noexcept;
template< typename T, typename __Allocator__ = __allocator__ >
class unbounded_channel {
public:
typedef T value_type;
unbounded_channel( unbounded_channel const& other) = delete;
unbounded_channel & operator=( unbounded_channel const& other) = delete;
explicit unbounded_channel( __Allocator__ const& alloc = Allocator() ) noexcept;
void close() noexcept;
unbounded_channel( unbounded_channel const& other) = delete;
unbounded_channel & operator=( unbounded_channel const& other) = delete;
channel_op_status push( value_type const& va);
channel_op_status push( value_type && va);
void close() noexcept;
channel_op_status pop( value_type & va);
value_type value_pop();
channel_op_status try_pop( value_type & va);
template< typename Rep, typename Period >
channel_op_status pop_wait_for(
value_type & va,
std::chrono::duration< Rep, Period > const& timeout_duration);
template< typename Clock, typename Duration >
channel_op_status pop_wait_until(
value_type & va,
std::chrono::time_point< Clock, Duration > const& timeout_time);
};
channel_op_status push( value_type const& va);
channel_op_status push( value_type && va);
channel_op_status pop( value_type & va);
value_type value_pop();
channel_op_status try_pop( value_type & va);
template< typename Rep, typename Period >
channel_op_status pop_wait_for(
value_type & va,
std::chrono::duration< Rep, Period > const& timeout_duration);
template< typename Clock, typename Duration >
channel_op_status pop_wait_until(
value_type & va,
std::chrono::time_point< Clock, Duration > const& timeout_time);
};
}}
[heading Constructor]
@@ -234,55 +239,60 @@ time as (system time + `timeout_duration`).
[template_heading bounded_channel]
#include <boost/fiber/bounded_channel.hpp>
#include <boost/fiber/bounded_channel.hpp>
template< typename T, typename __Allocator__ = __allocator__ >
class bounded_channel {
public:
typedef T value_type;
namespace boost {
namespace fibers {
bounded_channel( std::size_t wm, __Allocator__ const& alloc = Allocator() );
bounded_channel( std::size_t hwm, std::size_t lwm, __Allocator__ const& alloc = Allocator() );
template< typename T, typename __Allocator__ = __allocator__ >
class bounded_channel {
public:
typedef T value_type;
bounded_channel( bounded_channel const& other) = delete;
bounded_channel & operator=( bounded_channel const& other) = delete;
bounded_channel( std::size_t wm, __Allocator__ const& alloc = Allocator() );
bounded_channel( std::size_t hwm, std::size_t lwm, __Allocator__ const& alloc = Allocator() );
std::size_t upper_bound() const noexcept;
std::size_t lower_bound() const noexcept;
bounded_channel( bounded_channel const& other) = delete;
bounded_channel & operator=( bounded_channel const& other) = delete;
void close() noexcept;
std::size_t upper_bound() const noexcept;
std::size_t lower_bound() const noexcept;
channel_op_status push( value_type const& va);
channel_op_status push( value_type && va);
template< typename Rep, typename Period >
channel_op_status push_wait_for(
value_type const& va,
std::chrono::duration< Rep, Period > const& timeout_duration);
channel_op_status push_wait_for( value_type && va,
std::chrono::duration< Rep, Period > const& timeout_duration);
template< typename Clock, typename Duration >
channel_op_status push_wait_until(
value_type const& va,
std::chrono::time_point< Clock, Duration > const& timeout_time);
template< typename Clock, typename Duration >
channel_op_status push_wait_until(
value_type && va,
std::chrono::time_point< Clock, Duration > const& timeout_time);
channel_op_status try_push( value_type const& va);
channel_op_status try_push( value_type && va);
void close() noexcept;
channel_op_status pop( value_type & va);
value_type value_pop();
template< typename Rep, typename Period >
channel_op_status pop_wait_for(
value_type & va,
std::chrono::duration< Rep, Period > const& timeout_duration);
template< typename Clock, typename Duration >
channel_op_status pop_wait_until(
value_type & va,
std::chrono::time_point< Clock, Duration > const& timeout_time);
channel_op_status try_pop( value_type & va);
};
channel_op_status push( value_type const& va);
channel_op_status push( value_type && va);
template< typename Rep, typename Period >
channel_op_status push_wait_for(
value_type const& va,
std::chrono::duration< Rep, Period > const& timeout_duration);
channel_op_status push_wait_for( value_type && va,
std::chrono::duration< Rep, Period > const& timeout_duration);
template< typename Clock, typename Duration >
channel_op_status push_wait_until(
value_type const& va,
std::chrono::time_point< Clock, Duration > const& timeout_time);
template< typename Clock, typename Duration >
channel_op_status push_wait_until(
value_type && va,
std::chrono::time_point< Clock, Duration > const& timeout_time);
channel_op_status try_push( value_type const& va);
channel_op_status try_push( value_type && va);
channel_op_status pop( value_type & va);
value_type value_pop();
template< typename Rep, typename Period >
channel_op_status pop_wait_for(
value_type & va,
std::chrono::duration< Rep, Period > const& timeout_duration);
template< typename Clock, typename Duration >
channel_op_status pop_wait_until(
value_type & va,
std::chrono::time_point< Clock, Duration > const& timeout_time);
channel_op_status try_pop( value_type & va);
};
}}
[heading Constructor]
+32 -27
View File
@@ -156,42 +156,47 @@ A timed wait operation might return because of timeout or not.
[template condition_variable_x[classname locktype template_rtype template_arg]
[class_heading [classname]]
#include <boost/fiber/condition_variable.hpp>
#include <boost/fiber/condition_variable.hpp>
class ``[classname]`` {
public:
``[classname]``();
~``[classname]``();
namespace boost {
namespace fibers {
``[classname]``( ``[classname]`` const&) = delete;
``[classname]`` & operator=( ``[classname]`` const&) = delete;
class ``[classname]`` {
public:
``[classname]``();
~``[classname]``();
void notify_one() noexcept;
void notify_all() noexcept;
``[classname]``( ``[classname]`` const&) = delete;
``[classname]`` & operator=( ``[classname]`` const&) = delete;
``[template_rtype]`` wait( ``[locktype]`` &);
void notify_one() noexcept;
void notify_all() noexcept;
template< ``[template_arg]`` Pred >
void wait( ``[locktype]`` &, Pred);
``[template_rtype]`` wait( ``[locktype]`` &);
template< ``[template_arg]`` Clock, typename Duration >
cv_status wait_until( ``[locktype]`` &,
std::chrono::time_point< Clock, Duration > const&);
template< ``[template_arg]`` Pred >
void wait( ``[locktype]`` &, Pred);
template< ``[template_arg]`` Clock, typename Duration, typename Pred >
bool wait_until( ``[locktype]`` &,
std::chrono::time_point< Clock, Duration > const&,
Pred);
template< ``[template_arg]`` Clock, typename Duration >
cv_status wait_until( ``[locktype]`` &,
std::chrono::time_point< Clock, Duration > const&);
template< ``[template_arg]`` Rep, typename Period >
cv_status wait_for( ``[locktype]`` &,
std::chrono::duration< Rep, Period > const&);
template< ``[template_arg]`` Clock, typename Duration, typename Pred >
bool wait_until( ``[locktype]`` &,
std::chrono::time_point< Clock, Duration > const&,
Pred);
template< ``[template_arg]`` Rep, typename Period, typename Pred >
bool wait_for( ``[locktype]`` &,
std::chrono::duration< Rep, Period > const&,
Pred);
};
template< ``[template_arg]`` Rep, typename Period >
cv_status wait_for( ``[locktype]`` &,
std::chrono::duration< Rep, Period > const&);
template< ``[template_arg]`` Rep, typename Period, typename Pred >
bool wait_for( ``[locktype]`` &,
std::chrono::duration< Rep, Period > const&,
Pred);
};
}}
[heading Constructor]
+3 -3
View File
@@ -72,13 +72,13 @@ fiber_properties].
[heading Custom Scheduler Class]
Now we can derive a custom scheduler from [template_link
sched_algorithm_with_properties], specifying our custom property class
algorithm_with_properties], specifying our custom property class
`priority_props` as the template parameter.
[priority_scheduler]
Our example `priority_scheduler` doesn't override [member_link
sched_algorithm_with_properties..new_properties]: we're content with
algorithm_with_properties..new_properties]: we're content with
allocating `priority_props` instances on the heap.
[heading Replace Default Scheduler]
@@ -114,6 +114,6 @@ until it blocks (or yields, or terminates) for some other reason. As shown in
the `launch()` function above, it is reasonable to launch a fiber and
immediately set relevant properties -- such as, for instance, its priority.
Your custom scheduler can then make use of this information next time the
fiber manager calls [member_link sched_algorithm_with_properties..pick_next].
fiber manager calls [member_link algorithm_with_properties..pick_next].
[endsect]
+49 -10
View File
@@ -20,14 +20,18 @@
bool operator<( fiber const& l, fiber const& r) noexcept;
void swap( fiber & l, fiber & r) noexcept;
struct sched_algorithm;
template< typename PROPS >
struct sched_algorithm_with_properties;
class round_robin;
template< typename SchedAlgo, typename ... Args >
void use_scheduling_algorithm( Args && ... args);
bool has_ready_fibers();
namespace algo {
struct algorithm;
template< typename PROPS >
struct algorithm_with_properties;
class round_robin;
class shared_round_robin;
}
namespace this_fiber {
@@ -195,6 +199,9 @@ be entered when the fiber scheduler has a chance to resume it later.]]
#include <boost/fiber/fiber.hpp>
namespace boost {
namespace fibers {
class fiber {
public:
class id;
@@ -246,6 +253,8 @@ be entered when the fiber scheduler has a chance to resume it later.]]
bool has_ready_fibers() noexcept;
}}
[heading Default constructor]
@@ -385,13 +394,13 @@ default-constructed __fiber_id__.]]
[variablelist
[[Preconditions:] [`*this` refers to a fiber of execution. [function_link
use_scheduling_algorithm] has been called from this thread with a subclass of
[template_link sched_algorithm_with_properties] with the same template
[template_link algorithm_with_properties] with the same template
argument `PROPS`.]]
[[Returns:] [a reference to the scheduler properties instance for `*this`.]]
[[Throws:] [`std::bad_cast` if `use_scheduling_algorithm()` was called with a
`sched_algorithm_with_properties` subclass with some other template parameter
`algorithm_with_properties` subclass with some other template parameter
than `PROPS`.]]
[[Note:] [[template_link sched_algorithm_with_properties] provides a way for a
[[Note:] [[template_link algorithm_with_properties] provides a way for a
user-coded scheduler to associate extended properties, such as priority, with
a fiber instance. This method allows access to those user-provided properties.]]
[[See also:] [[link custom Customization]]]
@@ -466,6 +475,9 @@ create a default [class_link round_robin] instance for this thread.]]
#include <boost/fiber/fiber.hpp>
namespace boost {
namespace fibers {
class id {
public:
constexpr id() noexcept;
@@ -487,6 +499,8 @@ create a default [class_link round_robin] instance for this thread.]]
operator<<( std::basic_ostream< charT, traitsT > &, id const&);
};
}}
[heading Constructor]
constexpr id() noexcept;
@@ -596,8 +610,13 @@ explicitly-launched fiber.
#include <boost/fiber/operations.hpp>
namespace boost {
namespace fibers {
fiber::id get_id() noexcept;
}}
[variablelist
[[Returns:] [An instance of __fiber_id__ that represents the currently
executing fiber.]]
@@ -608,9 +627,14 @@ executing fiber.]]
#include <boost/fiber/operations.hpp>
namespace boost {
namespace fibers {
template< typename Clock, typename Duration >
void sleep_until( std::chrono::time_point< Clock, Duration > const& abs_time);
}}
[variablelist
[[Effects:] [Suspends the current fiber until the time point specified by
`abs_time` has been reached.]]
@@ -628,9 +652,14 @@ exceptions are referred to as ['timeout-related exceptions.]]]]
#include <boost/fiber/operations.hpp>
namespace boost {
namespace fibers {
template< class Rep, class Period >
void sleep_for( std::chrono::duration< Rep, Period > const& rel_time);
}}
[variablelist
[[Effects:] [Suspends the current fiber until the time duration specified by
`rel_time` has elapsed.]]
@@ -643,8 +672,13 @@ there are no guarantees about how soon after that it might resume.]]
#include <boost/fiber/operations.hpp>
namespace boost {
namespace fibers {
void yield() noexcept;
}}
[variablelist
[[Effects:] [Reliquishes execution control, allowing other fibers to run.]]
[[Throws:] [Nothing.]]
@@ -657,19 +691,24 @@ to run.]]
#include <boost/fiber/operations.hpp>
namespace boost {
namespace fibers {
template< typename PROPS >
PROPS & properties();
}}
[variablelist
[[Preconditions:] [[function_link use_scheduling_algorithm] has been called
from this thread with a subclass of [template_link
sched_algorithm_with_properties] with the same template argument `PROPS`.]]
algorithm_with_properties] with the same template argument `PROPS`.]]
[[Returns:] [a reference to the scheduler properties instance for the
currently running fiber.]]
[[Throws:] [`std::bad_cast` if `use_scheduling_algorithm()` was called with a
`sched_algorithm_with_properties` subclass with some other template parameter
`algorithm_with_properties` subclass with some other template parameter
than `PROPS`.]]
[[Note:] [[template_link sched_algorithm_with_properties] provides a way for a
[[Note:] [[template_link algorithm_with_properties] provides a way for a
user-coded scheduler to associate extended properties, such as priority, with
a fiber instance. This function allows access to those user-provided
properties.]]
+3 -3
View File
@@ -144,9 +144,9 @@
[def __already_retrieved__ `future_errc::future_already_retrieved`]
[def __already_satisfied__ `future_errc::future_already_satisfied`]
[def __algo__ [class_link sched_algorithm]]
[def __algo_awakened__ [member_link sched_algorithm..awakened]]
[def __algo_pick_next__ [member_link sched_algorithm..pick_next]]
[def __algo__ [class_link algorithm]]
[def __algo_awakened__ [member_link algorithm..awakened]]
[def __algo_pick_next__ [member_link algorithm..pick_next]]
[def __async__ `async()`]
[def __barrier_wait__ [member_link barrier..wait]]
[def __cond_wait_for__ [member_link condition_variable..wait_for]]
+5
View File
@@ -26,6 +26,9 @@ order.
#include <boost/fiber/fss.hpp>
namespace boost {
namespace fibers {
template< typename T >
class fiber_specific_ptr {
public:
@@ -51,6 +54,8 @@ order.
void reset( T *);
};
}}
[heading Constructor]
fiber_specific_ptr();
+19
View File
@@ -55,6 +55,11 @@ Timed wait-operations (__wait_for__ and __wait_until__) return the state of the
A __future__ contains a [link shared_state shared state] which is not shared with any other future.
#include <boost/fiber/future/future.hpp>
namespace boost {
namespace fibers {
template< typename R >
class future {
public:
@@ -91,6 +96,8 @@ A __future__ contains a [link shared_state shared state] which is not shared wit
std::chrono::time_point< Clock, Duration > const& timeout_time) const;
};
}}
[heading Default constructor]
future() noexcept;
@@ -257,6 +264,11 @@ timeout-related exceptions.]]
A __shared_future__ contains a [link shared_state shared state] which might be
shared with other __shared_future__ instances.
#include <boost/fiber/future/future.hpp>
namespace boost {
namespace fibers {
template< typename R >
class shared_future {
public:
@@ -295,6 +307,8 @@ shared with other __shared_future__ instances.
std::chrono::time_point< Clock, Duration > const& timeout_time) const;
};
}}
[heading Default constructor]
shared_future();
@@ -352,6 +366,9 @@ const&`, otherwise `false`.]]
#include <boost/fiber/future/async.hpp>
namespace boost {
namespace fibers {
template< class Function, class ... Args >
future<
std::result_of_t<
@@ -377,6 +394,8 @@ const&`, otherwise `false`.]]
async( ``[link class_launch `launch`]`` policy, __allocator_arg_t__, __StackAllocator__ salloc,
Function && fn, Args && ... args);
}}
[variablelist
[[Effects:] [Executes `fn` in a [class_link fiber] and returns an associated
[template_link future].]]
+3 -3
View File
@@ -69,7 +69,7 @@ wake up again on its next expiration.
Since, in this thought experiment, we always pass control to the fiber manager
via `yield()`, the calling fiber is never blocked. Therefore there is always
at least one ready fiber. Therefore the fiber manager never calls [member_link
sched_algorithm..suspend_until].
algorithm..suspend_until].
Using
[@http://www.boost.org/doc/libs/release/doc/html/boost_asio/reference/io_service/post.html
@@ -247,7 +247,7 @@ Doubtless you have been asking yourself: why are we calling
`suspend_until()`, whose very API was designed for just such a purpose?
Under normal circumstances, when the fiber manager finds no ready fibers, it
calls [member_link sched_algorithm..suspend_until]. Why test
calls [member_link algorithm..suspend_until]. Why test
`has_ready_fibers()` in the lambda loop? Why not leverage the normal
mechanism?
@@ -257,7 +257,7 @@ Consider the lambda loop shown above. The only __boost_fiber__ APIs it engages
are `has_ready_fibers()` and [ns_function_link this_fiber..yield]. `yield()`
does not ['block] the calling fiber: the calling fiber does not become
unready. It is immediately passed back to [member_link
sched_algorithm..awakened], to be resumed in its turn when all other ready
algorithm..awakened], to be resumed in its turn when all other ready
fibers have had a chance to run. In other words: during a `yield()` call,
['there is always at least one ready fiber.]
+2 -2
View File
@@ -43,12 +43,12 @@ which the fiber migrates.
Thus, fiber migration is accomplished by sharing state between instances of a
user-coded __algo__ implementation running on different threads. The fiber[s]
original thread calls [member_link sched_algorithm..awakened], passing the
original thread calls [member_link algorithm..awakened], passing the
fiber[s] [class_link context][^*]. The `awakened()` implementation calls
__context_detach__.
At some later point, when the same or a different thread calls [member_link
sched_algorithm..pick_next], the `pick_next()` implementation selects a ready
algorithm..pick_next], the `pick_next()` implementation selects a ready
fiber and calls __context_attach__ on it before returning it.
As stated above, a `context` for which `is_context(pinned_context) == true`
+20
View File
@@ -11,6 +11,9 @@
[class_heading mutex]
#include <boost/fiber/mutex.hpp>
namespace boost {
namespace fibers {
class mutex {
public:
@@ -25,6 +28,8 @@
void unlock();
};
}}
__mutex__ provides an exclusive-ownership mutex. At most one fiber can own the
lock on a given instance of __mutex__ at any time. Multiple concurrent calls to
__lock__, __try_lock__ and __unlock__ shall be permitted.
@@ -76,6 +81,9 @@ otherwise.]]
#include <boost/fiber/timed_mutex.hpp>
namespace boost {
namespace fibers {
class timed_mutex {
public:
timed_mutex();
@@ -94,6 +102,8 @@ otherwise.]]
bool try_lock_for( std::chrono::duration< Rep, Period > const& timeout_duration);
};
}}
__timed_mutex__ provides an exclusive-ownership mutex. At most one fiber can own
the lock on a given instance of __timed_mutex__ at any time. Multiple concurrent
calls to __lock__, __try_lock__, __try_lock_until__, __try_lock_for__ and
@@ -177,6 +187,9 @@ otherwise.]]
#include <boost/fiber/recursive_mutex.hpp>
namespace boost {
namespace fibers {
class recursive_mutex {
public:
recursive_mutex();
@@ -190,6 +203,8 @@ otherwise.]]
void unlock();
};
}}
__recursive_mutex__ provides an exclusive-ownership recursive mutex. At most one
fiber can own the lock on a given instance of __recursive_mutex__ at any time.
Multiple concurrent calls to __lock__, __try_lock__ and __unlock__ shall be
@@ -236,6 +251,9 @@ otherwise.]]
#include <boost/fiber/recursive_timed_mutex.hpp>
namespace boost {
namespace fibers {
class recursive_timed_mutex {
public:
recursive_timed_mutex();
@@ -254,6 +272,8 @@ otherwise.]]
bool try_lock_for( std::chrono::duration< Rep, Period > const& timeout_duration);
};
}}
__recursive_timed_mutex__ provides an exclusive-ownership recursive mutex. At
most one fiber can own the lock on a given instance of
__recursive_timed_mutex__ at any time. Multiple concurrent calls to __lock__,
+7
View File
@@ -26,6 +26,11 @@ Conventional usage of `packaged_task<>` is like this:
This is, in fact, pretty much what [ns_function_link fibers..async]
encapsulates.
#include <boost/fiber/future/packaged_task.hpp>
namespace boost {
namespace fibers {
template< class R, typename ... Args >
class packaged_task< R( Args ... ) > {
public:
@@ -61,6 +66,8 @@ encapsulates.
template< typename Signature >
void swap( packaged_task< Signature > &, packaged_task< Signature > &) noexcept;
}}
[heading Default constructor `packaged_task()`]
packaged_task() noexcept;
+7
View File
@@ -12,6 +12,11 @@ A __promise__ provides a mechanism to store a value (or exception) that can
later be retrieved from the corresponding __future__ object. `promise<>` and
`future<>` communicate via their underlying [link shared_state shared state].
#include <boost/fiber/future/promise.hpp>
namespace boost {
namespace fibers {
template< typename R >
class promise {
public:
@@ -45,6 +50,8 @@ later be retrieved from the corresponding __future__ object. `promise<>` and
template< typename R >
void swap( promise< R > &, promise< R > &) noexcept;
}
[heading Default constructor]
promise();
+90 -62
View File
@@ -22,12 +22,12 @@ different schedulers. By default, __boost_fiber__ implicitly instantiates
[class_link round_robin] as the scheduler for each thread.
You are explicitly permitted to code your own __algo__ subclass. For the most
part, your `sched_algorithm` subclass need not defend against cross-thread
part, your `algorithm` subclass need not defend against cross-thread
calls: the fiber manager intercepts and defers such calls. Most
`sched_algorithm` methods are only ever directly called from the thread whose
`algorithm` methods are only ever directly called from the thread whose
fibers it is managing [mdash] with exceptions as documented below.
Your `sched_algorithm` subclass is engaged on a particular thread by calling
Your `algorithm` subclass is engaged on a particular thread by calling
[function_link use_scheduling_algorithm]:
void thread_fn() {
@@ -39,15 +39,19 @@ A scheduler class must implement interface __algo__. __boost_fiber__ provides
one scheduler: [class_link round_robin].
[class_heading sched_algorithm]
[class_heading algorithm]
`sched_algorithm` is the abstract base class defining the interface that a
`algorithm` is the abstract base class defining the interface that a
fiber scheduler must implement.
#include <boost/fiber/algorithm.hpp>
#include <boost/fiber/algo/algorithm.hpp>
struct sched_algorithm {
virtual ~sched_algorithm();
namespace boost {
namespace fibers {
namespace algo {
struct algorithm {
virtual ~algorithm();
virtual void awakened( context *) noexcept = 0;
@@ -60,7 +64,9 @@ fiber scheduler must implement.
virtual void notify() noexcept = 0;
};
[member_heading sched_algorithm..awakened]
}}}
[member_heading algorithm..awakened]
virtual void awakened( context * f) noexcept = 0;
@@ -74,7 +80,7 @@ queue.]]
[[See also:] [[class_link round_robin]]]
]
[member_heading sched_algorithm..pick_next]
[member_heading algorithm..pick_next]
virtual context * pick_next() noexcept = 0;
@@ -87,7 +93,7 @@ queue.]]
[[See also:] [[class_link round_robin]]]
]
[member_heading sched_algorithm..has_ready_fibers]
[member_heading algorithm..has_ready_fibers]
virtual bool has_ready_fibers() const noexcept = 0;
@@ -95,7 +101,7 @@ queue.]]
[[Returns:] [`true` if scheduler has fibers ready to run.]]
]
[member_heading sched_algorithm..suspend_until]
[member_heading algorithm..suspend_until]
virtual void suspend_until( std::chrono::steady_clock::time_point const& abs_time) noexcept = 0;
@@ -105,7 +111,7 @@ time-point `abs_time`.]]
[[Note:] [This method allows a custom scheduler to yield control to the
containing environment in whatever way makes sense. The fiber manager is
stating that `suspend_until()` need not return until `abs_time` [mdash] or
[member_link sched_algorithm..notify] is called [mdash] whichever comes first.
[member_link algorithm..notify] is called [mdash] whichever comes first.
The interaction with `notify()` means that, for instance, calling
[@http://en.cppreference.com/w/cpp/thread/sleep_until
`std::this_thread::sleep_until(abs_time)`] would be too simplistic.
@@ -115,29 +121,33 @@ The interaction with `notify()` means that, for instance, calling
round_robin..notify].]]
[[Note:] [Given that `notify()` might be called from another thread, your
`suspend_until()` implementation [mdash] like the rest of your
`sched_algorithm` implementation [mdash] must guard any data it shares with
`algorithm` implementation [mdash] must guard any data it shares with
your `notify()` implementation.]]
]
[member_heading sched_algorithm..notify]
[member_heading algorithm..notify]
virtual void notify() noexcept = 0;
[variablelist
[[Effects:] [Requests the scheduler to return from a pending call to
[member_link sched_algorithm..suspend_until].]]
[[Note:] [Alone among the `sched_algorithm` methods, `notify()` may be called
[member_link algorithm..suspend_until].]]
[[Note:] [Alone among the `algorithm` methods, `notify()` may be called
from another thread. Your `notify()` implementation must guard any data it
shares with the rest of your `sched_algorithm` implementation.]]
shares with the rest of your `algorithm` implementation.]]
]
[class_heading round_robin]
This class implements __algo__, scheduling fibers in round-robin fashion.
#include <boost/fiber/round_robin.hpp>
#include <boost/fiber/algo/round_robin.hpp>
class round_robin : public sched_algorithm {
namespace boost {
namespace fibers {
namespace algo {
class round_robin : public algorithm {
virtual void awakened( context *) noexcept;
virtual context * pick_next() noexcept;
@@ -149,6 +159,8 @@ This class implements __algo__, scheduling fibers in round-robin fashion.
virtual void notify() noexcept;
};
}}}
[member_heading round_robin..awakened]
virtual void awakened( context * f) noexcept;
@@ -209,7 +221,7 @@ wakes `suspend_until()` via
[heading Custom Scheduler Fiber Properties]
A scheduler class directly derived from __algo__ can use any information
available from [class_link context] to implement the `sched_algorithm`
available from [class_link context] to implement the `algorithm`
interface. But a custom scheduler might need to track additional properties
for a fiber. For instance, a priority-based scheduler would need to track a
fiber's priority.
@@ -223,6 +235,9 @@ A custom fiber properties class must be derived from `fiber_properties`.
#include <boost/fiber/properties.hpp>
namespace boost {
namespace fibers {
class fiber_properties {
public:
fiber_properties( context *) noexcept;
@@ -233,6 +248,8 @@ A custom fiber properties class must be derived from `fiber_properties`.
void notify() noexcept;
};
}}
[heading Constructor]
fiber_properties( context * f) noexcept;
@@ -250,13 +267,13 @@ to the base-class `fiber_properties` constructor.]]
[variablelist
[[Effects:] [Pass control to the custom [template_link
sched_algorithm_with_properties] subclass's [member_link
sched_algorithm_with_properties..property_change] method.]]
algorithm_with_properties] subclass's [member_link
algorithm_with_properties..property_change] method.]]
[[Throws:] [Nothing.]]
[[Note:] [A custom scheduler's [member_link
sched_algorithm_with_properties..pick_next] method might dynamically select
algorithm_with_properties..pick_next] method might dynamically select
from the ready fibers, or [member_link
sched_algorithm_with_properties..awakened] might instead insert each ready
algorithm_with_properties..awakened] might instead insert each ready
fiber into some form of ready queue for `pick_next()`. In the latter case, if
application code modifies a fiber property (e.g. priority) that should affect
that fiber's relationship to other ready fibers, the custom scheduler must be
@@ -270,16 +287,20 @@ behavior of the `pick_next()` method, you need not call `notify()` when that
property is modified.]]
]
[template_heading sched_algorithm_with_properties]
[template_heading algorithm_with_properties]
A custom scheduler that depends on a custom properties class `PROPS` should be
derived from `sched_algorithm_with_properties<PROPS>`. `PROPS` should be
derived from `algorithm_with_properties<PROPS>`. `PROPS` should be
derived from [class_link fiber_properties].
#include <boost/fiber/algorithm.hpp>
namespace boost {
namespace fibers {
namespace algo {
template< typename PROPS >
struct sched_algorithm_with_properties {
struct algorithm_with_properties {
virtual void awakened( context *, PROPS &) noexcept = 0;
virtual context * pick_next() noexcept;
@@ -297,20 +318,22 @@ derived from [class_link fiber_properties].
virtual fiber_properties * new_properties( context *);
};
[member_heading sched_algorithm_with_properties..awakened]
}}}
[member_heading algorithm_with_properties..awakened]
virtual void awakened( context * f, PROPS & properties) noexcept;
[variablelist
[[Effects:] [Informs the scheduler that fiber `f` is ready to run, like
[member_link sched_algorithm..awakened]. Passes the fiber's associated `PROPS`
[member_link algorithm..awakened]. Passes the fiber's associated `PROPS`
instance.]]
[[Throws:] [Nothing.]]
[[Note:] [A `sched_algorithm_with_properties<>` subclass must override this
method instead of `sched_algorithm::awakened()`.]]
[[Note:] [A `algorithm_with_properties<>` subclass must override this
method instead of `algorithm::awakened()`.]]
]
[member_heading sched_algorithm_with_properties..pick_next]
[member_heading algorithm_with_properties..pick_next]
virtual context * pick_next() noexcept;
@@ -318,40 +341,40 @@ method instead of `sched_algorithm::awakened()`.]]
[[Returns:] [the fiber which is to be resumed next, or `nullptr` if there is no
ready fiber.]]
[[Throws:] [Nothing.]]
[[Note:] [same as [member_link sched_algorithm..pick_next]]]
[[Note:] [same as [member_link algorithm..pick_next]]]
]
[member_heading sched_algorithm_with_properties..has_ready_fibers]
[member_heading algorithm_with_properties..has_ready_fibers]
virtual bool has_ready_fibers() const noexcept;
[variablelist
[[Returns:] [`true` if scheduler has fibers ready to run.]]
[[Throws:] [Nothing.]]
[[Note:] [same as [member_link sched_algorithm..has_ready_fibers]]]
[[Note:] [same as [member_link algorithm..has_ready_fibers]]]
]
[member_heading sched_algorithm_with_properties..suspend_until]
[member_heading algorithm_with_properties..suspend_until]
virtual void suspend_until( std::chrono::steady_clock::time_point const& abs_time) noexcept = 0;
[variablelist
[[Effects:] [Informs the scheduler that no fiber will be ready until
time-point `abs_time`.]]
[[Note:] [same as [member_link sched_algorithm..suspend_until]]]
[[Note:] [same as [member_link algorithm..suspend_until]]]
]
[member_heading sched_algorithm_with_properties..notify]
[member_heading algorithm_with_properties..notify]
virtual void notify() noexcept = 0;
[variablelist
[[Effects:] [Requests the scheduler to return from a pending call to
[member_link sched_algorithm_with_properties..suspend_until].]]
[[Note:] [same as [member_link sched_algorithm..notify]]]
[member_link algorithm_with_properties..suspend_until].]]
[[Note:] [same as [member_link algorithm..notify]]]
]
[member_heading sched_algorithm_with_properties..properties]
[member_heading algorithm_with_properties..properties]
PROPS& properties( context * f) noexcept;
@@ -359,15 +382,15 @@ time-point `abs_time`.]]
[[Returns:] [the `PROPS` instance associated with fiber `f`.]]
[[Throws:] [Nothing.]]
[[Note:] [The fiber's associated `PROPS` instance is already passed to
[member_link sched_algorithm_with_properties..awakened] and [member_link
sched_algorithm_with_properties..property_change]. However, every [class_link
sched_algorithm] subclass is expected to track a collection of ready
[member_link algorithm_with_properties..awakened] and [member_link
algorithm_with_properties..property_change]. However, every [class_link
algorithm] subclass is expected to track a collection of ready
[class_link context] instances. This method allows your custom scheduler
to retrieve the [class_link fiber_properties] subclass instance for any
`context` in its collection.]]
]
[member_heading sched_algorithm_with_properties..property_change]
[member_heading algorithm_with_properties..property_change]
virtual void property_change( context * f, PROPS & properties) noexcept;
@@ -381,14 +404,14 @@ fiber_properties] subclass explicitly calls [member_link
fiber_properties..notify].]]
]
[member_heading sched_algorithm_with_properties..new_properties]
[member_heading algorithm_with_properties..new_properties]
virtual fiber_properties * new_properties( context * f);
[variablelist
[[Returns:] [A new instance of [class_link fiber_properties] subclass
`PROPS`.]]
[[Note:] [By default, `sched_algorithm_with_properties<>::new_properties()`
[[Note:] [By default, `algorithm_with_properties<>::new_properties()`
simply returns `new PROPS(f)`, placing the `PROPS` instance on the heap.
Override this method to allocate `PROPS` some other way. The returned
`fiber_properties` pointer must point to the `PROPS` instance to be associated
@@ -406,16 +429,19 @@ Of particular note is the fact that `context` contains a hook to participate
in a [@http://www.boost.org/doc/libs/release/doc/html/intrusive/list.html
`boost::intrusive::list`] typedef'ed as
`boost::fibers::scheduler::ready_queue_t`. This hook is reserved for use by
[class_link sched_algorithm] implementations. (For instance, [class_link
[class_link algorithm] implementations. (For instance, [class_link
round_robin] contains a `ready_queue_t` instance to manage its ready fibers.)
See [member_link context..ready_is_linked], [member_link context..ready_link],
[member_link context..ready_unlink].
Your `sched_algorithm` implementation may use any container you desire to
Your `algorithm` implementation may use any container you desire to
manage passed `context` instances. `ready_queue_t` avoids some of the overhead
of typical STL containers.
#include <boost/fiber/context.hpp>
namespace boost {
namespace fibers {
enum class type {
none = ``['unspecified]``,
@@ -464,6 +490,8 @@ of typical STL containers.
bool operator<( context const& l, context const& r) noexcept;
}}
[static_member_heading context..active]
static context * active() noexcept;
@@ -498,10 +526,10 @@ default-constructed __fiber_id__.]]
[[Note:] [`f` must not be the running fiber[s] context. It must not be
__blocked__ or terminated. It must not be a `pinned_context`. It must be
currently detached. It must not currently be linked into a [class_link
sched_algorithm] implementation[s] ready queue. Most of these conditions are
implied by `f` being owned by a `sched_algorithm` implementation: that is, it
has been passed to [member_link sched_algorithm..awakened] but has not yet
been returned by [member_link sched_algorithm..pick_next]. Typically a
algorithm] implementation[s] ready queue. Most of these conditions are
implied by `f` being owned by a `algorithm` implementation: that is, it
has been passed to [member_link algorithm..awakened] but has not yet
been returned by [member_link algorithm..pick_next]. Typically a
`pick_next()` implementation would call `attach()` with the `context*` it is
about to return. It must first remove `f` from its ready queue. You should
never pass a `pinned_context` to `attach()` because you should never have
@@ -521,13 +549,13 @@ called its `detach()` method in the first place.]]
currently associated. `*this` must not be the running fiber[s] context. It
must not be __blocked__ or terminated. It must not be a `pinned_context`. It
must not be detached already. It must not already be linked into a [class_link
sched_algorithm] implementation[s] ready queue. Most of these conditions are
implied by `*this` being passed to [member_link sched_algorithm..awakened]; an
algorithm] implementation[s] ready queue. Most of these conditions are
implied by `*this` being passed to [member_link algorithm..awakened]; an
`awakened()` implementation must, however, test for `pinned_context`. It must
call `detach()` ['before] linking `*this` into its ready queue.]]
[[Note:] [In particular, it is erroneous to attempt to migrate a fiber from
one thread to another by calling both `detach()` and `attach()` in the
[member_link sched_algorithm..pick_next] method. `pick_next()` is called on
[member_link algorithm..pick_next] method. `pick_next()` is called on
the intended destination thread. `detach()` must be called on the fiber[s]
original thread. You must call `detach()` in the corresponding `awakened()`
method.]]
@@ -571,7 +599,7 @@ no longer considered a valid context.]]
bool ready_is_linked() const noexcept;
[variablelist
[[Returns:] [`true` if `*this` is stored in a [class_link sched_algorithm]
[[Returns:] [`true` if `*this` is stored in a [class_link algorithm]
implementation's ready-queue.]]
[[Throws:] [Nothing]]
[[Note:] [Specifically, this method indicates whether [member_link
@@ -589,8 +617,8 @@ remote-ready-queue.]]
[[Throws:] [Nothing]]
[[Note:] [A `context` signaled as ready by another thread is first stored in
the fiber manager's remote-ready-queue. This is the mechanism by which the
fiber manager protects a [class_link sched_algorithm] implementation from
cross-thread [member_link sched_algorithm..awakened] calls.]]
fiber manager protects a [class_link algorithm] implementation from
cross-thread [member_link algorithm..awakened] calls.]]
]
[member_heading context..wait_is_linked]
@@ -699,8 +727,8 @@ with a pointer to `this` at some future time.]]
[[Effects:] [Mark the fiber associated with context `*ctx` as being ready to
run. This does not immediately resume that fiber; rather it passes the fiber
to the scheduler for subsequent resumption. If the scheduler is idle (has not
returned from a call to [member_link sched_algorithm..suspend_until]),
[member_link sched_algorithm..notify] is called to wake it up.]]
returned from a call to [member_link algorithm..suspend_until]),
[member_link algorithm..notify] is called to wake it up.]]
[[Throws:] [Nothing]]
[[Note:] [This is a low-level API potentially useful for integration with
other frameworks. It is not intended to be directly invoked by a typical
+22
View File
@@ -72,6 +72,9 @@ virtual addresses are used.]
#include <boost/fiber/protected_fixedsize.hpp>
namespace boost {
namespace fibers {
struct protected_fixedsize {
protected_fixesize(std::size_t size = traits_type::default_size());
@@ -80,6 +83,8 @@ virtual addresses are used.]
void deallocate( stack_context &);
}
}}
[member_heading protected_fixedsize..allocate]
stack_context allocate();
@@ -114,6 +119,9 @@ end of each stack. The memory is managed internally by
#include <boost/fiber/pooled_fixedsize_stack.hpp>
namespace boost {
namespace fibers {
struct pooled_fixedsize_stack {
pooled_fixedsize_stack(std::size_t stack_size = traits_type::default_size(), std::size_t next_size = 32, std::size_t max_size = 0);
@@ -122,6 +130,8 @@ end of each stack. The memory is managed internally by
void deallocate( stack_context &);
}
}}
[hding pooled_fixedsize..Constructor]
pooled_fixedsize_stack(std::size_t stack_size, std::size_t next_size, std::size_t max_size);
@@ -160,6 +170,8 @@ address of the stack.]]
[[Effects:] [Deallocates the stack space.]]
]
[note This stack allocator is not thread safe.]
[class_heading fixedsize_stack]
@@ -171,6 +183,9 @@ end of each stack. The memory is simply managed by `std::malloc()` and
#include <boost/context/fixedsize_stack.hpp>
namespace boost {
namespace fibers {
struct fixedsize_stack {
fixedsize_stack(std::size_t size = traits_type::default_size());
@@ -179,6 +194,8 @@ end of each stack. The memory is simply managed by `std::malloc()` and
void deallocate( stack_context &);
}
}}
[member_heading fixedsize..allocate]
stack_context allocate();
@@ -220,6 +237,9 @@ command line.]
#include <boost/fiber/segmented_stack.hpp>
namespace boost {
namespace fibers {
struct segmented_stack {
segmented_stack(std::size_t stack_size = traits_type::default_size());
@@ -228,6 +248,8 @@ command line.]
void deallocate( stack_context &);
}
}}
[member_heading segmented..allocate]
stack_context allocate();
+1
View File
@@ -251,6 +251,7 @@ int main( int argc, char* argv[]) {
//]
print( tag(), ": io_service returned");
print( "Thread ", thread_names.lookup(), ": stopping");
std::cout << "done." << std::endl;
return EXIT_SUCCESS;
} catch ( std::exception const& e) {
print("Exception: ", e.what(), "\n");
+1 -1
View File
@@ -32,7 +32,7 @@ namespace boost {
namespace fibers {
namespace asio {
class round_robin : public sched_algorithm {
class round_robin : public algo::algorithm {
private:
typedef scheduler::ready_queue_t rqueue_t;
+6 -6
View File
@@ -73,7 +73,7 @@ private:
//[priority_scheduler
class priority_scheduler :
public boost::fibers::sched_algorithm_with_properties< priority_props > {
public boost::fibers::algo::algorithm_with_properties< priority_props > {
private:
typedef boost::fibers::scheduler::ready_queue_t/*< See [link ready_queue_t]. >*/ rqueue_t;
@@ -87,9 +87,9 @@ public:
rqueue_() {
}
// For a subclass of sched_algorithm_with_properties<>, it's important to
// For a subclass of algorithm_with_properties<>, it's important to
// override the correct awakened() overload.
/*<< You must override the [member_link sched_algorithm_with_properties..awakened]
/*<< You must override the [member_link algorithm_with_properties..awakened]
method. This is how your scheduler receives notification of a
fiber that has become ready to run. >>*/
virtual void awakened( boost::fibers::context * ctx, priority_props & props) noexcept {
@@ -115,7 +115,7 @@ public:
//->
}
/*<< You must override the [member_link sched_algorithm_with_properties..pick_next]
/*<< You must override the [member_link algorithm_with_properties..pick_next]
method. This is how your scheduler actually advises the fiber manager
of the next fiber to run. >>*/
virtual boost::fibers::context * pick_next() noexcept {
@@ -132,13 +132,13 @@ public:
return ctx;
}
/*<< You must override [member_link sched_algorithm_with_properties..has_ready_fibers]
/*<< You must override [member_link algorithm_with_properties..has_ready_fibers]
to inform the fiber manager of the state of your ready queue. >>*/
virtual bool has_ready_fibers() const noexcept {
return ! rqueue_.empty();
}
/*<< Overriding [member_link sched_algorithm_with_properties..property_change]
/*<< Overriding [member_link algorithm_with_properties..property_change]
is optional. This override handles the case in which the running
fiber changes the priority of another ready fiber: a fiber already in
our queue. In that case, move the updated fiber within the queue. >>*/
+5 -81
View File
@@ -6,10 +6,10 @@
#include <chrono>
#include <condition_variable>
#include <cstddef>
#include <deque>
#include <iomanip>
#include <iostream>
#include <mutex>
#include <queue>
#include <sstream>
#include <string>
#include <thread>
@@ -25,82 +25,6 @@ static std::mutex mtx_count{};
static boost::fibers::condition_variable_any cnd_count{};
typedef std::unique_lock< std::mutex > lock_t;
/*****************************************************************************
* shared_ready_queue scheduler
*****************************************************************************/
class shared_ready_queue : public boost::fibers::sched_algorithm {
private:
typedef std::queue< boost::fibers::context * > rqueue_t;
static rqueue_t rqueue_;
static std::mutex rqueue_mtx_;
rqueue_t local_queue_{};
public:
//[awakened_ws
virtual void awakened( boost::fibers::context * ctx) noexcept {
BOOST_ASSERT( nullptr != ctx);
if ( ctx->is_context( boost::fibers::type::pinned_context) ) { /*<
recognize when we're passed this thread's main fiber (or an
implicit library helper fiber): never put those on the shared
queue
>*/
local_queue_.push( ctx);
} else {
ctx->detach();
lock_t lk(rqueue_mtx_); /*<
worker fiber, enqueue on shared queue
>*/
rqueue_.push( ctx);
}
}
//]
//[pick_next_ws
virtual boost::fibers::context * pick_next() noexcept {
boost::fibers::context * ctx( nullptr);
lock_t lk(rqueue_mtx_);
if ( ! rqueue_.empty() ) { /*<
pop an item from the ready queue
>*/
ctx = rqueue_.front();
rqueue_.pop();
lk.unlock();
BOOST_ASSERT( nullptr != ctx);
boost::fibers::context::active()->attach( ctx); /*<
attach context to current scheduler via the active fiber
of this thread
>*/
} else {
lk.unlock();
if ( ! local_queue_.empty() ) { /*<
nothing in the ready queue, return main or dispatcher fiber
>*/
ctx = local_queue_.front();
local_queue_.pop();
}
}
return ctx;
}
//]
virtual bool has_ready_fibers() const noexcept {
lock_t lock(rqueue_mtx_);
return ! rqueue_.empty() || ! local_queue_.empty();
}
void suspend_until( std::chrono::steady_clock::time_point const& time_point) noexcept {
// let scheduler (dispatcher-fiber) spin
}
void notify() noexcept {
}
};
shared_ready_queue::rqueue_t shared_ready_queue::rqueue_{};
std::mutex shared_ready_queue::rqueue_mtx_{};
/*****************************************************************************
* example fiber function
*****************************************************************************/
@@ -141,8 +65,8 @@ void thread( barrier * b) {
std::ostringstream buffer;
buffer << "thread started " << std::this_thread::get_id() << std::endl;
std::cout << buffer.str() << std::flush;
boost::fibers::use_scheduling_algorithm< shared_ready_queue >(); /*<
Install the scheduling algorithm `shared_ready_queue` in order to
boost::fibers::use_scheduling_algorithm< boost::fibers::algo::shared_round_robin >(); /*<
Install the scheduling algorithm `boost::fibers::algo::shared_round_robin` in order to
join the work sharing.
>*/
b->wait(); /*< sync with other threads: allow them to start processing >*/
@@ -162,8 +86,8 @@ void thread( barrier * b) {
int main( int argc, char *argv[]) {
std::cout << "main thread started " << std::this_thread::get_id() << std::endl;
//[main_ws
boost::fibers::use_scheduling_algorithm< shared_ready_queue >(); /*<
Install the scheduling algorithm `shared_ready_queue` in the main thread
boost::fibers::use_scheduling_algorithm< boost::fibers::algo::shared_round_robin >(); /*<
Install the scheduling algorithm `boost::fibers::algo::shared_round_robin` in the main thread
too, so each new fiber gets launched into the shared pool.
>*/
+2 -2
View File
@@ -85,7 +85,7 @@ public:
}
};
class victim_algo : public boost::fibers::sched_algorithm {
class victim_algo : public boost::fibers::algo::algorithm {
private:
typedef work_stealing_queue rqueue_t;
@@ -139,7 +139,7 @@ public:
}
};
class thief_algo : public boost::fibers::sched_algorithm {
class thief_algo : public boost::fibers::algo::algorithm {
private:
typedef boost::fibers::scheduler::ready_queue_t rqueue_t;
typedef work_stealing_queue ws_rqueue_t;
@@ -3,8 +3,8 @@
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#ifndef BOOST_FIBERS_ALGORITHM_H
#define BOOST_FIBERS_ALGORITHM_H
#ifndef BOOST_FIBERS_ALGO_ALGORITHM_H
#define BOOST_FIBERS_ALGO_ALGORITHM_H
#include <cstddef>
#include <chrono>
@@ -24,8 +24,10 @@ namespace fibers {
class context;
struct BOOST_FIBERS_DECL sched_algorithm {
virtual ~sched_algorithm() {}
namespace algo {
struct BOOST_FIBERS_DECL algorithm {
virtual ~algorithm() {}
virtual void awakened( context *) noexcept = 0;
@@ -38,7 +40,7 @@ struct BOOST_FIBERS_DECL sched_algorithm {
virtual void notify() noexcept = 0;
};
class BOOST_FIBERS_DECL sched_algorithm_with_properties_base : public sched_algorithm {
class BOOST_FIBERS_DECL algorithm_with_properties_base : public algorithm {
public:
// called by fiber_properties::notify() -- don't directly call
virtual void property_change_( context * f, fiber_properties * props) noexcept = 0;
@@ -49,13 +51,13 @@ protected:
};
template< typename PROPS >
struct sched_algorithm_with_properties : public sched_algorithm_with_properties_base {
typedef sched_algorithm_with_properties_base super;
struct algorithm_with_properties : public algorithm_with_properties_base {
typedef algorithm_with_properties_base super;
// Mark this override 'final': sched_algorithm_with_properties subclasses
// Mark this override 'final': algorithm_with_properties subclasses
// must override awakened() with properties parameter instead. Otherwise
// you'd have to remember to start every subclass awakened() override
// with: sched_algorithm_with_properties<PROPS>::awakened(fb);
// with: algorithm_with_properties<PROPS>::awakened(fb);
virtual void awakened( context * f) noexcept override final {
fiber_properties * props = super::get_properties( f);
if ( nullptr == props) {
@@ -69,10 +71,10 @@ struct sched_algorithm_with_properties : public sched_algorithm_with_properties_
"new_properties() must return properties class");
super::set_properties( f, props);
}
// Set sched_algo_ again every time this fiber becomes READY. That
// Set 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);
// algorithm subclass instance.
props->set_algorithm( this);
// Okay, now forward the call to subclass override.
awakened( f, properties(f) );
@@ -90,7 +92,7 @@ struct sched_algorithm_with_properties : public sched_algorithm_with_properties_
virtual void property_change( context * f, PROPS & props) noexcept {
}
// implementation for sched_algorithm_with_properties_base method
// implementation for algorithm_with_properties_base method
void property_change_( context * f, fiber_properties * props ) noexcept override final {
property_change( f, * static_cast< PROPS * >( props) );
}
@@ -103,10 +105,10 @@ struct sched_algorithm_with_properties : public sched_algorithm_with_properties_
}
};
}}
}}}
#ifdef BOOST_HAS_ABI_HEADERS
# include BOOST_ABI_SUFFIX
#endif
#endif // BOOST_FIBERS_ALGORITHM_H
#endif // BOOST_FIBERS_ALGO_ALGORITHM_H
@@ -3,8 +3,8 @@
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#ifndef BOOST_FIBERS_DEFAULT_ROUND_ROBIN_H
#define BOOST_FIBERS_DEFAULT_ROUND_ROBIN_H
#ifndef BOOST_FIBERS_ALGO_ROUND_ROBIN_H
#define BOOST_FIBERS_ALGO_ROUND_ROBIN_H
#include <condition_variable>
#include <chrono>
@@ -12,7 +12,7 @@
#include <boost/config.hpp>
#include <boost/fiber/algorithm.hpp>
#include <boost/fiber/algo/algorithm.hpp>
#include <boost/fiber/context.hpp>
#include <boost/fiber/detail/config.hpp>
#include <boost/fiber/scheduler.hpp>
@@ -23,12 +23,13 @@
namespace boost {
namespace fibers {
namespace algo {
class context;
class BOOST_FIBERS_DECL round_robin : public sched_algorithm {
class BOOST_FIBERS_DECL round_robin : public algorithm {
private:
scheduler::ready_queue_t ready_queue_{};
typedef scheduler::ready_queue_t rqueue_t;
rqueue_t rqueue_{};
std::mutex mtx_{};
std::condition_variable cnd_{};
bool flag_{ false };
@@ -50,10 +51,10 @@ public:
virtual void notify() noexcept;
};
}}
}}}
#ifdef BOOST_HAS_ABI_HEADERS
# include BOOST_ABI_SUFFIX
#endif
#endif // BOOST_FIBERS_DEFAULT_ROUND_ROBIN_H
#endif // BOOST_FIBERS_ALGO_ROUND_ROBIN_H
@@ -0,0 +1,77 @@
// Copyright Oliver Kowalke 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)
#ifndef BOOST_FIBERS_ALGO_SHARED_ROUND_ROBIN_H
#define BOOST_FIBERS_ALGO_SHARED_ROUND_ROBIN_H
#include <condition_variable>
#include <chrono>
#include <deque>
#include <mutex>
#include <boost/config.hpp>
#include <boost/fiber/algo/algorithm.hpp>
#include <boost/fiber/context.hpp>
#include <boost/fiber/detail/config.hpp>
#include <boost/fiber/scheduler.hpp>
#ifdef BOOST_HAS_ABI_HEADERS
# include BOOST_ABI_PREFIX
#endif
namespace boost {
namespace fibers {
namespace algo {
class BOOST_FIBERS_DECL shared_round_robin : public algorithm {
private:
typedef std::deque< context * > rqueue_t;
typedef scheduler::ready_queue_t lqueue_t;
static rqueue_t rqueue_;
static std::mutex rqueue_mtx_;
lqueue_t lqueue_{};
std::mutex mtx_{};
std::condition_variable cnd_{};
bool flag_{ false };
bool suspend_;
public:
shared_round_robin() = default;
shared_round_robin( bool suspend) :
suspend_{ suspend } {
}
shared_round_robin( shared_round_robin const&) = delete;
shared_round_robin( shared_round_robin &&) = delete;
shared_round_robin & operator=( shared_round_robin const&) = delete;
shared_round_robin & operator=( shared_round_robin &&) = delete;
void awakened( context * ctx) noexcept;
context * pick_next() noexcept;
bool has_ready_fibers() const noexcept {
std::unique_lock< std::mutex > lock( rqueue_mtx_);
return ! rqueue_.empty() || ! lqueue_.empty();
}
void suspend_until( std::chrono::steady_clock::time_point const& time_point) noexcept;
void notify() noexcept;
};
}}}
#ifdef BOOST_HAS_ABI_HEADERS
# include BOOST_ABI_SUFFIX
#endif
#endif // BOOST_FIBERS_ALGO_SHARED_ROUND_ROBIN_H
+3 -1
View File
@@ -7,7 +7,9 @@
#ifndef BOOST_FIBERS_H
#define BOOST_FIBERS_H
#include <boost/fiber/algorithm.hpp>
#include <boost/fiber/algo/algorithm.hpp>
#include <boost/fiber/algo/round_robin.hpp>
#include <boost/fiber/algo/shared_round_robin.hpp>
#include <boost/fiber/barrier.hpp>
#include <boost/fiber/bounded_channel.hpp>
#include <boost/fiber/condition_variable.hpp>
+3 -3
View File
@@ -52,7 +52,7 @@ PROPS & properties() {
fibers::context::active()->get_properties();
if ( ! props) {
// props could be nullptr if the thread's main fiber has not yet
// yielded (not yet passed through sched_algorithm_with_properties::
// yielded (not yet passed through algorithm_with_properties::
// awakened()). Address that by yielding right now.
yield();
// Try again to obtain the fiber_properties subclass instance ptr.
@@ -60,7 +60,7 @@ PROPS & properties() {
// have happened while we were yielding!
props = fibers::context::active()->get_properties();
// Could still be hosed if the running manager isn't a subclass of
// sched_algorithm_with_properties.
// algorithm_with_properties.
BOOST_ASSERT_MSG(props, "this_fiber::properties not set");
}
return dynamic_cast< PROPS & >( * props );
@@ -78,7 +78,7 @@ bool has_ready_fibers() noexcept {
template< typename SchedAlgo, typename ... Args >
void use_scheduling_algorithm( Args && ... args) noexcept {
boost::fibers::context::active()->get_scheduler()
->set_sched_algo(
->set_algo(
std::unique_ptr< SchedAlgo >(
new SchedAlgo( std::forward< Args >( args) ... ) ) );
}
+11 -6
View File
@@ -24,17 +24,22 @@
namespace boost {
namespace fibers {
struct sched_algorithm;
class context;
namespace algo {
struct algorithm;
}
class BOOST_FIBERS_DECL fiber_properties {
protected:
// initialized by constructor
context * ctx_;
// set every time this fiber becomes READY
sched_algorithm * sched_algo_{ nullptr };
algo::algorithm * algo_{ nullptr };
// Inform the relevant sched_algorithm instance that something important
// Inform the relevant algorithm instance that something important
// has changed, so it can (presumably) adjust its data structures
// accordingly.
void notify() noexcept;
@@ -54,10 +59,10 @@ public:
// destroyed via that pointer.
virtual ~fiber_properties() = default;
// not really intended for public use, but sched_algorithm_with_properties
// not really intended for public use, but algorithm_with_properties
// must be able to call this
void set_sched_algorithm( sched_algorithm * algo) noexcept {
sched_algo_ = algo;
void set_algorithm( algo::algorithm * algo) noexcept {
algo_ = algo;
}
};
+3 -3
View File
@@ -18,7 +18,7 @@
#include <boost/intrusive_ptr.hpp>
#include <boost/intrusive/set.hpp>
#include <boost/fiber/algorithm.hpp>
#include <boost/fiber/algo/algorithm.hpp>
#include <boost/fiber/context.hpp>
#include <boost/fiber/detail/config.hpp>
#include <boost/fiber/detail/data.hpp>
@@ -63,7 +63,7 @@ private:
context, detail::worker_hook, & context::worker_hook_ >,
intrusive::constant_time_size< false > > worker_queue_t;
std::unique_ptr< sched_algorithm > sched_algo_;
std::unique_ptr< algo::algorithm > algo_;
context * main_ctx_{ nullptr };
intrusive_ptr< context > dispatcher_ctx_{};
// worker-queue contains all context' mananged by this scheduler
@@ -124,7 +124,7 @@ public:
bool has_ready_fibers() const noexcept;
void set_sched_algo( std::unique_ptr< sched_algorithm >) noexcept;
void set_algo( std::unique_ptr< algo::algorithm >) noexcept;
void attach_main_context( context *) noexcept;
+14 -68
View File
@@ -1,3 +1,4 @@
// Copyright Oliver Kowalke 2015.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
@@ -6,14 +7,15 @@
#include <algorithm>
#include <cassert>
#include <chrono>
#include <condition_variable>
#include <cstddef>
#include <cstdint>
#include <cstdlib>
#include <queue>
#include <deque>
#include <iostream>
#include <memory>
#include <mutex>
#include <numeric>
#include <sstream>
#include <vector>
#include <boost/fiber/all.hpp>
@@ -21,71 +23,16 @@
#include "barrier.hpp"
#include "bind/bind_processor.hpp"
using allocator_type = boost::fibers::fixedsize_stack;
using channel_type = boost::fibers::unbounded_channel< std::uint64_t >;
using clock_type = std::chrono::steady_clock;
using duration_type = clock_type::duration;
using lock_type = std::unique_lock< std::mutex >;
using time_point_type = clock_type::time_point;
using channel_type = boost::fibers::unbounded_channel< std::uint64_t >;
using allocator_type = boost::fibers::fixedsize_stack;
static bool done = false;
static std::mutex mtx{};
static boost::fibers::condition_variable_any cnd{};
using lock_t = std::unique_lock< std::mutex >;
class shared_queue_scheduler : public boost::fibers::sched_algorithm {
private:
typedef boost::fibers::scheduler::ready_queue_t queue_t;
static queue_t rqueue_;
static std::mutex rqueue_mtx_;
queue_t lqueue_{};
public:
virtual void awakened( boost::fibers::context * ctx) noexcept {
if ( ! ctx->is_context( boost::fibers::type::pinned_context) ) {
ctx->detach();
lock_t lk( rqueue_mtx_);
ctx->ready_link( rqueue_);
} else {
ctx->ready_link( lqueue_);
}
}
virtual boost::fibers::context * pick_next() noexcept {
boost::fibers::context * ctx( nullptr);
lock_t lk( rqueue_mtx_);
if ( ! rqueue_.empty() ) {
ctx = & rqueue_.front();
rqueue_.pop_front();
lk.unlock();
BOOST_ASSERT( nullptr != ctx);
boost::fibers::context::active()->attach( ctx);
} else {
lk.unlock();
if ( ! lqueue_.empty() ) {
ctx = & lqueue_.front();
lqueue_.pop_front();
}
}
return ctx;
}
virtual bool has_ready_fibers() const noexcept {
lock_t lock( rqueue_mtx_);
return ! rqueue_.empty() || ! lqueue_.empty();
}
void suspend_until( std::chrono::steady_clock::time_point const& time_point) noexcept {
// do not block thread; spin in dispatcher-fiber till ready fibers are available
}
void notify() noexcept {
}
};
shared_queue_scheduler::queue_t shared_queue_scheduler::rqueue_{};
std::mutex shared_queue_scheduler::rqueue_mtx_{};
// microbenchmark
void skynet( allocator_type & salloc, channel_type & c, std::size_t num, std::size_t size, std::size_t div) {
@@ -110,26 +57,25 @@ void skynet( allocator_type & salloc, channel_type & c, std::size_t num, std::si
void thread( unsigned int i, barrier * b) {
bind_to_processor( i);
boost::fibers::use_scheduling_algorithm< shared_queue_scheduler >();
boost::fibers::use_scheduling_algorithm< boost::fibers::algo::shared_round_robin >();
b->wait();
lock_t lk( mtx);
lock_type lk( mtx);
cnd.wait( lk, [](){ return done; });
BOOST_ASSERT( done);
}
int main() {
try {
boost::fibers::use_scheduling_algorithm< shared_queue_scheduler >();
boost::fibers::use_scheduling_algorithm< boost::fibers::algo::shared_round_robin >();
unsigned int n = std::thread::hardware_concurrency();
barrier b( n);
n -= 1; // this thread
bind_to_processor( n);
bind_to_processor( n - 1);
std::size_t stack_size{ 4048 };
std::size_t size{ 100000 };
std::size_t div{ 10 };
std::vector< std::thread > threads;
for ( unsigned int i = 0; i < n; i++) {
threads.push_back( std::thread( thread, i, & b) );
for ( unsigned int i = 1; i < n; ++i) {
threads.push_back( std::thread( thread, i - 1, & b) );
};
allocator_type salloc{ stack_size };
std::uint64_t result{ 0 };
@@ -141,7 +87,7 @@ int main() {
result = rc.value_pop();
duration = clock_type::now() - start;
std::cout << "Result: " << result << " in " << duration.count() / 1000000 << " ms" << std::endl;
lock_t lk( mtx);
lock_type lk( mtx);
done = true;
lk.unlock();
cnd.notify_all();
+5 -4
View File
@@ -4,7 +4,7 @@
// (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/fiber/algo/algorithm.hpp"
#include "boost/fiber/context.hpp"
@@ -14,20 +14,21 @@
namespace boost {
namespace fibers {
namespace algo {
//static
fiber_properties *
sched_algorithm_with_properties_base::get_properties( context * ctx) noexcept {
algorithm_with_properties_base::get_properties( context * ctx) noexcept {
return ctx->get_properties();
}
//static
void
sched_algorithm_with_properties_base::set_properties( context * ctx, fiber_properties * props) noexcept {
algorithm_with_properties_base::set_properties( context * ctx, fiber_properties * props) noexcept {
ctx->set_properties( props);
}
}}
}}}
#ifdef BOOST_HAS_ABI_HEADERS
# include BOOST_ABI_SUFFIX
@@ -4,7 +4,7 @@
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#include "boost/fiber/round_robin.hpp"
#include "boost/fiber/algo/round_robin.hpp"
#include <boost/assert.hpp>
@@ -14,21 +14,22 @@
namespace boost {
namespace fibers {
namespace algo {
void
round_robin::awakened( context * ctx) noexcept {
BOOST_ASSERT( nullptr != ctx);
BOOST_ASSERT( ! ctx->ready_is_linked() );
ctx->ready_link( ready_queue_);
ctx->ready_link( rqueue_);
}
context *
round_robin::pick_next() noexcept {
context * victim{ nullptr };
if ( ! ready_queue_.empty() ) {
victim = & ready_queue_.front();
ready_queue_.pop_front();
if ( ! rqueue_.empty() ) {
victim = & rqueue_.front();
rqueue_.pop_front();
BOOST_ASSERT( nullptr != victim);
BOOST_ASSERT( ! victim->ready_is_linked() );
}
@@ -37,7 +38,7 @@ round_robin::pick_next() noexcept {
bool
round_robin::has_ready_fibers() const noexcept {
return ! ready_queue_.empty();
return ! rqueue_.empty();
}
void
@@ -61,7 +62,7 @@ round_robin::notify() noexcept {
cnd_.notify_all();
}
}}
}}}
#ifdef BOOST_HAS_ABI_HEADERS
# include BOOST_ABI_SUFFIX
+101
View File
@@ -0,0 +1,101 @@
// 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/fiber/algo/shared_round_robin.hpp"
#include <boost/assert.hpp>
#include "boost/fiber/type.hpp"
#ifdef BOOST_HAS_ABI_HEADERS
# include BOOST_ABI_PREFIX
#endif
namespace boost {
namespace fibers {
namespace algo {
//[awakened_ws
void
shared_round_robin::awakened( context * ctx) noexcept {
if ( ctx->is_context( type::pinned_context) ) { /*<
recognize when we're passed this thread's main fiber (or an
implicit library helper fiber): never put those on the shared
queue
>*/
lqueue_.push_back( * ctx);
} else {
ctx->detach();
std::unique_lock< std::mutex > lk( rqueue_mtx_); /*<
worker fiber, enqueue on shared queue
>*/
rqueue_.push_back( ctx);
}
}
//]
//[pick_next_ws
context *
shared_round_robin::pick_next() noexcept {
context * ctx( nullptr);
std::unique_lock< std::mutex > lk( rqueue_mtx_);
if ( ! rqueue_.empty() ) { /*<
pop an item from the ready queue
>*/
ctx = rqueue_.front();
rqueue_.pop_front();
lk.unlock();
BOOST_ASSERT( nullptr != ctx);
context::active()->attach( ctx); /*<
attach context to current scheduler via the active fiber
of this thread
>*/
} else {
lk.unlock();
if ( ! lqueue_.empty() ) { /*<
nothing in the ready queue, return main or dispatcher fiber
>*/
ctx = & lqueue_.front();
lqueue_.pop_front();
}
}
return ctx;
}
//]
void
shared_round_robin::suspend_until( std::chrono::steady_clock::time_point const& time_point) noexcept {
if ( suspend_) {
if ( (std::chrono::steady_clock::time_point::max)() == time_point) {
std::unique_lock< std::mutex > lk( mtx_);
cnd_.wait( lk, [this](){ return flag_; });
flag_ = false;
} else {
std::unique_lock< std::mutex > lk( mtx_);
cnd_.wait_until( lk, time_point, [this](){ return flag_; });
flag_ = false;
}
}
}
void
shared_round_robin::notify() noexcept {
if ( suspend_) {
std::unique_lock< std::mutex > lk( mtx_);
flag_ = true;
lk.unlock();
cnd_.notify_all();
}
}
shared_round_robin::rqueue_t shared_round_robin::rqueue_{};
std::mutex shared_round_robin::rqueue_mtx_{};
}}}
#ifdef BOOST_HAS_ABI_HEADERS
# include BOOST_ABI_SUFFIX
#endif
+3 -3
View File
@@ -7,7 +7,7 @@
#include <boost/assert.hpp>
#include "boost/fiber/algorithm.hpp"
#include "boost/fiber/algo/algorithm.hpp"
#include "boost/fiber/scheduler.hpp"
#include "boost/fiber/context.hpp"
@@ -20,7 +20,7 @@ namespace fibers {
void
fiber_properties::notify() noexcept {
BOOST_ASSERT( nullptr != sched_algo_);
BOOST_ASSERT( nullptr != 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
@@ -28,7 +28,7 @@ fiber_properties::notify() noexcept {
// 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 ( ctx_->ready_is_linked() ) {
static_cast< sched_algorithm_with_properties_base * >( sched_algo_)->
static_cast< algo::algorithm_with_properties_base * >( algo_)->
property_change_( ctx_, this);
}
}
+14 -14
View File
@@ -11,9 +11,9 @@
#include <boost/assert.hpp>
#include "boost/fiber/algo/round_robin.hpp"
#include "boost/fiber/context.hpp"
#include "boost/fiber/exceptions.hpp"
#include "boost/fiber/round_robin.hpp"
#ifdef BOOST_HAS_ABI_HEADERS
# include BOOST_ABI_PREFIX
@@ -24,7 +24,7 @@ namespace fibers {
context *
scheduler::get_next_() noexcept {
context * ctx = sched_algo_->pick_next();
context * ctx = algo_->pick_next();
//BOOST_ASSERT( nullptr == ctx);
//BOOST_ASSERT( this == ctx->get_scheduler() );
return ctx;
@@ -89,7 +89,7 @@ scheduler::sleep2ready_() noexcept {
// reset sleep-tp
ctx->tp_ = (std::chrono::steady_clock::time_point::max)();
// push new context to ready-queue
sched_algo_->awakened( ctx);
algo_->awakened( ctx);
} else {
break; // first context with now < deadline
}
@@ -97,7 +97,7 @@ scheduler::sleep2ready_() noexcept {
}
scheduler::scheduler() noexcept :
sched_algo_{ new round_robin() } {
algo_{ new algo::round_robin() } {
}
scheduler::~scheduler() {
@@ -135,7 +135,7 @@ scheduler::dispatch() noexcept {
bool no_worker = worker_queue_.empty();
if ( shutdown_) {
// notify sched-algorithm about termination
sched_algo_->notify();
algo_->notify();
if ( no_worker) {
break;
}
@@ -164,7 +164,7 @@ scheduler::dispatch() noexcept {
suspend_time = i->tp_;
}
// no ready context, wait till signaled
sched_algo_->suspend_until( suspend_time);
algo_->suspend_until( suspend_time);
}
}
// release termianted context'
@@ -194,7 +194,7 @@ scheduler::set_ready( context * ctx) noexcept {
// for safety unlink it from ready-queue
ctx->ready_unlink();
// push new context to ready-queue
sched_algo_->awakened( ctx);
algo_->awakened( ctx);
}
void
@@ -214,7 +214,7 @@ scheduler::set_remote_ready( context * ctx) noexcept {
remote_ready_queue_.push_back( ctx);
lk.unlock();
// notify scheduler
sched_algo_->notify();
algo_->notify();
}
#if (BOOST_EXECUTION_CONTEXT==1)
@@ -337,16 +337,16 @@ scheduler::suspend( detail::spinlock_lock & lk) noexcept {
bool
scheduler::has_ready_fibers() const noexcept {
return sched_algo_->has_ready_fibers();
return algo_->has_ready_fibers();
}
void
scheduler::set_sched_algo( std::unique_ptr< sched_algorithm > algo) noexcept {
scheduler::set_algo( std::unique_ptr< algo::algorithm > algo) noexcept {
// move remaining cotnext in current scheduler to new one
while ( sched_algo_->has_ready_fibers() ) {
algo->awakened( sched_algo_->pick_next() );
while ( algo_->has_ready_fibers() ) {
algo->awakened( algo_->pick_next() );
}
sched_algo_ = std::move( algo);
algo_ = std::move( algo);
}
void
@@ -375,7 +375,7 @@ scheduler::attach_dispatcher_context( intrusive_ptr< context > dispatcher_ctx) n
// the dispatcher-context is resumed and
// scheduler::dispatch() is executed
dispatcher_ctx_->scheduler_ = this;
sched_algo_->awakened( dispatcher_ctx_.get() );
algo_->awakened( dispatcher_ctx_.get() );
}
void