[/
 / 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:std_executors Proposed Standard Executors]

Asio provides a complete implementation of the proposed standard executors, as
described in [@http://wg21.link/P0443r13 P0443r13], [@http://wg21.link/P1348r0
P1348r0], and [@http://wg21.link/P1393r0 P1393r0].

Just as with executors under the Networking TS model, a standard executor
represents a policy as to how, when, and where a piece of code should be
executed. Most existing code should continue to work with little or no change.

[heading Standard Executor Implementations in Asio]

The [link asio.reference.io_context.executor_type `io_context::executor_type`],
[link asio.reference.thread_pool.executor_type `thread_pool::executor_type`],
[link asio.reference.system_executor `system_executor`], and [link
asio.reference.strand `strand`] executors meet the requirements for the
proposed standard executors. For compatibility, these classes also meet the
requirements for the Networking TS model of executors.

[heading Standard Executor Use in Asio]

All I/O objects such as [link asio.reference.ip__tcp.socket `ip::tcp::socket`],
asynchronous operations, and utilities including [link asio.reference.dispatch
`dispatch`], [link asio.reference.post `post`], [link asio.reference.defer
`defer`], [link asio.reference.get_associated_executor
`get_associated_executor`], [link asio.reference.bind_executor
`bind_executor`], [link asio.reference.make_work_guard `make_work_guard`],
[link asio.reference.spawn `spawn`], [link asio.reference.co_spawn `co_spawn`],
[link asio.reference.async_compose `async_compose`], [link
asio.reference.use_future `use_future`], etc., can interoperate with both
proposed standard executors, and with Networking TS executors. Asio's
implementation determines at compile time which model a particular executor
meets; the proposed standard executor model is used in preference if both are
detected.

Support for the existing Networking TS model of executors can be disabled
by defining `ASIO_NO_TS_EXECUTORS`.

[heading Polymorphic I/O Executor]

The [link asio.reference.any_io_executor `any_io_executor`] type alias is the
default runtime-polymorphic executor for all I/O objects. This type alias
points to the [link asio.reference.execution__any_executor
`execution::any_executor<>`] template with a set of supportable properties
specified for use with I/O.

This new name may break existing code that directly uses the old polymorphic
wrapper, [link asio.reference.executor `executor`]. If required for backward
compatibility, `ASIO_USE_TS_EXECUTOR_AS_DEFAULT` can be defined, which changes
the `any_io_executor` type alias to instead point to the `executor` polymorphic
wrapper.

[heading Implementing a Minimal I/O Executor]

Standard executor properties make what were previously hard requirements on an
executor (such as work counting, or the ability to distinguish between `post`,
`dispatch`, and `defer`) into optional facilities. With this relaxation, the
minimal requirements for an I/O executor are:

* Conformance to the [link asio.reference.Executor1.standard_executors
  `executor`] concept.

* The ability to query the [link asio.reference.execution__context
  `execution::context`] property, with the result being [link
  asio.reference.execution_context `execution_context&`] or a reference to a
  class that is derived from `execution_context`.

* The `execute` operation having, at minimum, the [link
  asio.reference.execution__blocking_t.never `execution::blocking.never`]
  semantic.

The following example shows a minimal I/O executor. Given a queue submission
operation implemented elsewhere:

```
queue_t queue_create();
template <typename F> void queue_submit(queue_t q, F f);
```

the executor may be defined as follows:

```
struct minimal_io_executor
{
  asio::execution_context* context_;
  queue_t queue_;

  bool operator==(const minimal_io_executor& other) const noexcept
  {
    return context_ == other.context_ && queue_ == other.queue_;
  }

  bool operator!=(const minimal_io_executor& other) const noexcept
  {
    return !(*this == other);
  }

  asio::execution_context& query(
      asio::execution::context_t) const noexcept
  {
    return *context_;
  }

  static constexpr asio::execution::blocking_t::never_t query(
      asio::execution::blocking_t) noexcept
  {
    // This executor always has blocking.never semantics.
    return asio::execution::blocking.never;
  }

  template <class F>
  void execute(F f) const
  {
    queue_submit(queue_, std::move(f));
  }
};
```

This executor may be created as follows:

```
asio::execution_context context;
queue_t queue = queue_create();
minimal_io_executor executor{&context, queue};
```

and then used with I/O objects:

```
asio::ip::tcp::acceptor acceptor(executor);
```

or assigned into the [link asio.reference.any_io_executor `any_io_executor`]
polymorphic wrapper:

```
asio::any_io_executor poly_executor = executor;
```

[heading Traits for Deducing Conformance to the Executor Concept]

Older C++ standards and compilers require some assistance to determine whether
an executor implementation conforms to the `executor` concept and type
requirements. This is achieved through specialisation of traits. The following
code shows a specialisation of these traits for the `minimal_io_executor`
example from above:

```
namespace asio {
namespace traits {

#if !defined(ASIO_HAS_DEDUCED_EXECUTE_MEMBER_TRAIT)

template <typename F>
struct execute_member<minimal_io_executor, F>
{
  static constexpr bool is_valid = true;
  static constexpr bool is_noexcept = true;
  typedef void result_type;
};

#endif // !defined(ASIO_HAS_DEDUCED_EXECUTE_MEMBER_TRAIT)
#if !defined(ASIO_HAS_DEDUCED_EQUALITY_COMPARABLE_TRAIT)

template <>
struct equality_comparable<minimal_io_executor>
{
  static constexpr bool is_valid = true;
  static constexpr bool is_noexcept = true;
};

#endif // !defined(ASIO_HAS_DEDUCED_EQUALITY_COMPARABLE_TRAIT)
#if !defined(ASIO_HAS_DEDUCED_QUERY_MEMBER_TRAIT)

template <>
struct query_member<minimal_io_executor,
    asio::execution::context_t>
{
  static constexpr bool is_valid = true;
  static constexpr bool is_noexcept = true;
  typedef asio::execution_context& result_type;
};

#endif // !defined(ASIO_HAS_DEDUCED_QUERY_MEMBER_TRAIT)
#if !defined(ASIO_HAS_DEDUCED_QUERY_STATIC_CONSTEXPR_MEMBER_TRAIT)

template <typename Property>
struct query_static_constexpr_member<minimal_io_executor, Property,
    typename enable_if<
      std::is_convertible<Property, asio::execution::blocking_t>::value
    >::type>
{
  static constexpr bool is_valid = true;
  static constexpr bool is_noexcept = true;
  typedef asio::execution::blocking_t::never_t result_type;
  static constexpr result_type value() noexcept { return result_type(); }
};

#endif // !defined(ASIO_HAS_DEDUCED_QUERY_STATIC_CONSTEXPR_MEMBER_TRAIT)

} // namespace traits
} // namespace asio
```

Asio uses an extensive set of traits to implement all of the proposed standard
executor functionality on older C++ standards. These traits may be found under
the [^asio/traits] include directory.

[endsect]
