Awaitables / tmc::spawn()#
From within a tmc::task
, you can co_await
any awaitable (including other tmc::task
) directly.
You can also customize and/or combine awaitables using these utility functions:
tmc::spawn()
to customize a single awaitabletmc::spawn_many()
to customize a group of awaitables / retrieve results in an array or vectortmc::spawn_tuple()
to customize a group of awaitables / retrieve results in a tupletmc::spawn_func()
to convert a single functor into an awaitabletmc::spawn_func_many()
to convert a group of functors into awaitables / retrieve results in an array or vector
Since the output of the spawn()
family of functions is an awaitable, they can be composed by passing them into each other.
Rvalue-Only Awaitables#
Most TMC awaitables (including tmc::task
) are single-use / “rvalue-only awaitables” / linear types.
They must be used as temporaries, or passed as rvalue references, and then ultimately consumed.
They cannot be copied.
They must be moved into any co_await or spawn() operation that uses them.
Moved-from awaitables must not be used afterward.
Awaitables that have not been moved-from must be finalized by
co_await
ordetach()
before they are destroyed.They return results which must be moved into a value in the awaiting coroutine’s scope.
They cannot be co_awaited or spawn()ed more than once.
Where possible, these rules are enforced at compile time. Additionally, in Debug builds, there are runtime asserts that verify that you have not violated the above preconditions. In Release builds, these runtime checks are inactive for performance reasons. The purpose of these checks is not to constrain you, but to prevent difficult-to-debug runtime errors by giving feedback earlier in the development process.
The easiest way to use rvalue-only awaitables is to use them as temporaries in immediately-awaited expressions:
// temporary rvalue
co_await expr();
// explicit rvalue cast required
auto t = expr();
co_await std::move(t);
// wrappers have the same rules - temporary rvalue
co_await spawn_tuple(expr());
// explicit rvalue cast required
auto t = expr();
co_await tmc::spawn_tuple(std::move(t));
Lvalue-Only Awaitables#
A small number of TMC awaitables are “lvalue-only awaitables”. For example,the awaitable produced by result_each()
must be awaited multiple times to produce a sequence of values. These types cannot be awaited as rvalues or xvalues -
you must first assign them to a named variable before awaiting them.
// temporary rvalue not allowed, an lvalue must be created
auto t = expr();
co_await t;
co_await t;
// wrappers have the same rules
auto t = expr();
co_await tmc::spawn_tuple(t);
co_await tmc::spawn_tuple(t);
3rd Party / Unknown Awaitables - It Just Works#
When you await or spawn an awaitable of a type that is unknown to TMC, that awaitable will automatically
be wrapped into a tmc::task
. This provides full compatibility for all awaitables,
at a small performance cost. If you want to remove this overhead, see Integrating External Awaitables.