...one of the most highly
regarded and expertly designed C++ library projects in the
world.
— Herb Sutter and Andrei
Alexandrescu, C++
Coding Standards
Boost.Fiber provides a framework for micro-/userland-threads (fibers) scheduled cooperatively. The API contains classes and functions to manage and synchronize fibers similiarly to standard thread support library.
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 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 call/cc from Boost.Context; the classes in this library manage, schedule and, when needed, synchronize those execution contexts. A context switch between threads usually costs thousands of CPU cycles on x86, compared to a fiber switch with less than a hundred cycles. A fiber runs 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:
namespace boost::fibers namespace boost::this_fiber
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 distribute your program across more hardware cores, though it can make more effective use of the core on which it's running.
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 can be migrated from one thread to another, though the library does not do this by default. It is possible for you to supply a custom scheduler that migrates fibers between threads. You may specify custom fiber properties to help your scheduler decide which fibers are permitted to migrate. Please see Migrating fibers between threads and Customization for more details.
Boost.Fiber allows to multiplex fibers
across multiple
cores
(see numa::work_stealing
).
A fiber launched on a particular thread continues running on that thread unless migrated. It might be unblocked (see Blocking below) by some other thread, but that only transitions the fiber from “blocked” to “ready” on its current thread — it does not cause the fiber to resume on the thread that unblocked it.
Unless migrated, a fiber may access 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
.
The fiber synchronization objects provided by this library will, by default,
safely synchronize fibers running on different threads. However, this level
of synchronization can be removed (for performance) by building the library
with BOOST_FIBERS_NO_ATOMICS
defined. When the library is built with that macro, you must ensure that all
the fibers referencing a particular synchronization object are running in the
same thread. Please see Synchronization.
Normally, when this documentation states that a particular fiber blocks (or equivalently, suspends), 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 and other asynchronous I/O operations can straightforwardly be adapted for Boost.Fiber: see Integrating Fibers with Asynchronous Callbacks.
Boost.Fiber depends upon Boost.Context. Boost version 1.61.0 or greater is required.
Note | |
---|---|
This library requires C++11! |
Important | |
---|---|
Windows using fcontext_t: turn off global program optimization (/GL) and change /EHsc (compiler assumes that functions declared as extern "C" never throw a C++ exception) to /EHs (tells compiler assumes that functions declared as extern "C" may throw an exception). |