|  | 
        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) { e.execute((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]). [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. —end
        note]
      
An executor type's destructor shall not block pending completion of the submitted function objects. [Note: The ability to wait for completion of submitted function objects may be provided by the associated execution context. —end note]
        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 e.execute(f):
      
DECAY_COPY(std::forward<F>(f)) on the calling thread to create cf that will be invoked at most once
            by an execution agent.
          f.
          [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. —end note]
        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. [Note: The copy constructor, comparison
        operators, and context()
        member function continue to remain valid until ctx
        is destroyed. —end note]
      
        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 18. Executor requirements
| expression | type | 
                  assertion/note | 
|---|---|---|
| 
                   | 
                  Shall not exit via an exception. | |
| 
                   | 
                  Shall not exit via an exception. | |
| 
                   | 
                   | 
                  Returns  | 
| 
                   | 
                   | 
                  Same as  | 
| 
                   | 
                   | 
                  Shall not exit via an exception. | 
| 
                   | Shall not exit via an exception. | |
| 
                   | 
                  Shall not exit via an exception. | |
| 
                   | 
                  Effects: Creates an object  | |
| 
                   | 
                  Effects: Creates an object  |