Language | Build | Coverage | License |
---|---|---|---|
μfiber is a minimalistic, header-only fiber library compatible with the
asynchronous operation model specified in the C++ Networking TS and implemented
in Boost.ASIO. It implements an interface similar to boost::asio::spawn()
, but
takes advantage of C++11 move semantics to avoid additional memory allocations
that are necessary in boost::asio::spawn()
and supports more than 2 results of
an asynchronous operation. Additionally, this library does not use deprecated
Boost libraries, so it generates no deprecation warnings when used.
μfiber depends on:
- Boost.Context
- Boost.ASIO
μfiber is header-only, so you only need to add the include directory to the
include paths in your build system. An install
target is available in CMake
which will install the headers and a CMake find_package
configuration script
for easy consumption in projects built with CMake:
mkdir build
cd build
cmake ..
make install
After installation, a project built with CMake can consume the library using
find_package
:
find_package(ufiber REQUIRED)
target_link_libraries(my_target PUBLIC ufiber::ufiber)
- Echo - a well documented, echo server that uses fibers to accept clients and then sends back octets to the client.
template<class Executor>
class yield_token
{
public:
using executor_type = Executor;
executor_type get_executor() noexcept;
};
struct broken_promise final : std::exception
{
char const* what() const noexcept final;
};
yield_token
is a lightweight handle to the currently running fiber, which may
be used as a CompletionToken
in an asynchronous initiation function. The
behavior of a yield_token
, outside the fiber it was created on, is undefined.
Executor
shall satisfy the constraints of
Executor.
The executor on which the current fiber runs can be retrieved via
get_executor()
.
When yield_token
is used as a
CompletionToken,
the asynchronous initiation function that it is passed to as an argument will
suspend execution of the current fiber. Once the operation completes, the fiber
is resumed and the async function will return the results of the operation as
its return value. The return type depends on the number of results:
- 0 results: returns
void
- 1 result: returns
T
- N results: returns
std::tuple<T...>
If an asynchronous operation has been abandoned, the fiber will be resumed but
the asynchronous initiation function will throw ufiber::broken_promise
. This
happens if an io_context
is destroyed with pending asynchronous operations. If
exceptions are disabled, the user will need to provide their own definition of
[[noreturn]] ufiber::detail::throw_broken_promise()
. Note that, if an
operation has been abandoned, the fiber may not be running within the context of
its executor, so catching ufiber::broken_promise
is not recommended.
Any exception (except for ufiber::broken_promise
) that excapes the fiber's
main function, will result in a call to std::terminate()
(same behavior as in
std::thread
).
Fibers are created by calling one of the provided overloads of the spawn()
function. Fibers are scheduled on the executor they are spawned on, migration
between threads is managed by the executor. Note that when using this library
with executors provided by ASIO, one needs to either avoid using TLS
variables or ensure that the executor cannot schedule the fiber on another
thread.
-
Spawn a fiber on
ex
with the function objectf
as its main function. The implementation will construct an object fromf
viaDECAY_COPY
. This function participates in overload resolution if and only if,E
satisfies the constraints of Executor:template<class E, class F> auto spawn(E const& ex, F&& f) -> /* SFINAE */ void;
Requires:
F
shall be invokable with the signaturevoid(yield_token<E>)
Example usage:
boost::asio::io_context io; boost::asio::strand<decltype(io)::executor_type> strand{io.get_executor()}; ufiber::spawn( strand, [](ufiber::yield_token<decltype(strand)> yield) { // The fiber will run on the strand boost::asio::steady_timer timer{yield.get_executor().context()}; timer.expires_from_now(std::chrono::seconds{1}); auto ec = timer.async_wait(yield); // Handle the timer expiration });
-
Spawn a fiber on
ctx
's executor with the function objectf
as its main function. The implementation will construct an object fromf
viaDECAY_COPY
. This function participates in overload resolution if and only if,Ctx
satisfies the constraints of ExecutionContext:template<class Ctx, class F> auto spawn(Ctx& ctx, F&& f) -> /* SFINAE */ void;
Requires:
F
shall be invokable with the signaturevoid(yield_token<Ctx::executor_type>)
.
Example usage:
boost::asio::io_context io; ufiber::spawn( io, [](auto yield) { boost::asio::steady_timer timer{yield.get_executor().context()}; timer.expires_from_now(std::chrono::seconds{1}); auto ec = timer.async_wait(yield); // Handle the timer expiration });
-
Spawn a fiber on
ex
with the function objectf
as its main function,sa
will be used to allocate the fiber's stack. The implementation will construct an object fromf
viaDECAY_COPY
:template<class Alloc, class E, class F> void spawn(std::allocator_arg_t arg, StackAllocator&& sa, E const& ex, F&& f);
Requires:
E
shall satisfy the constraints of Executor.F
shall be invokable with the signaturevoid(yield_token<E>)
.Alloc
shall satisfy the constraints of StackAllocator.
Example usage:
boost::asio::io_context io; ufiber::spawn( std::allocator_arg, boost::context::protected_fixedsize{PTHREAD_STACK_MIN}, io.get_executor(), [](auto yield) { boost::asio::signal_set set{yield.get_executor().context()}; set.add(SIGINT); set.add(SIGHUP); auto [ec, sig_num] = set.async_wait(yield); // Handle the signal });
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)