Instead, when the main fiber is passed to awakened(), stash it in a separate
slot and make pick_next() return it only when the shared queue is empty.
Update ready_fibers() to include a non-empty main_fiber slot.
Add a note to the condition_variable::wait_for(..., pred) overload.
fiber_specific_ptr::reset() has no default argument.
Remove mention of launch policy deferred, since no API accepts a launch
policy argument.
Copy construction or copy assignment of a shared_future leaves other.valid()
unchanged. It won't be 'true' unless it was 'true' before.
Mention that [shared_]future::get_exception_ptr() does not invalidate.
Note that 'blocks' and 'suspends' are used interchangeably.
Add some cross-references; add link to std::allocator_arg_t. Clarify the
cross-reference to the paragraph describing BOOST_FIBERS_NO_ATOMICS.
Reformat some overly-long source lines.
When the running thread's main fiber calls this_fiber::properties<>() without
yet having passed through sched_algorithm_with_properties::awakened(), which
is what actually instantiates the fiber_properties subclass, you could end up
without a properties instance. Fortunately there's an easy workaround: call
yield().
Remove that workaround from examples/priority.cpp.
The complex logic to make a single pass through the ready queue to find the
old location and also the new insertion point was unfortunately error-prone.
Recast it as two separate passes: a simple search loop to find and unlink the
fiber_context*, then a call to awakened() to reinsert it in the correct place.
Passing the futures from the argument-pack functions through a function-call
boundary forces the runtime to perform all the async() calls _first,_ then
make a separate pass through the futures to obtain results.
Introduce Runner and Example classes to collect and ultimately run lambdas
illustrating use of each different wait_something() variant.
Move Verbose up to the top for use by Runner. Similarly, move sleeper() for
use by those lambdas.
The body of main() then reduces to a Runner::run() call.
Now that wait_first_simple() is again based on Done (a bool protected by a
condition variable) rather than a barrier(2), have to introduce
wait_all_simple_impl() to manage the barrier.
This reverts commit 59a3afd209, reinstating the
Done wrapper.
While it is true that a barrier(2) will wake up when the second fiber calls
wait(), it then _resets._ This means that the _third_ fiber will wait() for
the fourth, and so on. If an odd number of fibers binds that barrier, the last
of them will hang until shutdown.
We want Done.wait() to wake up on the first notify() call, and for every
subsequent notify() call to be a no-op. Apparently Done is the correct
mechanism after all.
We now have:
wait_any_value(): for when passed functions cannot throw exceptions;
wait_first_outcome(): get earliest result/exception;
wait_first_success(): get first non-exception result.
The casual reader, on encountering these in the Fiber examples directory,
might otherwise assume they're intended to illustrate something about the
Fiber library.
when_any_simple() is "simple" in the sense that we don't care about return
values or possible exceptions -- we only want to know when the shortest
subtask completes.
This source is a work in progress. We intend to add more cases.
This covers both generic callbacks (adapt_callbacks.cpp,
adapt_method_calls.cpp) and custom Asio completion tokens (yield.hpp,
promise_completion_token.hpp, detail/yield.hpp, detail/promise_handler.hpp).
Mark up the relevant source files to provide code snippets for callbacks.qbk.
This illustrates how to interface a synchronous Fiber function with an async
API whose notification consists of an abstract base class with virtual
success/error methods.
In essence, yield_t et al. become very like use_future_t et al. The only real
difference is the async_result return type and get() method. Factor out common
functionality into promise_completion_token and promise_handler.
Remove the boost::fibers::asio::spawn() function and its basic_yield_context
infrastructure. Fix up all existing references in example source code.
sched_algorithm_with_properties<> must intercept awakened() calls before
forwarding control to the subclass override. That pretty much requires two
different virtual methods: one for sched_algorithm subclasses WITHOUT
properties, the other for sched_algorithm_with_properties subclasses.
Originally we used a different name (awakened_props()), but that was
bothersome. It makes more sense to use a different awakened() overload
instead, one that passes in the relevant properties object.
Fix examples/priority.cpp accordingly.
Also delegate instantiation of a new properties object to new_properties()
virtual method. Overriding this method allows you to customize allocation,
instead of sched_algorithm_with_properties<> unconditionally putting the new
properties object on the heap. Validate the new_properties() return value.
This illustrates use of a sched_algorithm_with_properties<PROPS> subclass that
defines, and supports, a priority property. The example includes a (somewhat
contrived) case in which a fiber must be moved within the scheduler's ready
queue due to a priority change.
Every sched_algorithm_with_properties<PROPS> subclass awakened() call must
ensure that control reaches sched_algorithm_with_properties<PROPS>::awakened()
_before_ any logic in the subclass method attempts to access properties. This
turns out to be all too easy to forget.
So instead, advise subclasses to overrride new awakened_props() method. Base-
class method sets things up and then calls awakened_props(). Moreover, when
the compiler supports it, sched_algorithm_with_properties<PROPS>::awakened()
is now marked 'final' to remind subclass authors to override awakened_props()
instead.
Priority is another property that's only relevant for future sched_algorithm
implementations. We don't even have an example yet. It's a good candidate for
moving to a specific fiber_properties subclass for that specific
sched_algorithm implementation.
thread_affinity is a good example of a property relevant only to a particular
sched_algorithm implementation. In examples/cpp03/migration, introduce an
'affinity' subclass of fiber_properties with a thread_affinity data
member.
Derive workstealing_round_robin from sched_algorithm_with_properties<affinity>
and, as required by that base class, forward awakened() calls to base-class
awakened() method.
Reimplement workstealing_round_robin's queue from a std::deque to a "by hand"
intrusive singly-linked list so we can efficiently remove an arbitrary item.
Make steal() method, instead of always popping the last item, scan the list to
find the last item willing to migrate (! thread_affinity).
From examples/cpp03/migration/workstealing_round_robin.hpp, an example of a
user-supplied sched_algorithm implementation, remove all boost/fiber/detail
#includes. These should no longer be needed.
Change sched_algorithm_with_properties::properties(worker_fiber*) method to
accept fiber_base* instead. The original signature was introduced when every
sched_algorithm implementation necessarily manipulated worker_fiber* pointers.
Now we're intentionally avoiding the need.
For the same reason, introduce a fiber_properties::back_ptr typedef so
subclasses can opaquely pass such pointers through their own constructor to
the base-class constructor.
Some reviewers disliked that to build a custom sched_algorithm subclass, you
must necessarily manipulate pointers to classes in the boost::fibers::detail
namespace. Redefine all such methods to use fiber_base* rather than
detail::worker_fiber*.
Hoist fiber_base* into boost::fibers namespace. We considered an opaque
typedef rather than fiber_base*, but in fact a sched_algorithm subclass may
need is_ready().
Moreover, a sched_algorithm subclass may well want to use an intrusive linked
list to queue fibers. Hoist worker_fiber::nxt_ pointer into fiber_base, and
remove worker_fiber::next() and next_reset() methods. This allows recasting
detail::fifo in terms of fiber_base*, therefore round_robin can be stated
almost entirely in terms of fiber_base* rather than worker_fiber*.
Recast fiber constructor taking worker_fiber* to take fiber_base* instead.
This constructor is used solely for fiber migration; with relevant functions
now returning fiber_base*, we think we no longer need fiber(worker_fiber*).
Introduce fiber_properties class from which to derive a specific properties
class for a particular user-coded sched_algorithm subclass.
Add sched_algorithm::property_change(worker_fiber*, fiber_properties*) method
to allow a fiber_properties subclass method to notify the sched_algorithm
subclass of a change in a relevant property. This generalizes the present
priority() method.
Introduce sched_algorithm_with_properties<PROPS> template class from which to
derive a user-coded scheduler that uses fiber_properties subclass PROPS. Give
it ref-returning properties(fiber::id) and properties(worker_fiber*) methods.
Introduce fiber_properties* field in worker_fiber, and initialize it to 0.
Delete it when the worker_fiber is destroyed. Give it pointer-flavored access
methods. Normally this field will remain 0; but the first time the
worker_fiber is passed to a sched_algorithm_with_properties<PROPS> subclass,
its awakened() method will instantiate the relevant PROPS subclass.
Add ref-returning fiber::properties<PROPS>() method. Calling this method
presumes that the current thread's sched_algorithm is derived from
sched_algorithm_with_properties<PROPS>.
Also add this_fiber::properties<PROPS>() with the same restriction.
Add ref-returning fm_properties<PROPS>() functions to implement
fiber::properties<PROPS>() and this_fiber::properties<PROPS>().
Allow sched_algorithm_with_properties to extract the worker_fiber* from a
fiber::id (aka worker_fiber::id) so it can present public-facing
properties(id) method as well as properties(worker_fiber*) method.
(cherry picked from commit 18c7f2c13b9642826b42aa3f6fa0a6642fce9cbc)
Conflicts:
include/boost/fiber/detail/worker_fiber.hpp
include/boost/fiber/fiber_manager.hpp