Quantum Library : A scalable C++ coroutine framework
Quantum is a full-featured and powerful C++ framework build on top of the Boost coroutine library. The framework allows users to dispatch units of work (a.k.a. tasks) as coroutines and execute them concurrently using the 'reactor' pattern.
Features
- Header-only library and interface-based design.
- Full integration with Boost asymmetric coroutine library.
- Highly parallelized coroutine framework for CPU-bound workloads.
- Support for long-running or blocking IO tasks.
- Allows explicit and implicit cooperative yielding between coroutines.
- Task continuations and coroutine chaining for serializing work execution.
- Synchronous and asynchronous dispatching using futures and promises similar to STL.
- Support for streaming futures which allows faster processing of large data sets.
- Support for future references.
- Cascading execution output during task continuations (a.k.a. past futures).
- Task prioritization.
- Internal error handling and exception forwarding.
- Ability to write lock-free code by synchronizing coroutines on dedicated queues.
- Coroutine-friendly mutexes and condition variables for locking critical code paths or synchronizing access to external objects.
- Fast pre-allocated memory pools for internal objects and coroutines.
- Parallel
forEach
andmapReduce
functions. - Various stats API.
- NEW
Sequencer
class allowing strict FIFO ordering of tasks based on sequence ids.
Sample code
Quantum is very simple and easy to use:
using namespace Bloomberg::quantum;
// Define a coroutine
int getDummyValue(CoroContext<int>::Ptr ctx)
{
int value;
... //do some work
ctx->yield(); //be nice and let other coroutines run (optional cooperation)
... //do more work and calculate 'value'
return ctx->set(value);
}
// Create a dispatcher
Dispatcher dispatcher;
// Dispatch a work item to do some work and return a value
int result = dispatcher.post(getDummyValue)->get();
Chaining tasks can also be straightforward. In this example we produce various types in a sequence.
using namespace Bloomberg::quantum;
// Create a dispatcher
Dispatcher dispatcher;
auto ctx = dispatcher.postFirst([](CoroContext<int>::Ptr ctx)->int {
return ctx->set(55); //Set the 1st value
})->then<double>([](CoroContext<double>::Ptr ctx)->int {
// Get the first value and add something to it
return ctx->set(ctx->getPrev<int>() + 22.33); //Set the 2nd value
})->then<std::string>([](CoroContext<std::string>::Ptr ctx)->int {
return ctx->set("Hello world!"); //Set the 3rd value
})->finally<std::list<int>>([](CoroContext<std::list<int>>::Ptr ctx)->int {
return ctx->set(std::list<int>{1,2,3}); //Set 4th value
})->end();
int i = ctx->getAt<int>(0); //This will throw 'FutureAlreadyRetrievedException'
//since future was already read in the 2nd coroutine
double d = ctx->getAt<double>(1); //returns 77.33
std::string s = ctx->getAt<std::string>(2); //returns "Hello world!";
std::list<int>& listRef = ctx->getRefAt<std::list<int>>(3); //get list reference
std::list<int>& listRef2 = ctx->getRef(); //get another list reference.
//The 'At' overload is optional for last chain future
std::list<int> listValue = ctx->get(); //get list value
Building and installing
Quantum is a header-only library and as such no targets need to be built. To install simply run:
> cmake -Bbuild <options> .
> cd build
> make install
CMake options
Various CMake options can be used to configure the output:
QUANTUM_BUILD_DOC
: Build Doxygen documentation. DefaultOFF
.QUANTUM_ENABLE_DOT
: Enable generation of DOT viewer files. DefaultOFF
.QUANTUM_VERBOSE_MAKEFILE
: Enable verbose cmake output. DefaultON
.QUANTUM_ENABLE_TESTS
: Builds thetests
target. DefaultOFF
.QUANTUM_BOOST_STATIC_LIBS
: Link with Boost static libraries. DefaultON
.QUANTUM_BOOST_USE_MULTITHREADED
: Use Boost multi-threaded libraries. DefaultON
.QUANTUM_INSTALL_ROOT
: Specify custom install path. Default is/usr/local/include
for Linux orc:/Program Files
for Windows.BOOST_ROOT
: Specify a different Boost install directory.GTEST_ROOT
: Specify a different GTest install directory.
Note: options must be preceded with -D
when passed as arguments to CMake.
Running tests
Run the following from the top directory:
> cmake -Bbuild -DQUANTUM_ENABLE_TESTS=ON <options> .
> cd build
> make quantum_test && ctest
Using
To use the library simply include <quantum/quantum.h>
in your application. Also, the following libraries must be included in the link:
boost_context
pthread
Quantum library is fully is compatible with C++11
, C++14
and C++17
language features. See compiler options below for more details.
Compiler options
The following compiler options can be set when building your application:
__QUANTUM_PRINT_DEBUG
: Prints debug and error information tostdout
andstderr
respectively.__QUANTUM_USE_DEFAULT_ALLOCATOR
: Disable pool allocation for internal objects (other than coroutines stacks) and use default system allocators instead.__QUANTUM_USE_DEFAULT_CORO_ALLOCATOR
: Disable pool allocation for coroutine stacks and use default system allocator instead.__QUANTUM_ALLOCATE_POOL_FROM_HEAP
: Pre-allocates object pool from heap instead of the application stack (default). This affects internal object allocations other than coroutines. Coroutine pools are always heap-allocated due to their size.
Application-wide settings
Various application-wide settings can be configured via ThreadTraits
, AllocatorTraits
and StackTraits
.
Documentation
Please see the wiki page for a detailed overview of this library, use-case scenarios and examples.
For class description visit the API reference page.