[/
 / Copyright (c) 2003-2024 Christopher M. Kohlhoff (chris at kohlhoff dot com)
 /
 / 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)
 /]

[section:completion_tokens Completion Tokens]

[$completion_token_model.png [width 436px]]

A key goal of Asio's asynchronous model is to support multiple composition
mechanisms. This is achieved via a ['completion token], which the user passes
to an asynchronous operation's initiating function to customise the API surface
of the library. By convention, the completion token is the final argument to an
asynchronous operation's initiating function.

For example, if the user passes a lambda (or other function object) as the
completion token, the asynchronous operation behaves as previously described:
the operation begins, and when the operation completes the result is passed to
the lambda.

  socket.async_read_some(buffer,
      [](error_code e, size_t)
      {
        // ...
      }
    );

When the user passes the [link asio.reference.use_future use_future] completion
token, the operation behaves as though implemented in terms of a `promise` and
`future` pair. The initiating function does not just launch the operation, but
also returns a future that may be used to await the result.

  future<size_t> f =
    socket.async_read_some(
        buffer, use_future
      );
  // ...
  size_t n = f.get();

Similarly, when the user passes the [link asio.reference.use_awaitable
use_awaitable] completion token, the initiating function behaves as though it
is an `awaitable`-based coroutine [footnote The [link asio.reference.awaitable
awaitable] class template is included with the Asio library as a return type
for C++20 coroutines. These coroutines can be trivially implemented in terms of
other `awaitable`-based coroutines.]. However, in this case the initiating
function does not launch the asynchronous operation directly. It only returns
the `awaitable`, which in turn launches the operation when it is co_await-ed.

  awaitable<void> foo()
  {
    size_t n =
      co_await socket.async_read_some(
          buffer, use_awaitable
        );

    // ...
  }

Finally, the [link asio.reference.yield_context yield_context] completion token
causes the initiating function to behave as a synchronous operation within the
context of a stackful coroutine. It not only begins the asynchronous operation,
but blocks the stackful coroutine until it is complete. From the point of the
stackful coroutine, it is a synchronous operation.

  void foo(asio::yield_context yield)
  {
    size_t n = socket.async_read_some(buffer, yield);

    // ...
  }

All of these uses are supported by a single implementation of the
`async_read_some` initiating function.

To achieve this, an asynchronous operation must first specify a ['completion
signature] (or, possibly, signatures) that describes the arguments that will be
passed to its completion handler.

Then, the operation's initiating function takes the completion signature,
completion token, and its internal implementation and passes them to the
['async_result] trait. The `async_result` trait is a customisation point that
combines these to first produce a concrete completion handler, and then launch
the operation.

[$completion_token_transform.png [width 856px]]
 
To see this in practice, let's use a detached thread to adapt a synchronous
operation into an asynchronous one:[footnote For illustrative purposes only.
Not recommended for real world use!]

  template <
      completion_token_for<void(error_code, size_t)> /*< The `completion_token_for`
                                                         concept checks whether
                                                         the user-supplied
                                                         completion token will
                                                         satisfy the specified
                                                         completion signature.
                                                         For older versions of
                                                         C++, simply use `typename`
                                                         here instead. >*/
        CompletionToken>
  auto async_read_some(
      tcp::socket& s,
      const mutable_buffer& b,
      CompletionToken&& token)
  {
    auto init = []( /*< Define a function object that contains the code to
                        launch the asynchronous operation. This is passed the
                        concrete completion handler, followed by any additional
                        arguments that were passed through the `async_result`
                        trait. >*/
        auto completion_handler,
        tcp::socket* s,
        const mutable_buffer& b)
    {
      std::thread( /*< The body of the function object spawns a new thread to
                       perform the operation. >*/
          [](
              auto completion_handler,
              tcp::socket* s,
              const mutable_buffer& b
            )
          {
            error_code ec;
            size_t n = s->read_some(b, ec);
            std::move(completion_handler)(ec, n); /*< Once the operation
                                                      completes, the completion
                                                      handler is passed the
                                                      result. >*/
          },
          std::move(completion_handler),
          s,
          b
        ).detach();
    };

    return async_result< /*< The `async_result` trait is passed the (decayed)
                             completion token type, and the completion
                             signatures of the asynchronous operation. >*/
        decay_t<CompletionToken>,
        void(error_code, size_t)
      >::initiate(
          init, /*< Call the trait’s `initiate` member function, first passing
                    the function object that launches the operation. >*/
          std::forward<CompletionToken>(token), /*< Next comes the forwarded
                                                    completion token. The trait
                                                    implementation will convert
                                                    this into a concrete
                                                    completion handler. >*/
          &s, /*< Finally, pass any additional arguments for the function
                  object. Assume that these may be decay-copied and moved by the
                  trait implementation. >*/
          b
        );
  }

In practice we should call the [link asio.reference.async_initiate
async_initiate] helper function, rather than use the `async_result` trait
directly. The `async_initiate` function automatically performs the necessary
decay and forward of the completion token, and also enables backwards
compatibility with legacy completion token implementations.

  template <
      completion_token_for<void(error_code, size_t)>
        CompletionToken>
  auto async_read_some(
      tcp::socket& s,
      const mutable_buffer& b,
      CompletionToken&& token)
  {
    auto init = /* ... as above ... */;

    return async_initiate<
        CompletionToken,
        void(error_code, size_t)
      >(init, token, &s, b);
  }

We can think of the completion token as a kind of proto completion handler. In
the case where we pass a function object (like a lambda) as the completion
token, it already satisfies the completion handler requirements. The
async_result primary template handles this case by trivially forwarding the
arguments through:

  template <class CompletionToken, completion_signature... Signatures>
  struct async_result
  {
    template <
        class Initiation,
        completion_handler_for<Signatures...> CompletionHandler,
        class... Args>
    static void initiate(
        Initiation&& initiation,
        CompletionHandler&& completion_handler,
        Args&&... args)
    {
      std::forward<Initiation>(initiation)(
          std::forward<CompletionHandler>(completion_handler),
          std::forward<Args>(args)...);
    }
  };

We can see here that this default implementation avoids copies of all
arguments, thus ensuring that eager initiation is as efficient as possible.

On the other hand, a lazy completion token (such as `use_awaitable` above) may
capture these arguments for deferred launching of the operation. For example, a
specialisation for a trivial [link asio.reference.deferred deferred] token
(that simply packages the operation for later) might look something like this:

  template <completion_signature... Signatures>
  struct async_result<deferred_t, Signatures...>
  {
    template <class Initiation, class... Args>
    static auto initiate(Initiation initiation, deferred_t, Args... args)
    {
      return [
          initiation = std::move(initiation),
          arg_pack = std::make_tuple(std::move(args)...)
        ](auto&& token) mutable
      {
        return std::apply(
            [&](auto&&... args)
            {
              return async_result<decay_t<decltype(token)>, Signatures...>::initiate(
                  std::move(initiation),
                  std::forward<decltype(token)>(token),
                  std::forward<decltype(args)>(args)...
                );
            },
            std::move(arg_pack)
          );
      };
    }
  };

[endsect]
