2
0
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:
Oliver Kowalke
2015-07-20 19:40:03 +02:00
15 changed files with 608 additions and 293 deletions

View File

@@ -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]

View File

@@ -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]

View File

@@ -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]

View File

@@ -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]

View File

@@ -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();

View File

@@ -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]

View File

@@ -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]

View File

@@ -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__]]
]

View File

@@ -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]

View File

@@ -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]

View File

@@ -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();
}

View File

@@ -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);
}
};
}}

View File

@@ -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 );
}
};

View File

@@ -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 {

View File

@@ -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