mirror of
https://github.com/boostorg/fiber.git
synced 2026-02-17 13:42:21 +00:00
Merge pull request #35 from nat-goodspeed/develop
Incomplete documentation pass
This commit is contained in:
@@ -18,7 +18,7 @@
|
||||
class condition_variable;
|
||||
class condition_variable_any;
|
||||
|
||||
The class `condition_variable` provides a mechanism for one fiber to wait for
|
||||
The class `condition_variable` provides a mechanism for a fiber to wait for
|
||||
notification on `condition_variable`. When the fiber awakens from the wait, then
|
||||
it checks to see if the appropriate condition is now true, and continues if so.
|
||||
If the condition is not true, then the fiber calls `wait` again to resume
|
||||
@@ -26,7 +26,7 @@ waiting. In the simplest case, this condition is just a boolean variable:
|
||||
|
||||
boost::fibers::condition_variable cond;
|
||||
boost::fibers::mutex mtx;
|
||||
bool data_ready;
|
||||
bool data_ready = false;
|
||||
|
||||
void process_data();
|
||||
|
||||
@@ -107,16 +107,22 @@ optimize as described for `boost::thread::condition_variable`.
|
||||
void wait( LockType & lk, Pred predicate);
|
||||
|
||||
template< typename LockType, typename Clock, typename Duration >
|
||||
cv_status wait_until( LockType & lk, std::chrono::time_point< Clock, Duration > const& timeout_time);
|
||||
cv_status wait_until( LockType & lk,
|
||||
std::chrono::time_point< Clock, Duration > const& timeout_time);
|
||||
|
||||
template< typename LockType, typename Clock, typename Duration, typename Pred >
|
||||
bool wait_until( LockType & lk, std::chrono::time_point< Clock, Duration > const& timeout_time, Pred pred);
|
||||
bool wait_until( LockType & lk,
|
||||
std::chrono::time_point< Clock, Duration > const& timeout_time,
|
||||
Pred pred);
|
||||
|
||||
template< typename LockType, typename Rep, typename Period >
|
||||
cv_status wait_for( LockType & lk, std::chrono::duration< Rep, Period > const& timeout_duration);
|
||||
cv_status wait_for( LockType & lk,
|
||||
std::chrono::duration< Rep, Period > const& timeout_duration);
|
||||
|
||||
template< typename LockType, typename Rep, typename Period, typename Pred >
|
||||
bool wait_for( LockType & lk, std::chrono::duration< Rep, Period > const& timeout_duration, Pred pred);
|
||||
bool wait_for( LockType & lk,
|
||||
std::chrono::duration< Rep, Period > const& timeout_duration,
|
||||
Pred pred);
|
||||
};
|
||||
|
||||
typedef condition_variable condition_variable_any;
|
||||
@@ -204,36 +210,39 @@ fibers calling `wait` on `*this` must wait on `lk` objects governing the
|
||||
['same] `mutex`. Three distinct objects are involved in any
|
||||
`condition_variable::wait()` call: the `condition_variable` itself, the
|
||||
`mutex` coordinating access between fibers and a lock object (e.g.
|
||||
`mutex::scoped_lock`). In some sense it would be nice if the
|
||||
`std::lock_guard`). In some sense it would be nice if the
|
||||
`condition_variable`'s constructor could accept the related `mutex` object,
|
||||
enforcing agreement across all `wait()` calls; but the existing APIs prevent
|
||||
that. Instead we must require the `wait()` call to accept a reference to the
|
||||
local lock object. It is an error to reuse a given `condition_variable`
|
||||
instance with lock objects that reference ['different] underlying `mutex`
|
||||
objects. It would be like a road intersection with traffic lights disconnected
|
||||
from one another: sooner or later a collision will result.]]
|
||||
objects. It would be like a road intersection with traffic lights independent
|
||||
of one another: sooner or later a collision will result.]]
|
||||
]
|
||||
|
||||
[template_member_heading condition_variable..wait_until]
|
||||
|
||||
template< typename LockType, typename Clock, typename Duration >
|
||||
cv_status wait_until( LockType & lk, std::chrono::time_point< Clock, Duration > const& timeout_time);
|
||||
cv_status wait_until( LockType & lk,
|
||||
std::chrono::time_point< Clock, Duration > const& timeout_time);
|
||||
|
||||
template< typename LockType, typename Clock, typename Duration, typename Pred >
|
||||
bool wait_until( LockType & lk, std::chrono::time_point< Clock, Duration > const& timeout_time, Pred pred);
|
||||
bool wait_until( LockType & lk,
|
||||
std::chrono::time_point< Clock, Duration > const& timeout_time,
|
||||
Pred pred);
|
||||
|
||||
[variablelist
|
||||
[[Precondition:] [`lk` is locked by the current fiber, and either no other
|
||||
fiber is currently waiting on `*this`, or the execution of the `mutex()` member
|
||||
function on the `lk` objects supplied in the calls to `wait`, `wait_for` or
|
||||
`wait_until` in all the fibers currently waiting on `*this` would return the
|
||||
same value as `lk->mutex()` for this call to `wait`.]]
|
||||
same value as `lk.mutex()` for this call to `wait_until`.]]
|
||||
[[Effects:] [Atomically call `lk.unlock()` and blocks the current fiber. The
|
||||
fiber will unblock when notified by a call to `this->notify_one()` or
|
||||
`this->notify_all()`, when the time as reported by `clock_type::now()`
|
||||
`this->notify_all()`, when the system time
|
||||
would be equal to or later than the specified `timeout_time`, or spuriously.
|
||||
When the fiber is unblocked (for whatever reason), the lock is reacquired by
|
||||
invoking `lk.lock()` before the call to `wait` returns. The lock is also
|
||||
invoking `lk.lock()` before the call to `wait_until` returns. The lock is also
|
||||
reacquired by invoking `lk.lock()` if the function exits with an exception.
|
||||
The member function accepting `pred` is shorthand for: ``
|
||||
|
||||
@@ -253,27 +262,30 @@ __interrupt__ on the __fiber__ object associated with the current fiber of
|
||||
execution.]]
|
||||
[[Returns:] [The overload without `pred` returns `cv_status::no_timeout` if
|
||||
awakened by `notify_one()` or `notify_all()`, or `cv_status::timeout` if
|
||||
awakened because `clock_type::now()` is past `timeout_time`.]]
|
||||
awakened because the system time is past `timeout_time`.]]
|
||||
[[Returns:] [The overload accepting `pred` returns `false` if the call is
|
||||
returning because the time specified by `timeout_time` was reached and the
|
||||
predicate returns `false`, `true` otherwise.]]
|
||||
[[Note:] [See [*Note] for `wait()`.]]
|
||||
[[Note:] [See [*Note] for [member_link condition_variable..wait].]]
|
||||
]
|
||||
|
||||
[template_member_heading condition_variable..wait_for]
|
||||
|
||||
template< typename LockType, typename Rep, typename Period >
|
||||
cv_status wait_for( LockType & lk, std::chrono::duration< Rep, Period > const& timeout_duration);
|
||||
cv_status wait_for( LockType & lk,
|
||||
std::chrono::duration< Rep, Period > const& timeout_duration);
|
||||
|
||||
template< typename LockType, typename Rep, typename Period, typename Pred >
|
||||
bool wait_for( LockType & lk, std::chrono::duration< Rep, Period > const& timeout_duration, Pred pred);
|
||||
bool wait_for( LockType & lk,
|
||||
std::chrono::duration< Rep, Period > const& timeout_duration,
|
||||
Pred pred);
|
||||
|
||||
[variablelist
|
||||
[[Precondition:] [`lk` is locked by the current fiber, and either no other
|
||||
fiber is currently waiting on `*this`, or the execution of the `mutex()` member
|
||||
function on the `lk` objects supplied in the calls to `wait`, `wait_for` or
|
||||
`wait_until` in all the fibers currently waiting on `*this` would return the
|
||||
same value as `lk->mutex()` for this call to `wait`.]]
|
||||
same value as `lk.mutex()` for this call to `wait_for`.]]
|
||||
[[Effects:] [Atomically call `lk.unlock()` and blocks the current fiber. The
|
||||
fiber will unblock when notified by a call to `this->notify_one()` or
|
||||
`this->notify_all()`, when a time interval equal to or greater than the
|
||||
@@ -281,7 +293,7 @@ specified `timeout_duration` has elapsed, or spuriously. When the fiber is
|
||||
unblocked (for whatever reason), the lock is reacquired by invoking
|
||||
`lk.lock()` before the call to `wait` returns. The lock is also reacquired by
|
||||
invoking `lk.lock()` if the function exits with an exception.
|
||||
The member function accepting `pred` is shorthand for: ``
|
||||
The `wait_for()` member function accepting `pred` is shorthand for: ``
|
||||
|
||||
while ( ! pred() ) {
|
||||
if ( cv_status::timeout == wait_for( lk, timeout_duration) ) {
|
||||
@@ -303,7 +315,7 @@ awakened because at least `timeout_duration` has elapsed.]]
|
||||
[[Returns:] [The overload accepting `pred` returns `false` if the call is
|
||||
returning because at least `timeout_duration` has elapsed and the predicate
|
||||
returns `false`, `true` otherwise.]]
|
||||
[[Note:] [See [*Note] for `wait()`.]]
|
||||
[[Note:] [See [*Note] for [member_link condition_variable..wait].]]
|
||||
]
|
||||
|
||||
[endsect]
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
http://www.boost.org/LICENSE_1_0.txt
|
||||
]
|
||||
|
||||
[#custom]
|
||||
[section:custom Customization]
|
||||
|
||||
__boost_fiber__ allows to customize the scheduling algorithm by a user-defined
|
||||
@@ -12,72 +13,16 @@ implementation.
|
||||
A fiber-scheduler must implement interface __algo__. __boost_fiber__ provides
|
||||
scheduler [class_link round_robin].
|
||||
|
||||
In order to use a custom 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);
|
||||
...
|
||||
}
|
||||
|
||||
You are explicitly permitted to code your own __algo__ subclass, and to pass
|
||||
it to [function_link set_scheduling_algorithm].
|
||||
[heading fiber_context]
|
||||
|
||||
|
||||
[class_heading sched_algorithm]
|
||||
|
||||
#include <boost/fiber/algorithm.hpp>
|
||||
|
||||
struct sched_algorithm {
|
||||
virtual ~sched_algorithm() {}
|
||||
|
||||
virtual void awakened( fiber_context *) = 0;
|
||||
|
||||
virtual fiber_context * pick_next() = 0;
|
||||
|
||||
virtual std::size_t ready_fibers() const noexcept = 0
|
||||
};
|
||||
|
||||
void set_scheduling_algorithm( sched_algorithm *);
|
||||
|
||||
[member_heading algorithm..awakened]
|
||||
|
||||
virtual void awakened( fiber_context * f) = 0;
|
||||
|
||||
[variablelist
|
||||
[[Effects:] [Marks fiber `f`, to be ready to run.]]
|
||||
]
|
||||
|
||||
[member_heading algorithm..pick_next]
|
||||
|
||||
virtual fiber_context * pick_next() = 0;
|
||||
|
||||
[variablelist
|
||||
[[Effects:] [Depending on the scheduling algorithm, this function returns the
|
||||
fiber which has to be resumed next.]]
|
||||
]
|
||||
|
||||
[member_heading algorithm..ready_fibers]
|
||||
|
||||
virtual std::size_t ready_fibers() const noexcept = 0;
|
||||
|
||||
[variablelist
|
||||
[[Effects:] [Returns `false` if scheduling algorithm has fibers ready to run,
|
||||
otherwise `true`.]]
|
||||
]
|
||||
|
||||
|
||||
[ns_function_heading fibers..set_scheduling_algorithm]
|
||||
|
||||
void set_scheduling_algorithm( sched_algorithm * a);
|
||||
|
||||
[variablelist
|
||||
[[Effects:] [Registers `a` as scheduling algorithm.]]
|
||||
]
|
||||
|
||||
< TODO >
|
||||
The fiber_context class is mostly supposed to be opaque. However, certain
|
||||
members are available for use by a custom scheduling algorithm.
|
||||
|
||||
|
||||
[endsect]
|
||||
|
||||
@@ -52,7 +52,7 @@
|
||||
[heading Tutorial]
|
||||
|
||||
Each __fiber__ represents a micro-thread which will be launched and managed
|
||||
cooperatively by a scheduler. Objects of type __fiber__ are only moveable.
|
||||
cooperatively by a scheduler. Objects of type __fiber__ are move-only.
|
||||
|
||||
boost::fibers::fiber f1; // not-a-fiber
|
||||
|
||||
@@ -91,7 +91,7 @@ The spawned __fiber__ is enqueued in the list of ready-to-run fibers at construc
|
||||
|
||||
[heading Exceptions]
|
||||
|
||||
A exception thrown by the function or callable object passed to the __fiber__
|
||||
An exception escaping from the function or callable object passed to the __fiber__
|
||||
constructor calls `std::terminate()`.
|
||||
If you need to know which exception was thrown, use __future__ and
|
||||
__packaged_task__.
|
||||
@@ -183,6 +183,18 @@ __interruption_enabled__.
|
||||
* __sleep_until__
|
||||
* __yield__
|
||||
* __interruption_point__
|
||||
* [member_link bounded_channel..push]
|
||||
* [member_link bounded_channel..pop]
|
||||
* [member_link bounded_channel..value_pop]
|
||||
* [member_link unbounded_channel..push]
|
||||
* [member_link unbounded_channel..pop]
|
||||
* [member_link unbounded_channel..value_pop]
|
||||
* [member_link future..wait]
|
||||
* [member_link future..get]
|
||||
* [member_link future..get_exception_ptr]
|
||||
* [member_link shared_future..wait]
|
||||
* [member_link shared_future..get]
|
||||
* [member_link shared_future..get_exception_ptr]
|
||||
|
||||
[#class_fiber_id]
|
||||
[heading Fiber IDs]
|
||||
@@ -273,11 +285,13 @@ operators on __fiber_id__ yield a total order for every non-equal __fiber_id__.
|
||||
|
||||
[variablelist
|
||||
[[Preconditions:] [`Fn` must be copyable or movable.]]
|
||||
[[Effects:] [`fn` is copied into internal storage for access by the new fiber.]]
|
||||
[[Effects:] [`fn` is copied or moved into internal storage for access by the new fiber.]]
|
||||
[[Postconditions:] [`*this` refers to the newly created fiber of execution.]]
|
||||
[[Throws:] [__fiber_exception__ if an error occurs.]]
|
||||
[[Note:] [StackAllocator is required to allocate a stack for the internal
|
||||
__econtext__.]]
|
||||
__econtext__. If StackAllocator is not explicitly passed, a
|
||||
__fixedsize_stack__ is used by default.]]
|
||||
[[See also:] [[link stack]]]
|
||||
]
|
||||
|
||||
[heading Move constructor]
|
||||
@@ -314,7 +328,7 @@ value of `other.get_id()` prior to the assignment.]]
|
||||
[[Throws:] [Nothing.]]
|
||||
[[Note:] [The programmer must ensure that the destructor is never executed while
|
||||
the fiber is still __joinable__. Even if you know (by calling
|
||||
[member_link fiber..operator safe_bool]) that the fiber has completed, you must
|
||||
[member_link fiber..operator bool]) that the fiber has completed, you must
|
||||
still call either __join__ or __detach__ before destroying the `fiber` object.]]
|
||||
]
|
||||
|
||||
@@ -326,7 +340,7 @@ still call either __join__ or __detach__ before destroying the `fiber` object.]
|
||||
[[Returns:] [`true` if `*this` refers to a fiber of execution, which may or
|
||||
may not have completed; otherwise `false`.]]
|
||||
[[Throws:] [Nothing]]
|
||||
[[See also:] [[member_link fiber..operator safe_bool]]]
|
||||
[[See also:] [[member_link fiber..operator bool]]]
|
||||
]
|
||||
|
||||
[member_heading fiber..join]
|
||||
@@ -386,18 +400,26 @@ interruption enabled.]]
|
||||
[[Throws:] [Nothing]]
|
||||
]
|
||||
|
||||
< TODO >
|
||||
[#fiber_properties]
|
||||
[heading Member function `template< typename PROPS > PROPS & properties()`]
|
||||
[template_member_heading fiber..properties]
|
||||
|
||||
template< typename PROPS >
|
||||
PROPS & properties();
|
||||
|
||||
[variablelist
|
||||
[[Preconditions:] [`*this` refers to a fiber of execution.]]
|
||||
[[Effects:] []]
|
||||
[[Throws:] []]
|
||||
[[Note:] []]
|
||||
[[Preconditions:] [`*this` refers to a fiber of execution. [function_link
|
||||
set_scheduling_algorithm] has been called from this thread with a pointer to a
|
||||
(subclass) instance of [template_link sched_algorithm_with_properties] with the
|
||||
same template argument `PROPS`. `*this` has been scheduled for execution at
|
||||
least once.]]
|
||||
[[Returns:] [a reference to the scheduler properties instance for `*this`.]]
|
||||
[[Throws:] [`std::bad_cast` if `set_scheduling_algorithm()` was passed a
|
||||
pointer to a `sched_algorithm_with_properties` subclass with some other
|
||||
template parameter than `PROPS`.]]
|
||||
[[Note:] [[template_link sched_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
|
||||
-- but only after this fiber has been scheduled for the first time.]]
|
||||
[[See also:] [[link custom]]]
|
||||
]
|
||||
|
||||
[member_heading fiber..operator bool]
|
||||
@@ -420,7 +442,7 @@ distinguish whether the referenced fiber of execution is still running.]]
|
||||
[[Returns:] [`true` if `*this` does not refer to a fiber of execution or if
|
||||
its fiber of execution has terminated, `false` otherwise.]]
|
||||
[[Throws:] [Nothing]]
|
||||
[[See also:] [[member_link fiber..operator safe_bool], __joinable__]]
|
||||
[[See also:] [[member_link fiber..operator bool], __joinable__]]
|
||||
]
|
||||
|
||||
[member_heading fiber..swap]
|
||||
@@ -453,8 +475,6 @@ prior to the call.]]
|
||||
[[Effects:] [Directs __boost_fiber__ to use `scheduler`, which must be a
|
||||
concrete subclass of __algo__, as the scheduling algorithm for all fibers in
|
||||
the current thread.]]
|
||||
[[Returns:] [Previous __algo__ instance set for the current thread, 0 if this
|
||||
is the first __boost_fiber__ entry point called by the current thread.]]
|
||||
[[Note:] [If you want a given thread to use a non-default scheduling
|
||||
algorithm, make that thread call `set_scheduling_algorithm()` before any other
|
||||
__boost_fiber__ entry point. If no scheduler has been set for the current
|
||||
@@ -466,6 +486,7 @@ lifespan of the referenced `scheduler` object. The caller must eventually
|
||||
destroy the passed `scheduler`, just as it must allocate it in the first
|
||||
place.]]
|
||||
[[Throws:] [Nothing]]
|
||||
[[See also:] [[link scheduling], [link custom]]]
|
||||
]
|
||||
|
||||
[function_heading ready_fibers]
|
||||
@@ -530,7 +551,7 @@ or both represent __not_a_fiber__, `false` otherwise.]]
|
||||
bool operator!=( id const& other) const noexcept;
|
||||
|
||||
[variablelist
|
||||
[[Returns:] [`! (other == * this)]]
|
||||
[[Returns:] [[`! (other == * this)]]]
|
||||
[[Throws:] [Nothing.]]
|
||||
]
|
||||
|
||||
@@ -595,7 +616,7 @@ implementation-defined total order of `fiber::id` values places `*this` before
|
||||
fibers::fiber::id get_id() noexcept;
|
||||
void yield();
|
||||
template< typename Clock, typename Duration >
|
||||
void sleep_until( std::chrono::time_point< Clock, Duration > const& sleep_time)
|
||||
void sleep_until( std::chrono::time_point< Clock, Duration > const& abs_time)
|
||||
template< typename Rep, typename Period >
|
||||
void sleep_for( std::chrono::duration< Rep, Period > const& timeout_duration);
|
||||
template< typename PROPS >
|
||||
@@ -626,7 +647,7 @@ executing fiber.]]
|
||||
#include <boost/fiber/operations.hpp>
|
||||
|
||||
template< typename Clock, typename Duration >
|
||||
void sleep_until( std::chrono::time_point< Clock, Duration > const& sleep_time)
|
||||
void sleep_until( std::chrono::time_point< Clock, Duration > const& abs_time)
|
||||
|
||||
[variablelist
|
||||
[[Effects:] [Suspends the current fiber until the time point specified by
|
||||
@@ -647,8 +668,8 @@ guarantees about how soon after `abs_time` it might resume.]]
|
||||
[variablelist
|
||||
[[Effects:] [Suspends the current fiber until the time duration specified by
|
||||
`rel_time` has elapsed.]]
|
||||
[[Throws:] [Nothing if operations of std::chrono::duration<Rep, Period> do not throw
|
||||
exceptions. __fiber_interrupted__ if the current fiber is interrupted.]]
|
||||
[[Throws:] [Any exceptions thrown by operations of `std::chrono::duration<Rep,
|
||||
Period>`. __fiber_interrupted__ if the current fiber is interrupted.]]
|
||||
[[Note:] [`sleep_for()` is one of the predefined __interruption_points__.]]
|
||||
[[Note:][The current fiber will not resume before `rel_time` has elapsed, but
|
||||
there are no guarantees about how soon after that it might resume.]]
|
||||
@@ -663,9 +684,11 @@ there are no guarantees about how soon after that it might resume.]]
|
||||
[variablelist
|
||||
[[Effects:] [Reliquishes execution control, allowing other fibers to run.]]
|
||||
[[Throws:] [__fiber_resource_error__ if an error occurs.]]
|
||||
[[Note:] [`yield()` is ['not] an interruption point. A fiber that calls
|
||||
`yield()` is not suspended: it is immediately passed to the scheduler as ready
|
||||
to run.]]
|
||||
]
|
||||
|
||||
< TODO >
|
||||
[ns_function_heading this_fiber..properties]
|
||||
|
||||
#include <boost/fiber/operations.hpp>
|
||||
@@ -674,9 +697,19 @@ there are no guarantees about how soon after that it might resume.]]
|
||||
PROPS & properties();
|
||||
|
||||
[variablelist
|
||||
[[Effects:] []]
|
||||
[[Throws:] []]
|
||||
[[Note:] []]
|
||||
[[Preconditions:] [[function_link set_scheduling_algorithm] has been called
|
||||
from this thread with a pointer to a (subclass) instance of [template_link
|
||||
sched_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 `set_scheduling_algorithm()` was passed a
|
||||
pointer to a `sched_algorithm_with_properties` subclass with some other
|
||||
template parameter than `PROPS`.]]
|
||||
[[Note:] [[template_link sched_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.]]
|
||||
[[See also:] [[link custom]]]
|
||||
]
|
||||
|
||||
[ns_function_heading this_fiber..interruption_point]
|
||||
|
||||
@@ -64,6 +64,11 @@
|
||||
]
|
||||
[template static_member_link[class_name method_name] [member_link [class_name]..[method_name]]]
|
||||
|
||||
[template data_member_heading[class_name member_name]
|
||||
[hding [class_name]_[member_name]..Data member [`[member_name]]]
|
||||
]
|
||||
[template data_member_link[class_name member_name] [member_link [class_name]..[member_name]]]
|
||||
|
||||
[template function_heading[function_name]
|
||||
[hding [function_name]..Non-member function [`[function_name]()]]
|
||||
]
|
||||
@@ -87,7 +92,7 @@
|
||||
[def __barrier__ [class_link barrier]]
|
||||
[def __condition__ [class_link condition_variable]]
|
||||
[def __disable_interruption__ [class_link disable_interruption]]
|
||||
[def __econtext__ ['execution_context]]
|
||||
[def __econtext__ [@boost:/libs/context/doc/html/context/econtext.html ['execution_context]]]
|
||||
[def __fiber__ [class_link fiber]]
|
||||
[def __fiber_exception__ `fiber_exception`]
|
||||
[def __fiber_group__ [class_link fiber_group]]
|
||||
@@ -111,8 +116,9 @@
|
||||
[def __segmented_stack__ [class_link segmented_stack]]
|
||||
[def __segmented_stack_stack__ ['segmented_stack-stack]]
|
||||
[def __shared_future__ [template_link shared_future]]
|
||||
[def __stack_allocator__ ['stack_allocator]]
|
||||
[def __stack_allocator_concept__ [link stack_allocator_concept ['stack-allocator concept]]]
|
||||
[def __stack_context__ [class_link stack_context]]
|
||||
[def __stack_context__ [@boost:/libs/context/doc/html/context/stack/stack_context.html `stack_context`]]
|
||||
[def __timed_mutex__ [class_link timed_mutex]]
|
||||
[def __wait_for__ [member_link future..wait_for]]
|
||||
[def __wait__ [member_link future..wait]]
|
||||
@@ -160,7 +166,16 @@
|
||||
[include fiber.qbk]
|
||||
[include scheduling.qbk]
|
||||
[include stack.qbk]
|
||||
[#synchronization]
|
||||
[section:synchronization Synchronization]
|
||||
|
||||
In general, __boost_fiber__ synchronization objects can neither be moved nor
|
||||
copied. A synchronization object acts as a mutually-agreed rendezvous point
|
||||
between different fibers. If such an object were copied somewhere else, the
|
||||
new copy would have no consumers. If such an object were ['moved] somewhere
|
||||
else, leaving the original instance in an unspecified state, existing
|
||||
consumers would behave strangely.
|
||||
|
||||
[include mutexes.qbk]
|
||||
[include condition_variables.qbk]
|
||||
[include barrier.qbk]
|
||||
|
||||
@@ -58,12 +58,6 @@ A __future__ contains a shared state which is not shared with any other future.
|
||||
|
||||
future & operator=( future const& other) = delete;
|
||||
|
||||
void swap( future & other) noexcept;
|
||||
|
||||
operator safe_bool() const noexcept;
|
||||
|
||||
bool operator!() const noexcept;
|
||||
|
||||
bool valid() const noexcept;
|
||||
|
||||
shared_future< R > share();
|
||||
@@ -72,6 +66,8 @@ A __future__ contains a shared state which is not shared with any other future.
|
||||
R & get(); // member only of future< R & > template specialization
|
||||
void get(); // member only of future< void > template specialization
|
||||
|
||||
std::exception_ptr get_exception_ptr();
|
||||
|
||||
void wait() const;
|
||||
|
||||
template< class Rep, class Period >
|
||||
@@ -120,33 +116,6 @@ After construction is `false == other.valid()`]]
|
||||
[[Throws:] [Nothing.]]
|
||||
]
|
||||
|
||||
[member_heading future..swap]
|
||||
|
||||
void swap( future & other) noexcept;
|
||||
|
||||
[variablelist
|
||||
[[Effects:] [Swaps the shared state between other and `this`.]]
|
||||
[[Throws:] [Nothing.]]
|
||||
]
|
||||
|
||||
[member_heading future..operator safe_bool]
|
||||
|
||||
operator safe_bool() const noexcept;
|
||||
|
||||
[variablelist
|
||||
[[Effects:] [Returns `true` if future is valid.]]
|
||||
[[Throws:] [Nothing.]]
|
||||
]
|
||||
|
||||
[operator_heading future..operator_not..operator!]
|
||||
|
||||
bool operator!() const noexcept;
|
||||
|
||||
[variablelist
|
||||
[[Effects:] [Returns `false` if future is valid.]]
|
||||
[[Throws:] [Nothing.]]
|
||||
]
|
||||
|
||||
[member_heading future..valid]
|
||||
|
||||
bool valid() const noexcept;
|
||||
@@ -173,11 +142,20 @@ After construction is `false == other.valid()`]]
|
||||
|
||||
[variablelist
|
||||
[[Precondition:] [`true == valid()`]]
|
||||
[[Effects:] [Returns the result.]]
|
||||
[[Returns:] [the result. If result not yet set, waits.]]
|
||||
[[Postcondition:] [`false == valid()`]]
|
||||
[[Throws:] [__future_error__ with error condition __no_state__.]]
|
||||
]
|
||||
|
||||
[member_heading future..get_exception_ptr]
|
||||
|
||||
std::exception_ptr get_exception_ptr();
|
||||
|
||||
[variablelist
|
||||
[[Precondition:] [`true == valid()`]]
|
||||
[[Returns:] [the exception pointer, if set. If no result is set, waits.]]
|
||||
]
|
||||
|
||||
[member_heading future..wait]
|
||||
|
||||
void wait();
|
||||
@@ -233,12 +211,6 @@ A __shared_future__ contains a shared state which might be shared with other fut
|
||||
|
||||
shared_future & operator=( shared_future const& other) noexcept;
|
||||
|
||||
void swap( shared_future & other) noexcept;
|
||||
|
||||
operator safe_bool() const noexcept;
|
||||
|
||||
bool operator!() const noexcept;
|
||||
|
||||
bool valid() const noexcept;
|
||||
|
||||
R const& get(); // member only of generic shared_future template
|
||||
@@ -277,7 +249,7 @@ After construction is `false == other.valid()`.]]
|
||||
|
||||
[heading Copy constructor]
|
||||
|
||||
shared_future( shared_future cosnt& other) noexcept;
|
||||
shared_future( shared_future const& other) noexcept;
|
||||
|
||||
[variablelist
|
||||
[[Effects:] [Constructs a future with the shared state of other.
|
||||
@@ -310,33 +282,6 @@ return * this;
|
||||
[[Throws:] [Nothing.]]
|
||||
]
|
||||
|
||||
[member_heading shared_future..swap]
|
||||
|
||||
void swap( shared_future & other) noexcept;
|
||||
|
||||
[variablelist
|
||||
[[Effects:] [Swaps the shared state between other and `this`.]]
|
||||
[[Throws:] [Nothing.]]
|
||||
]
|
||||
|
||||
[member_heading shared_future..operator safe_bool]
|
||||
|
||||
operator safe_bool() const noexcept;
|
||||
|
||||
[variablelist
|
||||
[[Effects:] [Returns `true` if future is valid.]]
|
||||
[[Throws:] [Nothing.]]
|
||||
]
|
||||
|
||||
[operator_heading shared_future..operator_not..operator!]
|
||||
|
||||
bool operator!() const noexcept;
|
||||
|
||||
[variablelist
|
||||
[[Effects:] [Returns `false` if future is valid.]]
|
||||
[[Throws:] [Nothing.]]
|
||||
]
|
||||
|
||||
[member_heading shared_future..valid]
|
||||
|
||||
bool valid() const noexcept;
|
||||
@@ -359,6 +304,15 @@ return * this;
|
||||
[[Throws:] [__future_error__ with error condition __no_state__.]]
|
||||
]
|
||||
|
||||
[member_heading shared_future..get_exception_ptr]
|
||||
|
||||
std::exception_ptr get_exception_ptr();
|
||||
|
||||
[variablelist
|
||||
[[Precondition:] [`true == valid()`]]
|
||||
[[Returns:] [the exception pointer, if set. If no result is set, waits.]]
|
||||
]
|
||||
|
||||
[member_heading shared_future..wait]
|
||||
|
||||
void wait();
|
||||
|
||||
142
doc/mutexes.qbk
142
doc/mutexes.qbk
@@ -19,7 +19,7 @@
|
||||
|
||||
mutex( mutex const& other) = delete;
|
||||
mutex & operator=( mutex const& other) = delete;
|
||||
|
||||
|
||||
void lock();
|
||||
bool try_lock();
|
||||
void unlock();
|
||||
@@ -32,6 +32,31 @@ __lock__, __try_lock__ and __unlock__ shall be permitted.
|
||||
Any fiber blocked in __lock__ is suspended in the scheduler until the owning
|
||||
fiber releases the lock by calling __unlock__.
|
||||
|
||||
[member_heading mutex..lock]
|
||||
|
||||
void lock();
|
||||
|
||||
[variablelist
|
||||
[[Effects:] []]
|
||||
]
|
||||
|
||||
[member_heading mutex..try_lock]
|
||||
|
||||
bool try_lock();
|
||||
|
||||
[variablelist
|
||||
[[Effects:] []]
|
||||
[[Returns:] []]
|
||||
]
|
||||
|
||||
[member_heading mutex..unlock]
|
||||
|
||||
void unlock();
|
||||
|
||||
[variablelist
|
||||
[[Effects:] []]
|
||||
]
|
||||
|
||||
|
||||
[class_heading timed_mutex]
|
||||
|
||||
@@ -60,6 +85,51 @@ 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
|
||||
__unlock__ shall be permitted.
|
||||
|
||||
[member_heading timed_mutex..lock]
|
||||
|
||||
void lock();
|
||||
|
||||
[variablelist
|
||||
[[Effects:] []]
|
||||
]
|
||||
|
||||
[member_heading timed_mutex..try_lock]
|
||||
|
||||
bool try_lock();
|
||||
|
||||
[variablelist
|
||||
[[Effects:] []]
|
||||
[[Returns:] []]
|
||||
]
|
||||
|
||||
[member_heading timed_mutex..unlock]
|
||||
|
||||
void unlock();
|
||||
|
||||
[variablelist
|
||||
[[Effects:] []]
|
||||
]
|
||||
|
||||
[template_member_heading timed_mutex..try_lock_until]
|
||||
|
||||
template< typename Clock, typename Duration >
|
||||
bool try_lock_until( std::chrono::time_point< Clock, Duration > const& timeout_time)
|
||||
|
||||
[variablelist
|
||||
[[Effects:] []]
|
||||
[[Returns:] []]
|
||||
]
|
||||
|
||||
[template_member_heading timed_mutex..try_lock_for]
|
||||
|
||||
template< typename Rep, typename Period >
|
||||
bool try_lock_for( std::chrono::duration< Rep, Period > const& timeout_duration)
|
||||
|
||||
[variablelist
|
||||
[[Effects:] []]
|
||||
[[Returns:] []]
|
||||
]
|
||||
|
||||
|
||||
[class_heading recursive_mutex]
|
||||
|
||||
@@ -87,6 +157,31 @@ additional level of ownership of the mutex. __unlock__ must be called once for
|
||||
each level of ownership acquired by a single fiber before ownership can be
|
||||
acquired by another fiber.
|
||||
|
||||
[member_heading recursive_mutex..lock]
|
||||
|
||||
void lock();
|
||||
|
||||
[variablelist
|
||||
[[Effects:] []]
|
||||
]
|
||||
|
||||
[member_heading recursive_mutex..try_lock]
|
||||
|
||||
bool try_lock();
|
||||
|
||||
[variablelist
|
||||
[[Effects:] []]
|
||||
[[Returns:] []]
|
||||
]
|
||||
|
||||
[member_heading recursive_mutex..unlock]
|
||||
|
||||
void unlock();
|
||||
|
||||
[variablelist
|
||||
[[Effects:] []]
|
||||
]
|
||||
|
||||
|
||||
[class_heading recursive_timed_mutex]
|
||||
|
||||
@@ -121,5 +216,50 @@ ownership of the mutex. __unlock__ must be called once for each level of
|
||||
ownership acquired by a single fiber before ownership can be acquired by another
|
||||
fiber.
|
||||
|
||||
[member_heading recursive_timed_mutex..lock]
|
||||
|
||||
void lock();
|
||||
|
||||
[variablelist
|
||||
[[Effects:] []]
|
||||
]
|
||||
|
||||
[member_heading recursive_timed_mutex..try_lock]
|
||||
|
||||
bool try_lock();
|
||||
|
||||
[variablelist
|
||||
[[Effects:] []]
|
||||
[[Returns:] []]
|
||||
]
|
||||
|
||||
[member_heading recursive_timed_mutex..unlock]
|
||||
|
||||
void unlock();
|
||||
|
||||
[variablelist
|
||||
[[Effects:] []]
|
||||
]
|
||||
|
||||
[template_member_heading recursive_timed_mutex..try_lock_until]
|
||||
|
||||
template< typename Clock, typename Duration >
|
||||
bool try_lock_until( std::chrono::time_point< Clock, Duration > const& timeout_time)
|
||||
|
||||
[variablelist
|
||||
[[Effects:] []]
|
||||
[[Returns:] []]
|
||||
]
|
||||
|
||||
[template_member_heading recursive_timed_mutex..try_lock_for]
|
||||
|
||||
template< typename Rep, typename Period >
|
||||
bool try_lock_for( std::chrono::duration< Rep, Period > const& timeout_duration)
|
||||
|
||||
[variablelist
|
||||
[[Effects:] []]
|
||||
[[Returns:] []]
|
||||
]
|
||||
|
||||
|
||||
[endsect]
|
||||
|
||||
@@ -17,8 +17,8 @@ Each fiber has its own stack.
|
||||
A fiber can save the current execution state, including all registers
|
||||
and CPU flags, the instruction pointer, and the stack pointer and later restore
|
||||
this state.
|
||||
The idea is to have multiple execution paths running on a single thread using a
|
||||
sort of cooperative scheduling (versus threads, which are preemptively scheduled). The
|
||||
The idea is to have multiple execution paths running on a single thread using
|
||||
cooperative scheduling (versus threads, which are preemptively scheduled). The
|
||||
running fiber decides explicitly when it should yield to allow another fiber to
|
||||
run (context switching).
|
||||
__boost_fiber__ internally uses __econtext__ from __boost_context__; the classes in
|
||||
@@ -68,6 +68,11 @@ A fiber launched on a particular thread will always run on that thread. A
|
||||
fiber can count on thread-local storage; however that storage will be shared
|
||||
among all fibers running on the same thread.
|
||||
|
||||
The fiber synchronization objects provided by this library will not, by
|
||||
default, safely synchronize fibers running on different threads. However, they
|
||||
can be coerced to provide that safety by building the library with
|
||||
`BOOST_FIBERS_THREADSAFE` defined. Please see [link synchronization].
|
||||
|
||||
For fiber-local storage, please see __fsp__.
|
||||
|
||||
[#blocking]
|
||||
@@ -90,15 +95,12 @@ entire thread. Fiber authors are encouraged to consistently use asynchronous
|
||||
I/O. __boost_asio__ explicitly supports fibers; other asynchronous I/O
|
||||
operations can straightforwardly be adapted for __boost_fiber__.
|
||||
|
||||
Synchronization between a fiber running on one thread and a fiber running on a
|
||||
different thread is an advanced topic.
|
||||
|
||||
[warning This library is ['not] an official Boost library]
|
||||
[warning This library is ['not] an official Boost library.]
|
||||
|
||||
__boost_fiber__ depends upon __boost_context__.
|
||||
Boost version 1.58.0 or greater is required.
|
||||
|
||||
[info This library is C++14-only!]
|
||||
[note This library is C++14-only!]
|
||||
|
||||
|
||||
[endsect]
|
||||
|
||||
@@ -95,10 +95,11 @@ channel operations return the state of the channel.
|
||||
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);
|
||||
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);
|
||||
std::chrono::time_point< Clock,
|
||||
Duration > const& timeout_time);
|
||||
};
|
||||
|
||||
[member_heading unbounded_channel..is_closed]
|
||||
@@ -118,7 +119,8 @@ channel operations return the state of the channel.
|
||||
[variablelist
|
||||
[[Effects:] [Deactivates the channel. No values can be put after calling
|
||||
`this->close()`. Fibers blocked in `this->pop()`, `this->pop_wait_for()`
|
||||
or `this->pop_wait_until()` will return `closed`.]]
|
||||
or `this->pop_wait_until()` will return `closed`. Fibers blocked in
|
||||
`this->value_pop()` will receive an exception.]]
|
||||
[[Throws:] [Nothing.]]
|
||||
[[Note:] [`close()` is like closing a pipe. It informs waiting consumers
|
||||
that no more values will arrive.]]
|
||||
@@ -140,7 +142,7 @@ non-empty.]]
|
||||
channel_op_status push( value_type && va);
|
||||
|
||||
[variablelist
|
||||
[[Effects:] [Enchannels the value in the channel and wakes up a fiber blocked on
|
||||
[[Effects:] [Enqueues the value in the channel and wakes up a fiber blocked on
|
||||
`this->pop()`, `this->pop_wait_for()` or `this->pop_wait_until()`.]]
|
||||
[[Throws:] [Nothing.]]
|
||||
]
|
||||
@@ -150,9 +152,9 @@ non-empty.]]
|
||||
channel_op_status pop( value_type & va);
|
||||
|
||||
[variablelist
|
||||
[[Effects:] [Dechannels a value from the channel. If the channel `is_empty()`, the
|
||||
[[Effects:] [Dequeues a value from the channel. If the channel `is_empty()`, the
|
||||
fiber gets suspended until at least one new item is `push()`ed (return value
|
||||
`success` and `va` contains dechanneld value) or the channel gets `close()`d
|
||||
`success` and `va` contains dequeued value) or the channel gets `close()`d
|
||||
(return value `closed`).]]
|
||||
[[Throws:] [__fiber_interrupted__]]
|
||||
]
|
||||
|
||||
@@ -5,132 +5,324 @@
|
||||
http://www.boost.org/LICENSE_1_0.txt
|
||||
]
|
||||
|
||||
[#scheduling]
|
||||
[section:scheduling Scheduling]
|
||||
|
||||
Fibers are managed by a scheduler which coordinates the sequence of fibers
|
||||
running or waiting.
|
||||
Each thread is required to have its own scheduler. By default, __boost_fiber__
|
||||
creates for each thread a scheduler ([class_link round_robin]).
|
||||
The fibers in a thread are coordinated by a fiber manager. Fibers trade
|
||||
control cooperatively, rather than preemptively: the currently-running fiber
|
||||
retains control until it invokes some operation that passes control to the
|
||||
manager. Each time a fiber suspends (or yields), the fiber manager consults a
|
||||
scheduler to determine which fiber will run next.
|
||||
|
||||
__boost_fiber__ provides the fiber manager, but the scheduler is a
|
||||
customization point. (See [link custom].)
|
||||
|
||||
Each thread has its own scheduler. By default, __boost_fiber__ implicitly
|
||||
instantiates [class_link round_robin] as the scheduler for each thread.
|
||||
|
||||
To prevent the library from heap-allocating the default scheduler for a given
|
||||
thread, that thread must call [function_link set_scheduling_algorithm] before
|
||||
any other __boost_fiber__ entry point.
|
||||
|
||||
A fiber-scheduler must implement interface __algo__.
|
||||
You are explicitly permitted to code your own __algo__ subclass, and to pass
|
||||
it to [function_link set_scheduling_algorithm].
|
||||
|
||||
|
||||
void thread_fn() {
|
||||
my_fiber_scheduler mfs;
|
||||
boost::fibers::set_scheduling_algorithm( & mfs);
|
||||
...
|
||||
}
|
||||
|
||||
|
||||
[class_heading sched_algorithm]
|
||||
|
||||
#include <boost/fiber/algorithm.hpp>
|
||||
|
||||
struct sched_algorithm {
|
||||
virtual ~sched_algorithm() {}
|
||||
|
||||
virtual void awakened( fiber_context *) = 0;
|
||||
|
||||
virtual fiber_context * pick_next() = 0;
|
||||
};
|
||||
|
||||
[member_heading sched_algorithm..awakened]
|
||||
|
||||
virtual void awakened( fiber_context * f) = 0;
|
||||
|
||||
[variablelist
|
||||
[[Effects:] [Marks fiber `f`, to be ready to run.]]
|
||||
]
|
||||
|
||||
[member_heading sched_algorithm..pick_next]
|
||||
|
||||
virtual fiber_context * 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( fiber_context *, int) noexcept = 0;
|
||||
|
||||
[variablelist
|
||||
[[Effects:] [Resets the priority of fiber `f`.]]
|
||||
]
|
||||
|
||||
|
||||
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].
|
||||
A scheduler class must implement interface __algo__. __boost_fiber__ provides
|
||||
one scheduler: [class_link round_robin].
|
||||
|
||||
|
||||
[class_heading sched_algorithm]
|
||||
|
||||
`sched_algorithm` is the abstract base class defining the interface that a
|
||||
fiber scheduler must implement.
|
||||
|
||||
#include <boost/fiber/algorithm.hpp>
|
||||
|
||||
struct sched_algorithm
|
||||
{
|
||||
virtual ~sched_algorithm() {}
|
||||
|
||||
virtual void awakened( detail::worker_fiber *) = 0;
|
||||
virtual void awakened( fiber_context *) = 0;
|
||||
|
||||
virtual detail::worker_fiber * pick_next() = 0;
|
||||
virtual fiber_context * pick_next() = 0;
|
||||
|
||||
virtual void priority( detail::worker_fiber *, int) noexcept = 0;
|
||||
virtual std::size_t ready_fibers() const noexcept = 0;
|
||||
};
|
||||
|
||||
[member_heading sched_algorithm..awakened]
|
||||
|
||||
virtual void awakened( detail::worker_fiber * f) = 0;
|
||||
virtual void awakened( fiber_context * f) = 0;
|
||||
|
||||
[variablelist
|
||||
[[Effects:] [Marks fiber `f`, to be ready to run.]]
|
||||
[[Effects:] [Informs the scheduler that fiber `f` is ready to run. Fiber `f`
|
||||
might be newly launched, or it might have been blocked but has just been
|
||||
awakened, or it might have called [ns_function_link this_fiber..yield].]]
|
||||
[[Note:] [This method advises the scheduler to add fiber `f` to its collection
|
||||
of fibers ready to run. A typical scheduler implementation places `f` into a
|
||||
queue.]]
|
||||
[[See also:] [[class_link round_robin]]]
|
||||
]
|
||||
|
||||
[member_heading sched_algorithm..pick_next]
|
||||
|
||||
virtual detail::worker_fiber * pick_next() = 0;
|
||||
virtual fiber_context * pick_next() = 0;
|
||||
|
||||
[variablelist
|
||||
[[Effects:] [Depending on the scheduling algorithm, this function returns the
|
||||
fiber which is to be resumed next.]]
|
||||
[[Returns:] [the fiber which is to be resumed next, or `nullptr` if there is no
|
||||
ready fiber.]]
|
||||
[[Note:] [This is where the scheduler actually specifies the fiber which is to
|
||||
run next. A typical scheduler implementation chooses the head of the ready
|
||||
queue.]]
|
||||
[[See also:] [[class_link round_robin]]]
|
||||
]
|
||||
|
||||
[member_heading sched_algorithm..priority]
|
||||
[member_heading sched_algorithm..ready_fibers]
|
||||
|
||||
virtual void priority( detail::worker_fiber *, int) noexcept = 0;
|
||||
virtual std::size_t ready_fibers() const noexcept = 0;
|
||||
|
||||
[variablelist
|
||||
[[Effects:] [Resets the priority of fiber `f`.]]
|
||||
[[Effects:] [Returns 0 if scheduling algorithm has fibers ready to run,
|
||||
otherwise nonzero.]]
|
||||
]
|
||||
|
||||
|
||||
[class_heading round_robin]
|
||||
|
||||
This class implements __algo__ and schedules fibers in round-robin fashion
|
||||
(ignores `fiber::priority()`).
|
||||
This class implements __algo__, scheduling fibers in round-robin fashion.
|
||||
|
||||
[info The example section provides a scheduler used for migrating fibers
|
||||
(work-stealing) between threads (different schedulers).]
|
||||
#include <boost/fiber/round_robin.hpp>
|
||||
|
||||
class round_robin: public sched_algorithm
|
||||
{
|
||||
virtual void awakened( fiber_context *);
|
||||
|
||||
virtual fiber_context * pick_next();
|
||||
};
|
||||
|
||||
[member_heading round_robin..awakened]
|
||||
|
||||
virtual void awakened( fiber_context * f);
|
||||
|
||||
[variablelist
|
||||
[[Effects:] [Enqueues fiber `f` onto a ready queue.]]
|
||||
]
|
||||
|
||||
[member_heading round_robin..pick_next]
|
||||
|
||||
virtual fiber_context * pick_next();
|
||||
|
||||
[variablelist
|
||||
[[Returns:] [the fiber at the head of the ready queue, or 0 if the queue is
|
||||
empty.]]
|
||||
[[Note:] [Placing ready fibers onto a queue, and returning them from the head
|
||||
of that queue, shares the thread between ready fibers in round-robin fashion.]]
|
||||
]
|
||||
|
||||
|
||||
< TODO >
|
||||
[heading customized scheduler]
|
||||
[heading Custom Scheduler Fiber Properties]
|
||||
|
||||
A scheduler class directly derived from __algo__ can use any information
|
||||
available from [class_link fiber_context] to implement the `sched_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.
|
||||
|
||||
__boost_fiber__ provides a mechanism by which your custom scheduler can
|
||||
associate custom properties with each fiber.
|
||||
|
||||
[class_heading fiber_properties]
|
||||
|
||||
A custom fiber properties class must be derived from `fiber_properties`.
|
||||
|
||||
#include <boost/fiber/properties.hpp>
|
||||
|
||||
class fiber_properties
|
||||
{
|
||||
public:
|
||||
fiber_properties( back_ptr f );
|
||||
|
||||
virtual ~fiber_properties() {}
|
||||
|
||||
protected:
|
||||
void notify();
|
||||
};
|
||||
|
||||
[heading Constructor]
|
||||
|
||||
fiber_properties( back_ptr f );
|
||||
|
||||
[variablelist
|
||||
[[Effects:] [Constructs base-class component of custom subclass.]]
|
||||
[[Note:] [Your subclass constructor must accept a `back_ptr` and pass it to
|
||||
the base-class `fiber_properties` constructor.]]
|
||||
]
|
||||
|
||||
[member_heading fiber_properties..notify]
|
||||
|
||||
void notify();
|
||||
|
||||
[variablelist
|
||||
[[Effects:] [Pass control to the custom [template_link
|
||||
sched_algorithm_with_properties] subclass's [member_link
|
||||
sched_algorithm_with_properties..property_change] method.]]
|
||||
[[Note:] [A custom scheduler's [member_link
|
||||
sched_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
|
||||
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
|
||||
given the opportunity to reorder its ready queue. The custom property subclass
|
||||
should implement an access method to modify such a property; that access
|
||||
method should call `notify()` once the new property value has been stored.
|
||||
This passes control to the custom scheduler's `property_change()` method,
|
||||
allowing the custom scheduler to reorder its ready queue appropriately. Use at
|
||||
your discretion. Of course, if you define a property which does not affect the
|
||||
behavior of the `pick_next()` method, you need not call `notify()` when that
|
||||
property is modified.]]
|
||||
]
|
||||
|
||||
[template_heading sched_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 [class_link fiber_properties].
|
||||
|
||||
#include <boost/fiber/algorithm.hpp>
|
||||
|
||||
template< typename PROPS >
|
||||
struct sched_algorithm_with_properties
|
||||
{
|
||||
// override this method instead of sched_algorithm::awakened()
|
||||
virtual void awakened( fiber_context * f, PROPS& properties ) = 0;
|
||||
|
||||
virtual fiber_context * pick_next();
|
||||
|
||||
// obtain f's associated PROPS instance
|
||||
PROPS& properties( fiber_context * f);
|
||||
|
||||
// override this to be notified by PROPS::notify()
|
||||
virtual void property_change( fiber_context * f, PROPS & properties);
|
||||
|
||||
// Override this to customize instantiation of PROPS, e.g. use a different
|
||||
// allocator. Each PROPS instance is associated with a particular
|
||||
// fiber_context.
|
||||
virtual fiber_properties * new_properties( fiber_context * f);
|
||||
};
|
||||
|
||||
[member_heading sched_algorithm_with_properties..awakened]
|
||||
|
||||
virtual void awakened( fiber_context * f, PROPS& properties );
|
||||
|
||||
[variablelist
|
||||
[[Effects:] [Informs the scheduler that fiber `f` is ready to run, like
|
||||
[member_link sched_algorithm..awakened]. Passes the fiber's associated `PROPS`
|
||||
instance.]]
|
||||
[[Note:] [A `sched_algorithm_with_properties<>` subclass must override this
|
||||
method instead of `sched_algorithm::awakened()`.]]
|
||||
]
|
||||
|
||||
[member_heading sched_algorithm_with_properties..pick_next]
|
||||
|
||||
virtual fiber_context * pick_next();
|
||||
|
||||
[variablelist
|
||||
[[Returns:] [the fiber which is to be resumed next, or `nullptr` if there is no
|
||||
ready fiber.]]
|
||||
[[Note:] [same as [member_link sched_algorithm..pick_next]]]
|
||||
]
|
||||
|
||||
[member_heading sched_algorithm_with_properties..properties]
|
||||
|
||||
PROPS& properties( fiber_context * f);
|
||||
|
||||
[variablelist
|
||||
[[Returns:] [the `PROPS` instance associated with fiber `f`.]]
|
||||
[[Note:] [The fiber's associated `PROPS` instance is already passed to
|
||||
`awakened()` and `property_change()`. However, every [class_link
|
||||
sched_algorithm] subclass is expected to track a collection of ready
|
||||
[class_link fiber_context] instances. This method allows your custom scheduler
|
||||
to retrieve the [class_link fiber_properties] subclass instance for any
|
||||
`fiber_context` in its collection.]]
|
||||
]
|
||||
|
||||
[member_heading sched_algorithm_with_properties..property_change]
|
||||
|
||||
virtual void property_change( fiber_context * f, PROPS & properties);
|
||||
|
||||
[variablelist
|
||||
[[Effects:] [Notify the custom scheduler of a possibly-relevant change to a
|
||||
property belonging to fiber `f`. `properties` contains the new values of
|
||||
all relevant properties.]]
|
||||
[[Note:] [This method is only called when a custom [class_link
|
||||
fiber_properties] subclass explicitly calls [member_link
|
||||
fiber_properties..notify].]]
|
||||
]
|
||||
|
||||
[member_heading sched_algorithm_with_properties..new_properties]
|
||||
|
||||
virtual fiber_properties * new_properties( fiber_context * f);
|
||||
|
||||
[variablelist
|
||||
[[Returns:] [A new instance of [class_link fiber_properties] subclass
|
||||
`PROPS`.]]
|
||||
[[Note:] [By default, `sched_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
|
||||
with fiber `f`.]]
|
||||
]
|
||||
|
||||
[class_heading fiber_context]
|
||||
|
||||
While you are free to treat `fiber_context*` as an opaque token, certain
|
||||
`fiber_context` members may be useful to a custom scheduler implementation.
|
||||
|
||||
(Most `fiber_context` members are implementation details; most of interest are
|
||||
implementations of [class_link fiber] methods.)
|
||||
|
||||
#include <boost/fiber/fiber_context.hpp>
|
||||
|
||||
class fiber_context
|
||||
{
|
||||
public:
|
||||
|
||||
fiber_context * nxt;
|
||||
|
||||
static fiber_context * main_fiber();
|
||||
|
||||
};
|
||||
|
||||
[data_member_heading fiber_context..nxt]
|
||||
|
||||
fiber_context * nxt;
|
||||
|
||||
[variablelist
|
||||
[[Note:] [This link pointer may be used to help implement a custom scheduler's
|
||||
ready queue. It is overwritten by the fiber manager every time this
|
||||
`fiber_context` is returned by [member_link sched_algorithm..pick_next], but
|
||||
between the time the fiber manager passes this `fiber_context` to [member_link
|
||||
sched_algorithm..awakened] (or [member_link
|
||||
sched_algorithm_with_properties..awakened]) and the time your `pick_next()`
|
||||
returns it to the fiber manager, it is available for use by your custom
|
||||
scheduler.]]
|
||||
]
|
||||
|
||||
[static_member_heading fiber_context..main_fiber]
|
||||
|
||||
static fiber_context * main_fiber();
|
||||
|
||||
[variablelist
|
||||
[[Returns:] [the `fiber_context` associated with the "main" fiber of the
|
||||
thread: the one implicitly created by the thread itself, rather than one
|
||||
explicitly created by __boost_fiber__.]]
|
||||
]
|
||||
|
||||
[endsect]
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
http://www.boost.org/LICENSE_1_0.txt
|
||||
]
|
||||
|
||||
[#stack]
|
||||
[section:stack Stack allocation]
|
||||
|
||||
A __fiber__ uses internally a __econtext__ which manages a set of registers and a stack.
|
||||
@@ -15,7 +16,7 @@ which is required to model a __stack_allocator_concept__.
|
||||
[heading __stack_allocator_concept__]
|
||||
A __stack_allocator__ must satisfy the __stack_allocator_concept__ requirements
|
||||
shown in the following table, in which `a` is an object of a
|
||||
__stack_allocator__ type, `sctx` is a `stack_context`, and `size` is a `std::size_t`:
|
||||
__stack_allocator__ type, `sctx` is a __stack_context__, and `size` is a `std::size_t`:
|
||||
|
||||
[table
|
||||
[[expression][return type][notes]]
|
||||
@@ -26,7 +27,7 @@ __stack_allocator__ type, `sctx` is a `stack_context`, and `size` is a `std::siz
|
||||
]
|
||||
[
|
||||
[`a.allocate()`]
|
||||
[`stack_context`]
|
||||
[__stack_context__]
|
||||
[creates a stack]
|
||||
]
|
||||
[
|
||||
@@ -40,12 +41,13 @@ __stack_allocator__ type, `sctx` is a `stack_context`, and `size` is a `std::siz
|
||||
against exceeding the context's available stack size rather than leaving it as
|
||||
undefined behaviour.]
|
||||
|
||||
[important Calling `deallocate()` with a `stack_context` not previously passed
|
||||
to `allocate()` results in undefined behaviour.]
|
||||
[important Calling `deallocate()` with a __stack_context__ not obtained from
|
||||
`allocate()` results in undefined behaviour.]
|
||||
|
||||
[note The memory for the stack is not required to be aligned; alignment takes
|
||||
place inside __econtext__.]
|
||||
|
||||
See also [@boost:/libs/context/doc/html/context/stack.html Boost.Context stack allocation].
|
||||
|
||||
[class_heading protected_fixedsize_stack]
|
||||
|
||||
@@ -55,9 +57,9 @@ It appends a guard page at the end of each stack to protect against exceeding
|
||||
the stack. If the guard page is accessed (read or write operation) a
|
||||
segmentation fault/access violation is generated by the operating system.
|
||||
|
||||
[important Using __pfixedsize_stack__ is expensive. That is, launching a
|
||||
new fiber with a new stack is expensive; the allocated stack is just as
|
||||
efficient to use as any other stack.]
|
||||
[important Using __pfixedsize_stack__ is expensive. Launching a new fiber with
|
||||
a stack of this type incurs the overhead of setting the memory protection;
|
||||
once allocated, this stack is just as efficient to use as __fixedsize_stack__.]
|
||||
|
||||
[note The appended `guard page` is [*not] mapped to physical memory, only
|
||||
virtual addresses are used.]
|
||||
@@ -74,19 +76,17 @@ end of each stack. The memory is simply managed by `std::malloc()` and
|
||||
|
||||
[class_heading segmented_stack]
|
||||
|
||||
__boost_fiber__ supports usage of a __segmented_stack__, e. g. the size of
|
||||
__boost_fiber__ supports usage of a __segmented_stack__, i.e.
|
||||
the stack grows on demand. The fiber is created with a minimal stack size
|
||||
and will be increased as required.
|
||||
which will be increased as required.
|
||||
Class __segmented_stack__ models the __stack_allocator_concept__.
|
||||
In contrast to __pfixedsize_stack__ and __fixedsize_stack__ it creates a
|
||||
stack which grows on demand.
|
||||
|
||||
[note Segmented stacks are currently only supported by [*gcc] from version
|
||||
[*4.7] [*clang] from version [*3.4] onwards. In order to use a
|
||||
__segmented_stack__ __boost_fiber__ must be built with
|
||||
[*4.7] and [*clang] from version [*3.4] onwards. In order to use a
|
||||
__segmented_stack__, __boost_fiber__ must be built with
|
||||
property `segmented-stacks`, e.g. [*toolset=gcc segmented-stacks=on] at b2/bjam
|
||||
command line.
|
||||
|
||||
[endsect]
|
||||
command line.]
|
||||
|
||||
[endsect]
|
||||
|
||||
@@ -56,10 +56,10 @@ public:
|
||||
{}
|
||||
|
||||
// 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)
|
||||
// override the correct awakened() overload.
|
||||
virtual void awakened(boost::fibers::fiber_context* f, priority_props& props)
|
||||
{
|
||||
int f_priority = properties(f).get_priority();
|
||||
int f_priority = props.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
|
||||
@@ -75,7 +75,7 @@ public:
|
||||
f->nxt = *fp;
|
||||
*fp = f;
|
||||
|
||||
std::cout << "awakened(" << properties(f).name << "): ";
|
||||
std::cout << "awakened(" << props.name << "): ";
|
||||
describe_ready_queue();
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include <cstddef>
|
||||
|
||||
#include <boost/config.hpp>
|
||||
#include <boost/assert.hpp>
|
||||
|
||||
#include <boost/fiber/properties.hpp>
|
||||
#include <boost/fiber/detail/config.hpp>
|
||||
@@ -47,15 +48,20 @@ struct sched_algorithm_with_properties : public sched_algorithm_with_properties_
|
||||
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);
|
||||
// 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);
|
||||
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);
|
||||
props = new_properties( f);
|
||||
// It is not good for new_properties() to return 0.
|
||||
BOOST_ASSERT_MSG(props, "new_properties() must return non-NULL");
|
||||
// new_properties() must return instance of (a subclass of) PROPS
|
||||
BOOST_ASSERT_MSG(dynamic_cast<PROPS*>(props),
|
||||
"new_properties() must return properties class");
|
||||
super::set_properties( f, props);
|
||||
}
|
||||
// Set sched_algo_ again every time this fiber becomes READY. That
|
||||
@@ -64,11 +70,11 @@ struct sched_algorithm_with_properties : public sched_algorithm_with_properties_
|
||||
props->set_sched_algorithm( this);
|
||||
|
||||
// Okay, now forward the call to subclass override.
|
||||
awakened_props( f);
|
||||
awakened( f, properties(f) );
|
||||
}
|
||||
|
||||
// subclasses override this method instead of the original awakened()
|
||||
virtual void awakened_props( fiber_context *) = 0;
|
||||
virtual void awakened( fiber_context *, PROPS& ) = 0;
|
||||
|
||||
// used for all internal calls
|
||||
PROPS& properties( fiber_context * f) {
|
||||
@@ -83,6 +89,14 @@ struct sched_algorithm_with_properties : public sched_algorithm_with_properties_
|
||||
void property_change_( fiber_context * f, fiber_properties * props ) final {
|
||||
property_change( f, * static_cast< PROPS * >( props) );
|
||||
}
|
||||
|
||||
// Override this to customize instantiation of PROPS, e.g. use a different
|
||||
// allocator. Each PROPS instance is associated with a particular
|
||||
// fiber_context.
|
||||
virtual fiber_properties * new_properties( fiber_context * f)
|
||||
{
|
||||
return new PROPS( f);
|
||||
}
|
||||
};
|
||||
|
||||
}}
|
||||
|
||||
@@ -137,7 +137,9 @@ public:
|
||||
|
||||
template< typename PROPS >
|
||||
PROPS & properties() {
|
||||
return dynamic_cast< PROPS & >( * impl_->get_properties() );
|
||||
fiber_properties* props = impl_->get_properties();
|
||||
BOOST_ASSERT_MSG(props, "fiber::properties not set");
|
||||
return dynamic_cast< PROPS & >( * props );
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -331,7 +331,7 @@ public:
|
||||
void resume() {
|
||||
BOOST_ASSERT( is_running() ); // set by the scheduler-algorithm
|
||||
|
||||
ctx_();
|
||||
ctx_.resume();
|
||||
}
|
||||
|
||||
std::chrono::high_resolution_clock::time_point const& time_point() const noexcept {
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include <mutex> // std::unique_lock
|
||||
|
||||
#include <boost/config.hpp>
|
||||
#include <boost/assert.hpp>
|
||||
|
||||
#include <boost/fiber/detail/config.hpp>
|
||||
#include <boost/fiber/detail/scheduler.hpp>
|
||||
@@ -52,7 +53,10 @@ void sleep_for( std::chrono::duration< Rep, Period > const& timeout_duration) {
|
||||
|
||||
template< typename PROPS >
|
||||
PROPS & properties() {
|
||||
return dynamic_cast< PROPS & >( * fibers::detail::scheduler::instance()->active()->get_properties() );
|
||||
fibers::fiber_properties* props =
|
||||
fibers::detail::scheduler::instance()->active()->get_properties();
|
||||
BOOST_ASSERT_MSG(props, "this_fiber::properties not set");
|
||||
return dynamic_cast< PROPS & >( * props );
|
||||
}
|
||||
|
||||
} // this_fiber
|
||||
|
||||
Reference in New Issue
Block a user