A coroutine is a function that can suspend execution to be resumed later. Coroutines are stackless: they suspend execution by returning to the caller. This allows for sequential code that executes asynchronously (e.g. to handle non-blocking I/O without explicit callbacks), and also supports algorithms on lazy-computed infinite sequences and other uses.
A function is a coroutine if its definition does any of the following:
co_await
operator to suspend execution until resumed task<> tcp_echo_server() { char data[1024]; for (;;) { size_t n = co_await socket.async_read_some(buffer(data)); co_await async_write(socket, buffer(data, n)); } }
co_yield
to suspend execution returning a value generator<int> iota(int n = 0) { while(true) co_yield n++; }
co_return
to complete execution returning a value lazy<int> f() { co_return 7; }
Every coroutine must have a return type that satisfies a number of requirements, noted below.
Coroutines cannot use variadic arguments, plain return statements, or placeholder return types (auto
or Concept
).
Constexpr functions, constructors, destructors, and the main function cannot be coroutines.
Each coroutine is associated with.
When a coroutine begins execution, it performs the following:
operator new
(see below) promise.get_return_object()
and keeps the result in a local variable. The result of that call will be returned to the caller when the coroutine first suspends. Any exceptions thrown up to and including this step propagate back to the caller, not placed in the promise. promise.initial_suspend()
and co_await
's its result. Typical Promise types either return a suspend_always
, for lazily-started coroutines, or suspend_never
, for eagerly-started coroutines. co_await promise.initial_suspend()
resumes, starts executing the body of the coroutine When a coroutine reaches a suspension point.
When a coroutine reaches the co_return
statement, it performs the following:
promise.return_void()
for co_return;
co_return expr
where expr has type void Promise::return_void()
member function in this case. promise.return_value(expr)
for co_return expr
where expr has non-void type promise.final_suspend()
and co_await
's the result. If the coroutine ends with an uncaught exception, it performs the following:
promise.unhandled_exception()
from within the catch-block promise.final_suspend()
and co_await
's the result (e.g. to resume a continuation or publish a result). It's undefined behavior to resume a coroutine from this point. When the coroutine state is destroyed either because it terminated via co_return or uncaught exception, or because it was destroyed via its handle, it does the following:
operator delete
to free the memory used by the coroutine state coroutine state is allocated on the heap via non-array operator new
.
If the Promise
type defines a class-level replacement, it will be used, otherwise global operator new
will be used.
If the Promise
type defines a placement form of operator new
that takes additional parameters, and they match an argument list where the first argument is the size requested (of type std::size_t
) and the rest are the coroutine function arguments, those arguments will be passed to operator new
(this makes it possible to use leading-allocator-convention for coroutines).
The call to operator new
can be optimized out (even if custom allocator is used) if.
in that case, coroutine state is embedded in the caller's stack frame (if the caller is an ordinary function) or coroutine state (if the caller is a coroutine).
If allocation fails, the coroutine throws std::bad_alloc
, unless the Promise type defines the member function Promise::get_return_object_on_allocation_failure()
. If that member function is defined, allocation uses the nothrow
form of operator new
and on allocation failure, the coroutine immediately returns the object obtained from Promise::get_return_object_on_allocation_failure()
to the caller.
The Promise type is determined by the compiler from the return type of the coroutine using std::coroutine_traits
.
If the coroutine is defined as task<float> foo(std::string x, bool flag);
, then its Promise
type is std::coroutine_traits<task<float>, std::string, bool>::promise_type
.
If the coroutine is a non-static member function, such as task<void> my_class::method1(int x) const;
, its Promise
type is std::coroutine_traits<task<void>, const my_class&, int>::promise_type
.
The unary operator co_await
suspends a coroutine and returns control to the caller. Its operand is an expression whose type must either define operator co_await
, or be convertible to such type by means of the current coroutine's Promise::await_transform
.
co_await expr |
First, expr is converted to an awaitable as follows:
await_transform
, then the awaitable is promise.await_transform(expr)
Then, the awaiter object is obtained, as follows:
operator co_await
gives a single best overload, the awaiter is the result of that call (awaitable.operator co_await()
for member overload, operator co_await(static_cast<Awaitable&&>(awaitable))
for the non-member overload) If the expression above is a prvalue, the awaiter object is a temporary materialized from it. Otherwise, if the expression above is an glvalue, the awaiter object is the object to which it refers.
Then, awaiter.await_ready()
is called (this is a short-cut to avoid the cost of suspension if it's known that the result is ready or can be completed synchronously). If its result, contextually-converted to bool is false
then.
awaiter.await_suspend(handle)
is called, where handle is the coroutine handle representing the current coroutine. Inside that function, the suspended coroutine state is observable via that handle, and it's this function's responsibility to schedule it to resume on some executor, or to be destroyed (returning false counts as scheduling) true
returns control to the caller/resumer of the current coroutine false
resumes the current coroutine. handle.resume()
) (note this may chain to eventually cause the current coroutine to resume) awaiter.await_resume()
is called, and its result is the result of the whole co_await expr
expression. If the coroutine was suspended in the co_await expression, and is later resumed, the resume point is immediately before the call to awaiter.await_resume()
.
Note that because the coroutine is fully suspended before entering awaiter.await_suspend()
, that function is free to transfer the coroutine handle across threads, with no additional synchronization. For example, it can put it inside a callback, scheduled to run on a threadpool when async I/O operation completes. This also means the current coroutine may resume and finish on that threadpool, concurrently, while still inside await_suspend(), and so await_suspend() should not expect the awaiter (the *this
object) to be accessible after the handle was published to other threads.
Note: the awaiter object is part of coroutine state (as a temporary whose lifetime crosses a suspension point) and is destroyed before the co_await expression finishes. It can be used to maintain per-operation state as required by some async I/O APIs without resorting to additional heap allocations.
The standard library defines two trivial awaitables: std::suspend_always
and std::suspend_never
.
Yield-expression returns a value to the caller and suspends the current coroutine: it is the common building block of resumable generator functions.
co_yield expr | ||
co_yield braced-init-list |
It is equivalent to.
co_await promise.yield_value(expr)
A typical generator's yield_value would store (copy/move or just store the address of, since the argument's lifetime crosses the suspension point inside the co_await) its argument into the generator object and return std::suspend_always
, transferring control to the caller/resumer.
© cppreference.com
Licensed under the Creative Commons Attribution-ShareAlike Unported License v3.0.
http://en.cppreference.com/w/cpp/language/coroutines