[/
 / 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:Executor1 Executor requirements]

[heading Standard executors]

Let `executor-of-impl` be the exposition-only concept

    template<class E, class F>
      concept executor-of-impl =
        invocable<decay_t<F>&> &&
        constructible_from<decay_t<F>, F> &&
        move_constructible<decay_t<F>> &&
        copy_constructible<E> &&
        is_nothrow_copy_constructible_v<E> &&
        equality_comparable<E> /* nothrow */ &&
        requires(const E& e, F&& f) {
          execution::execute(e, (F&&)f);
        };

Then the `executor` and `executor_of` concepts are defined as follows:

    template<class E>
      concept executor =
        executor-of-impl<E, execution::invocable_archetype>;

    template<class E, class F>
      concept executor_of =
        executor<E> &&
        executor-of-impl<E, F>;

Neither an executor's equality comparison nor `swap` operation shall exit via
an exception.

None of an executor type's copy constructor, destructor, equality comparison,
`swap` function, `execute` function, or associated `query` functions shall
introduce data races as a result of concurrent invocations of those functions
from different threads.

For any two (possibly const) values `x1` and `x2` of some executor type `X`,
`x1 == x2` shall return `true` only if `asio::query(x1,p) == asio::query(x2,p)`
for every property `p` where both `asio::query(x1,p)` and `asio::query(x2,p)`
are well-formed and result in a non-void type that is `equality_comparable`
(C++Std [equalitycomparable]). [inline_note The above requirements imply that `x1
== x2` returns `true` if `x1` and `x2` can be interchanged with identical
effects. An executor may conceptually contain additional properties which are
not exposed by a named property type that can be observed via `asio::query`; in
this case, it is up to the concrete executor implementation to decide if these
properties affect equality. Returning `false` does not necessarily imply that
the effects are not identical.]

An executor type's destructor shall not block pending completion of the
submitted function objects. [inline_note The ability to wait for completion of
submitted function objects may be provided by the associated execution
context.]

In addition to the above requirements, types `E` and `F` model `executor_of`
only if they satisfy the requirements of the Table below.

Let:

* `e` denotes a (possibly const) executor object of type `E`,

* `cf` denotes the function object `DECAY_COPY(std::forward<F>(f))`

* `f` denotes a function of type `F&&` invocable as `cf()` and where
  `decay_t<F>` models `move_constructible`.

The expression `execution::execute(e, f)`:

* Evaluates `DECAY_COPY(std::forward<F>(f))` on the calling thread to create
  `cf` that will be invoked at most once by an execution agent.

* May block pending completion of this invocation. Synchronizes with
  [intro.multithread] the invocation of `f`.

* Shall not propagate any exception thrown by the function object or any other
  function submitted to the executor.

[inline_note The treatment of exceptions thrown by one-way submitted functions
is implementation-defined. The forward progress guarantee of the associated
execution agent(s) is implementation-defined.]

[heading Networking TS-style executors]

