#### C++20 Coroutines Support

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.

###### Error Handling

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
        }

        // ...
      }
    }

###### Coroutines and Per-Operation Cancellation

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`.

###### Co-ordinating Parallel Coroutines

| 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;

###### Lightweight Coroutines Implementing Asynchonous Operations

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));
    }

###### See Also

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.