blob: 1649fd3b68090a27c2f7349d6f50a3b872d8ad32 [file] [log] [blame]
[/
/ Copyright (c) 2003-2021 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]