|  | 
          Support for C++20 Coroutines is provided via the awaitable class template, the
          use_awaitable
          completion token, and the co_spawn()
          function. These facilities allow programs to implement asynchronous logic
          in a synchronous manner, in conjunction with the co_await
          keyword, as shown in the following example:
        
asio::co_spawn(executor, echo(std::move(socket)), asio::detached); // ... asio::awaitable<void> echo(tcp::socket socket) { try { char data[1024]; for (;;) { std::size_t n = co_await socket.async_read_some(asio::buffer(data), asio::use_awaitable); co_await async_write(socket, asio::buffer(data, n), asio::use_awaitable); } } catch (std::exception& e) { std::printf("echo Exception: %s\n", e.what()); } }
          The first argument to co_spawn() is an executor
          that determines the context in which the coroutine is permitted to execute.
          For example, a server's per-client object may consist of multiple coroutines;
          they should all run on the same strand
          so that no explicit synchronisation is required.
        
          The second argument is an awaitable<R>,
          that is the result of the coroutine's entry point function, and in the
          above example is the result of the call to echo.
          (Alternatively, this argument can be a function object that returns the
          awaitable<R>.) The template parameter R is the type of return value produced
          by the coroutine. In the above example, the coroutine returns void.
        
          The third argument is a completion token, and this is used by co_spawn()
          to produce a completion handler with signature void(std::exception_ptr, R).
          This completion handler is invoked with the result of the coroutine once
          it has finished. In the above example we pass a completion token type,
          asio::detached,
          which is used to explicitly ignore the result of an asynchronous operation.
        
          In this example the body of the coroutine is implemented in the echo function. When the use_awaitable completion token is passed
          to an asynchronous operation, the operation's initiating function returns
          an awaitable that may be
          used with the co_await
          keyword:
        
std::size_t n = co_await socket.async_read_some(asio::buffer(data), asio::use_awaitable);
Where an asynchronous operation's handler signature has the form:
void handler(asio::error_code ec, result_type result);
          the resulting type of the co_await
          expression is result_type.
          In the async_read_some
          example above, this is size_t.
          If the asynchronous operation fails, the error_code
          is converted into a system_error
          exception and thrown.
        
Where a handler signature has the form:
void handler(asio::error_code ec);
          the co_await expression
          produces a void result. As
          above, an error is passed back to the coroutine as a system_error
          exception.
        
          To perform explicit error handling, rather than the default exception-throwing
          behaviour, use the as_tuple or redirect_error completion token
          adapters.
        
          The as_tuple completion
          token adapter packages the completion handler arguments into a single tuple,
          which is then returned as the result of the awaited operation. For example:
        
asio::awaitable<void> echo(tcp::socket socket) { char data[1024]; for (;;) { std::tuple<asio::error_code, std::size_t> result = co_await socket.async_read_some(asio::buffer(data), asio::as_tuple(asio::use_awaitable)); if (!std::get<0>(result)) { // success } // ... } }
The result can also be captured directly into a structured binding:
asio::awaitable<void> echo(tcp::socket socket) { char data[1024]; for (;;) { auto [ec, n] = co_await socket.async_read_some( asio::buffer(data), asio::as_tuple(asio::use_awaitable)); if (!ec) { // success } // ... } }
          Alternatively, the redirect_error
          completion token adapter may be used to capture the error into a supplied
          error_code variable:
        
asio::awaitable<void> echo(tcp::socket socket) { char data[1024]; for (;;) { asio::error_code ec; std::size_t n = co_await socket.async_read_some(asio::buffer(data), asio::redirect_error(asio::use_awaitable, ec)); if (!ec) { // success } // ... } }
          All threads of execution created by co_spawn
          have a cancellation state that records the current state of any cancellation
          requests made to the coroutine. To access this state, use this_coro::cancellation_state as follows:
        
asio::awaitable<void> my_coroutine() { asio::cancellation_state cs = co_await asio::this_coro::cancellation_state; // ... if (cs.cancelled() != asio::cancellation_type::none) // ... }
          When first created by co_spawn,
          the thread of execution has a cancellation state that supports cancellation_type::terminal values only. To change the cancellation
          state, call this_coro::reset_cancellation_state.
        
          By default, continued execution of a cancelled coroutine will trigger an
          exception from any subsequent co_await
          of an awaitable<>
          object. This behaviour can be changed by using this_coro::throw_if_cancelled.
        
| ![[Note]](../../../note.png) | Note | 
|---|---|
| This is an experimental feature. | 
          The logical operators || and
          && have been overloaded
          for awaitable<>,
          to allow coroutines to be trivially awaited in parallel.
        
          When awaited using &&,
          the co_await expression
          waits until both operations have completed successfully. As a "short-circuit"
          evaluation, if one operation fails with an exception, the other is immediately
          cancelled. For example:
        
std::tuple<std::size_t, std::size_t> results = co_await ( async_read(socket, input_buffer, use_awaitable) && async_write(socket, output_buffer, use_awaitable) );
          Following completion of a &&
          operation, the results of all operations are concatenated into a tuple.
          In the above example, the first size_t
          represents the non-exceptional component of the async_read
          result, and the second size_t
          is the result of the async_write.
        
          When awaited using ||, the
          co_await expression waits
          until either operation succeeds. As a "short-circuit" evaluation,
          if one operation succeeds without throwing an exception, the other is immediately
          cancelled. For example:
        
std::variant<std::size_t, std::monostate> results = co_await ( async_read(socket, input_buffer, use_awaitable) || timer.async_wait(use_awaitable) );
          Following completion of a ||
          operation, the result of the first operation to complete non-exceptionally
          is placed into a std::variant. The active index of the variant
          reflects which of the operations completed first. In the above example,
          index 0 corresponds to the
          async_read operation.
        
          These operators may be enabled by adding the #include:
        
#include <asio/experimental/awaitable_operators.hpp>
          and then bringing the contents of the experimental::awaitable_operators
          namespace into scope:
        
using namespace asio::experimental::awaitable_operators;
          The experimental::co_composed template facilitates a lightweight
          implementation of user-defined asynchronous operations using C++20 coroutines.
          The following example illustrates a simple asynchronous operation that
          implements an echo protocol in terms of a coroutine:
        
template <typename CompletionToken> auto async_echo(tcp::socket& socket, CompletionToken&& token) { return asio::async_initiate< CompletionToken, void(asio::error_code)>( asio::experimental::co_composed< void(asio::error_code)>( [](auto state, tcp::socket& socket) -> void { try { state.throw_if_cancelled(true); state.reset_cancellation_state( asio::enable_terminal_cancellation()); for (;;) { char data[1024]; std::size_t n = co_await socket.async_read_some( asio::buffer(data), asio::deferred); co_await asio::async_write(socket, asio::buffer(data, n), asio::deferred); } } catch (const asio::system_error& e) { co_return {e.code()}; } }, socket), token, std::ref(socket)); }
co_spawn, detached, as_tuple, redirect_error, awaitable, use_awaitable_t, use_awaitable, this_coro::executor, experimental::co_composed, Coroutines examples, Resumable C++20 Coroutines, Stackful Coroutines, Stackless Coroutines.