OliverKowalke2013Oliver Kowalke
Distributed under the Boost Software License, Version 1.0. (See accompanying
file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
C++ Library to cooperatively schedule and synchronize micro-threads
FiberOverviewBoost.Fiber provides a framework for micro-/userland-threads
(fibers) scheduled cooperatively. The API contains classes and functions to
manage and synchronize fibers similiarly to Boost.Thread.
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 running fiber decides explicitly when it should yield to allow
another fiber to run (context switching). Boost.Fiber
internally uses coroutines from Boost.Coroutine;
the classes in this library manage, schedule and, when needed, synchronize
those coroutines. A context switch between threads usually costs thousands
of CPU cycles on x86, compared to a fiber switch with a few hundred cycles.
A fiber can only run on a single thread at any point in time.
In order to use the classes and functions described here, you can either include
the specific headers specified by the descriptions of each class or function,
or include the master library header:
#include<boost/fiber/all.hpp>
which includes all the other headers in turn.
The namespaces used are:
namespaceboost::fibersnamespaceboost::this_fiberFibers
and Threads
Control is cooperatively passed between fibers launched on a given thread.
At a given moment, on a given thread, at most one fiber is running.
Spawning additional fibers on a given thread does not increase your program's
utilization of hardware cores.
On the other hand, a fiber may safely access any resource exclusively owned
by its parent thread without explicitly needing to defend that resource against
concurrent access by other fibers on the same thread. You are already guaranteed
that no other fiber on that thread is concurrently touching that resource.
This can be particularly important when introducing concurrency in legacy code.
You can safely spawn fibers running old code, using asynchronous I/O to interleave
execution.
In effect, fibers provide a natural way to organize concurrent code based on
asynchronous I/O. Instead of chaining together completion handlers, code running
on a fiber can make what looks like a normal blocking function call. That call
can cheaply suspend the calling fiber, allowing other fibers on the same thread
to run. When the operation has completed, the suspended fiber resumes, without
having to explicitly save or restore its state. Its local stack variables persist
across the call.
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.
For fiber-local storage, please see fiber_specific_ptr.
Blocking
Normally, when this documentation states that a particular fiber blocks,
it means that it yields control, allowing other fibers on the same thread to
run. The synchronization mechanisms provided by Boost.Fiber
have this behavior.
A fiber may, of course, use normal thread synchronization mechanisms; however
a fiber that invokes any of these mechanisms will block its entire thread,
preventing any other fiber from running on that thread in the meantime. For
instance, when a fiber wants to wait for a value from another fiber in the
same thread, using std::future would be unfortunate: std::future::get()
would block the whole thread, preventing the other fiber from delivering its
value. Use future<> instead.
Similarly, a fiber that invokes a normal blocking I/O operation will block
its 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.
This library is not an official Boost library
Boost.Fiber depends upon Boost.Chrono,
Boost.Coroutine and
Boost.Move. Boost version
1.55.0 or greater is required.
Fiber managementSynopsis
#include<boost/fiber/all.hpp>namespaceboost{namespacefibers{classfiber;booloperator<(fiberconst&l,fiberconst&r)noexcept;voidswap(fiber&l,fiber&r)noexcept;classfiber_group;classattributes;structalgorithm;classround_robin;voidset_scheduling_algorithm(algorithm*al)}namespacethis_fiber{fibers::idget_id()noexcept;voidyield();voidsleep_until(fibers::clock_type::time_pointconst&timeout_point);template<typenameRep,typenamePeriod>voidsleep_for(fibers::clock_type::duration<Rep,Period>const&timeout_duration);voidinterruption_point();boolinterruption_requested()noexcept;boolinterruption_enabled()noexcept;classdisable_interruption;classrestore_interruption;}}Tutorial
Each fiber represents a micro-thread which will be launched and managed
cooperatively by a scheduler. Objects of type fiber are only moveable.
boost::fibers::fiberf1;// not-a-fibervoidf(){boost::fibers::fiberf2(some_fn);f1=boost::move(f2);// f2 moved to f1}Launching
A new fiber is launched by passing an object of a callable type that can be
invoked with no parameters. If the object must not (or cannot) be copied, then
boost::ref can be used to pass in a reference to the function
object. In this case, the user must ensure that the referenced object outlives
the newly-created fiber.
structcallable{voidoperator()();};boost::fibers::fibercopies_are_safe(){callablex;returnboost::fibers::fiber(x);}// x is destroyed, but the newly-created fiber has a copy, so this is OKboost::fibers::fiberoops(){callablex;returnboost::fibers::fiber(boost::ref(x));}// x is destroyed, but the newly-created fiber still has a reference// this leads to undefined behaviour
Control is immediately transferred to the spawned fiber at construction.
When the constructor returns, the fiber might be complete or might
have suspended.
Exceptions
Exceptions thrown by the function or callable object passed to the fiber
constructor
are consumed by the framework. If you need to know which exception was thrown,
use future<> and packaged_task<>.
Detaching
A fiber can be detached by explicitly invoking the fiber::detach() member
function. After fiber::detach() is called on a fiber object, that
object represents not-a-fiber. The fiber object may then
safely be destroyed.
boost::fibers::fiber(some_fn).detach();Joining
In order to wait for a fiber to finish, the fiber::join() member function
of the fiber object can be used. fiber::join() will block
until the fiber object has completed. If the fiber has
already completed, or the fiber object represents not-a-fiber,
then fiber::join() returns immediately.
voidsome_fn(){...}boost::fibers::fiberf(some_fn);...f.join();
If the fiber has already completed, then fiber::join() returns immediately
and the joined fiber object becomes not-a-fiber.
Destruction
When a fiber object representing a valid execution context is destroyed,
the program terminates if the fiber is fiber::joinable(). If
you intend the fiber to outlive the fiber object that launched it,
use the fiber::detach()
method.
{boost::fibers::fiberf(some_fn);}// std::terminate() will be called{boost::fibers::fiberf(some_fn);f.detach();}// okay, program continuesInterruption
A valid fiber can be interrupted by invoking its fiber::interrupt() member
function. The next time that fiber executes one of the specific interruption-points
with interruption enabled, a fiber_interrupted
exception will be thrown. If this exception is not caught, the fiber will be
terminated, its stack unwound, its stack objects properly destroyed.
With disable_interruption a fiber can avoid being
interrupted.
// interruption enabled at this point{boost::this_fiber::disable_interruptiondi1;// interruption disabled{boost::this::fiber::disable_interruptiondi2;// interruption still disabled}// di2 destroyed; interruption state restored// interruption still disabled}// di destroyed; interruption state restored// interruption enabled
At any point, the interruption state for the current thread can be queried
by calling this_fiber::interruption_enabled().
The following interruption-points
are defined and will throw fiber_interrupted
if this_fiber::interruption_requested() and
this_fiber::interruption_enabled().
fiber::join()barrier::wait()condition_variable::wait()condition_variable::wait_for()condition_variable::wait_until()this_fiber::sleep_for()this_fiber::sleep_until()this_fiber::interruption_point()Fiber
IDs
Objects of class fiber::id can be
used to identify fibers. Each running fiber has a unique fiber::id obtainable
from the corresponding fiber
by calling the fiber::get_id() member
function. Objects of class fiber::id can be
copied, and used as keys in associative containers: the full range of comparison
operators is provided. They can also be written to an output stream using the
stream insertion operator, though the output format is unspecified.
Each instance of fiber::id either
refers to some fiber, or not-a-fiber. Instances that refer
to not-a-fiber compare equal to each other, but not equal
to any instances that refer to an actual fiber. The comparison operators on
fiber::id yield a total order for every non-equal
fiber::id.
Class
fiber#include<boost/fiber/fiber.hpp>classfiber{public:typedefuspecified-classid;fiber()noexcept;template<typenameFn>explicitfiber(Fnfn,attributesconst&attr=attributes(),stack_allocatorconst&stack_alloc=stack_allocator(),std::allocator<fiber>const&alloc=std::allocator<fiber>());template<typenameFn,typenameStackAllocator>explicitfiber(Fnfn,attributesconst&attr,StackAllocatorconst&stack_alloc,std::allocator<fiber>const&alloc=std::allocator<fiber>());template<typenameFn,typenameStackAllocator,typenameAllocator>explicitfiber(Fnfn,attributesconst&attr,StackAllocatorconst&stack_alloc,Allocatorconst&alloc);~fiber();fiber(fiberconst&other)=delete;fiber&operator=(fiberconst&other)=delete;fiber(fiber&&other)noexcept;fiber&operator=(fiber&&other)noexcept;operatorsafe_bool()constnoexcept;booloperator!()constnoexcept;voidswap(fiber&other)noexcept;booljoinable()constnoexcept;idget_id()constnoexcept;intpriority()constnoexcept;voidpriority(int)noexcept;boolthread_affinity()constnoexcept;voidthread_affinity(bool)noexcept;voiddetach()noexcept;voidjoin();voidinterrupt()noexcept;};booloperator<(fiberconst&l,fiberconst&r)noexcept;voidswap(fiber&l,fiber&r)noexcept;Default
constructor
fiber()noexcept;Effects:
Constructs a fiber instance that refers to not-a-fiber.
Postconditions:this->get_id()==fiber::id()Throws:
Nothing
Constructor
template<typenameFn>explicitfiber(Fnfn,attributesconst&attr=attributes(),stack_allocatorconst&stack_alloc=stack_allocator(),std::allocator<fiber>const&alloc=std::allocator<fiber>());template<typenameFn,typenameStackAllocator>explicitfiber(Fnfn,attributesconst&attr,StackAllocatorconst&stack_alloc,std::allocator<fiber>const&alloc=std::allocator<fiber>());template<typenameFn,typenameStackAllocator,typenameAllocator>explicitfiber(Fnfn,attributesconst&attr,StackAllocatorconst&stack_alloc,Allocatorconst&alloc);Preconditions:Fn must be copyable
or movable.
Effects:fn is copied 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 coroutine.
Move
constructor
fiber(fiber&&other)noexcept;Effects:
Transfers ownership of the fiber managed by other
to the newly constructed fiber instance.
Postconditions:other.get_id()==fiber::id() and get_id() returns the value of other.get_id()
prior to the construction
Throws:
Nothing
Move
assignment operator
fiber&operator=(fiber&&other)noexcept;Effects:
Transfers ownership of the fiber managed by other
(if any) to *this.
Postconditions:other->get_id()==fiber::id() and get_id() returns the value of other.get_id()
prior to the assignment.
Throws:
Nothing
Destructor
~fiber();Effects:
If the fiber is fiber::joinable(), calls std::terminate.
Destroys *this.
Throws:
Nothing.
Note:
The programmer must ensure that the destructor is never executed while
the fiber is still fiber::joinable(). Even if you know
(by calling fiber::operator safe_bool()) that
the fiber has completed, you must still call either fiber::join() or
fiber::detach() before destroying the fiber
object.
Member function joinable()
booljoinable()constnoexcept;Returns:true if *this
refers to a fiber of execution, which may or may not have completed;
otherwise false.
Throws:
Nothing
See also:fiber::operator safe_bool()
Member function join()
voidjoin();Preconditions:
the fiber is fiber::joinable().
Effects:
If *this
refers to a fiber of execution, waits for that fiber to complete.
Postconditions:
If *this
refers to a fiber of execution on entry, that fiber has completed.
*this
no longer refers to any fiber of execution.
Throws:fiber_interrupted if
the current fiber is interrupted or system_errorError Conditions:resource_deadlock_would_occur: if
this->get_id()==boost::this_fiber::get_id(). invalid_argument:
if the fiber is not fiber::joinable().
Notes:join()
is one of the predefined interruption-points.
Member function detach()
voiddetach();Preconditions:
the fiber is fiber::joinable().
Effects:
The fiber of execution becomes detached, and no longer has an associated
fiber object.
Postconditions:*this
no longer refers to any fiber of execution.
Throws:system_errorError Conditions:invalid_argument: if the fiber is
not fiber::joinable().
Member function get_id()
fiber::idget_id()constnoexcept;Returns:
If *this
refers to a fiber of execution, an instance of fiber::id that represents that fiber. Otherwise
returns a default-constructed fiber::id.
Throws:
Nothing
See also:this_fiber::get_id()
Member function interrupt()
voidinterrupt();Effects:
If *this
refers to a fiber of execution, request that the fiber will be interrupted
the next time it enters one of the predefined interruption-points
with interruption enabled, or if it is currently blocked
in a call to one of the predefined interruption-points
with interruption enabled.
Throws:
Nothing
Member
function priority(int)voidpriority(int)noexcept;Preconditions:*this
refers to a fiber of execution.
Effects:
set priority for the fiber referenced by *this.
Throws:
Nothing
Note:
The meaning of particular int
values is determined by the specific algorithm passed
to set_scheduling_algorithm(). round_robin and
round_robin_ws ignore fiber::priority().
Member function priority()
intpriority()constnoexcept;Preconditions:*this
refers to a fiber of execution.
Returns:
priority for the fiber referenced by *this.
Throws:
Nothing
Member
function thread_affinity(bool)voidthread_affinity(bool)noexcept;Preconditions:*this
refers to a fiber of execution.
Effects:
Set thread affinity for the fiber referenced by *this.
Throws:
Nothing
Note:
"Thread affinity" is only meaningful for certain scheduler
algorithms, such as round_robin_ws. Many schedulers
ignore this fiber attribute. With a scheduler that might potentially
migrate a fiber from its initial thread to another, the value true prevents migration: the fiber will
always run on its current thread. The default is false:
normally, with an applicable scheduler, a fiber is allowed to migrate
across threads.
See also:this_fiber::thread_affinity()Member
function thread_affinity()boolthread_affinity()constnoexcept;Preconditions:*this
refers to a fiber of execution.
Returns:
thread affinity for the fiber referenced by *this.
Throws:
Nothing
See also:this_fiber::thread_affinity()
Member function operator safe_bool()
operatorsafe_bool()constnoexcept;Returns:true if *this
refers to a fiber of execution which has not yet terminated, false otherwise. Compare to fiber::joinable(),
which does not distinguish whether the referenced fiber of execution
is still running.
Throws:
Nothing
See also:fiber::joinable()
Member function
operator!()
booloperator!()constnoexcept;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:fiber::operator safe_bool(), fiber::joinable()
Member function swap()
voidswap(fiber&other)noexcept;Effects:
Exchanges the fiber of execution associated with *this and other,
so *this
becomes associated with the fiber formerly associated with other, and vice-versa.
Postconditions:this->get_id()
returns the same value as other.get_id() prior to the call. other.get_id()
returns the same value as this->get_id() prior to the call.
Throws:
Nothing
Non-member function swap()voidswap(fiber&l,fiber&r)noexcept;Effects:
Same as l.swap(r).
Non-member
function set_scheduling_algorithm()voidset_scheduling_algorithm(algorithm*scheduler);Effects:
Directs Boost.Fiber to use scheduler, which must be a concrete
subclass of algorithm, as the scheduling algorithm for
all fibers in the current thread.
Returns:
Previous algorithm 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 thread by
the time Boost.Fiber needs to use
it, the library will heap-allocate a default round_robin instance
for this thread.
Note:set_scheduling_algorithm() does not take
ownership of the passed algorithm*: Boost.Fiber
does not claim responsibility for the lifespan of the referenced scheduler object. The caller must
eventually destroy the passed scheduler,
just as it must allocate it in the first place. (Storing the pointer
in a boost::thread_specific_ptr is one way to
ensure that the instance is destroyed on thread termination.)
Throws:
Nothing
Class
fiber_groupfiber_group represents a collection of fibers which can be
collectively waited on or interrupted. fiber_group is neither
copyable nor movable.
#include<boost/fiber/fiber_group.hpp>classfiber_group{public:fiber_group(){}~fiber_group();fiber_group(fiber_groupconst&other)=delete;fiber_group&operator=(fiber_groupconst&other)=delete;boolis_this_fiber_in();boolis_fiber_in(fiber*f);template<typenameFn>fiber*create_fiber(Fnfn,attributesattrs=attributes());template<typenameFn,typenameStackAllocator>fiber*create_fiber(Fnfn,attributesattrs,StackAllocatorconst&stack_alloc);template<typenameFn,typenameStackAllocator,typenameAllocator>fiber*create_fiber(Fnfn,attributesattrs,StackAllocatorconst&stack_alloc,Allocatorconst&alloc);voidadd_fiber(fiber*f);voidremove_fiber(fiber*f);voidjoin_all();voidinterrupt_all();std::size_tsize()const;};Constructor
fiber_group();Effects:
Create a new fiber group with no fibers.
Destructor
~fiber_group();Effects:
Destroy *this
and all fiber objects in the group.
Member
function create_fiber()
template<typenameFn>fiber*create_fiber(Fnfn,attributesattrs=attributes());template<typenameFn,typenameStackAllocator>fiber*create_fiber(Fnfn,attributesattrs,StackAllocatorconst&stack_alloc);template<typenameFn,typenameStackAllocator,typenameAllocator>fiber*create_fiber(Fnfn,attributesattrs,StackAllocatorconst&stack_alloc,Allocatorconst&alloc);Effects:
Create a new fiber object as-if by newfiber(fn)
and add it to the group.
Postcondition:this->size()
is increased by one, the new fiber is running.
Returns:
A pointer to the new fiber object.
Member
function add_fiber()
voidadd_fiber(fiber*f);Precondition:
The expression deletef is well-formed and will not result
in undefined behaviour and is_fiber_in(f)==false.
Effects:
Add fiber object pointed to by f
to the group.
Postcondition:this->size()
is increased by one.
Note:
Unless the same f is
later passed to remove_fiber(), *this becomes responsible for the lifespan
of *f.
When the fiber_group
is destroyed, *f
will also be destroyed.
The Precondition implies that f
must point to a heap fiber object. This is erroneous:
boost::fibers::fiber_groupfg;{boost::fibers::fiberf(somefunc);fg.add_fiber(&f);}// WRONG!
Instead, use the following:
boost::fibers::fiber_groupfg;{boost::fibers::fiber*f=newboost::fibers::fiber(somefunc);fg.add_fiber(f);}// okay, fg is now responsible for *f
Member
function remove_fiber()
voidremove_fiber(fiber*f);Effects:
If f is a member of
the group, remove it without calling delete.
Postcondition:
If f was a member of
the group, this->size()
is decreased by one.
Member
function join_all()
voidjoin_all();Requires:is_this_fiber_in()==false.
Effects:
Call join()
on each fiber object in the group.
Postcondition:
Every fiber in the group has terminated.
Note:
Since fiber::join() is one of the predefined interruption-points,
join_all()
is also an interruption point.
Member
function is_this_fiber_in()
boolis_this_fiber_in();Returns:
true if there is a fiber f
in the group such that f.get_id()==this_fiber::get_id().
Member
function is_fiber_in()
boolis_fiber_in(fiber*f);Returns:
true if there is a fiber fx
in the group such that fx.get_id()==f->get_id().
Member
function interrupt_all()
voidinterrupt_all();Effects:
Call interrupt()
on each fiber object in the group.
Member function
size()
intsize();Returns:
The number of fibers in the group.
Throws:
Nothing.
Class
attributes
Class attributes is used
to transfer parameters required to set up a fiber's context.
#include<boost/fiber/attributes.hpp>structattributes{std::size_tsize;attributes()BOOST_NOEXCEPT;explicitattributes(std::size_tsize_)BOOST_NOEXCEPT;};Constructor
attributes();attributes(std::size_tsize);Effects:
Parameter size determines
the stack size.
Throws:
Nothing.
Namespace this_fibernamespaceboost{namespacethis_fiber{fibers::fiber::idget_id()noexcept;voidyield();voidsleep_until(fibers::clock_type::time_pointconst&sleep_time);template<typenameRep,typenamePeriod>voidsleep_for(chrono::duration<Rep,Period>const&timeout_duration);boolthread_affinity()noexcept;voidthread_affinity(boolreq)noexcept;voidinterruption_point();boolinterruption_requested()noexcept;boolinterruption_enabled()noexcept;classdisable_interruption;classrestore_interruption;}}
Non-member
function this_fiber::get_id()#include<boost/fiber/operations.hpp>fiber::idget_id()noexcept;Returns:
An instance of fiber::id that
represents the currently executing fiber.
Throws:
Nothing.
Non-member
function this_fiber::sleep_until()#include<boost/fiber/operations.hpp>voidsleep_until(fibers::clock_type&abs_time);Effects:
Suspends the current fiber until the time point specified by abs_time has been reached.
Throws:fiber_interrupted if
the current fiber is interrupted.
Note:sleep_until()
is one of the predefined interruption-points.
Note:
The current fiber will not resume before abs_time,
but there are no guarantees about how soon after abs_time
it might resume.
Non-member
function this_fiber::sleep_for()#include<boost/fiber/operations.hpp>template<classRep,classPeriod>voidsleep_for(chrono::duration<Rep,Period>const&rel_time);Effects:
Suspends the current fiber until the time duration specified by rel_time has elapsed.
Throws:
Nothing if operations of chrono::duration<Rep, Period> do not
throw exceptions. 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.
Non-member function
this_fiber::yield()#include<boost/fiber/operations.hpp>voidyield();Effects:
Reliquishes execution control, allowing other fibers to run.
Throws:fiber_resource_error
if an error occurs.
Non-member
function this_fiber::thread_affinity()#include<boost/fiber/operations.hpp>boolthread_affinity()noexcept;voidthread_affinity(boolreq)noexcept;Effects:
Set or report fiber::thread_affinity() for the
currently running fiber.
Throws:
Nothing.
Note:fiber::thread_affinity() is false
by default.
Non-member
function this_fiber::interruption_point()#include<boost/fiber/interruption.hpp>
void interruption_point();
Effects:
Check to see if the current fiber has been interrupted.
Throws:fiber_interrupted if
this_fiber::interruption_enabled() and
this_fiber::interruption_requested() both
return true.
Non-member
function this_fiber::interruption_requested()#include<boost/fiber/interruption.hpp>boolinterruption_requested()noexcept;Returns:true if interruption has
been requested for the current fiber, false
otherwise.
Throws:
Nothing.
Non-member
function this_fiber::interruption_enabled()#include<boost/fiber/interruption.hpp>boolinterruption_enabled()noexcept;Returns:true if interruption is
enabled for the current fiber, false
otherwise.
Throws:
Nothing.
Note:
Interruption is enabled by default.
Class
disable_interruption#include<boost/fiber/interruption.hpp>classdisable_interruption{public:disable_interruption()noexcept;~disable_interruption()noexcept;disable_interruption(constdisable_interruption&)=delete;disable_interruption&operator=(constdisable_interruption&)=delete;};Constructor
disable_interruption()noexcept;Effects:
Stores the current state of this_fiber::interruption_enabled() and
disables interruption for the current fiber.
Postconditions:this_fiber::interruption_enabled() returns
false for the current
fiber.
Throws:
Nothing.
Destructor
~disable_interruption()noexcept;Preconditions:
Must be called from the same fiber on which *this was constructed.
Effects:
Restores the state of this_fiber::interruption_enabled() for
the current fiber to the state saved at construction of *this.
Postconditions:this_fiber::interruption_enabled() for
the current fiber returns the value stored by the constructor of *this.
Throws:
Nothing.
Class
restore_interruption#include<boost/fiber/interruption.hpp>classrestore_interruption{public:explicitrestore_interruption(disable_interruption&disabler)noexcept;~restore_interruption()noexcept;restore_interruption(constrestore_interruption&)=delete;restore_interruption&operator=(constrestore_interruption&)=delete;};Constructor
explicitrestore_interruption(disable_interruption&disabler)noexcept;Preconditions:
Must be called from the same fiber on which disabler
was constructed.
Effects:
Restores the current state of this_fiber::interruption_enabled() for
the current fiber to that saved in disabler.
Postconditions:this_fiber::interruption_enabled() for
the current fiber returns the value stored in the constructor of disabler.
Throws:
Nothing.
Destructor
~restore_interruption()noexcept;Preconditions:
Must be called from the same fiber on which *this was constructed.
Effects:
Disables interruption for the current fiber.
Postconditions:this_fiber::interruption_enabled() for
the current fiber returns false.
Throws:
Nothing.
voidfoo(){// interruption is enabled{boost::this_fiber::disable_interruptiondi;// interruption is disabled{boost::this_fiber::restore_interruptionri(di);// interruption now enabled}// ri destroyed, interruption disable again}// di destructed, interruption state restored// interruption now enabled}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 allocates for each thread a scheduler
( round_robin) on the heap. To prevent the library from heap-allocating
a default scheduler for a given thread, that thread must call set_scheduling_algorithm() before
any other Boost.Fiber entry point.
voidthread_fn(){my_fiber_schedulermfs;boost::fibers::set_scheduling_algorithm(&mfs);...}
A fiber-scheduler must implement interface algorithm. Boost.Fiber provides two schedulers: round_robin and
round_robin_ws.
You are explicitly permitted to code your own algorithm subclass,
and to pass it to set_scheduling_algorithm().
Class algorithm#include<boost/fiber/algorithm.hpp>structalgorithm{virtual~algorithm(){}algorithm(algorithmconst&)=delete;algorithm&operator=(algorithmconst&)=delete;virtualvoidspawn(detail::fiber_base::ptr_tconst&)=0;virtualvoidpriority(detail::fiber_base::ptr_tconst&,int)noexcept=0;virtualvoidjoin(detail::fiber_base::ptr_tconst&)=0;virtualdetail::fiber_base::ptr_tactive()noexcept=0;virtualboolrun()=0;virtualvoidwait(unique_lock<detail::spinlock>&)=0;virtualboolwait_until(clock_type::time_pointconst&,unique_lock<detail::spinlock>&)=0;template<typenameRep,typenamePeriod>boolwait_for(chrono::duration<Rep,Period>const&,unique_lock<detail::spinlock>&);virtualvoidyield()=0;virtualdetail::fiber_base::idget_main_id()=0;virtualdetail::notify::ptr_tget_main_notifier()=0;};voidset_scheduling_algorithm(algorithm*);
Member function spawn()
virtualvoidspawn(detail::fiber_base::ptr_tconst&f)=0;Effects:
Spawns fiber f, e.g.
f will be entered the
first time or resumed where it was suspended before. Suspends the current
fiber.
Member function
priority()
virtualvoidpriority(detail::fiber_base::ptr_tconst&f,intp)noexcept=0;Effects:
Sets priority p for fiber
f. Interpretation of
"priority" is entirely up to the subclass.
Throws:
Nothing
Member function join()
virtualvoidjoin(detail::fiber_base::ptr_tconst&f)=0;Effects:
Suspends the current fiber until fiber f
terminates.
Member function
active()
virtualdetail::fiber_base::ptr_tactive()noexcept=0;Returns:
The active fiber, or a null-pointer if the current execution context
is not a fiber managed by this algorithm instance.
Note:
While the initial context for a given thread is conceptually also a fiber,
it has no associated detail::fiber_base
instance. This is when active() returns null. Higher-level Boost.Fiber operations treat a thread's initial
context as equivalent to an explicitly-launched fiber, but this method
operates below that level of abstraction.
See also:algorithm::get_main_id()
Member function run()
virtualboolrun()=0;Effects:
Selects one fiber in ready state and runs it until it suspends. The choice
of fiber is up to the subclass instance.
Returns:
Returns true if one fiber
was ready and successfully executed, false
otherwise.
Member function wait()
virtualvoidwait(unique_lock<detail::spinlock>&lk)=0;Precondition:lk is locked by the current
fiber. It locks the spinlock protecting the internal state of a mutex
or condition_variable.
Effects:
Current fiber is set to waiting-state and gets suspended.
Postcondition:lk is unlocked.
Member
function wait_until()
virtualboolwait_until(clock_type::time_pointconst&timeout_time,unique_lock<detail::spinlock>&lk)=0;Precondition:lk is locked by the current
fiber. It locks the spinlock protecting the internal state of a mutex
or condition_variable.
Effects:
Current fiber is set to waiting-state and gets suspended.
Returns:
Returns true if fiber was
resumed before time-point timeout_time
was reached, false otherwise.
Postcondition:lk is unlocked.
Member function
wait_for()
template<typenameRep,typenamePeriod>boolwait_for(chrono::duration<Rep,Period>const&timeout_duration,unique_lock<detail::spinlock>&lk)Precondition:lk is locked by the current
fiber. It locks the spinlock protecting the internal state of a mutex
or condition_variable.
Effects:
Current fiber is set to waiting-state and gets suspended.
Returns:
Returns true if fiber was
resumed before time-duration timeout_duration
has passed, false otherwise.
Postcondition:lk is unlocked.
Member function yield()
virtualvoidyield()=0;Effects:
Suspends active fiber without waiting; that is, the current fiber is
immediately set to ready-state.
Member
function get_main_id()
virtualdetail::fiber_base::idget_main_id()=0;Returns:
A fiber_base::id associated with the initial context
of the thread on which the scheduler is running.
Note:
While the initial context for a given thread is conceptually also a fiber,
it has no associated detail::fiber_base
instance. Higher-level Boost.Fiber operations
treat a thread's initial context as equivalent to an explicitly-launched
fiber, but this method operates below that level of abstraction.
See also:algorithm::active()
Member
function get_main_notifier()
virtualdetail::notify::ptr_tget_main_notifier()=0;Returns:
A notify::ptr_t associated with the initial context
of the thread on which the scheduler is running.
Note:
While the initial context for a given thread is conceptually also a fiber,
it has no associated detail::fiber_base
instance. Higher-level Boost.Fiber operations
treat a thread's initial context as equivalent to an explicitly-launched
fiber, but this method operates below that level of abstraction. The
notify::ptr_t is used in condition_variable
and mutex to signal threads initial context.
See also:algorithm::active()
Class round_robin
This class implements algorithm and schedules fibers in round-robin
fashion.
Class
round_robin_wsround_robin_ws is intended
to be used for migrating fibers between threads (different schedulers). For
this purpose the class has two additional functions - steal_from() and migrate_to(). This functionality can be used to implement
work-stealing/-sharing in a threadpool.
boost::fibers::round_robin_wsrr;boost::fibers::set_scheduling_algorithm(&rr);// steal a fiber from a scheduler `other_rr` running in another threadboost::fibers::fiberf(other_rr->steal_from());// check if stealing was successfulif(f){// migrate stolen fiber to scheduler running in this threadrr.migrate_to(f);// detach fiberf.detach();}Stack allocation
A fiber uses internally a coroutine which manages
a set of registers and a stack. The memory used by the stack is allocated/deallocated
via a stack_allocator
which is required to model a stack-allocator concept.
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:
expression
return type
notes
a.allocate(sctx,size)void
creates a stack of at least size
bytes and stores both values in sctxa.deallocate(sctx)void
deallocates the stack created by a.allocate()
The implementation of allocate() might include logic to protect against
exceeding the context's available stack size rather than leaving it as undefined
behaviour.
Calling deallocate()
with a stack_context not
previously passed to allocate() results in undefined behaviour.
The memory for the stack is not required to be aligned; alignment takes place
inside coroutine.
Class
stack_allocatorBoost.Coroutine provides
the class coroutine-allocator which models the stack-allocator
concept. 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.
The appended guardpage
is not mapped to physical memory, only virtual
addresses are used.
#include<boost/fiber/stack_allocator.hpp>classstack_allocator{staticboolis_stack_unbound();staticstd::size_tmaximum_stacksize();staticstd::size_tdefault_stacksize();staticstd::size_tminimum_stacksize();voidallocate(stack_context&,std::size_tsize);voiddeallocate(stack_context&);}
Static
member function is_stack_unbound()
staticboolis_stack_unbound();Returns:
Returns true if the environment
defines no limit for the size of a stack.
Static
member function maximum_stacksize()
staticstd::size_tmaximum_stacksize();Preconditions:is_stack_unbound()
returns false.
Returns:
Returns the maximum size in bytes of stack defined by the environment.
Static
member function default_stacksize()
staticstd::size_tdefault_stacksize();Returns:
Returns a default stack size, which may be platform specific. If is_stack_unbound()
returns true then the present
implementation returns the maximum of 64kB and minimum_stacksize().
Static
member function minimum_stacksize()
staticstd::size_tminimum_stacksize();Returns:
Returns the minimum size in bytes of stack required by the environment:
Win32 4kB, Win64 8kB, defined by rlimit on POSIX.
Member
function allocate()
voidallocate(stack_context&,std::size_tsize);Preconditions:minimum_stacksize()>size
and !is_stack_unbound()&&(maximum_stacksize()<size).
Effects:
Allocates memory of at least size
Bytes and stores a pointer to the stack and its actual size in sctx.
Returns:voidNote:
Stores pointer to the start address of the new stack. Depending on the
architecture (stack grows downwards vs. upwards), the stored address
is the highest/lowest address of the stack.
Member
function deallocate()
voiddeallocate(stack_context&sctx);Preconditions:sctx.sp is valid, minimum_stacksize()>sctx.size and !is_stack_unbound()&&(maximum_stacksize()<size).
Effects:
Deallocates the stack space.
Note:sctx must have been set
by a previous call to allocate().
Class stack_contextBoost.Coroutine provides
the class stack_context which will contain the stack pointer
and the size of the stack. In case of a segmented-stack,
stack_context contains some extra control structures.
structstack_context{void*sp;std::size_tsize;// might contain additional control structures,// for instance for segmented stacks}Member
variable void*spValue:
Pointer to the beginning of the stack.
Note:
Whether the "beginning" of the stack is its lowest address
or its highest address is architecture-dependent.
Member
variable std::size_tsizeValue:
Actual size of the stack, in bytes.
Segmented
stacks
Boost.Fiber supports use of a segmented-stack,
whose size grows on demand. The fiber is created with a minimal stack size,
which will be increased as required.
Segmented stacks are currently only supported by gcc
from version 4.7 onwards. In order to use
a segmented-stack, Boost.Fiber
must be built with toolset=gcc segmented-stacks=on
at b2/bjam command-line. Applications must be compiled with compiler-flags
-fsplit-stack -DBOOST_USE_SEGMENTED_STACKS.
SynchronizationMutex Types
Class mutex#include<boost/fiber/mutex.hpp>classmutex{public:mutex();~mutex();mutex(mutexconst&other)=delete;mutex&operator=(mutexconst&other)=delete;voidlock();booltry_lock();voidunlock();typedefunique_lock<mutex>scoped_lock;};mutex provides an exclusive-ownership mutex. At most one fiber
can own the lock on a given instance of mutex at any time. Multiple
concurrent calls to lock(), try_lock() and unlock() shall be permitted.
Any fiber blocked in lock() is suspended in the scheduler until the
owning fiber releases the lock by calling unlock().
Class timed_mutex#include<boost/fiber/timed_mutex.hpp>classtimed_mutex:privateboost::noncopyable{public:timed_mutex();~timed_mutex();timed_mutex(timed_mutexconst&other)=delete;timed_mutex&operator=(timed_mutexconst&other)=delete;voidlock();voidunlock();booltry_lock();booltry_lock_until(clock_type::time_pointconst&timeout_time);template<typenameRep,typenamePeriod>booltry_lock_for(chrono::duration<Rep,Period>const&timeout_duration)typedefunique_lock<timed_mutex>scoped_lock;};timed_mutex provides an exclusive-ownership mutex. At most
one fiber can own the lock on a given instance of timed_mutex at
any time. Multiple concurrent calls to lock(), try_lock(), try_lock_until(), try_lock_for() and unlock() shall be permitted.
Class
recursive_mutex#include<boost/fiber/recursive_mutex.hpp>classrecursive_mutex:privateboost::noncopyable{public:recursive_mutex();~recursive_mutex();recursive_mutex(recursive_mutexconst&other)=delete;recursive_mutex&operator=(recursive_mutexconst&other)=delete;voidlock();booltry_lock()noexcept;voidunlock();typedefunique_lock<recursive_mutex>scoped_lock;};recursive_mutex provides an exclusive-ownership recursive
mutex. At most one fiber can own the lock on a given instance of recursive_mutex at
any time. Multiple concurrent calls to lock(), try_lock() and unlock() shall be permitted. A fiber that already
has exclusive ownership of a given recursive_mutex instance
can call lock()
or try_lock()
to acquire an 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.
Class
recursive_timed_mutex#include<boost/fiber/recursive_timed_mutex.hpp>classrecursive_timed_mutex:privateboost::noncopyable{public:recursive_timed_mutex();~recursive_timed_mutex();recursive_timed_mutex(recursive_timed_mutexconst&other)=delete;recursive_timed_mutex&operator=(recursive_timed_mutexconst&other)=delete;voidlock();booltry_lock()noexcept;voidunlock();booltry_lock_until(clock_type::time_pointconst&timeout_time);template<typenameRep,typenamePeriod>booltry_lock_for(chrono::duration<Rep,Period>const&timeout_duration)typedefunique_lock<recursive_timed_mutex>scoped_lock;};recursive_timed_mutex provides an exclusive-ownership
recursive mutex. At most one fiber can own the lock on a given instance of
recursive_timed_mutex at any time. Multiple concurrent
calls to lock(),
try_lock(),
try_lock_for(),
try_lock_until()
and unlock()
shall be permitted. A fiber that already has exclusive ownership of a given
recursive_timed_mutex instance can call lock(),
try_lock(),
try_lock_for()
or try_lock_until()
to acquire an 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.
Condition VariablesSynopsis
enumclasscv_status;{no_timeout,timeout};classcondition_variable;classcondition_variable_any;
The class condition_variable
provides a mechanism for one 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 waiting.
In the simplest case, this condition is just a boolean variable:
boost::fibers::condition_variablecond;boost::fibers::mutexmtx;booldata_ready;voidprocess_data();voidwait_for_data_to_process(){boost::fibers::mutex::scoped_locklk(mtx);while(!data_ready){cond.wait(lk);}process_data();}
Notice that the lk is passed
to wait: wait
will atomically add the fiber to the set of fibers waiting on the condition
variable, and unlock the mutex. When the fiber is awakened, the mutex will
be locked again before the call to wait
returns. This allows other fibers to acquire the mutex in order to update
the shared data, and ensures that the data associated with the condition
is correctly synchronized.
In the meantime, another fiber sets the condition to true,
and then calls either notify_one
or notify_all on the condition
variable to wake one waiting fiber or all the waiting fibers respectively.
voidretrieve_data();voidprepare_data();voidprepare_data_for_processing(){retrieve_data();prepare_data();{boost::lock_guard<boost::fibers::mutex>lk(mtx);data_ready=true;}cond.notify_one();}
Note that the same mutex is locked before the shared data is updated, but
that the mutex does not have to be locked across the call to notify_one.
Boost.Fiber provides both condition_variable and condition_variable_any
because Boost.Thread provides
both. (Boost.Fiber also provides the name
condition, which has been
deprecated in Boost.Thread.)
However, boost::fiber::condition_variable and boost::fiber::condition_variable_any
are the same class; like boost::thread::condition_variable_any,
its wait() method will accept any form of lock. boost::fiber::condition_variable_any
has no need to further optimize as described for boost::thread::condition_variable.
Class
condition_variable#include<boost/fiber/condition.hpp>enumcv_status{no_timeout=1,timeout};classcondition_variable{public:condition_variable();~condition_variable();condition_variable(condition_variableconst&)=delete;condition_variable&operator=(condition_variableconst&)=delete;voidnotify_one();voidnotify_all();template<typenameLockType>voidwait(LockType&lk);template<typenameLockType,typenamePred>voidwait(LockType&lk,Predpredicate);template<typenameLockType>cv_statuswait_until(LockType&lk,clock_type::time_pointconst&timeout_time);template<typenameLockType,typenamePred>boolwait_until(LockType&lk,clock_type::time_pointconst&timeout_time,Predpred);template<typenameLockType,typenameRep,typenamePeriod>cv_statuswait_for(LockType&lk,chrono::duration<Rep,Period>const&timeout_duration);template<typenameLockType,typenameRep,typenamePeriod,typenamePred>boolwait_for(LockType&lk,chrono::duration<Rep,Period>const&timeout_duration,Predpred);};typedefcondition_variablecondition_variable_any;Constructor
condition_variable()Effects:
Creates the object.
Throws:
Nothing.
Destructor
~condition_variable()Precondition:
All fibers waiting on *this have been notified by a call to
notify_one or notify_all (though the respective
calls to wait, wait_for or wait_until
need not have returned).
Effects:
Destroys the object.
Throws:
Nothing.
Member
function notify_one()
voidnotify_one();Effects:
If any fibers are currently blocked
waiting on *this
in a call to wait,
wait_for or wait_until, unblocks one of those
fibers.
Throws:
Nothing.
Note:
It is arbitrary which waiting fiber is resumed.
Member
function notify_all()
voidnotify_all();Effects:
If any fibers are currently blocked
waiting on *this
in a call to wait,
wait_for or wait_until, unblocks all of those
fibers.
Throws:
Nothing.
Note:
This is why a waiting fiber must also check for
the desired program state using a mechanism external to the condition_variable, and retry the
wait until that state is reached. A fiber waiting on a condition_variable might well wake
up a number of times before the desired state is reached.
Templated
member function wait()
template<typenameLockType>voidwait(LockType&lk);template<typenameLockType,typenamePred>voidwait(LockType&lk,Predpred);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 in all the
fibers currently waiting on *this would return the same value as
lk->mutex()
for this call to wait.
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(), 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:
while(!pred()){wait(lk);}Postcondition:lk is locked by the
current fiber.
Throws:fiber_exception if
an error occurs. fiber_interrupted
if the wait was interrupted by a call to fiber::interrupt() on
the fiber object associated with the current fiber of execution.
Note:
The Precondition is a bit dense. It merely states that all the fibers
calling wait on *this
must wait on lk objects
governing the samemutex.
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 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.
Templated
member function wait_until()
template<typenameLockType>cv_statuswait_until(LockType&lk,clock_type::time_pointconst&timeout_time);template<typenameLockType,typenamePred>boolwait_until(LockType&lk,clock_type::time_pointconst&timeout_time,Predpred);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.
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()
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 reacquired
by invoking lk.lock()
if the function exits with an exception. The member function accepting
pred is shorthand for:
while(!pred()){if(cv_status::timeout==wait_until(lk,timeout_time))returnpred();}returntrue;
That is, even if wait_until() times out, it can still return true if pred() returns true
at that time.
Postcondition:lk is locked by the
current fiber.
Throws:fiber_exception if
an error occurs. fiber_interrupted
if the wait was interrupted by a call to fiber::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.
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().
Templated
member function wait_for()
template<typenameLockType,typenameRep,typenamePeriod>cv_statuswait_for(LockType&lk,chrono::duration<Rep,Period>const&timeout_duration);template<typenameLockType,typenameRep,typenamePeriod,typenamePred>boolwait_for(LockType&lk,chrono::duration<Rep,Period>const&timeout_duration,Predpred);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.
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 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:
while(!pred()){if(cv_status::timeout==wait_for(lk,timeout_duration))returnpred();}returntrue;
That is, even if wait_for() times out, it can still return true if pred() returns true
at that time.
Postcondition:lk is locked by the
current fiber.
Throws:fiber_exception if
an error occurs. fiber_interrupted
if the wait was interrupted by a call to fiber::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 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().
Barriers
A barrier is a concept also known as a rendezvous, it
is a synchronization point between multiple contexts of execution (fibers).
The barrier is configured for a particular number of fibers (n), and as fibers reach the barrier they
must wait until all n fibers
have arrived. Once the n-th
fiber has reached the barrier, all the waiting fibers can proceed, and the
barrier is reset.
Class barrier#include<boost/fiber/barrier.hpp>classbarrier{public:barrier(unsignedintinitial);barrier(barrierconst&other)=delete;barrier&operator=(barrierconst&other)=delete;boolwait();};
Instances of barrier are not copyable or movable.
Constructor
barrier(unsignedintinitial);Effects:
Construct a barrier for initial
fibers.
Throws:std::invalid_argument if initial is zero
Member function wait()
boolwait();Effects:
Block until initial
fibers have called wait
on *this.
When the initial-th
fiber calls wait, all
waiting fibers are unblocked, and the barrier is reset.
Returns:true for exactly one fiber
from each batch of waiting fibers, false
otherwise.
Throws:fiber_exceptionNote:
interruption point
QueuesBoost.Fiber provides a bounded and a unbounded
queue suitable to synchonize fibers via message passing.
typedefboost::fibers::unbounded_queue<int>queue_t;voidsend(queue_t&queue){for(inti=0;i<5;++i)queue.push(i);queue.close();}voidrecv(queue_t&queue){inti;while(boost::fibers::queue_op_status::success==queue.pop(i)){std::cout<<"received "<<i<<std::endl;}}queue_tqueue;boost::fibers::fiberf1(boost::bind(send,ref(queue)));boost::fibers::fiberf2(boost::bind(recv,ref(queue)));f1.join();f2.join();Enumeration
queue_op_status
Queue operations return the state of the queue.
enumclassqueue_op_status{success,empty,full,closed,timeout};successEffects:
Operation was successful.
emptyEffects:
Queue is empty, operation failed.
fullEffects:
Queue is full, operation failed.
closedEffects:
Queue is closed, operation failed.
timeoutEffects:
The operation did not become ready before specified timeout elapsed.
Template
unbounded_queue<>#include<boost/fiber/unbounded_queue.hpp>template<typenameT>classunbounded_queue{public:typedefTvalue_type;unbounded_queue(unbounded_queueconst&other)=delete;unbounded_queue&operator=(unbounded_queueconst&other)=delete;boolis_closed()const;voidclose();boolis_empty();voidpush(value_typeconst&va);voidpush(value_type&&va);queue_op_statuspop(value_type&va);template<typenameRep,typenamePeriod>queue_op_statuspop_wait_for(value_type&va,chrono::duration<Rep,Period>const&timeout_duration);queue_op_statuspop_wait_until(value_type&va,clock_type::time_pointconst&timeout_time);queue_op_statustry_pop(value_type&va);};
Member
function is_closed()
boolis_closed()const;Returns:true if queue has been
closed.
Throws:
Nothing.
Note:
The queue is not closed by default.
Member
function close()
voidclose();Effects:
Deactivates the queue. 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.
Throws:
Nothing.
Note:close()
is like closing a pipe. It informs waiting consumers that no more values
will arrive.
Member
function is_empty()
boolis_empty();Effects:
Returns true if the queue
currently contains no data.
Throws:
Nothing.
Note:
This condition is transient. An is_empty() queue can become non-empty.
Member
function push()
voidpush(value_typeconst&va);voidpush(value_type&&va);Effects:
Enqueues the value in the queue and wakes up a fiber blocked on this->pop(),
this->pop_wait_for()
or this->pop_wait_until().
Throws:
Nothing.
Member
function pop()
queue_op_statuspop(value_type&va);Effects:
Dequeues a value from the queue. If the queue is_empty(), the fiber gets suspended until at
least one new item is push()ed (return value success
and va contains dequeued
value) or the queue gets close()d (return value closed).
Throws:fiber_interrupted
Member
function pop_wait_for()
template<typenameRep,typenamePeriod>queue_op_statuspop_wait_for(value_type&va,chrono::duration<Rep,Period>const&timeout_duration)Effects:
Accepts chrono::duration and internally computes
a clock_type::time_point as (clock_type::now()+timeout_duration). If (!this->is_empty()),
immediately dequeues a value from the queue. Otherwise the fiber gets
suspended until at least one new item is push()ed (return value success
and va contains dequeued
value), or the queue gets close()d (return value closed),
or the time as reported by clock_type::now() reaches the computed clock_type::time_point (return value timeout).
Throws:fiber_interrupted
Member
function pop_wait_until()
queue_op_statuspop_wait_until(value_type&va,clock_type::time_pointconst&timeout_time)Effects:
Accepts a clock_type::time_point.
If (!this->is_empty()), immediately dequeues a value from
the queue. Otherwise the fiber gets suspended until at least one new
item is push()ed
(return value success
and va contains dequeued
value), or the queue gets close()d (return value closed),
or the time as reported by clock_type::now() reaches the passed clock_type::time_point (return value timeout).
Throws:fiber_interrupted
Member
function try_pop()
queue_op_statustry_pop(value_type&va);Effects:
If this->is_empty(),
returns empty. If
this->is_closed(),
returns closed. Otherwise
it returns success
and va contains the
dequeued value.
Throws:
Nothing.
Template
bounded_queue<>#include<boost/fiber/bounded_queue.hpp>template<typenameT>classbounded_queue{public:typedefTvalue_type;bounded_queue(bounded_queueconst&other)=delete;bounded_queue&operator=(bounded_queueconst&other)=delete;bounded_queue(std::size_twm);bounded_queue(std::size_thwm,std::size_tlwm);boolis_closed()const;voidclose();boolis_empty();boolis_full();queue_op_statuspush(value_typeconst&va);queue_op_statuspush(value_type&&va);template<typenameRep,typenamePeriod>queue_op_statuspush_wait_for(value_typeconst&va,chrono::duration<Rep,Period>const&timeout_duration);template<typenameRep,typenamePeriod>queue_op_statuspush_wait_for(value_type&&va,chrono::duration<Rep,Period>const&timeout_duration);queue_op_statuspush_wait_until(value_typeconst&va,clock_type::time_pointconst&timeout_time);queue_op_statuspush_wait_until(value_type&&va,clock_type::time_pointconst&timeout_time);queue_op_statustry_push(value_typeconst&va);queue_op_statustry_push(value_type&&va);queue_op_statuspop(value_type&va);template<typenameRep,typenamePeriod>queue_op_statuspop_wait_for(value_type&va,chrono::duration<Rep,Period>const&timeout_duration);queue_op_statuspop_wait_until(value_type&va,clock_type::time_pointconst&timeout_time);queue_op_statustry_pop(value_type&va);};Constructor
bounded_queue(std::size_twm);bounded_queue(std::size_thwm,std::size_tlwm);Preconditions:hwm>=lwmEffects:
Constructs an object of class bounded_queue.
The constructor with two arguments constructs an object of class bounded_queue with a high-watermark
of hwm and a low-watermark
of lwm items. The constructor
with one argument effectively sets both hwm
and lwm to the same
value wm.
Throws:invalid_argument if
lwm>hwm.
Notes:
Once the number of values in the queue reaches hwm,
any call to push(),
push_wait_for()
or push_wait_until() will block until the number of values
in the queue has dropped below lwm.
That is, if lwm<hwm, the queue can be in
a state in which push(), push_wait_for() or push_wait_until() calls will block (is_full() returns true)
even though the number of values in the queue is less than hwm.
Member
function is_closed()
boolis_closed()const;Returns:true if queue has been
closed.
Throws:
Nothing.
Note:
The queue is not closed by default.
Member
function close()
voidclose();Effects:
Deactivates the queue. 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.
Throws:
Nothing.
Note:close()
is like closing a pipe. It informs waiting consumers that no more values
will arrive.
Member
function is_empty()
boolis_empty();Effects:
Returns true if the queue
currently contains no data.
Throws:
Nothing.
Note:
This condition is transient. An is_empty() queue can become non-empty.
Member
function is_full()
boolis_full();Effects:
Returns true if the queue
cannot accept more data at present. This happens when the number of
values in the queue reaches wm
or hwm. Once the queue
becomes full, however, it continues refusing new values until the number
of values drops below lwm.
Throws:
Nothing.
Note:
This condition is transient.
Member function
push()
queue_op_statuspush(value_typeconst&va);queue_op_statuspush(value_type&&va);Effects:
If this->is_closed(),
returns closed. If
(!this->is_full()), enqueues the value in the queue,
wakes up a fiber blocked on this->pop(), this->pop_wait_for() or this->pop_wait_until() and returns success.
Otherwise the fiber gets suspended until the number of values in the
queue drops below lwm
(return value success),
or the queue is close()d (return value closed).
Throws:fiber_interrupted
Member
function push_wait_for()
template<typenameRep,typenamePeriod>queue_op_statuspush_wait_for(value_typeconst&va,chrono::duration<Rep,Period>const&timeout_duration);template<typenameRep,typenamePeriod>queue_op_statuspush_wait_for(value_type&&va,chrono::duration<Rep,Period>const&timeout_duration);Effects:
Accepts chrono::duration and internally computes
a clock_type::time_point as (clock_type::now()+timeout_duration). If this->is_closed(), returns closed.
If (!this->is_full()), enqueues the value in the queue,
wakes up a fiber blocked on this->pop()this->pop_wait_for() or this->pop_wait_until() and returns success.
Otherwise the fiber gets suspended until the number of values in the
queue drops below lwm
(return value success),
the queue is close()d (return value closed),
or the time as reported by clock_type::now() reaches the computed clock_type::time_point (return value timeout).
Throws:fiber_interrupted
Member
function push_wait_until()
queue_op_statuspush_wait_until(value_typeconst&va,clock_type::time_pointconst&timeout_time);queue_op_statuspush_wait_until(value_type&&va,clock_type::time_pointconst&timeout_time);Effects:
Accepts a clock_type::time_point.
If this->is_closed(),
returns closed. If
(!this->is_full()), enqueues the value in the queue,
wakes up a fiber blocked on this->pop(), this->pop_wait_for() or this->pop_wait_until() and returns success.
Otherwise the fiber gets suspended until the number of values in the
queue drops below lwm
(return value success),
the queue is close()d (return value closed),
or the time as reported by clock_type::now() reaches the passed clock_type::time_point (return value timeout).
Throws:fiber_interrupted
Member
function try_push()
queue_op_statustry_push(value_typeconst&va);queue_op_statustry_push(value_type&&va);Effects:
If this->is_full(),
returns full. If this->is_closed(),
returns closed. Otherwise
enqueues the value in the queue, wakes up a fiber blocked on this->pop(),
this->pop_wait_for()
or this->pop_wait_until()
and returns success.
Throws:fiber_interrupted
Member function
pop()
queue_op_statuspop(value_type&va);Effects:
Dequeues a value from the queue. If the queue is_empty(), the fiber gets suspended until at
least one new item is push()ed (return value success
and va contains dequeued
value) or the queue gets close()d (return value closed).
Once the number of items remaining in the queue drops below lwm, any fibers blocked on push(),
push_wait_for()
or push_wait_until() may resume.
Throws:fiber_interrupted
Member
function pop_wait_for()
template<typenameRep,typenamePeriod>queue_op_statuspop_wait_for(value_type&va,chrono::duration<Rep,Period>const&timeout_duration)Effects:
Accepts chrono::duration and internally computes
a clock_type::time_point as (clock_type::now()+timeout_duration). If (!this->is_empty()),
immediately dequeues a value from the queue. Otherwise the fiber gets
suspended until at least one new item is push()ed (return value success
and va contains dequeued
value), or the queue gets close()d (return value closed),
or the time as reported by clock_type::now() reaches the computed clock_type::time_point (return value timeout).
Throws:fiber_interrupted
Member
function pop_wait_until()
queue_op_statuspop_wait_until(value_type&va,clock_type::time_pointconst&timeout_time)Effects:
Accepts a clock_type::time_point.
If (!this->is_empty()), immediately dequeues a value from
the queue. Otherwise the fiber gets suspended until at least one new
item is push()ed
(return value success
and va contains dequeued
value), or the queue gets close()d (return value closed),
or the time as reported by clock_type::now() reaches the passed clock_type::time_point (return value timeout).
Throws:fiber_interrupted
Member
function try_pop()
queue_op_statustry_pop(value_type&va);Effects:
If this->is_empty(),
returns empty. If
this->is_closed(),
returns closed. Otherwise
it returns success
and va contains the
dequeued value.
Throws:
Nothing.
FuturesOverview
The futures library provides a means of handling synchronous future values,
whether those values are generated by another fiber, or on a single fiber
in response to external stimuli, or on-demand.
This is done through the provision of four class templates: future<> and
shared_future<> which are used to retrieve the asynchronous
results, and promise<> and packaged_task<> which
are used to generate the asynchronous results.
An instance of future<> holds the one and only reference
to a result. Ownership can be transferred between instances using the move
constructor or move-assignment operator, but at most one instance holds a
reference to a given asynchronous result. When the result is ready, it is
returned from future::get() by rvalue-reference to allow the result
to be moved or copied as appropriate for the type.
On the other hand, many instances of shared_future<> may
reference the same result. Instances can be freely copied and assigned, and
shared_future::get()
returns a non const
reference so that multiple calls to shared_future::get()
are
safe. You can move an instance of future<> into an instance
of shared_future<>, thus transferring ownership
of the associated asynchronous result, but not vice-versa.
async()
is a simple way of running asynchronous tasks. A call to async() returns a future<> that
will deliver the result of the task.
Creating
asynchronous values
You can set the value in a future with either a promise<> or
a packaged_task<>. A packaged_task<> is
a callable object with void
return that wraps a function or callable object returning the specified type.
When the packaged_task<> is invoked, it invokes the
contained function in turn, and populates a future with the contained function's
return value. This is an answer to the perennial question: "How do I
return a value from a fiber?" Package the function you wish to run as
a packaged_task<> and pass the packaged task to the
fiber constructor. The future retrieved from the packaged task can then be
used to obtain the return value. If the function throws an exception, that
is stored in the future in place of the return value.
intcalculate_the_answer_to_life_the_universe_and_everything(){return42;}boost::fibers::packaged_task<int()>pt(calculate_the_answer_to_life_the_universe_and_everything);boost::fibers::future<int>fi=pt.get_future();boost::fibers::fiber(boost::move(pt)).detach();// launch task on a fiberfi.wait();// wait for it to finishassert(fi.is_ready());assert(fi.has_value());assert(!fi.has_exception());assert(fi.get()==42);
A promise<> is a bit more low level: it just provides explicit
functions to store a value or an exception in the associated future. A promise
can therefore be used where the value might come from more than one possible
source, or where a single operation may produce multiple values.
boost::fibers::promise<int>pi;boost::fibers::future<int>fi;fi=pi.get_future();pi.set_value(42);assert(fi.is_ready());assert(fi.has_value());assert(!fi.has_exception());assert(fi.get()==42);Future
A future provides a mechanism to access the result of an asynchronous operation.
Enumeration
future_status
Timed wait-operations ( future::wait_for() and future::wait_until())
return the state of the future.
enumclassfuture_status{ready,timeout,deferred};readyEffects:
The shared state is ready.
timeoutEffects:
The shared state did not become ready before timout has passed.
deferredEffects:
The function is deferred, e.g. result will be computed only when
explictly requested.
Template future<>
A future<> contains a shared state which is not shared
with any other future.
template<typenameR>classfuture{public:future()noexcept;future(future&&other)noexcept;future(futureconst&other)=delete;~future();future&operator=(future&&other)noexcept;future&operator=(futureconst&other)=delete;voidswap(future&other)noexcept;operatorsafe_bool()constnoexcept;booloperator!()constnoexcept;boolvalid()constnoexcept;shared_future<R>share();Rget();voidwait()const;template<classRep,classPeriod>future_statuswait_for(chrono::duration<Rep,Period>const&timeout_duration)const;future_statuswait_until(clock_type::time_pointconst&timeout_time)const;};Default
constructor
future();Effects:
Creates a future with no shared state. After construction is false==vaild().
Throws:
Nothing.
Move constructor
future(future&&other)noexcept;Effects:
Constructs a future with the shared state of other. After construction
is false==other.valid()Throws:
Nothing.
Destructor
~future();Effects:
Destructs the future; ownership is abandoned.
Throws:
Nothing.
Member
function operator=()
future&operator=(future&&other)noexcept;Effects:
Moves the shared state of other to this.
After construction is false==other.valid()Throws:
Nothing.
Member function swap()
voidswap(future&other)noexcept;Effects:
Swaps the shared state between other and this.
Throws:
Nothing.
Member function operator safe_bool()
operatorsafe_bool()constnoexcept;Effects:
Returns true if future
is valid.
Throws:
Nothing.
Member
function operator!()
booloperator!()constnoexcept;Effects:
Returns false if future
is valid.
Throws:
Nothing.
Member function valid()
boolvalid()constnoexcept;Effects:
Returns true if future
contains a shared state.
Throws:
Nothing.
Member function share()
shared_future<R>share();Effects:
Move the state to a shared_future<>.
Throws:future_error with
error condtion future_errc::no_state.
Member function get()
Rget();Precondition:true==valid()Effects:
Returns the result.
Postcondition:false==valid()Throws:future_error with
error condtion future_errc::no_state.
Member function wait()
voidwait();Effects:
Waits for the result will become available.
Throws:future_error with
error condtion future_errc::no_state.
Templated member
function wait_for()
template<classRep,classPeriod>future_statuswait_for(chrono::duration<Rep,Period>const&timeout_duration)const;Effects:
Waits for the result will become available or timeout_duration
has passed.
Result:
A future_status is
returned indicating the reason for returning.
Throws:future_error with
error condtion future_errc::no_state.
Member function
wait_until()
future_statuswait_until(clock_type::time_pointconst&timeout_time)const;Effects:
Waits for the result will become available or timeout_time
has passed.
Result:
A future_status is
returned indicating the reason for returning.
Throws:future_error with
error condtion future_errc::no_state.
Template
shared_future<>
A shared_future<> contains a shared state which
might be shared with other futures.
template<typenameR>classshared_future{public:shared_future()noexcept;~shared_future();shared_future(shared_futureconst&other);shared_future(future<R>&&other)noexcept;shared_future(shared_future&&other)noexcept;shared_future&operator=(shared_future&&other)noexcept;shared_future&operator=(future&&other)noexcept;shared_future&operator=(shared_futureconst&other)noexcept;voidswap(shared_future&other)noexcept;operatorsafe_bool()constnoexcept;booloperator!()constnoexcept;boolvalid()constnoexcept;Rget();voidwait()const;template<classRep,classPeriod>future_statuswait_for(chrono::duration<Rep,Period>const&timeout_duration)const;future_statuswait_until(clock_type::time_pointconst&timeout_time)const;};Default
constructor
shared_future();Effects:
Creates a future with no shared state. After construction is false==vaild().
Throws:
Nothing.
Move constructor
shared_future(future&&other)noexcept;shared_future(shared_future&&other)noexcept;Effects:
Constructs a future with the shared state of other. After construction
is false==other.valid().
Throws:
Nothing.
Copy constructor
shared_future(shared_futurecosnt&other)noexcept;Effects:
Constructs a future with the shared state of other. After construction
is true==other.valid().
Throws:
Nothing.
Destructor
~shared_future();Effects:
Destructs the future; ownership is abandoned if not shared.
Throws:
Nothing.
Member
function operator=()
shared_future&operator=(future&&other)noexcept;shared_future&operator=(shared_future&&other)noexcept;shared_future&operator=(shared_futureconst&other)noexcept;Effects:
Transfers the ownership of shared state according to:
shared_futuretmp(other);swap(tmp);return*this;Throws:
Nothing.
Member
function swap()
voidswap(shared_future&other)noexcept;Effects:
Swaps the shared state between other and this.
Throws:
Nothing.
Member function operator safe_bool()
operatorsafe_bool()constnoexcept;Effects:
Returns true if future
is valid.
Throws:
Nothing.
Member
function operator!()
booloperator!()constnoexcept;Effects:
Returns false if future
is valid.
Throws:
Nothing.
Member
function valid()
boolvalid()constnoexcept;Effects:
Returns true if future
contains a shared state.
Throws:
Nothing.
Member function
get()
Rget();Precondition:true==valid()Effects:
Returns the result.
Postcondition:false==valid()Throws:future_error with
error condtion future_errc::no_state.
Member
function wait()
voidwait();Effects:
Waits for the result will become available.
Throws:future_error with
error condtion future_errc::no_state.
Templated
member function wait_for()
template<classRep,classPeriod>future_statuswait_for(chrono::duration<Rep,Period>const&timeout_duration)const;Effects:
Waits for the result will become available or timeout_duration
has passed.
Result:
A future_status is
returned indicating the reason for returning.
Throws:future_error with
error condtion future_errc::no_state.
Member
function wait_until()
future_statuswait_until(clock_type::time_pointconst&timeout_time)const;Effects:
Waits for the result will become available or timeout_time
has passed.
Result:
A future_status is
returned indicating the reason for returning.
Throws:future_error with
error condtion future_errc::no_state.
Template
promise<>
A promise<> provides a mechanism to store a value that
can later be accessed asynchronously by a future<> object.
template<typenameR>classpromise{public:promise();template<typenameAllocator>promise(boost::allocator_arg_t,Allocatoralloc);promise(promise&&other)noexcept;promise(promiseconst&other)=delete;~promise();promise&operator=(promise&&other)noexcept;promise&operator=(promiseconst&other)=delete;voidswap(promise&other)noexcept;operatorsafe_bool()constnoexcept;booloperator!()constnoexcept;future<R>get_future();voidset_value(Rconst&value);voidset_value(R&&value);voidset_exception(exception_ptrp);};Default
constructor
promise();Effects:
Creates a promise with an empty shared state.
Throws:
Nothing.
Constructor
template<typenameAllocator>promise(boost::allocator_arg_t,Allocatoralloc);Effects:
Creates a promise with an empty shared state by using alloc.
Throws:
Nothing.
Move constructor
promise(promise&&other)noexcept;Effects:
Creates a promise by moving the shared state from other.
Postcondition:other contains no
valid shared state.
Throws:
Nothing.
Destructor
~promise();Effects:
Destroys *this
and abandons the shared state if shared state is ready; otherwise
stores future_error
with error condition future_errc::broken_promise.
Throws:
Nothing.
Member
function operator=()
promise&operator=(promise&&other)noexcept;Effects:
Transfers the ownership of shared state to *this.
Postcondition:other contains no
valid shared state.
Throws:
Nothing.
Member function swap()
voidswap(promise&other)noexcept;Effects:
Swaps the shared state between other and *this.
Throws:
Nothing.
Member function operator safe_bool()
operatorsafe_bool()constnoexcept;Effects:
Returns true if *this
contains a non-empty shared state.
Throws:
Nothing.
Member
function operator!()
booloperator!()constnoexcept;Effects:
Returns true if *this
contains an empty shared state.
Throws:
Nothing.
Member
function get_future()
future<R>get_future();Returns:
A future<> with the same shared state.
Throws:future_error with
future_errc::future_already_retrieved or future_errc::no_state.
Member function
set_value()
voidset_value(Rconst&value);voidset_value(R&&value);Effects:
Store the result in the shared state and marks the state as ready.
Throws:future_error with
future_errc::future_already_satisfied or future_errc::no_state.
Member
function set_exception()
voidset_exception(exception_ptr);Effects:
Store an exception pointer in the shared state and marks the state
as ready.
Throws:future_error with
future_errc::future_already_satisfied or future_errc::no_state.
Template
packaged_task<>
A packaged_task<> wraps a callable target that
returns a value so that the return value can be computed asynchronously.
template<classR>classpackaged_task<R()>{public:packaged_task()noexcept;template<typenameFn>explicitpackaged_task(Fn&&fn);template<typenameFn,typenameAllocator>explicitpackaged_task(boost::allocator_arg_t,Allocatorconst&alloc,Fn&&fn);packaged_task(packaged_task&&other)noexcept;packaged_task(packaged_taskconst&other)=delete;~packaged_task();packaged_task&operator=(packaged_task&&other)noexcept;packaged_task&operator=(packaged_taskconst&other)=delete;voidswap(packaged_task&other)noexcept;operatorsafe_bool()constnoexcept;booloperator!()constnoexcept;boolvalid()constnoexcept;future<R>get_future();voidoperator()();voidreset();};Default
constructor packaged_task()packaged_task();Effects:
Constructs an object of class packaged_task
with no shared state.
Throws:
Nothing.
Templated
constructor template<>packaged_task()template<typenameFn>explicitpackaged_task(Fn&&fn);template<typenameFn,typenameAllocator>explicitpackaged_task(boost::allocator_arg_t,Allocatorconst&alloc,Fn&&fn);Effects:
Constructs an object of class packaged_task
with a shared state and stores the callable target fn internally.
Throws:
Nothing.
Note:
The signature of Fn
should have a return type convertible to R.
Move
constructor
packaged_task(packaged_task&&other)noexcept;Effects:
Creates a packaged_task by moving the shared state from other.
Postcondition:other contains no
valid shared state.
Throws:
Nothing.
Destructor
~packaged_task();Effects:
Destroys *this
and abandons the shared state if shared state is ready; otherwise
stores future_error
with error condition future_errc::broken_promise.
Throws:
Nothing.
Member
function operator=()
packaged_task&operator=(packaged_task&&other)noexcept;Effects:
Transfers the ownership of shared state to *this.
Postcondition:other contains no
valid shared state.
Throws:
Nothing.
Member
function swap()
voidswap(packaged_task&other)noexcept;Effects:
Swaps the shared state between other and *this.
Throws:
Nothing.
Member function operator safe_bool()
operatorsafe_bool()constnoexcept;Effects:
Returns true if *this
contains a non-empty shared state.
Throws:
Nothing.
Member
function operator!()
booloperator!()constnoexcept;Effects:
Returns true if *this
contains an empty shared state.
Throws:
Nothing.
Member
function valid()
boolvalid()constnoexcept;Effects:
Returns true if *this
contains a shared state.
Throws:
Nothing.
Member
function get_future()
future<R>get_future();Returns:
A future<> with the same shared state.
Throws:future_error with
future_errc::future_already_retrieved or future_errc::no_state.
Member
function operator()()
voidoperator()();Effects:
Invokes the stored callable target. Any exception thrown by the callable
target fn is stored
in the shared state. Otherwise, the value returned by fn is stored in the shared state.
Throws:future_error with
future_errc::no_state.
Member
function reset()
voidreset();Effects:
Resets the shared state and abondons the result of previous executions.
A new shared state is constructed.
Throws:future_error with
future_errc::no_state.
Fiber local storageSynopsis
Fiber local storage allows a separate instance of a given data item for each
fiber.
Cleanup
at fiber exit
When a fiber exits, the objects associated with each fiber_specific_ptr instance
are destroyed. By default, the object pointed to by a pointer p is destroyed by invoking deletep,
but this can be overridden for a specific instance of fiber_specific_ptr by
providing a cleanup routine func
to the constructor. In this case, the object is destroyed by invoking func(p). The cleanup functions are called in an unspecified
order.
Class
fiber_specific_ptr#include<boost/fiber/fss.hpp>template<typenameT>classfiber_specific_ptr{public:typedefTelement_type;fiber_specific_ptr();explicitfiber_specific_ptr(void(*fn)(T*));~fiber_specific_ptr();fiber_specific_ptr(fiber_specific_ptrconst&other)=delete;fiber_specific_ptr&operator=(fiber_specific_ptrconst&other)=delete;T*get()const;T*operator->()const;T&operator*()const;T*release();voidreset(T*t);};Constructor
fiber_specific_ptr();explicitfiber_specific_ptr(void(*fn)(T*));Requires:deletethis->get() is well-formed; fn(this->get())
does not throw
Effects:
Construct a fiber_specific_ptr object for storing
a pointer to an object of type T
specific to each fiber. When reset() is called, or the fiber exits, fiber_specific_ptr calls
fn(this->get()).
If the no-arguments constructor is used, the default delete-based
cleanup function will be used to destroy the fiber-local objects.
Throws:fiber_resource_error
if an error occurs.
Destructor
~fiber_specific_ptr();Requires:
All the fiber specific instances associated to this fiber_specific_ptr
(except
maybe the one associated to this fiber) must be null.
Effects:
Calls this->reset()
to clean up the associated value for the current fiber, and destroys
*this.
Throws:
Nothing.
Remarks:
The requirement is due to the fact that in order to delete all these
instances, the implementation should be forced to maintain a list of
all the fibers having an associated specific ptr, which is against the
goal of fiber specific data. In general, a fiber_specific_ptr should
outlive the fibers that use it.
Care needs to be taken to ensure that any fibers still running after an instance
of fiber_specific_ptr has been destroyed do not call
any member functions on that instance.
Member
function get()
T*get()const;Returns:
The pointer associated with the current fiber.
Throws:
Nothing.
The initial value associated with an instance of fiber_specific_ptr is
NULL for each fiber.
Member
function operator->()
T*operator->()const;Returns:this->get()Throws:
Nothing.
Member
function operator*()
T&operator*()const;Requires:this->get is not NULL.
Returns:*(this->get())Throws:
Nothing.
Member
function release()
T*release();Effects:
Return this->get()
and store NULL as the
pointer associated with the current fiber without invoking the cleanup
function.
Postcondition:this->get()==0Throws:
Nothing.
Member
function reset()
voidreset(T*new_value=0);Effects:
If this->get()!=new_value and this->get() is non-NULL,
invoke deletethis->get() or fn(this->get()) as appropriate. Store new_value as the pointer associated
with the current fiber.
Postcondition:this->get()==new_valueThrows:fiber_resource_error
if an error occurs.
Asynchronous network I/O (boost.asio)
In the past, code using asio's asynchronous operations
was scattered by callbacks. Boost.Asio
provides with its new asynchronous result feature a new
way to simplify the code and make it easier to read. boost::asio::yield_context
internally uses Boost.Coroutine:
voidecho(boost::asio::ip::tcp::socket&socket,boost::asio::yield_contextyield){chardata[128];// read asynchronous data from socket// execution context will be suspended until// some bytes are read from socketstd::size_tn=socket.async_read_some(boost::asio::buffer(data),yield);// write some bytes asynchronouslyboost::asio::async_write(socket,boost::asio::buffer(data,n),yield);}
Unfortunately Boost.Coroutine
(boost::asio::yield_context) does not provide primitives
to synchronize different coroutines (execution contexts).
Boost.Fiber provides an integration of fibers into Boost.Asio
so that asynchronous operations from Boost.Asio
can be used together with fibers, synchronized by primitives provided by Boost.Fiber.
The example section contains a complete publish-subscribe application demonstrating
the use of fibers with asio's asynchronous operations.
boost::fibers::asio::yield_context abstracts the fiber
in asio's context.
voidsubscriber::run(boost::fibers::asio::yield_fiberyield){boost::system::error_codeec;// read first message == channel namestd::stringchannel;boost::asio::async_read(socket_,boost::asio::buffer(channel),yield[ec]);if(ec)throwstd::runtime_error("no channel from subscriber");// register new channelreg_.subscribe(channel,shared_from_this());for(;;){boost::fibers::mutex::scoped_locklk(mtx_);// wait for published messages// fiber gets suspended and will be woken up if a// new message has to be published to subscribercond_.wait(lk);// '<fini>' terminates subscriber// data_ is a private member of subscriber and// gets filled by the publisher// notification of available data via condition_var cond_if("<fini>"==std::string(data_))break;// write message asynchronously to subscriber// fiber gets suspended until message was writtenboost::asio::async_write(socket_,boost::asio::buffer(data_,max_length),yield[ec]);if(ec)throwstd::runtime_error("publishing message failed");}}C10K
problem
The C10K-website 'The C10K problem', Dan Kegel from Dan Kegel describes the problem of handling ten thousand clients
simultaneously and which strategies are possible.
Boost.Fiber and Boost.Asio
support the strategy 'serve many clients with each server thread, and use asynchronous
I/O' without scattering the logic across many callbacks (as was asio's previous
strategy) and overloading the operating system with too many threads. (Beyond
a certain number of threads, the overhead of the kernel scheduler starts to
swamp the available cores.)
Because Boost.Fiber contains synchronization
primitives, it is easy to synchronize different fibers and use asynchronous
network I/O at the same time.
Boost.Fiber provides the same classes and
interfaces as Boost.Thread.
Therefore developers are able to use patterns familiar from multi-threaded
programming. For instance the strategy 'serve one client with one thread' could
be transformed into 'serve one client with one fiber'.
Acknowledgments
I'd like to thank Yuriy Krasnoschek and especially Nat Goodspeed.