<html>
<head>
<title>Networking TS changes to support proposed Executors TS</title>
<style>
ins {color:#00A000}
ins p code {color:#00A000}
p ins code {color:#00A000}
del {color:#A00000}
del p code {color:#A00000}
p del code {color:#A00000}
</style>
</head>
<body>

<pre>
Document number: P0958R1
Date:            2018-05-06
Project:         Programming Language C++
Audience:        SG1 - Concurrency and Parallelism, LEWG
Reply-to:        Christopher Kohlhoff &lt;chris&#x40;kohlhoff.com&gt;
</pre>

<h1>Networking TS changes to support proposed Executors TS</h1>

<h2>Introduction</h2>

<p>The purpose of this paper is to illustrate the likely changes to the Networking TS to conform to the proposed Executors TS in P0443R7. At this time, this paper is intended only as an aid to discussion of P0443R7, and is not intended for review and incorporation into the Networking TS.</p>

<p>All changes are relative to N4711.</p>

<h2>Summary of changes</h2>

<p>This paper proposes the following changes to the Networking TS:</p>

<ul>
<li><a href="#add.reference">Add a reference to the executors proposal.</a></li>
<li><a href="#remove.work.guard">Remove <code>executor_work_guard</code> and <code>make_work_guard</code>, as these have been superseded by the executors proposal's <code>execution::outstanding_work</code> property.</a></li>
<li><a href="#remove.executor.reqmts">Remove the Executor type requirements and the <code>is_executor</code> type trait.</a></li>
<li><a href="#specify.async.ops">Specify the "Requirements on asynchronous operations" in terms of the new executors model.</a></li>
<li><a href="#update.system.executor">Update <code>system_executor</code> and <code>system_context</code> to conform to the new executors model.</a></li>
<li><a href="#remove.polymorphic.wrapper">Remove the polymorphic wrapper <code>executor</code> and replace it with a type alias for the executors proposal's <code>execution::executor</code>.</a></li>
<li><a href="#specify.free.functions">Specify the free functions <code>dispatch</code>, <code>post</code>, and <code>defer</code> in terms of the new executors model.</a></li>
<li><a href="#update.strand">Update the <code>strand</code> adapter to conform to the new executors model.</a></li>
<li><a href="#update.use_future">Update the <code>use_future</code> completion token to conform to the new executors model.</a></li>
<li><a href="#update.io_context">Update <code>io_context</code> to conform to the new executors model.</a></li>
<li><a href="#add.context.member">Add <code>context()</code> member functions to I/O objects as a convenience.</a></li>
</ul>

<p>In addition, this paper proposes <a href="#modify.executors">some minor modifications to the executors proposal in P0443R7</a>. These changes enable the use of the polymorphic wrapper <code>execution::executor</code> in the Networking TS.</p>

<h2>Deployment experience</h2>

<p>An implementation of the changes below, including a subset of the P0443R7 specification to enable these changes (except for the polymorphic executor wrapper), can be found in a branch of the Asio library at <a href="https://github.com/chriskohlhoff/asio/tree/unified-executors">https://github.com/chriskohlhoff/asio/tree/unified-executors</a>.</p>

<p>This implementation was used to test a number of sample programs. The majority of these programs needed no modification.</p>

<h2>Proposed wording</h2>

<h3 id="add.reference">Add a reference to the executors proposal</h3>

<p><em>Add a reference to the executors proposal in <b>-5- Namespaces and headers [namespaces]</b>:</em></p>

<p>
-2- Unless otherwise specified, references to other entities described in this Technical Specification are assumed to be qualified with <code>std::experimental::net::v1::</code>, references to entities described in the C++ standard are assumed to be qualified with <code>std::</code>, <del>and</del> references to entities described in C++ Extensions for Library Fundamentals are assumed to be qualified with <code>std::experimental::fundamentals_v2::</code><ins>, and references to entities described in P0443R7 A Unified Executors Proposal for C++ are assumed to be qualified with <code>std::experimental::executors_v1::</code></ins>.
</p>

<h3 id="remove.work.guard">Remove superseded <code>executor_work_guard</code> and <code>make_work_guard</code></h3>

<p><em>Remove <code>executor_work_guard</code> from <b>-12.1- Header &lt;experimental/netfwd&gt; synopsis [fwd.decl.synop]</b>:</em></p>

<pre><del>
  template&lt;class Executor&gt;
    class executor_work_guard;
</del></pre>

<p><em>Remove <code>executor_work_guard</code> and <code>make_work_guard</code> from <b>-13.1- Header &lt;experimental/executor&gt; synopsis [async.synop]</b>:</em></p>

<pre><del>
  template&lt;class Executor&gt;
    class executor_work_guard;

  // 13.17, make_work_guard:

  template&lt;class Executor&gt;
    executor_work_guard&lt;Executor&gt;
      make_work_guard(const Executor&amp; ex);
  template&lt;class ExecutionContext&gt;
    executor_work_guard&lt;typename ExecutionContext::executor_type&gt;
      make_work_guard(ExecutionContext&amp; ctx);
  template&lt;class T&gt;
    executor_work_guard&lt;associated_executor_t&lt;T&gt;&gt;
      make_work_guard(const T&amp; t);
  template&lt;class T, class U&gt;
    auto make_work_guard(const T&amp; t, U&amp; u)
      -&gt; decltype(make_work_guard(get_associated_executor(t, forward&lt;U&gt;(u))));
</del></pre>

<p><em>Remove sections <b>-13.16- Class template executor_work_guard [async.exec.work.guard]</b> and <b>-13.17- Function make_work_guard [async.make.work.guard]</b> in their entirety.</em></p>

<h3 id="remove.executor.reqmts">Remove Executor requirements and <code>is_executor</code> type trait</h3>

<p><em>Modify <b>Table 3 - Template parameters and type requirements [summary]</b> as follows:</em></p>

<table border="1">
<tr>
<td><code>Executor</code></td>
<td><del>executor (13.2.2)</del><br/><ins>general requirements on executors (P0443R7)</ins></td>
</tr>
</table>

<p><em>Remove <code>is_executor</code> and <code>is_executor_v</code> from <b>-13.1- Header &lt;experimental/executor&gt; synopsis [async.synop]</b>:</em></p>

<pre><del>
  template&lt;class T&gt; struct is_executor;

  template&lt;class T&gt;
    constexpr bool is_executor_v = is_executor&lt;T&gt;::value;
</del></pre>

<p><em>Remove section <b>-13.2.2- Executor requirements [async.reqmts.executor]</b> in its entirety.</em></p>

<p><em>Modify <b>Table 5 - ExecutionContext requirements [async.reqmts.executioncontext]</b> as follows:</em></p>

<table border="1">
<tr>
<th>expression</th>
<th>return type</th>
<th>assertion/note pre/post-condition</th>
</tr>
<tr>
<td><code>X::executor_type</code></td>
<td><del>type meeting <code>Executor</code> (13.2.2) requirements</del><br/><ins>A type satisfying the general requirements on executors (P0443R7).</ins></td>
<td></td>
</tr>
</table>

<p><em>Modify section <b>-13.2.7.8- I/O executor [async.reqmts.async.io.exec]</b> as follows.</em></p>

<p>-1- An asynchronous operation has an associated executor satisfying the <del><code>Executor</code> (13.2.2) requirements</del><ins>general requirements on executors (P0443R7)</ins>. If not otherwise specified by the asynchronous operation, this associated executor is an object of type <code>system_executor</code>.</p>

<p><em>[...]</em></p>

<p>-3- Let <code>Executor1</code> be the type of the associated executor. Let <code>ex1</code> be a value of type <code>Executor1</code>, representing the associated executor object obtained as described above. <ins><code>execution::can_query_v&lt;Executor1, execution::context_t&gt;</code> shall be <code>true</code>, and <code>execution::query(ex1, execution::context_t)</code> shall yield a value of type <code>execution_context&amp;</code> or of type <code>E&amp;</code>, where <code>E</code> satisifies the <code>ExecutionContext</code> (13.2.3) requirements.</ins></p>

<p><em>Modify section <b>-13.2.7.9- Completion handler executor [async.reqmts.async.handler.exec]</b> as follows.</em></p>

<p>-1- A completion handler object of type <code>CompletionHandler</code> has an associated executor satisfying the <del>Executor requirements (13.2.2)</del><ins>general requirements on executors (P0443R7)</ins>. The type of this associated executor is <code>associated_executor_t&lt;CompletionHandler, Executor1&gt;</code>. Let <code>Executor2</code> be the type <code>associated_executor_t&lt;CompletionHandler, Executor1&gt;</code>. Let <code>ex2</code> be a value of type <code>Executor2</code> obtained by performing <code>get_associated_executor(completion_handler, ex1)</code>. <ins><code>execution::can_query_v&lt;Executor2, execution::context_t&gt;</code> shall be <code>true</code>, and <code>execution::query(ex2, execution::context_t)</code> shall yield a value of type <code>execution_context&amp;</code> or of type <code>E&amp;</code>, where <code>E</code> satisifies the <code>ExecutionContext</code> (13.2.3) requirements.</ins></p>

<p><em>Modify section <b>-13.2.7.14- Composed asynchronous operations [async.reqmts.async.composed]</b> as follows.</em></p>

<p>An intermediate operation's completion handler shall have an associated executor that is either:</p>

<ul>
<li>the type <code>Executor2</code> and object <code>ex2</code> obtained from the completion handler type <code>CompletionHandler</code> and object <code>completion_handler</code>; or</li>
<li>an object of an unspecified type satisfying the <del>Executor requirements (13.2.2)</del><ins>general requirements on executors (P0443R7)</ins>, that delegates executor operations to the type <code>Executor2</code> and object <code>ex2</code>.</li>
</ul>

<p><em>Remove section <b>-13.9- Class template is_executor [async.is.exec]</b> in its entirety.</em></p>

<p><em>Modify section <b>-13.10- Executor argument tag [async.executor.arg]</b> as follows.</em></p>

The <code>executor_arg_t</code> struct is an empty structure type used as a unique type to disambiguate constructor and function overloading. Specifically, types may have constructors with <code>executor_arg_t</code> as the first argument, immediately followed by an argument of a type that satisfies the <del>Executor requirements (13.2.2)</del><ins>general requirements on executors (P0443R7)</ins>.

<p><em>Modify section <b>-13.12- Class template associated_executor [async.assoc.exec]</b> as follows.</em></p>

<p>-1- Class template <code>associated_executor</code> is an associator (13.2.6) for <del>the <code>Executor</code> (13.2.2) type requirements</del><ins>executors</ins>, with default candidate type <code>system_executor</code> and default candidate object <code>system_executor()</code>.</p>

<p><em>Modify <b>Table 9 - associated_executor specialization requirements</b> as follows:</em></p>

<table border="1">
<tr>
<th>Expression</th>
<th>Return type</th>
<th>Note</th>
</tr>
<tr>
<td><code>typename X::type</code></td>
<td><del>A type meeting <code>Executor</code> requirements (13.2.2)</del><br/><ins>A type satisfying the general requirements on executors (P0443R7)</ins>.</td>
<td></td>
</tr>
</table>

<p><em>Modify section <b>-13.13- Function get_associated_executor [async.assoc.exec.get]</b> as follows.</em></p>

<p>-3- <b>Remarks:</b> This function shall not participate in overload resolution unless
<del><code>is_executor_v&lt;Executor&gt;</code> is <code>true</code></del><ins><code>is_convertible&lt;Executor&amp;, execution_context&amp;&gt;::value</code> is <code>false</code></ins>.</p>

<p><em>Modify section <b>-13.14- Class template executor_binder [async.exec.binder]</b> as follows.</em></p>

<p>-1- The class template <code>executor_binder</code> binds executors to objects. A specialization <code>executor_binder&lt;T, Executor&gt;</code> binds an executor of type <code>Executor</code> satisfying the <del>Executor requirements (13.2.2)</del><ins>general requirements on executors (P0443R7)</ins> to an object or function of type <code>T</code>.</p>

<p><em>Modify section <b>-13.15- Function bind_executor [async.bind.executor]</b> as follows.</em></p>

<p>-2- <b>Remarks:</b> This function shall not participate in overload resolution unless
<del><code>is_executor_v&lt;Executor&gt;</code> is <code>true</code></del><ins><code>is_convertible&lt;Executor&amp;, execution_context&amp;&gt;::value</code> is <code>false</code></ins>.</p>

<p><em>Modify section <b>-13.22- Function dispatch [async.dispatch]</b> as follows.</em></p>

<p>-8- <b>Remarks:</b> This function shall not participate in overload resolution unless
<del><code>is_executor_v&lt;Executor&gt;</code> is <code>true</code></del><ins><code>is_convertible&lt;Executor&amp;, execution_context&amp;&gt;::value</code> is <code>false</code></ins>.</p>

<p><em>Modify section <b>-13.23- Function post [async.post]</b> as follows.</em></p>

<p>-8- <b>Remarks:</b> This function shall not participate in overload resolution unless
<del><code>is_executor_v&lt;Executor&gt;</code> is <code>true</code></del><ins><code>is_convertible&lt;Executor&amp;, execution_context&amp;&gt;::value</code> is <code>false</code></ins>.</p>

<p><em>Modify section <b>-13.24- Function defer [async.defer]</b> as follows.</em></p>

<p>-8- <b>Remarks:</b> This function shall not participate in overload resolution unless
<del><code>is_executor_v&lt;Executor&gt;</code> is <code>true</code></del><ins><code>is_convertible&lt;Executor&amp;, execution_context&amp;&gt;::value</code> is <code>false</code></ins>.</p>

<p><em>Modify section <b>-13.25- Class template strand [async.strand]</b> as follows.</em></p>

<p>-1- The class template <code>strand</code> is a wrapper around an object of type <code>Executor</code> satisfying the <ins>OneWay</ins>Executor requirements (<del>13.2.2</del><ins>P0443R7</ins>).

<p><em>[...]</em></p>

<p>-2- <code>strand&lt;Executor&gt;</code> satisfies the <del><code>Executor</code> (13.2.2) requirements</del><ins>OneWayExecutor requirements (P0443R7)</ins>.</p>

<p><em>Modify <b>Table 17 - AsyncReadStream requirements [buffer.stream.reqmts.asyncreadstream]</b> as follows:</em></p>

<table border="1">
<tr>
<th>operation</th>
<th>type</th>
<th>semantics, pre/post-conditions</th>
</tr>
<tr>
<td><code>a.get_executor()</code></td>
<td>A type satisfying the <del><code>Executor</code> requirements (13.2.2)</del><ins>general requirements on executors (P0443R7)</ins>.</td>
<td>Returns the associated I/O executor.</td>
</tr>
</table>

<p><em>Modify <b>Table 19 - AsyncWriteStream requirements [buffer.stream.reqmts.asyncwritestream]</b> as follows:</em></p>

<table border="1">
<tr>
<th>operation</th>
<th>type</th>
<th>semantics, pre/post-conditions</th>
</tr>
<tr>
<td><code>a.get_executor()</code></td>
<td>A type satisfying the <del><code>Executor</code> requirements (13.2.2)</del><ins>general requirements on executors (P0443R7)</ins>.</td>
<td>Returns the associated I/O executor.</td>
</tr>
</table>

<h3 id="specify.async.ops">Specify the "Requirements on asynchronous operations" in terms of the new executors model</h3>

<p><em>Modify section <b>-13.2.7.10- Outstanding work [async.reqmts.async.work]</b> as follows.</em></p>

<p>-1- Until the asynchronous operation has completed, the asynchronous operation shall maintain:</p>
<del><ul>
<li>an object <code>work1</code> of type <code>executor_work_guard&lt;Executor1&gt;</code>, initialized as <code>work1(ex1)</code>, and where <code>work1.owns_work() == true</code>; and</li>
<li>an object <code>work2</code> of type <code>executor_work_guard&lt;Executor2&gt;</code>, initialized as <code>work2(ex2)</code>, and where <code>work2.owns_work() == true</code>.</li>
</ul></del>
<ins><ul>
<li>an executor object <code>work1</code>, initialized as <code>execution::prefer(ex1, execution::outstanding_work.tracked)</code>; and</li>
<li>an executor object <code>work2</code>, initialized as <code>execution::prefer(ex2, execution::outstanding_work.tracked)</code>.</li>
</ul></ins>

<p><em>Modify section <b>-13.2.7.12- Execution of completion handler on completion of asynchronous operation [async.reqmts.async.completion]</b> as follows:</em></p>

<p>-3- If an asynchronous operation completes immediately (that is, within the thread of execution calling the initiating function, and before the initiating function returns), the completion handler shall be submitted for execution as if by performing<del><code>ex2.post(std::move(f), alloc2)</code>. Otherwise, the completion handler shall be submitted for execution as if by performing <code>ex2.dispatch(std::move(f), alloc2)</code>.</del><ins>:</ins></p>
<ins>
<pre>
  execution::prefer(
    execution::require(ex2, execution::oneway, execution::single, execution::blocking.never),
      execution::allocator(alloc2)).execute(std::move(f));
</pre>
<p>Otherwise, the completion handler shall be submitted for execution as if by performing:</p>
<pre>
  execution::prefer(
    execution::require(work2, execution::oneway, execution::single),
      execution::blocking.possibly, execution::allocator(alloc2)).execute(std::move(f));
</pre></ins>

<h3 id="update.system.executor">Update <code>system_executor</code> and <code>system_context</code> to conform to the new executors model</h3>

<p><em>Remove <code>system_executor</code> from, and add <code>system_context</code> to, <b>-12.1- Header &lt;experimental/netfwd&gt; synopsis [fwd.decl.synop]</b>:</em></p>

<pre>
  <del>class system_executor;</del>
  <ins>class system_context;</ins>
</pre>

<p><em>Update <code>system_executor</code> to be a type alias in <b>-13.1- Header &lt;experimental/executor&gt; synopsis [async.synop]</b>:</em></p>

<pre>
  <del>class system_executor;</del>
  class system_context;
  <ins>using system_executor = system_context::executor_type;</ins>

  <del>bool operator==(const system_executor&amp;, const system_executor&amp;);
  bool operator!=(const system_executor&amp;, const system_executor&amp;);</del>
</pre>

<p><em>Remove section <b>-13.18- Class system_executor [async.system.exec]</b> in its entirety.</em></p>

<p><em>Modify section <b>-13.19- Class system_context [async.system.context]</b> as follows:</em></p>

<p>-1- Class <code>system_context</code> implements <del>the execution context associated with <code>system_executor</code> objects</del><ins>an execution context that represents the ability to run a submitted function object on any thread</ins>.

<pre>
namespace std {
namespace experimental {
namespace net {
inline namespace v1 {

  class system_context : public execution_context
  {
  public:
    // types:

    using executor_type = <del>system_executor</del><ins><em>see below</em></ins>;
</pre>

<p><em>[...]</em></p>

<p>-2- The class <code>system_context</code> satisfies the <code>ExecutionContext</code> (13.2.3) type requirements.</p>

<ins><p>-?- <code>executor_type</code> is an executor type conforming to the specification for <code>system_context</code> executor types described below. Executor objects of type <code>executor_type</code> have the following properties established:</p>
<ul>
<li><code>execution::oneway</code></li>
<li><code>execution::single</code></li>
<li><code>execution::blocking.possibly</code></li>
<li><code>execution::relationship.fork</code></li>
<li><code>execution::mapping.thread</code></li>
<li><code>execution::allocator(std::allocator&lt;void&gt;())</code></li>
</ul>
<p>-?- <code>system_context</code> executors having a different set of established properties are represented by a distinct, unspecified type. Function objects submitted via a <code>system_context</code> executor object are permitted to execute on any thread. To satisfy the requirements for the <code>execution::blocking.never</code> property, a <code>system_context</code> executor may create <code>thread</code> objects to run the submitted function objects. These <code>thread</code> objects are collectively referred to as system threads.</p></ins>

<p><em>[...]</em></p>

<pre>
executor_type get_executor() noexcept;
</pre>

<p>-5- <b>Returns:</b> <del><code>system_executor()</code></del><ins><code>executor_type()</code></ins>.</p>

<p><em>After section <b>-13.19- Class system_context [async.system.context]</b> insert a new section as follows:</em></p>

<ins>
<h3>-13.?- <code>system_context</code> executor types</h3>

<p>-1- All executor types accessible through <code>system_context::executor_type()</code>, <code>system_context::get_executor()</code>, and subsequent calls to the member function <code>require</code>, conform to the following specification.</p>

<pre>
namespace std {
namespace experimental {
namespace net {
inline namespace v1 {

  class <em>C</em>
  {
  public:
    // construct / copy / destroy:

    <em>C</em>() {}

    // executor operations:

    <em>see below</em> require(execution::blocking_t::possibly_t) const;
    <em>see below</em> require(execution::blocking_t::never_t) const;
    <em>see below</em> require(execution::relationship_t::fork_t) const;
    <em>see below</em> require(execution::relationship_t::continuation_t) const;
    <em>see below</em> require(execution::allocator_t&lt;void&gt;) const;
    template&lt;class ProtoAllocator&gt;
      <em>see below</em> require(const execution::allocator_t&lt;ProtoAllocator&gt;&amp; a) const;

    static constexpr execution::mapping_t query(execution::mapping_t) noexcept;
    static system_context&amp; query(execution::context_t) noexcept;
    static constexpr execution::blocking_t query(execution::blocking_t) noexcept;
    static constexpr execution::relationship_t query(execution::relationship_t) noexcept;
    <em>see below</em> query(execution::allocator_t&lt;void&gt;) const noexcept;
    template&lt;class ProtoAllocator&gt;
      <em>see below</em> query(const execution::allocator_t&lt;ProtoAllocator&gt;&amp;) const noexcept;

    template&lt;class Function&gt;
      void execute(Function&amp;&amp; f) const;
  };

  bool operator==(const <em>C</em>&amp; a, const <em>C</em>&amp; b) noexcept;
  bool operator!=(const <em>C</em>&amp; a, const <em>C</em>&amp; b) noexcept;

} // inline namespace v1
} // namespace net
} // namespace experimental
} // namespace std
</pre>

<p>-2- <em>C</em> is a type satisfying the OneWayExecutor requirements (P0443R7).</p>

<h4>-13.?.1- <code>system_context</code> executor operations</h4>

<pre>
<em>see below</em> require(execution::blocking_t::possibly_t) const;
<em>see below</em> require(execution::blocking_t::never_t) const;
<em>see below</em> require(execution::relationship_t::fork_t) const;
<em>see below</em> require(execution::relationship_t::continuation_t) const;
</pre>

<p>-1- <b>Returns:</b> A <code>system_context</code> executor object of an unspecified type conforming to these specifications, with the requested property established. When the requested property is part of a group that is defined as a mutually exclusive set, any other properties in the group are removed from the returned executor object. All other properties of the returned executor object are identical to those of <code>*this</code>.</p>

<pre>
<em>see below</em> require(execution::allocator_t&lt;void&gt;) const;
</pre>

<p>-2- <b>Returns:</b> <code>require(execution::allocator(std::allocator&lt;void&gt;()))</code>.</p>

<pre>
template&lt;class ProtoAllocator&gt;
  <em>see below</em> require(const execution::allocator_t&lt;ProtoAllocator&gt;&amp; a) const;
</pre>

<p>-3- <b>Returns:</b> A <code>system_context</code> executor object of an unspecified type conforming to these specifications, with the <code>execution::allocator_t&lt;ProtoAllocator&gt;</code> property established such that allocation and deallocation associated with function submission will be performed using a copy of <code>a.value()</code>. All other properties of the returned executor object are identical to those of <code>*this</code>.</p>

<pre>
static constexpr execution::mapping_t query(execution::mapping_t) noexcept;
</pre>

<p>-4- <b>Returns:</b> <code>true</code>.</p>

<pre>
static system_context&amp; query(execution::context_t) const;
</pre>

<p>-5- <b>Returns:</b> A reference to the <code>system_context</code> object.</p>

<pre>
static constexpr execution::blocking_t query(execution::blocking_t) noexcept;
static constexpr execution::relationship_t query(execution::relationship_t) noexcept;
</pre>

<p>-6- <b>Returns:</b> The established value of the property for the executor type <em>C</em>.</p>

<pre>
<em>see below</em> query(execution::allocator_t&lt;void&gt;) const noexcept;
template&lt;class ProtoAllocator&gt;
  <em>see below</em> query(const execution::allocator_t&lt;ProtoAllocator&gt;&amp;) const noexcept;
</pre>

<p>-7- <b>Returns:</b> The allocator object associated with the executor, with type and value as previously established by the <code>execution::allocator_t&lt;ProtoAllocator&gt;</code> property.</p>

<pre>
template&lt;class Function&gt;
  void execute(Function&amp;&amp; f) const
</pre>

<p>-8- <b>Effects:</b> Submits the function <code>f</code> for execution according to the OneWayExecutor requirements and the properties established for <code>*this</code>. If <code>f</code> exits via an exception, calls <code>std::terminate()</code>.</p>

<h4>-13.?.2- <code>system_context</code> executor comparisons</h4>

<pre>
bool operator==(const <em>C</em>&amp; a, const <em>C</em>&amp; b) noexcept;
</pre>

<p>-1- <b>Returns:</b> <code>true</code>.</p>

<pre>
bool operator!=(const <em>C</em>&amp; a, const <em>C</em>&amp; b) noexcept;
</pre>

<p>-2- <b>Returns:</b> <code>false</code>.</p>
</ins>

<h3 id="remove.polymorphic.wrapper">Remove the polymorphic wrapper <code>executor</code> and replace it with a type alias</h3>

<p><em>Remove <code>executor</code> from <b>-12.1- Header &lt;experimental/netfwd&gt; synopsis [fwd.decl.synop]</b>:</em></p>

<pre><del>
  class executor;
</del></pre>

<p><em>Remove classes <code>bad_executor</code> and <code>executor</code>, and add a new type alias <code>executor</code>, in <b>-13.1- Header &lt;experimental/executor&gt; synopsis [async.synop]</b>:</em></p>

<pre>
  <del>class bad_executor;

  class executor;

  bool operator==(const executor&amp; a, const executor&amp; b) noexcept;
  bool operator==(const executor&amp; e, nullptr_t) noexcept;
  bool operator==(nullptr_t, const executor&amp; e) noexcept;
  bool operator!=(const executor&amp; a, const executor&amp; b) noexcept;
  bool operator!=(const executor&amp; e, nullptr_t) noexcept;
  bool operator!=(nullptr_t, const executor&amp; e) noexcept;</del>
  <ins>using executor = execution::executor&lt;
    execution::oneway_t,
    execution::single_t,
    execution::context_as&lt;execution_context&amp;&gt;,
    execution::blocking_t::possibly_t,
    execution::blocking_t::never_t,
    execution::prefer_only&lt;execution::outstanding_work::untracked_t&gt;,
    execution::prefer_only&lt;execution::outstanding_work::tracked_t&gt;,
    execution::prefer_only&lt;execution::relationship_t::fork_t&gt;,
    execution::prefer_only&lt;execution::relationship_t::continuation_t&gt;&gt;;</ins>
</pre>

<p><em>[...]</em></p>

<pre>
} // inline namespace v1
} // namespace net
} // namespace experimental
<del>
  template&lt;class Allocator&gt;
    struct uses_allocator&lt;experimental::net::v1::executor, Allocator&gt;
      : true_type {};

</del>} // namespace std
</pre>

<p><em>Remove sections <b>-13.20- Class bad_executor [async.bad.exec]</b> and <b>-13.21- Class executor [async.executor]</b> in their entirety.</em></p>

<h3 id="specify.free.functions">Specify the free functions <code>dispatch</code>, <code>post</code>, and <code>defer</code> in terms of the new executors model</h3>

<p><em>Modify section <b>-13.22- Function dispatch [async.dispatch]</b> as follows:</em></p>

<p>-1- [<em>Note:</em> The function <code>dispatch</code> satisfies the requirements for an asynchronous operation (13.2.7), except for the requirement that the operation uses <del><code>post</code></del><ins>the <code>execution::blocking.never</code> property</ins> if it completes immediately. <em>--end note</em>]</p>

<p><em>[...]</em></p>

<p>-3- <b>Effects:</b>
<ul>
<li>Constructs an object <code>completion</code> of type <code>async_completion&lt;CompletionToken, void()&gt;</code>, initialized with <code>token</code>.</li>
<del><li>
Performs <code>ex.dispatch(std::move(completion.completion_handler), alloc)</code>, where <code>ex</code> is the result of <code>get_associated_executor(completion.completion_handler)</code>, and <code>alloc</code> is the result of <code>get_associated_allocator(completion.completion_handler)</code>.</li></del>
<ins><li>Performs:
<pre>
  execution::prefer(
    execution::require(ex, execution::oneway, execution::single),
      execution::blocking.possibly, execution::allocator(alloc)).execute(
        std::move(completion.completion_handler));
</pre>
where <code>ex</code> is the result of <code>get_associated_executor(completion.completion_handler)</code>, and <code>alloc</code> is the result of <code>get_associated_allocator(completion.completion_handler)</code>.</li></ins>
</ul>

<p><em>[...]</em></p>

<p>-6- <b>Effects:</b>
<ul>
<li>Constructs an object <code>completion</code> of type <code>async_completion&lt;CompletionToken, void()&gt;</code>, initialized with <code>token</code>.</li>
<li>Constructs a function object <code>f</code> containing as members:
<ul>
<ins><li>an executor object <code>w</code> for the completion handler's associated executor, initialized with <code>execution::prefer(get_associated_executor(h), execution::outstanding_work)</code>,</li></ins>
<li>a copy of the completion handler <code>h</code>, initialized with <code>std::move(completion.completion_handler)</code>,</li>
<del><li>an <code>executor_work_guard</code> object <code>w</code> for the completion handler's associated executor, initialized with <code>make_work_guard(h)</code>,</li></del>
</ul>
and where the effect of <code>f()</code> is:
<del><ul>
<li><code>w.get_executor().dispatch(std::move(h), alloc)</code>, where <code>alloc</code> is the result of <code>get_associated_allocator(h)</code>, followed by</li>
<li><code>w.reset()</code>.</li>
</ul></del>
<ins><pre>
  execution::prefer(
    execution::require(w, execution::oneway, execution::single),
      execution::blocking.possibly, execution::allocator(alloc)).execute(std::move(h));
</pre></ins>
where <code>alloc</code> is the result of <code>get_associated_allocator(h)</code>.</li>
<del><li>Performs <code>ex.dispatch(std::move(f), alloc)</code>, where <code>alloc</code> is the result of <code>get_associated_allocator(completion.completion_handler)</code> prior to the construction of <code>f</code>.</li></del>
<ins><li>Performs:
<pre>
  execution::prefer(
    execution::require(ex, execution::oneway, execution::single),
      execution::blocking.possibly, execution::allocator(alloc)).execute(std::move(f));
</pre>
</li></ins>
</ul>

<p><em>Modify section <b>-13.23- Function post [async.post]</b> as follows:</em></p>

<p>-3- <b>Effects:</b>
<ul>
<li>Constructs an object <code>completion</code> of type <code>async_completion&lt;CompletionToken, void()&gt;</code>, initialized with <code>token</code>.</li>
<del><li>
Performs <code>ex.post(std::move(completion.completion_handler), alloc)</code>, where <code>ex</code> is the result of <code>get_associated_executor(completion.completion_handler)</code>, and <code>alloc</code> is the result of <code>get_associated_allocator(completion.completion_handler)</code>.</li></del>
<ins><li>Performs:
<pre>
  execution::prefer(
    execution::require(ex, execution::oneway, execution::single, execution::blocking.never),
      execution::allocator(alloc)).execute(std::move(completion.completion_handler));
</pre>
where <code>ex</code> is the result of <code>get_associated_executor(completion.completion_handler)</code>, and <code>alloc</code> is the result of <code>get_associated_allocator(completion.completion_handler)</code>.</li></ins>
</ul>

<p><em>[...]</em></p>

<p>-6- <b>Effects:</b>
<ul>
<li>Constructs an object <code>completion</code> of type <code>async_completion&lt;CompletionToken, void()&gt;</code>, initialized with <code>token</code>.</li>
<li>Constructs a function object <code>f</code> containing as members:
<ul>
<ins><li>an executor object <code>w</code> for the completion handler's associated executor, initialized with <code>execution::prefer(get_associated_executor(h), execution::outstanding_work)</code>,</li></ins>
<li>a copy of the completion handler <code>h</code>, initialized with <code>std::move(completion.completion_handler)</code>,</li>
<del><li>an <code>executor_work_guard</code> object <code>w</code> for the completion handler's associated executor, initialized with <code>make_work_guard(h)</code>,</li></del>
</ul>
and where the effect of <code>f()</code> is:
<del><ul>
<li><code>w.get_executor().dispatch(std::move(h), alloc)</code>, where <code>alloc</code> is the result of <code>get_associated_allocator(h)</code>, followed by</li>
<li><code>w.reset()</code>.</li>
</ul></del>
<ins><pre>
  execution::prefer(
    execution::require(w, execution::oneway, execution::single),
      execution::blocking.possibly, execution::allocator(alloc)).execute(std::move(h));
</pre></ins>
where <code>alloc</code> is the result of <code>get_associated_allocator(h)</code>.</li>
<del><li>Performs <code>ex.dispatch(std::move(f), alloc)</code>, where <code>alloc</code> is the result of <code>get_associated_allocator(completion.completion_handler)</code> prior to the construction of <code>f</code>.</li></del>
<ins><li>Performs:
<pre>
  execution::prefer(
    execution::require(ex, execution::oneway, execution::single, execution::blocking.never),
      execution::allocator(alloc)).execute(std::move(f));
</pre>
</li></ins>
</ul>

<p><em>Modify section <b>-13.24- Function defer [async.defer]</b> as follows:</em></p>

<p>-1- [<em>Note:</em> The function <code>defer</code> satisfies the requirements for an asynchronous operation (13.2.7)<del>, except for the requirement that the operation uses <code>post</code> if it completes immediately</del>. <em>--end note</em>]</p>

<p><em>[...]</em></p>

<p>-3- <b>Effects:</b>
<ul>
<li>Constructs an object <code>completion</code> of type <code>async_completion&lt;CompletionToken, void()&gt;</code>, initialized with <code>token</code>.</li>
<del><li>
Performs <code>ex.defer(std::move(completion.completion_handler), alloc)</code>, where <code>ex</code> is the result of <code>get_associated_executor(completion.completion_handler)</code>, and <code>alloc</code> is the result of <code>get_associated_allocator(completion.completion_handler)</code>.</li></del>
<ins><li>Performs:
<pre>
  execution::prefer(
    execution::require(ex, execution::oneway, execution::single, execution::blocking.never),
      execution::relationship.continuation, execution::allocator(alloc)).execute(
        std::move(completion.completion_handler));
</pre>
where <code>ex</code> is the result of <code>get_associated_executor(completion.completion_handler)</code>, and <code>alloc</code> is the result of <code>get_associated_allocator(completion.completion_handler)</code>.</li></ins>
</ul>

<p><em>[...]</em></p>

<p>-6- <b>Effects:</b>
<ul>
<li>Constructs an object <code>completion</code> of type <code>async_completion&lt;CompletionToken, void()&gt;</code>, initialized with <code>token</code>.</li>
<li>Constructs a function object <code>f</code> containing as members:
<ul>
<ins><li>an executor object <code>w</code> for the completion handler's associated executor, initialized with <code>execution::prefer(get_associated_executor(h), execution::outstanding_work)</code>,</li></ins>
<li>a copy of the completion handler <code>h</code>, initialized with <code>std::move(completion.completion_handler)</code>,</li>
<del><li>an <code>executor_work_guard</code> object <code>w</code> for the completion handler's associated executor, initialized with <code>make_work_guard(h)</code>,</li></del>
</ul>
and where the effect of <code>f()</code> is:
<del><ul>
<li><code>w.get_executor().dispatch(std::move(h), alloc)</code>, where <code>alloc</code> is the result of <code>get_associated_allocator(h)</code>, followed by</li>
<li><code>w.reset()</code>.</li>
</ul></del>
<ins><pre>
  execution::prefer(
    execution::require(w, execution::oneway, execution::single),
      execution::blocking.possibly, execution::allocator(alloc)).execute(std::move(h));
</pre></ins>
where <code>alloc</code> is the result of <code>get_associated_allocator(h)</code>.</li>
<del><li>Performs <code>ex.dispatch(std::move(f), alloc)</code>, where <code>alloc</code> is the result of <code>get_associated_allocator(completion.completion_handler)</code> prior to the construction of <code>f</code>.</li></del>
<ins><li>Performs:
<pre>
  execution::prefer(
    execution::require(ex, execution::oneway, execution::single, execution::blocking.never),
      execution::relationship.continuation, execution::allocator(alloc)).execute(std::move(f));
</pre>
</li></ins>
</ul>

<h3 id="update.strand">Update the <code>strand</code> adapter to conform to the new executors model</h3>

<p><em>Modify section <b>-13.25- Class template strand [async.strand]</b> as follows:</em></p>

<pre>
namespace std {
namespace experimental {
namespace net {
inline namespace v1 {

  template&lt;class Executor&gt;
  class strand
  {
  public:
</pre>

<p><em>[...]</em></p>

<pre>
    <del>execution_context&amp; context() const noexcept;

    void on_work_started() const noexcept;
    void on_work_finished() const noexcept;

    template&lt;class Func, class ProtoAllocator&gt;
      void dispatch(Func&amp;&amp; f, const ProtoAllocator&amp; a) const;
    template&lt;class Func, class ProtoAllocator&gt;
      void post(Func&amp;&amp; f, const ProtoAllocator&amp; a) const;
    template&lt;class Func, class ProtoAllocator&gt;
      void defer(Func&amp;&amp; f, const ProtoAllocator&amp; a) const;</del>

    <ins>template&lt;class Property&gt;
      <em>see below</em> require(const Property&amp; p) const;

    template&lt;class Property&gt;
      static constexpr <em>see below</em> query(const Property&amp; p);

    template&lt;class Property&gt;
      <em>see below</em> query(const Property&amp; p) const;

    template&lt;class Function&gt;
      void execute(Function&amp;&amp; f) const;</ins>
</pre>

<p><em>[...]</em></p>

<p>-3- A strand provides guarantees of ordering and non-concurrency. Given:</p>

<ul>
<li>strand objects <code>s1</code> and <code>s2</code> such that <code>s1 == s2</code></li>
<li>a function object <code>f1</code> <del>added</del><ins>submitted</ins> to the strand <code>s1</code> <del>using <code>post</code> or <code>defer</code>, or using <code>dispatch</code></del><ins>when the <code>execution::blocking.never</code> property is established in <code>s1</code>, or</ins> when <code>s1.running_in_this_thread()</code> is <code>false</code></li>
<li>a function object <code>f2</code> <del>added</del><ins>submitted</ins> to the strand <code>s2</code> <del>using <code>post</code> or <code>defer</code>, or using <code>dispatch</code></del><ins>when the <code>execution::blocking.never</code> property is established in <code>s2</code>, or</ins> when <code>s2.running_in_this_thread()</code> is <code>false</code></li>
</ul>

<p><em>Modify section <b>-13.25.4- strand operations [async.strand.ops]</b> as follows:</em></p>

<pre>
bool running_in_this_thread() const noexcept;
</pre>

<p>-2- <b>Returns:</b> <code>true</code> if the current thread of execution is running a function that was submitted to the strand, or to any other strand object <del><code>s</code> such that <code>s == *this</code>, using <code>dispatch</code>, <code>post</code> or <code>defer</code></del><ins>that shares the same ordered, non-concurrent state, using <code>execute</code></ins>; otherwise <code>false</code>. [<em>Note:</em> That is, the current thread of execution's call chain includes a function that was submitted to the strand. <em>--end note</em>]</p>

<del>
<pre>
void on_work_started() const noexcept;
</pre>

<p>-4- <b>Effects:</b> Calls <code>inner_ex_.on_work_started()</code>.</p>

<pre>
void on_work_finished() const noexcept;
</pre>

<p>-5- <b>Effects:</b> Calls <code>inner_ex_.on_work_finished()</code>.</p>

<pre>
template&lt;class Func, class ProtoAllocator&gt;
  void dispatch(Func&amp;&amp; f, const ProtoAllocator&amp; a) const;
</pre>

<p>-6- <b>Effects:</b> If <code>running_in_this_thread()</code> is true, calls <code><em>DECAY_COPY</em>(forward&lt;Func&gt;(f))()</code> (C++ 2014 [thread.decaycopy]). [<em>Note:</em> If <code>f</code> exits via an exception, the exception propagates to the caller of <code>dispatch()</code>. <em>--end note</em>] Otherwise, requests invocation of <code>f</code>, as if by forwarding the function object <code>f</code> and allocator <code>a</code> to the executor <code>inner_ex_</code>, such that the guarantees of ordering and non-concurrency are met.</p>

<pre>
template&lt;class Func, class ProtoAllocator&gt;
  void post(Func&amp;&amp; f, const ProtoAllocator&amp; a) const;
</pre>

<p>-7- <b>Effects:</b> Requests invocation of <code>f</code>, as if by forwarding the function object <code>f</code> and allocator <code>a</code> to the executor <code>inner_ex_</code>, such that the guarantees of ordering and non-concurrency are met.</p>

template&lt;class Func, class ProtoAllocator&gt;
  void defer(Func&amp;&amp; f, const ProtoAllocator&amp; a) const;

<p>-8- <b>Effects:</b> Requests invocation of <code>f</code>, as if by forwarding the function object <code>f</code> and allocator <code>a</code> to the executor <code>inner_ex_</code>, such that the guarantees of ordering and non-concurrency are met.</p>
</del>

<ins>
<pre>
template&lt;class Property&gt;
  <em>see below</em> require(const Property&amp; p) const;
</pre>

<p>-?- <b>Returns:</b> A strand <code>s</code> of type <code>strand&lt;decay_t&lt;decltype(inner_ex_.require(p))&gt;&gt;</code>, where <code>s.inner_ex_</code> is initialized with <code>inner_ex_.require(p)</code>, and sharing the same ordered, non-concurrent state as <code>*this</code>.</p>

<p>-?- <b>Remarks:</b> Shall not participate in overload resolution unless <code>inner_ex_.require(p)</code> is well-formed.</p>

<pre>
template&lt;class Property&gt;
  static constexpr <em>see below</em> query(const Property&amp; p);
</pre>

<p>-?- <b>Returns:</b> <code>Executor::query(p)</code>.</p>

<p>-?- <b>Remarks:</b> Shall not participate in overload resolution unless <code>Executor::query(p)</code> is well-formed.</p>

<pre>
template&lt;class Property&gt;
  <em>see below</em> query(const Property&amp; p) const;
</pre>

<p>-?- <b>Returns:</b> <code>inner_ex_.query(p)</code>.</p>

<p>-?- <b>Remarks:</b> Shall not participate in overload resolution unless <code>inner_ex_.query(p)</code> is well-formed.</p>

<pre>
template&lt;class Function&gt;
  void execute(Function&amp;&amp; f) const;
</pre>

<p>-?- <b>Effects:</b> Submits <code>f</code> to the executor <code>inner_ex_</code>, such that the guarantees of ordering and non-concurrency are met.</p>
</ins>

<h3 id="update.use_future">Update the <code>use_future</code> completion token to conform to the new executors model</h3>

<p><em>Modify section <b>-13.26.2- use_future_t members [async.use.future.members]</b> as follows:</em></p>

<p>-8- For any executor type <code>E</code>, the associated object for the associator <code>associated_executor&lt;H, E&gt;</code> is an executor where, for function objects executed using the executor's <del><code>dispatch()</code>, <code>post()</code> or <code>defer()</code> functions</del><ins><code>execute()</code></ins> function, any exception thrown is caught by a function object and stored in the associated shared state.</p>

<p><em>Modify section <b>-13.26.3- Partial class template specialization async_result for use_future_t [async.use.future.result]</b> as follows:</em></p>

<p>-3- The implementation specializes <code>associated_executor</code> for <code>F</code>. For function objects executed using the associated executor's <del><code>dispatch()</code>, <code>post()</code> or <code>defer()</code> functions</del><ins><code>execute()</code> function</ins>, any exception thrown is caught by the executor and stored in the associated shared state.</p>

<p>-4- For any executor type <code>E</code>, the associated object for the associator <code>associated_executor&lt;F, E&gt;</code> is an executor where, for function objects executed using the executor's <del><code>dispatch()</code>, <code>post()</code> or <code>defer()</code> functions</del><ins><code>execute()</code> function</ins>, any exception thrown by a function object is caught by the executor and stored in the associated shared state.</p>

<h3 id="update.io_context">Update <code>io_context</code> to conform to the new executors model</h3>

<p><em>Modify section <b>-14.2- Class io_context [io_context.io_context]</b> as follows:</em></p>

<pre>
namespace std {
namespace experimental {
namespace net {
inline namespace v1 {

  class io_context : public execution_context
  {
  public:
    // types:

    <del>class executor_type;</del>
    <ins>using executor_type = <em>see below</em>;</ins>
</pre>

<p><em>[...]</em></p>

<p>-1- The class <code>io_context</code> satisfies the ExecutionContext type requirements (13.2.3).</p>

<ins><p>-?- <code>executor_type</code> is an executor type conforming to the specification for <code>io_context</code> executor types described below.</p></ins>

<p><em>[...]</em></p>

<p>-4- For an object of type <code>io_context</code>, <em>outstanding work</em> is defined as the sum of:</p>
<ul>
<del><li>the total number of calls to the <code>on_work_started</code> function, less the total number of calls to the <code>on_work_finished</code> function, to any executor of the <code>io_context</code>.</li></del>
<ins><li>the number of existing executor objects associated with the <code>io_context</code> for which the <code>execution::outstanding_work</code> property is established;</li></ins>
<li>the number of function objects that have been added to the <code>io_context</code> via any executor of the <code>io_context</code>, but not yet executed; and</li>
<li>the number of function objects that are currently being executed by the <code>io_context</code>.</li>
</ul>

<p><em>[...]</em></p>

<pre>
executor_type get_executor() noexcept;
</pre>

<p>-3- <b>Returns:</b> An executor that may be used for submitting function objects to the <code>io_context</code>. <ins>The returned executor has the following properties already established:</ins></p>
<ins><ul>
<li><code>execution::oneway</code></li>
<li><code>execution::single</code></li>
<li><code>execution::blocking.possibly</code></li>
<li><code>execution::relationship.fork</code></li>
<li><code>execution::outstanding_work.untracked</code></li>
<li><code>execution::mapping.thread</code></li>
<li><code>execution::allocator(std::allocator&lt;void&gt;())</code></li>
</ul>

<p>-?- <code>io_context</code> executors having a different set of established properties are represented by a distinct, unspecified type.</p></ins>

<p><em>[...]</em></p>

<p>-13- <b>Remarks:</b> This function may invoke additional function objects through nested calls to the <code>io_context</code> executor's <del><code>dispatch</code></del><ins><code>execute</code></ins> member function. These do not count towards the return value.</p>

<p><em>[...]</em></p>

<p>-19- <b>Remarks:</b> This function may invoke additional function objects through nested calls to the <code>io_context</code> executor's <del><code>dispatch</code></del><ins><code>execute</code></ins> member function. These do not count towards the return value.</p>

<p><em>[...]</em></p>

<p>-25- <b>Remarks:</b> This function may invoke additional function objects through nested calls to the <code>io_context</code> executor's <del><code>dispatch</code></del><ins><code>execute</code></ins> member function. These do not count towards the return value.</p>

<p><em>Remove section <b>-14.3- Class io_context::executor_type [io_context.exec]</b> in its entirety.</em></p>

<p><em>After section <b>-14.2- Class io_context [io_context.io_context]</b> insert a new section as follows:</em></p>

<ins><h3>-14.?- <code>io_context</code> executor types</h3>

<p>All executor types accessible through <code>io_context::get_executor()</code>, and subsequent calls to the member function <code>require</code>, conform to the following specification.<?p>

<pre>
namespace std {
namespace experimental {
namespace net {
inline namespace v1 {

  class <em>C</em>
  {
  public:
    // construct / copy / destroy:

    <em>C</em>(const <em>C</em>&amp; other) noexcept;
    <em>C</em>(<em>C</em>&amp;&amp; other) noexcept;

    <em>C</em>&amp; operator=(const <em>C</em>&amp; other) noexcept;
    <em>C</em>&amp; operator=(<em>C</em>&amp;&amp; other) noexcept;

    // executor operations:

    <em>see below</em> require(execution::blocking_t::possibly_t) const;
    <em>see below</em> require(execution::blocking_t::never_t) const;
    <em>see below</em> require(execution::relationship_t::fork_t) const;
    <em>see below</em> require(execution::relationship_t::continuation_t) const;
    <em>see below</em> require(execution::outstanding_work_t::untracked_t) const;
    <em>see below</em> require(execution::outstanding_work_t::tracked_t) const;
    <em>see below</em> require(execution::allocator_t&lt;void&gt;) const;
    template&lt;class ProtoAllocator&gt;
      <em>see below</em> require(const execution::allocator_t&lt;ProtoAllocator&gt;&amp; a) const;

    static constexpr execution::mapping_t query(execution::mapping_t) noexcept;
    io_context&amp; query(execution::context_t) const noexcept;
    static constexpr execution::blocking_t query(execution::blocking_t) noexcept;
    static constexpr execution::relationship_t query(execution::relationship_t) noexcept;
    static constexpr execution::outstanding_work_t query(execution::outstanding_work_t) noexcept;
    <em>see below</em> query(execution::allocator_t&lt;void&gt;) const noexcept;
    template&lt;class ProtoAllocator&gt;
      <em>see below</em> query(const execution::allocator_t&lt;ProtoAllocator&gt;&amp;) const noexcept;

    bool running_in_this_thread() const noexcept;

    template&lt;class Function&gt;
      void execute(Function&amp;&amp; f) const;
  };

  bool operator==(const <em>C</em>&amp; a, const <em>C</em>&amp; b) noexcept;
  bool operator!=(const <em>C</em>&amp; a, const <em>C</em>&amp; b) noexcept;

} // inline namespace v1
} // namespace net
} // namespace experimental
} // namespace std
</pre>

<p>-1- <em>C</em>is a type satisfying the OneWayExecutor requirements (P0443R7). Objects of type <em>C</em> are associated with an <code>io_context</code>, and function objects submitted using the <code>execute</code> member function will be executed by the <code>io_context</code> from within a run function.</p>

<h4>-14.?.1- <code>io_context</code> executor constructors</h4>

<pre>
<em>C</em>(const <em>C</em>&amp; other) noexcept;
</pre>

<p>-1- <b>Postconditions:</b> <code>*this == other</code>.</p>

<pre>
<em>C</em>(<em>C</em>&amp;&amp; other) noexcept;
</pre>

<p>-2- <b>Postconditions:</b> <code>*this</code> is equal to the prior value of <code>other</code>.</p>

<h4>-14.?.2- <code>io_context</code> executor assignment</h4>

<pre>
<em>C</em>&amp; operator=(const <em>C</em>&amp; other) noexcept;
</pre>

<p>-1- <b>Postconditions:</b> <code>*this == other</code>.</p>

<p>-2- <b>Returns:</b> <code>*this</code>.</p>

<pre>
<em>C</em>&amp; operator=(<em>C</em>&amp;&amp; other) noexcept;
</pre>

<p>-3- <b>Postconditions:</b> <code>*this</code> is equal to the prior value of <code>other</code>.</p>

<p>-4- <b>Returns:</b> <code>*this</code>.</p>

<h4>-14.?.3- <code>io_context</code> executor operations</h4>

<pre>
<em>see below</em> require(execution::blocking_t::possibly_t) const;
<em>see below</em> require(execution::blocking_t::never_t) const;
<em>see below</em> require(execution::relationship_t::fork_t) const;
<em>see below</em> require(execution::relationship_t::continuation_t) const;
<em>see below</em> require(execution::outstanding_work_t::untracked_t) const;
<em>see below</em> require(execution::outstanding_work_t::tracked_t) const;
</pre>

<p>-1- <b>Returns:</b> An executor object of an unspecified type conforming to these specifications, associated with the same <code>io_context</code> as <code>*this</code>, and having the requested property established. When the requested property is part of a group that is defined as a mutually exclusive set, any other properties in the group are removed from the returned executor object. All other properties of the returned executor object are identical to those of <code>*this</code>.</p>

<pre>
<em>see below</em> require(execution::allocator_t&lt;void&gt;) const;
</pre>

<p>-2- <b>Returns:</b> <code>require(execution::allocator(std::allocator&lt;void&gt;()))</code>.

<pre>
template&lt;class ProtoAllocator&gt;
  <em>see below</em> require(const execution::allocator_t&lt;ProtoAllocator&gt;&amp; a) const;
</pre>

<p>-3- <b>Returns:</b> An executor object of an unspecified type conforming to these specifications, associated with the same <code>io_context</code> as <code>*this</code>, with the <code>execution::allocator_t&lt;ProtoAllocator&gt;</code> property established such that allocation and deallocation associated with function submission will be performed using a copy of <code>a.value()</code>. All other properties of the returned executor object are identical to those of <code>*this</code>.</p>

<pre>
static constexpr execution::mapping_t query(execution::execution::mapping_t) noexcept;
</pre>

<p>-4- <b>Returns:</b> <code>true</code>.</p>

<pre>
io_context&amp; query(execution::context_t) const noexcept;
</pre>

<p>-5- <b>Returns:</b> A reference to the associated <code>io_context</code> object.</p>

<pre>
static constexpr execution::blocking_t query(execution::blocking_t) noexcept;
static constexpr execution::relationship_t query(execution::relationship_t) noexcept;
static constexpr execution::outstanding_work_t query(execution::outstanding_work_t) noexcept;
</pre>

<p>-6- <b>Returns:</b> The established value of the property for the executor type <em>C</em>.</p>

<pre>
<em>see below</em> query(execution::allocator_t&lt;void&gt;) const noexcept;
template&lt;class ProtoAllocator&gt;
  <em>see below</em> query(const execution::allocator_t&lt;ProtoAllocator&gt;&amp;) const noexcept;
</pre>

<p>-7- <b>Returns:</b> The allocator object associated with the executor, with type and value as previously established by the <code>execution::allocator_t&lt;ProtoAllocator&gt;</code> property.</p>

<pre>
bool running_in_this_thread() const noexcept;
</pre>

<p>-8- <b>Returns:</b> <code>true</code> if the current thread of execution is calling a run function of the associated <code>io_context</code> object. [<em>Note:</em> That is, the current thread of execution's call chain includes a run function. <em>--end note</em>]</p>

<pre>
template&lt;class Function&gt;
  void execute(Function&amp;&amp; f) const
</pre>

<p>-9- <b>Effects:</b> Submits the function <code>f</code> for execution on the <code>io_context</code> according to the OneWayExecutor requirements and the properties established for <code>*this</code>. If <code>f</code> exits via an exception, the exception does not propagate to the caller of <code>execute()</code>, but is instead subsequently propagated to a caller of a run function for the <code>io_context</code> object.</p>

<h4>-14.?.4- <code>io_context</code> executor comparisons</h4>

<pre>
bool operator==(const <em>C</em>&amp; a, const <em>C</em>&amp; b) noexcept;
</pre>

<p>-1- <b>Returns:</b> <code>addressof(a.query(execution::context_t)) == addressof(b.query(execution::context_t))</code>.</p>

<pre>
bool operator!=(const <em>C</em>&amp; a, const <em>C</em>&amp; b) noexcept;
</pre>

<p>-2- <b>Returns:</b> <code>!(a == b)</code>.</p>
</ins>

<h3 id="add.context.member">Add <code>context()</code> member functions to I/O objects as a convenience</h3>

<p><em>Modify section <b>-15.4- Class template basic_waitable_timer [timer.waitable]</b> as follows:</em></p>

<pre>
namespace std {
namespace experimental {
namespace net {
inline namespace v1 {

  template&lt;class Clock, class WaitTraits = wait_traits&lt;Clock&gt;&gt;
  class basic_waitable_timer
  {
  public:
</pre>

<p><em>[...]</em></p>

<pre>
    // 15.4.4, basic_waitable_timer operations:

    <ins>io_context&amp; context() noexcept;</ins>

    executor_type get_executor() noexcept;
</pre>

<p><em>[...]</em></p>

<pre>
basic_waitable_timer(io_context&amp; ctx, const time_point&amp; t);
</pre>

<p>-2- <b>Postconditions:</b></p>
<ul>
<ins><li><code>addressof(context()) == addressof(ctx)</code>.</li></ins>
</ul>

<p><em>[...]</em></p>

<pre>
basic_waitable_timer(io_context&amp; ctx, const duration&amp; d);
</pre>

<p>-3- <b>Effects:</b> Sets the expiry time as if by calling <code>expires_after(d)</code>.</p>

<p>-4- <b>Postconditions:</b> <del><code>get_executor() == ctx.get_executor()</code>.</del></p>
<ins><ul>
<li><code>addressof(context()) == addressof(ctx)</code>.</li>
<li><code>get_executor() == ctx.get_executor()</code>.</li>
</ul></ins>

<p><em>[...]</em></p>

<pre>
basic_waitable_timer(basic_waitable_timer&amp;&amp; rhs);
</pre>

<p>-5- <b>Effects:</b> Move constructs an object of class <code>basic_waitable_timer&lt;Clock, WaitTraits&gt;</code> that refers to the state originally represented by <code>rhs</code>.</p>

<p>-6- <b>Postconditions:</b></p>
<ul>
<ins><li><code>addressof(context()) == addressof(rhs.context())</code>.</li></ins>
</ul>

<p><em>[...]</em></p>

<pre>
basic_waitable_timer&amp; operator=(basic_waitable_timer&amp;&amp; rhs);
</pre>

<p>-1- <b>Effects:</b> Cancels any outstanding asynchronous operations associated with <code>*this</code> as if by calling <code>cancel()</code>, then moves into <code>*this</code> the state originally represented by <code>rhs</code>.

<p>-2- <b>Postconditions:</b>
<ul>
<ins><li><code>addressof(context()) == addressof(rhs.context())</code>.</li></ins>
</ul>

<p><em>[...]</em></p>

<h4>-15.4.4- basic_waitable_timer operations [timer.waitable.ops]</h4>

<ins><pre>
io_context&amp; context() noexcept;
</pre>

<p>-?- <b>Returns:</b> The associated execution context.</p></ins>

<p><em>Modify section <b>-18.6- Class template basic_socket [socket.basic]</b> as follows:</em></p>

<pre>
namespace std {
namespace experimental {
namespace net {
inline namespace v1 {

  template&lt;class Protocol&gt;
  class basic_socket
  {
  public:
</pre>

<p><em>[...]</em></p>

<pre>
    // 18.6.4, basic_socket operations:

    <ins>io_context&amp; context() noexcept;</ins>

    executor_type get_executor() noexcept;
</pre>

<p><em>[...]</em></p>

<pre>
explicit basic_socket(io_context&amp; ctx);
</pre>

<p>-1- <b>Postconditions:</b></p>
<ul>
<ins><li><code>addressof(context()) == addressof(ctx)</code>.</li></ins>
</ul>

<p><em>[...]</em></p>

<pre>
basic_socket(io_context&amp; ctx, const protocol_type&amp; protocol);
</pre>

<p>-2- <b>Effects:</b> Opens this socket as if by calling <code>open(protocol)</code>.</p>

<p>-3- <b>Postconditions:</b></p>
<ul>
<ins><li><code>addressof(context()) == addressof(ctx)</code>.</li></ins>
</ul>

<p><em>[...]</em></p>

<pre>
basic_socket(io_context&amp; ctx, const endpoint_type&amp; endpoint);
</pre>

<p>-4- <b>Effects:</b> Opens and binds this socket as if by calling:</p>
<pre>
  open(endpoint.protocol());
  bind(endpoint);
</pre>

<p>-5- <b>Postconditions:</b></p>
<ul>
<ins><li><code>addressof(context()) == addressof(ctx)</code>.</li></ins>
</ul>

<p><em>[...]</em></p>

<pre>
basic_socket(io_context&amp; ctx, const protocol_type&amp; protocol,
             const native_handle_type&amp; native_socket);
</pre>

<p>-6- <b>Requires:</b> <code>native_socket</code> is a native handle to an open socket.</p>

<p>-7- <b>Effects:</b> Assigns the existing native socket into this socket as if by calling <code>assign(protocol, native_socket)</code>.</p>

<p>-8- <b>Postconditions:</b></p>
<ul>
<li><ins><code>addressof(context()) == addressof(ctx)</code>.
</ul>

<p><em>[...]</em></p>

<pre>
basic_socket(basic_socket&amp;&amp; rhs);
</pre>

<p>-9- <b>Effects:</b> Move constructs an object of class <code>basic_socket&lt;Protocol&gt;</code> that refers to the state originally represented by <code>rhs</code>.</p>

<p>-10- <b>Postconditions:</b></p>
<ul>
<li><ins><code>addressof(context()) == addressof(rhs.context())</code>.
</ul>

<p><em>[...]</em></p>

<pre>
template&lt;class OtherProtocol&gt;
  basic_socket(basic_socket&lt;OtherProtocol&gt;&amp;&amp; rhs);
</pre>

<p>-11- <b>Requires:</b> <code>OtherProtocol</code> is implicitly convertible to <code>Protocol</code>.</p>

<p>-12- <b>Effects:</b> Move constructs an object of class <code>basic_socket&lt;Protocol&gt;</code> that refers to the state originally represented by <code>rhs</code>.</p>

<p>-13- <b>Postconditions:</b></p>
<ul>
<ins><li><code>addressof(context()) == addressof(rhs.context())</code>.</li></ins>
</ul>

<p><em>[...]</em></p>

<pre>
basic_socket&amp; operator=(basic_socket&amp;&amp; rhs);
</pre>

<p>-1- <b>Effects:</b> If <code>is_open()</code> is <code>true</code>, cancels all outstanding asynchronous operations associated with this socket. Completion handlers for canceled operations are passed an error code <code>ec</code> such that <code>ec == errc::operation_canceled</code> yields <code>true</code>. Disables the linger socket option to prevent the assignment from blocking, and releases socket resources as if by POSIX <code>close(native_handle())</code>. Moves into <code>*this</code> the state originally represented by <code>rhs</code>.</p>

<p>-2- <b>Postconditions:</b></p>
<ul>
<ins><li><code>addressof(context()) == addressof(rhs.context())</code>.</li></ins>
</ul>

<p><em>[...]</em></p>

<pre>
template&lt;class OtherProtocol&gt;
  basic_socket&amp; operator=(basic_socket&lt;OtherProtocol&gt;&amp;&amp; rhs);
</pre>

<p>-4- <b>Requires:</b> <code>OtherProtocol</code> is implicitly convertible to <code>Protocol</code>.</p>

<p>-5- <b>Effects:</b> If <code>is_open()</code> is <code>true</code>, cancels all outstanding asynchronous operations associated with this socket. Completion handlers for canceled operations are passed an error code <code>ec</code> such that <code>ec == errc::operation_canceled</code> yields <code>true</code>. Disables the linger socket option to prevent the assignment from blocking, and releases socket resources as if by POSIX <code>close(native_handle())</code>. Moves into <code>*this</code> the state originally represented by <code>rhs</code>.</p>

<p>-6- <b>Postconditions:</b></p>
<ul>
<ins><li><code>addressof(context()) == addressof(rhs.context())</code>.</li></ins>
</ul>

<p><em>[...]</em></p>

<h4>-18.6.4- basic_socket operations [socket.basic.ops]</h4>

<ins>
<pre>
io_context&amp; context() noexcept;
</pre>

<p>-?- <b>Returns:</b> The associated execution context.</p></ins>

<p><em>Modify section <b>-18.9- Class template basic_socket_acceptor [socket.acceptor]</b> as follows:</em></p>

<pre>
namespace std {
namespace experimental {
namespace net {
inline namespace v1 {

  template&lt;class Protocol&gt;
  class basic_socket_acceptor
  {
  public:
</pre>

<p><em>[...]</em></p>

<pre>
    // 18.9.4, basic_socket_acceptor operations:

    <ins>io_context&amp; context() noexcept;</ins>

    executor_type get_executor() noexcept;
</pre>

<p><em>[...]</em></p>

<pre>
explicit basic_socket_acceptor(io_context&amp; ctx);
</pre>

<p>-1- <b>Postconditions:</b></p>
<ul>
<ins><li><code>addressof(context()) == addressof(ctx)</code>.</li></ins>
</ul>

<p><em>[...]</em></p>

<pre>
basic_socket_acceptor(io_context&amp; ctx, const protocol_type&amp; protocol);
</pre>

<p>-2- <b>Effects:</b> Opens this acceptor as if by calling <code>open(protocol)</code>.</p>

<p>-3- <b>Postconditions:</b></p>
<ul>
<ins><li><code>addressof(context()) == addressof(ctx)</code>.</li></ins>
</ul>

<p><em>[...]</em></p>

<pre>
basic_socket_acceptor(io_context&amp; ctx, const endpoint_type&amp; endpoint,
                      bool reuse_addr = true);
</pre>

<p>-4- <b>Effects:</b> Opens and binds this acceptor as if by calling:</p>
<pre>
  open(endpoint.protocol());
  if (reuse_addr)
    set_option(reuse_address(true));
  bind(endpoint);
</pre>

<p>-5- <b>Postconditions:</b></p>
<ul>
<ins><li><code>addressof(context()) == addressof(ctx)</code>.</li></ins>
</ul>

<p><em>[...]</em></p>

<pre>
basic_socket_acceptor(io_context&amp; ctx, const protocol_type&amp; protocol,
                      const native_handle_type&amp; native_acceptor);
</pre>

<p>-6- <b>Requires:</b> <code>native_acceptor</code> is a native handle to an open acceptor.</p>

<p>-7- <b>Effects:</b> Assigns the existing native acceptor into this acceptor as if by calling <code>assign(protocol, native_acceptor)</code>.</p>

<p>-8- <b>Postconditions:</b></p>
<ul>
<li><ins><code>addressof(context()) == addressof(ctx)</code>.
</ul>

<p><em>[...]</em></p>

<pre>
basic_socket_acceptor(basic_socket_acceptor&amp;&amp; rhs);
</pre>

<p>-9- <b>Effects:</b> Move constructs an object of class <code>basic_socket_acceptor&lt;Protocol&gt;</code> that refers to the state originally represented by <code>rhs</code>.</p>

<p>-10- <b>Postconditions:</b></p>
<ul>
<li><ins><code>addressof(context()) == addressof(rhs.context())</code>.
</ul>

<p><em>[...]</em></p>

<pre>
template&lt;class OtherProtocol&gt;
  basic_socket_acceptor(basic_socket_acceptor&lt;OtherProtocol&gt;&amp;&amp; rhs);
</pre>

<p>-11- <b>Requires:</b> <code>OtherProtocol</code> is implicitly convertible to <code>Protocol</code>.</p>

<p>-12- <b>Effects:</b> Move constructs an object of class <code>basic_socket_acceptor&lt;Protocol&gt;</code> that refers to the state originally represented by <code>rhs</code>.</p>

<p>-13- <b>Postconditions:</b></p>
<ul>
<ins><li><code>addressof(context()) == addressof(rhs.context())</code>.</li></ins>
</ul>

<p><em>[...]</em></p>

<pre>
basic_socket_acceptor&amp; operator=(basic_socket_acceptor&amp;&amp; rhs);
</pre>

<p>-1- <b>Effects:</b> If <code>is_open()</code> is <code>true</code>, cancels all outstanding asynchronous operations associated with this acceptor, and releases acceptor resources as if by POSIX <code>close(native_handle())</code>. Then moves into <code>*this</code> the state originally represented by <code>rhs</code>. Completion handlers for canceled operations are passed an error code <code>ec</code> such that <code>ec == errc::operation_canceled</code> yields <code>true</code>.</p>

<p>-2- <b>Postconditions:</b></p>
<ul>
<ins><li><code>addressof(context()) == addressof(rhs.context())</code>.</li></ins>
</ul>

<p><em>[...]</em></p>

<pre>
template&lt;class OtherProtocol&gt;
  basic_socket_acceptor&amp; operator=(basic_socket_acceptor&lt;OtherProtocol&gt;&amp;&amp; rhs);
</pre>

<p>-4- <b>Requires:</b> <code>OtherProtocol</code> is implicitly convertible to <code>Protocol</code>.</p>

<p>-5- <b>Effects:</b> If <code>is_open()</code> is <code>true</code>, cancels all outstanding asynchronous operations associated with this acceptor, and releases acceptor resources as if by POSIX <code>close(native_handle())</code>. Then moves into <code>*this</code> the state originally represented by <code>rhs</code>. Completion handlers for canceled operations are passed an error code <code>ec</code> such that <code>ec == errc::operation_canceled</code> yields <code>true</code>.</p>

<p>-6- <b>Postconditions:</b></p>
<ul>
<ins><li><code>addressof(context()) == addressof(rhs.context())</code>.</li></ins>
</ul>

<p><em>[...]</em></p>

<h4>-18.9.4- basic_socket_acceptor operations [socket.acceptor.ops]</h4>

<ins>
<pre>
io_context&amp; context() noexcept;
</pre>

<p>-?- <b>Returns:</b> The associated execution context.</p></ins>

<p><em>Modify section <b>-21.17- Class template ip::basic_resolver [internet.resolver]</b> as follows:</em></p>

<pre>
namespace std {
namespace experimental {
namespace net {
inline namespace v1 {
namespace ip {

  template&lt;class InternetProtocol&gt;
  class basic_resolver : public resolver_base
  {
  public:
</pre>

<p><em>[...]</em></p>

<pre>
    // 21.17.4, basic_resolver operations:

    <ins>io_context&amp; context() noexcept;</ins>

    executor_type get_executor() noexcept;
</pre>

<p><em>[...]</em></p>

<pre>
explicit basic_resolver(io_context&amp; ctx);
</pre>

<p>-1- <b>Postconditions:</b><del> <code>get_executor() == ctx.get_executor()</code>.</del></p>
<ins><ul>
<li><code>addressof(context()) == addressof(ctx)</code>.</li>
<li><code>get_executor() == ctx.get_executor()</code>.</li>
</ul></ins>

<pre>
basic_resolver(basic_resolver&amp;&amp; rhs) noexcept;
</pre>

<p>-2- <b>Effects:</b> Move constructs an object of class <code>basic_resolver&lt;InternetProtocol&gt;</code> that refers to the state originally represented by <code>rhs</code>.

<p>-3- <b>Postconditions:</b> <del><code>get_executor() == rhs.get_executor()</code>.</del></p>
<ins><ul>
<li><code>addressof(context()) == addressof(rhs.context())</code>.</li>
<li><code>get_executor() == rhs.get_executor()</code>.</li>
</ul></ins>

<p><em>[...]</em></p>

<pre>
basic_resolver&amp; operator=(basic_resolver&amp;&amp; rhs);
</pre>

<p>-1- <b>Effects:</b> Cancels all outstanding asynchronous operations associated with <code>*this</code> as if by calling <code>cancel()</code>, then moves into <code>*this</code> the state originally represented by <code>rhs</code>.

<p>-2- <b>Postconditions:</b> <del><code>get_executor() == ctx.get_executor()</code>.</del></p>
<ins><ul>
<li><code>addressof(context()) == addressof(rhs.context())</code>.</li>
<li><code>get_executor() == rhs.get_executor()</code>.</li>
</ul></ins>

<p>-3- <b>Returns:</b> <code>*this</code>.</p>

<h4>21.17.4 <code>ip::basic_resolver</code> operations [internet.resolver.ops]</h4>

<ins><pre>
io_context&amp; context() noexcept;
</pre>

<p>-?- <b>Returns:</b> The associated execution context.</p></ins>

<h2 id="modify.executors">Proposed Modifications to P0443R7 A Unified Executors Proposal for C++</h2>

<p>The asynchronous operation requirements specified above stipulate that querying the <code>execution::context_t</code> property for Networking TS executors returns a type of either <code>execution_context&amp;</code>, or of <code>E&amp;</code> where <code>E</code> is a type that is unambiguously derived from <code>execution_context</code>.</p>

<p>However, the <code>execution::context_t</code> property as currently specified in P0443 uses <code>std::any</code> as its <code>polymorphic_query_result_type</code>. This prevents the polymorphic wrapper <code>execution::executor</code> from satisfying the above requirements.</p>

<p>Even without considering the impact of unified executors on the Networking TS, having <code>std::any</code> as the polymorphic query result type prevents us from exposing <code>static_thread_pool</code> as a context through the polymorphic wrapper. This is because <code>std::any</code> is not constructible from a reference to the non-copyable <code>static_thread_pool</code> type.</p>

<p>For these reasons, this paper proposes the following changes to P0443R7.</p>

<p><em>1. Remove the <code>polymorphic_query_result_type</code> from the <code>execution::context_t</code> property.</em></p>

<blockquote>
<pre>
struct context_t
{
  static constexpr bool is_requirable = false;
  static constexpr bool is_preferable = false;

  <del>using polymorphic_query_result_type = any;</del>

  template&lt;class Executor&gt;
    static constexpr decltype(auto) static_query_v
      = Executor::query(context_t());
};
</pre>
</blockquote>

<p><em>2. Relax the requirements on <code>polymorphic_query_result_type</code> to remove the <code>DefaultConstructible</code> requirement for query-only properties.</em></p>

<blockquote>
<p>A property type <code>P</code> may provide a nested type <code>polymorphic_query_result_type</code> that satisfies the <del><code>DefaultConstructible</code>, </del><code>CopyConstructible</code> and <code>Destructible</code> requirements. <ins>If <code>P::is_requirable == true</code> or <code>P::is_preferable == true</code>, <code>polymorphic_query_result_type</code> shall also satisfy the <code>DefaultConstructible</code> requirements.</ins> [*Note:* When present, this type allows the property to be used with the polymorphic <code>executor</code> wrapper. *--end note*]</p>
</blockquote>

<p><em>3. Fix a bug in the polymorphic executor constructor so that only the query-only properties in <code>SupportableProperties...</code> are required to queryable at the point of construction (as the other properties only need to become queryable after they are <code>require()</code>-ed).</em></p>

<blockquote>
<pre>
template&lt;class Executor&gt; executor(Executor e);
</pre>

<p><b>Remarks:</b> This function shall not participate in overload resolution unless:</p>
<ul>
<li><code>can_require_v&lt;Executor, P&gt;</code>, if <code>P::is_requirable</code>, where <code>P</code> is each property in <code>SupportableProperties...</code>.
<li><code>can_prefer_v&lt;Executor, P&gt;</code>, if <code>P::is_preferable</code>, where <code>P</code> is each property in <code>SupportableProperties...</code>.
<li>and <code>can_query_v&lt;Executor, P&gt;</code>, <ins>if <code>P::is_requirable == false</code> and <code>P::is_preferable == false</code>,</ins> where <code>P</code> is each property in <code>SupportableProperties...</code>.
</ul>
</blockquote>

<p><em>4. Add the following <code>context_as&lt;&gt;</code> property adapter. This adapter allows us to expose the <code>execution::context_t</code> property through the polymorphic wrapper using a polymorphic query type of our choosing.</em></p>

<ins><h3>-?- Struct <code>context_as</code></h3>

<p>-?- The <code>context_as</code> struct is a property adapter for the <code>context_t</code> property, to specify a <code>polymorphic_query_result_type</code>.</p>

<p>-?- [<em>Example:</em> <code>context_as</code> may be used with the polymorphic wrapper <code>executor</code> to expose the query-only property <code>context_t</code> using a suitable polymorphic type:</p>
<pre>
static_thread_pool pool;

execution::executor&lt;
    execution::single_t,
    execution::oneway_t,
    execution::context_as&lt;static_thread_pool&amp;&gt;
  &gt; my_executor(pool.executor());

// ...

static_thread_pool&amp; ctx =
  execution::query(my_executor, execution::context);
</pre>
<p><em>--end example</em>]</p>

<pre>
template&lt;class T&gt;
struct context_as
{
  static constexpr bool is_requirable = false;
  static constexpr bool is_preferable = false;

  using polymorphic_query_result_type = T;

  template&lt;class Executor&gt;
    static constexpr T static_query_v
      = context_t::static_query_v&lt;Executor&gt;;

  constexpr context_as() {}
  constexpr context_as(execution::context_t) {}

  template&lt;class Executor, class Property&gt;
  friend constexpr T query(const Executor&amp; ex, const Property&amp; p)
    noexcept(noexcept(execution::query(ex, execution::context)))
      -&gt; decltype(execution::query(ex, execution::context));
};
</pre>

<p>-?- The expression <code>context_as&lt;T&gt;::static_query_v&lt;E&gt;</code> is well-formed for some executor type <code>E</code> if and only if the expression <code>context_t::static_query_v&lt;E&gt;</code> is well-formed and can be used to initialize a constant of type <code>T</code>.</p>

<pre>
template&lt;class Executor, class Property&gt;
friend constexpr T query(const Executor&amp; ex, const Property&amp; p)
  noexcept(noexcept(execution::query(ex, execution::context)))
</pre>

<p>-?- <b>Returns:</b> <code>execution::query(ex, p.property)</code>.

<p>-?- <b>Remarks:</b> Shall not participate in overload resolution unless <code>std::is_same_v&lt;Property, context_as&gt;</code> is <code>true</code>, and the expression <code>execution::query(ex, execution::context)</code> is well-formed.</p></ins>

<h2>Revision History</h2>

<p>The following changes were made in revision 1 of this paper:</p>

<ul>
<li>Updated to match the unified executor facilities as described in P0443R7.</li>
<li>Added a section on deployment experience.</li>
<li>Specify that <code>system_context</code> executors use distinct types when different properties are established.</li>
<li>Added missing property queries on <code>system_context</code> executors.</li>
<li>Changed <code>strand</code> to conditionally forward static <code>query()</code> calls to the adapted executor.</li>
<li>Specify that <code>io_context</code> executors use distinct types when different properties are established.</li>
<li>Added missing property queries on <code>io_context</code> executors.</li>
<li>Added modifications to P0443R7 executors, including the <code>execution::context_as&lt;&gt;</code> property adapter.</li>
</ul>

</body>
</html>