The library describes a standard set of requirements for ['executors]. A type
meeting the `Executor` requirements embodies a set of rules for determining how
submitted function objects are to be executed.

A type `X` meets the `Executor` requirements if it satisfies the requirements of
`CopyConstructible` (C++Std [copyconstructible]) and `Destructible` (C++Std
[destructible]), as well as the additional requirements listed below.

No constructor, comparison operator, copy operation, move operation, swap
operation, or member functions `context`, `on_work_started`, and
`on_work_finished` on these types shall exit via an exception.

The executor copy constructor, comparison operators, and other member functions
defined in these requirements shall not introduce data races as a result of
concurrent calls to those functions from different threads.

Let `ctx` be the execution context returned by the executor's `context()`
member function. An executor becomes ['invalid] when the first call to
`ctx.shutdown()` returns. The effect of calling `on_work_started`,
`on_work_finished`, `dispatch`, `post`, or `defer` on an invalid executor is
undefined. [inline_note The copy constructor, comparison operators, and
`context()` member function continue to remain valid until `ctx` is destroyed.]

In the table below, `x1` and `x2` denote (possibly const) values of type `X`,
`mx1` denotes an xvalue of type `X`, `f` denotes a `MoveConstructible` (C++Std
[moveconstructible]) function object callable with zero arguments, `a` denotes
a (possibly const) value of type `A` meeting the `Allocator` requirements
(C++Std [allocator.requirements]), and `u` denotes an identifier.

[table Executor requirements
  [[expression] [type] [assertion/note[br]pre/post-conditions]]
  [
    [`X u(x1);`]
    []
    [Shall not exit via an exception.[br]
     [br]
     post: `u == x1` and
     `std::addressof(u.context()) == std::addressof(x1.context()).`]
  ]
  [
    [`X u(mx1);`]
    []
    [Shall not exit via an exception.[br]
     [br]
     post: `u` equals the prior value of `mx1` and
     `std::addressof(u.context())` equals the prior value of
     `std::addressof(mx1.context())`.]
  ]
  [
    [`x1 == x2`]
    [`bool`]
    [ Returns `true` only if `x1` and `x2` can be interchanged with identical
     effects in any of the expressions defined in these type requirements.
     [inline_note Returning `false` does not necessarily imply that the effects
     are not identical.][br]
     [br]
     `operator==` shall be reflexive, symmetric, and transitive, and shall not
     exit via an exception.]
  ]
  [
    [`x1 != x2`]
    [`bool`]
    [Same as `!(x1 == x2)`.]
  ]
  [
    [`x1.context()`]
    [`execution_context&`, or `E&` where `E` is a type that satifisfies the
     [link asio.reference.ExecutionContext `ExecutionContext`] requirements.]
    [Shall not exit via an exception.[br]
     [br]
     The comparison operators and member functions defined in these
     requirements shall not alter the reference returned by this function.]
  ]
  [
    [`x1.on_work_started()`]
    []
    [Shall not exit via an exception.]
  ]
  [
    [`x1.on_work_finished()`]
    []
    [Shall not exit via an exception.[br]
     [br]
     Precondition: A preceding call `x2.on_work_started()` where `x1 == x2`.]
  ]
  [
    [`x1.dispatch(std::move(f),a)`]
    []
    [Effects: Creates an object `f1` initialized with
     [^['DECAY_COPY]]`(forward<Func>(f))` (C++Std \[thread.decaycopy\]) in the
     current thread of execution . Calls `f1()` at most once. The executor may
     block forward progress of the caller until `f1()` finishes execution.[br]
     [br]
     Executor implementations should use the supplied allocator to allocate any
     memory required to store the function object. Prior to invoking the
     function object, the executor shall deallocate any memory allocated.
     [inline_note Executors defined in this Technical Specification always use
     the supplied allocator unless otherwise specified.][br]
     [br]
     Synchronization: The invocation of `dispatch` synchronizes with (C++Std
     \[intro.multithread\]) the invocation of `f1`.]
  ]
  [
    [`x1.post(std::move(f),a)`[br]
     `x1.defer(std::move(f),a)`]
    []
    [Effects: Creates an object `f1` initialized with
     [^['DECAY_COPY]]`(forward<Func>(f))` in the current thread of execution.
     Calls `f1()` at most once. The executor shall not block forward progress
     of the caller pending completion of `f1()`.[br]
     [br]
     Executor implementations should use the supplied allocator to allocate any
     memory required to store the function object. Prior to invoking the
     function object, the executor shall deallocate any memory allocated.
     [inline_note Executors defined in this Technical Specification always use
     the supplied allocator unless otherwise specified.][br]
     [br]
     Synchronization: The invocation of `post` or `defer` synchronizes with
     (C++Std \[intro.multithread\]) the invocation of `f1`.[br]
     [br]
     [inline_note Although the requirements placed on `defer` are identical to
     `post`, the use of `post` conveys a preference that the caller ['does not]
     block the first step of [^f1]'s progress, whereas `defer` conveys a
     preference that the caller ['does] block the first step of [^f1]. One use
     of `defer` is to convey the intention of the caller that [^f1] is a
     continuation of the current call context. The executor may use this
     information to optimize or otherwise adjust the way in which `f1` is
     invoked.]]
  ]
]

[endsect]
