<table style="width:79%;">
<colgroup>
<col width="27%" />
<col width="51%" />
</colgroup>
<tbody>
<tr class="odd">
<td align="left">Title:</td>
<td align="left">Dependent Execution for a Unified Executors Proposal for C++</td>
</tr>
<tr class="even">
<td align="left">Authors:</td>
<td align="left">Jared Hoberock, jhoberock@nvidia.com</td>
</tr>
<tr class="odd">
<td align="left"></td>
<td align="left">Michael Garland, mgarland@nvidia.com</td>
</tr>
<tr class="even">
<td align="left"></td>
<td align="left">Chris Kohlhoff, chris@kohlhoff.com</td>
</tr>
<tr class="odd">
<td align="left"></td>
<td align="left">Chris Mysen, mysen@google.com</td>
</tr>
<tr class="even">
<td align="left"></td>
<td align="left">Carter Edwards, hcedwar@sandia.gov</td>
</tr>
<tr class="odd">
<td align="left"></td>
<td align="left">Gordon Brown, gordon@codeplay.com</td>
</tr>
<tr class="even">
<td align="left">Other Contributors:</td>
<td align="left">Hans Boehm, hboehm@google.com</td>
</tr>
<tr class="odd">
<td align="left"></td>
<td align="left">Thomas Heller, thom.heller@gmail.com</td>
</tr>
<tr class="even">
<td align="left"></td>
<td align="left">Lee Howes, lwh@fb.com</td>
</tr>
<tr class="odd">
<td align="left"></td>
<td align="left">Bryce Lelbach, brycelelbach@gmail.com</td>
</tr>
<tr class="even">
<td align="left"></td>
<td align="left">Hartmut Kaiser, hartmut.kaiser@gmail.com</td>
</tr>
<tr class="odd">
<td align="left"></td>
<td align="left">Bryce Lelbach, brycelelbach@gmail.com</td>
</tr>
<tr class="even">
<td align="left"></td>
<td align="left">Gor Nishanov, gorn@microsoft.com</td>
</tr>
<tr class="odd">
<td align="left"></td>
<td align="left">Thomas Rodgers, rodgert@twrodgers.com</td>
</tr>
<tr class="even">
<td align="left"></td>
<td align="left">David Hollman, dshollm@sandia.gov</td>
</tr>
<tr class="odd">
<td align="left"></td>
<td align="left">Michael Wong, michael@codeplay.com</td>
</tr>
<tr class="even">
<td align="left">Document Number:</td>
<td align="left">P1244R0</td>
</tr>
<tr class="odd">
<td align="left">Date:</td>
<td align="left">2018-10-08</td>
</tr>
<tr class="even">
<td align="left">Audience:</td>
<td align="left">SG1 - Concurrency and Parallelism, LEWG</td>
</tr>
<tr class="odd">
<td align="left">Reply-to:</td>
<td align="left">sg1-exec@googlegroups.com</td>
</tr>
<tr class="even">
<td align="left">Abstract:</td>
<td align="left">This paper extends <a href="http://wg21.link/P0443">P0443</a>'s executors programming model with functionality to support dependent execution.</td>
</tr>
</tbody>
</table>
<h2 id="changelog"><span class="header-section-number">0.1</span> Changelog</h2>
<h3 id="revision-0"><span class="header-section-number">0.1.1</span> Revision 0</h3>
<p>Initial revision.</p>
<p>As directed by the SG1/LEWG straw poll taken during the 2018 Bellevue executors meeting, we have separated The Unified Executors programming model proposal into two papers. <a href="http://wg21.link/P0443">P0443</a> contains material related to one-way execution which the authors hope to standardize with C++20 as suggested by the Bellevue poll. This document contains remaining material related to dependent execution. We expect the form of this paper's material to evolve as committee consensus builds around a design for dependent execution.</p>
<h1 id="proposed-wording"><span class="header-section-number">1</span> Proposed Wording</h1>
<h3 id="header-exception-synopsis"><span class="header-section-number">1.0.1</span> Header <code>&lt;exception&gt;</code> synopsis</h3>
<pre><code>namespace std {
namespace experimental {
inline namespace executors_v1 {

  // Exception argument tag
  struct exception_arg_t { explicit exception_arg_t() = default; };
  inline constexpr exception_arg_t exception_arg{};

}
}
}</code></pre>
<h2 id="exception-argument-tag"><span class="header-section-number">1.1</span> Exception argument tag</h2>
<p>The <code>exception_arg_t</code> struct is an empty structure type used as a unique type to disambiguate constructor and function overloading. Specifically, functions passed to <code>then_execute</code> and <code>bulk_then_execute</code> may have <code>exception_arg_t</code> as an argument, immediately followed by an argument that should be interpreted as an exception thrown from a preceding function invocation.</p>
<h2 id="execution-support-library"><span class="header-section-number">1.2</span> Execution Support Library</h2>
<h3 id="header-execution-synopsis"><span class="header-section-number">1.2.1</span> Header <code>&lt;execution&gt;</code> synopsis</h3>
<pre><code>namespace std {
namespace experimental {
inline namespace executors_v1 {
namespace execution {

  // Interface-changing properties:

  struct twoway_t;
  struct then_t;
  struct bulk_twoway_t;
  struct bulk_then_t;

  constexpr twoway_t twoway;
  constexpr then_t then;
  constexpr bulk_twoway_t bulk_twoway;
  constexpr bulk_then_t bulk_then;

  // Executor type traits:

  template&lt;class Executor&gt; struct is_twoway_executor;
  template&lt;class Executor&gt; struct is_then_executor;
  template&lt;class Executor&gt; struct is_bulk_twoway_executor;
  template&lt;class Executor&gt; struct is_bulk_then_executor;

  template&lt;class Executor&gt; constexpr bool is_twoway_executor_v = is_twoway_executor&lt;Executor&gt;::value;
  template&lt;class Executor&gt; constexpr bool is_then_executor_v = is_then_executor&lt;Executor&gt;::value;
  template&lt;class Executor&gt; constexpr bool is_bulk_twoway_executor_v = is_bulk_twoway_executor&lt;Executor&gt;::value;
  template&lt;class Executor&gt; constexpr bool is_bulk_then_executor_v = is_bulk_then_executor&lt;Executor&gt;::value;

  template&lt;class Executor, class T&gt; struct executor_future;

  template&lt;class Executor, class T&gt; using executor_future_t = typename executor_future&lt;Executor, T&gt;::type;

} // namespace execution
} // inline namespace executors_v1
} // namespace experimental
} // namespace std</code></pre>
<h2 id="requirements"><span class="header-section-number">1.3</span> Requirements</h2>
<h3 id="future-requirements"><span class="header-section-number">1.3.1</span> <code>Future</code> requirements</h3>
<p>A type <code>F</code> meets the <code>Future</code> requirements for some value type <code>T</code> if <code>F</code> is <code>std::experimental::future&lt;T&gt;</code> (defined in the C++ Concurrency TS, ISO/IEC TS 19571:2016). [<em>Note:</em> This concept is included as a placeholder to be elaborated, with the expectation that the elaborated requirements for <code>Future</code> will expand the applicability of some executor customization points. <em>--end note</em>]</p>
<p>Forward progress guarantees are a property of the concrete <code>Future</code> type. [<em>Note:</em> <code>std::experimental::future&lt;T&gt;::wait()</code> blocks with forward progress guarantee delegation until the shared state is ready. <em>--end note</em>]</p>
<h3 id="twowayexecutor-requirements"><span class="header-section-number">1.3.2</span> <code>TwoWayExecutor</code> requirements</h3>
<p>The <code>TwoWayExecutor</code> requirements specify requirements for executors which submit function objects with a channel for awaiting the completion of a submitted function object and obtaining its result.</p>
<p>A type <code>X</code> satisfies the <code>TwoWayExecutor</code> requirements if it satisfies the general requirements on executors, as well as the requirements in the Table below.</p>
<p>In the Table below,</p>
<ul>
<li><code>x</code> denotes a (possibly const) executor object of type <code>X</code>,</li>
<li><code>cf</code> denotes the function object <code>DECAY_COPY(std::forward&lt;F&gt;(f))</code></li>
<li><code>f</code> denotes a function object of type <code>F&amp;&amp;</code> invocable as <code>cf()</code> and where <code>decay_t&lt;F&gt;</code> satisfies the <code>MoveConstructible</code> requirements,</li>
<li><code>R</code> denotes the type of the expression <code>cf()</code>.</li>
</ul>
<table style="width:69%;">
<colgroup>
<col width="18%" />
<col width="19%" />
<col width="31%" />
</colgroup>
<thead>
<tr class="header">
<th align="left">Expression</th>
<th align="left">Return Type</th>
<th align="left">Operational semantics</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td align="left"><code>x.twoway_execute(f)</code></td>
<td align="left">A type that satisfies the <code>Future</code> requirements for the value type <code>R</code>.</td>
<td align="left">Evaluates <code>DECAY_COPY(std::forward&lt;F&gt;(f))</code> on the calling thread to create <code>cf</code> that will be invoked at most once by an execution agent. <br/>May block pending completion of this function object. <br/> Synchronizes with [intro.multithread] the invocation of <code>f</code>. <br/> Stores the result of the invocation, or any exception thrown by the invocation, in the associated shared state of the returned <code>Future</code>.</td>
</tr>
</tbody>
</table>
<h3 id="thenexecutor-requirements"><span class="header-section-number">1.3.3</span> <code>ThenExecutor</code> requirements</h3>
<p>The <code>ThenExecutor</code> requirements specify requirements for executors which submit function objects whose invocation is predicated on the readiness of a specified future, and which provide a channel for awaiting the completion of the submitted function object and obtaining its result.</p>
<p>A type <code>X</code> satisfies the <code>ThenExecutor</code> requirements if it satisfies the general requirements on executors, as well as the requirements in the Table below.</p>
<p>In the Table below,</p>
<ul>
<li><code>x</code> denotes a (possibly const) executor object of type <code>X</code>,</li>
<li><code>fut</code> denotes a future object satisfying the <code>Future</code> requirements,</li>
<li><code>val</code> denotes any object stored in <code>fut</code>'s associated shared state when it becomes nonexceptionally ready,</li>
<li><code>e</code> denotes the object stored in <code>fut</code>'s associated shared state when it becomes exceptionally ready,</li>
<li><code>cf</code> denotes the function object <code>DECAY_COPY(std::forward&lt;F&gt;(f))</code>,</li>
<li><code>NORMAL</code> denotes the expression <code>cf(val)</code> if <code>fut</code>'s value type is non-<code>void</code> and <code>cf()</code> if <code>fut</code>'s value type is <code>void</code>,</li>
<li><code>EXCEPTIONAL</code> denotes the expression <code>cf(exception_arg, e)</code>,</li>
<li><code>f</code> denotes a function object of type <code>F&amp;&amp;</code> invocable as <code>NORMAL</code> or <code>EXCEPTIONAL</code> and where <code>decay_t&lt;F&gt;</code> satisfies the <code>MoveConstructible</code> requirements,</li>
<li>and <code>R</code> denotes the type of the expression <code>NORMAL</code>.</li>
</ul>
<table style="width:69%;">
<colgroup>
<col width="18%" />
<col width="19%" />
<col width="31%" />
</colgroup>
<thead>
<tr class="header">
<th align="left">Expression</th>
<th align="left">Return Type</th>
<th align="left">Operational semantics</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td align="left"><code>x.then_execute(f, fut)</code></td>
<td align="left">A type that satisfies the <code>Future</code> requirements for the value type <code>R</code>.</td>
<td align="left">Evaluates <code>DECAY_COPY(std::forward&lt;F&gt;(f))</code> on the calling thread to create <code>cf</code>. When <code>fut</code> becomes nonexceptionaly ready and if <code>NORMAL</code> is a well-formed expression then <code>NORMAL</code> is invoked by an execution agent at most once. <br/> Otherwise, when <code>fut</code> becomes exceptionally ready and if <code>EXCEPTIONAL</code> is a well-formed expression then <code>EXCEPTIONAL</code> is invoked at most once by an execution agent. <br/> If <code>NORMAL</code> and <code>EXCEPTIONAL</code> are both well-formed expressions, <code>decltype(EXCEPTIONAL)</code> shall be convertible to <code>R</code>. <br/> If <code>NORMAL</code> is not a well-formed expression and <code>EXCEPTIONAL</code> is a well-formed expression, <code>decltype(EXCEPTIONAL)</code> shall be convertible to <code>decltype(val)</code>. <br/> <br/> If neither <code>NORMAL</code> nor <code>EXCEPTIONAL</code> are well-formed expressions, the invocation of <code>then_execute</code> is ill-formed. <br/> May block pending completion of <code>NORMAL</code> or <code>EXCEPTIONAL</code>. <br/>Synchronizes with [intro.multithread] the invocation of <code>f</code>. <br/> Stores the result of either the <code>NORMAL</code> or <code>EXCEPTIONAL</code> expression, or any exception thrown by either, in the associated shared state of the returned <code>Future</code>. Otherwise, stores either <code>val</code> or <code>e</code> in the associated shared state of the returned <code>Future</code>.</td>
</tr>
</tbody>
</table>
<h3 id="bulktwowayexecutor-requirements"><span class="header-section-number">1.3.4</span> <code>BulkTwoWayExecutor</code> requirements</h3>
<p>The <code>BulkTwoWayExecutor</code> requirements specify requirements for executors which submit a function object with a channel for awaiting the completion of the submitted function object and obtaining its result.</p>
<p>A type <code>X</code> satisfies the <code>BulkTwoWayExecutor</code> requirements if it satisfies the general requirements on executors, as well as the requirements in the Table below.</p>
<p>In the Table below,</p>
<ul>
<li><code>x</code> denotes a (possibly const) executor object of type <code>X</code>,</li>
<li><code>n</code> denotes a shape object whose type is <code>executor_shape_t&lt;X&gt;</code>,</li>
<li><code>rf</code> denotes a <code>CopyConstructible</code> function object with zero arguments whose result type is <code>R</code>,</li>
<li><code>sf</code> denotes a <code>CopyConstructible</code> function object with zero arguments whose result type is <code>S</code>,</li>
<li><code>i</code> denotes a (possibly const) object whose type is <code>executor_index_t&lt;X&gt;</code>,</li>
<li><code>s</code> denotes an object whose type is <code>S</code>,</li>
<li><code>cf</code> denotes the function object <code>DECAY_COPY(std::forward&lt;F&gt;(f))</code>,</li>
<li>if <code>R</code> is non-void,
<ul>
<li><code>r</code> denotes an object whose type is <code>R</code>,</li>
<li><code>INVOKE_CF</code> denotes the expression <code>cf(i, r, s)</code> ,</li>
</ul></li>
<li>if <code>R</code> is void,
<ul>
<li><code>INVOKE_CF</code> denotes the expression <code>cf(i, r, s)</code> ,</li>
</ul></li>
<li><code>f</code> denotes a function object of type <code>F&amp;&amp;</code> invocable as <code>INVOKE_CF</code> and where <code>decay_t&lt;F&gt;</code> satisfies the <code>MoveConstructible</code> requirements.</li>
</ul>
<table style="width:69%;">
<colgroup>
<col width="18%" />
<col width="19%" />
<col width="31%" />
</colgroup>
<thead>
<tr class="header">
<th align="left">Expression</th>
<th align="left">Return Type</th>
<th align="left">Operational semantics</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td align="left"><code>x.bulk_twoway_execute(f, n, rf, sf)</code></td>
<td align="left">A type that satisfies the <code>Future</code> requirements for the value type <code>R</code>.</td>
<td align="left">Evaluates <code>DECAY_COPY(std::forward&lt;F&gt;(f))</code> on the calling thread to create a function object <code>cf</code>. <em>[Note:* Additional copies of <code>cf</code> may subsequently be created. *--end note]</em> For each value of <code>i</code> in shape <code>n</code>, <code>INVOKE_CF</code> (possibly with copy of <code>cf)</code>) will be invoked at most once by an execution agent that is unique for each value of <code>i</code>. If <code>R</code> is non-void, <code>rf()</code> will be invoked at most once to produce the value <code>r</code>. <code>sf()</code> will be invoked at most once to produce the value <code>s</code>. <br/> May block pending completion of one or more invocations of <code>cf</code>. <br/> Synchronizes with [intro.multithread] the invocations of <code>f</code>. <br/> Once all invocations of <code>f</code> finish execution, <code>r</code> or any exception thrown by an invocation of <code>f</code> are stored in the associated shared state of the returned <code>Future</code>.</td>
</tr>
</tbody>
</table>
<h3 id="bulkthenexecutor-requirements"><span class="header-section-number">1.3.5</span> <code>BulkThenExecutor</code> requirements</h3>
<p>The <code>BulkThenExecutor</code> requirements specify requirements for executors which submit function objects whose initiation is predicated on the readiness of a specified future, and which provide a channel for awaiting the completion of the submitted function object and obtaining its result.</p>
<p>A type <code>X</code> satisfies the <code>BulkThenExecutor</code> requirements if it satisfies the general requirements on executors, as well as the requirements in the Table below.</p>
<p>In the Table below,</p>
<ul>
<li><code>x</code> denotes a (possibly const) executor object of type <code>X</code>,</li>
<li><code>n</code> denotes a shape object whose type is <code>executor_shape_t&lt;X&gt;</code>,</li>
<li><code>fut</code> denotes a future object satisfying the Future requirements,</li>
<li><code>val</code> denotes any object stored in <code>fut</code>'s associated shared state when it becomes nonexceptionally ready,</li>
<li><code>e</code> denotes the object stored in <code>fut</code>'s associated shared state when it becomes exceptionally ready,</li>
<li><code>rf</code> denotes a <code>CopyConstructible</code> function object with zero arguments whose result type is <code>R</code>,</li>
<li><code>sf</code> denotes a <code>CopyConstructible</code> function object with zero arguments whose result type is <code>S</code>,</li>
<li><code>i</code> denotes a (possibly const) object whose type is <code>executor_index_t&lt;X&gt;</code>,</li>
<li><code>s</code> denotes an object whose type is <code>S</code>,</li>
<li><code>cf</code> denotes the function object <code>DECAY_COPY(std::forward&lt;F&gt;(f))</code>,</li>
<li>if <code>R</code> is non-void,
<ul>
<li><code>r</code> denotes an object whose type is <code>R</code>,</li>
<li><code>NORMAL</code> denotes the expression <code>cf(i, val, r, s)</code>,</li>
<li><code>EXCEPTIONAL</code> denotes the expression <code>cf(exception_arg, e, r, s)</code>,</li>
</ul></li>
<li>if <code>R</code> is void,
<ul>
<li><code>NORMAL</code> denotes the expression <code>cf(i, val, s)</code>,</li>
<li><code>EXCEPTIONAL</code> denotes the expression <code>cf(exception_arg, e, s)</code>,</li>
</ul></li>
<li><code>f</code> denotes a function object of type <code>F&amp;&amp;</code> invocable as <code>NORMAL</code> or <code>EXCEPTIONAL</code> and where <code>decay_t&lt;F&gt;</code> satisfies the <code>MoveConstructible</code> requirements,</li>
</ul>
<table>
<colgroup>
<col width="25%" />
<col width="37%" />
<col width="37%" />
</colgroup>
<thead>
<tr class="header">
<th align="left">Expression</th>
<th align="left">Return Type</th>
<th align="left">Operational semantics</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td align="left"><code>x.bulk_then_execute(f, n, fut, rf, sf)</code></td>
<td align="left">A type that satisfies the <code>Future</code> requirements for the value type <code>R</code>.</td>
<td align="left">Evaluates <code>DECAY_COPY(std::forward&lt;F&gt;(f))</code> on the calling thread to create function object <code>cf</code>. <em>[Note:* Additional copies of <code>cf</code> may subsequently be created. *--end note]</em> <br/> When <code>fut</code> becomes nonexceptionaly ready and if <code>NORMAL</code> is a well-formed expression then for each value of <code>i</code> in shape <code>n</code>, <code>NORMAL</code> (possibly with copy of <code>cf)</code>) will be invoked at most once by an execution agent that is unique for each value of <code>i</code>. If <code>R</code> is non-void then <code>rf()</code> will be invoked at most once to produce the value <code>r</code>. <code>sf()</code> will be invoked at most once to produce the value <code>s</code>. <br/> Otherwise, when <code>fut</code> becomes exceptionally ready and if <code>EXCEPTIONAL</code> is a well-formed expression then <code>EXCEPTIONAL</code> is invoked at most once by an execution agent. <br/> If <code>NORMAL</code> and <code>EXCEPTIONAL</code> are both well-formed expressions, <code>decltype(EXCEPTIONAL)</code> shall be convertible to <code>R</code>.<br/> If <code>NORMAL</code> is not a well-formed expression and <code>EXCEPTIONAL</code> is a well-formed expression, <code>decltype(EXCEPTIONAL)</code> shall be convertible to <code>decltype(val)</code>. <br/> If neither <code>NORMAL</code> nor <code>EXCEPTIONAL</code> are well-formed expressions, the invocation of <code>then_execute</code> is ill-formed. <br/> May block pending completion of <code>NORMAL</code> or <code>EXCEPTIONAL</code>. <br/> Synchronizes with [intro.multithread] the invocation of <code>f</code>. <br/> Stores the result of either the <code>NORMAL</code> or <code>EXCEPTIONAL</code> expression, or any exception thrown by either, in the associated shared state of the returned <code>Future</code>. Otherwise, stores either <code>val</code> or <code>e</code> in the associated shared state of the returned <code>Future</code>.</td>
</tr>
</tbody>
</table>
<h2 id="executor-properties"><span class="header-section-number">1.4</span> Executor properties</h2>
<h3 id="interface-changing-properties"><span class="header-section-number">1.4.1</span> Interface-changing properties</h3>
<pre><code>struct twoway_t;
struct then_t;
struct bulk_twoway_t;
struct bulk_then_t;</code></pre>
<table style="width:36%;">
<colgroup>
<col width="15%" />
<col width="20%" />
</colgroup>
<thead>
<tr class="header">
<th align="left">Property</th>
<th align="left">Requirements</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td align="left"><code>twoway_t</code></td>
<td align="left">The executor type satisfies the <code>TwoWayExecutor</code> requirements.</td>
</tr>
<tr class="even">
<td align="left"><code>then_t</code></td>
<td align="left">The executor type satisfies the <code>ThenExecutor</code> requirements.</td>
</tr>
<tr class="odd">
<td align="left"><code>bulk_twoway_t</code></td>
<td align="left">The executor type satisfies the <code>BulkTwoWayExecutor</code> requirements.</td>
</tr>
<tr class="even">
<td align="left"><code>bulk_then_t</code></td>
<td align="left">The executor type satisfies the <code>BulkThenExecutor</code> requirements.</td>
</tr>
</tbody>
</table>
<p>The <code>std::execution::oneway_t</code>, <code>twoway_t</code>, <code>then_t</code>, <code>std::execution::bulk_oneway_t</code>, <code>bulk_twoway_t</code> and <code>bulk_then_t</code> properties are mutually exclusive.</p>
<h4 id="twoway_t-customization-points"><span class="header-section-number">1.4.1.1</span> <code>twoway_t</code> customization points</h4>
<p>In addition to conforming to the above specification, the <code>twoway_t</code> property provides the following customization:</p>
<pre><code>struct twoway_t
{
  template&lt;class Executor&gt;
    friend see-below require(Executor ex, twoway_t);
};</code></pre>
<p>This customization point returns an executor that satisfies the <code>twoway_t</code> requirements by adapting the native functionality of an executor that does not satisfy the <code>twoway_t</code> requirements.</p>
<pre><code>template&lt;class Executor&gt;
  friend see-below require(Executor ex, twoway_t);</code></pre>
<p><em>Returns:</em> A value <code>e1</code> of type <code>E1</code> that holds a copy of <code>ex</code>. <code>E1</code> has member functions <code>require</code> and <code>query</code> that forward to the corresponding members of the copy of <code>ex</code>, if present. For some type <code>T</code>, the type yielded by <code>executor_future_t&lt;E1, T&gt;</code> is <code>executor_future_t&lt;Executor, T&gt;</code> if <code>then_t::static_query_v&lt;Executor&gt;</code> is true; otherwise, it is <code>std::experimental::future&lt;T&gt;</code>. <code>e1</code> has the same properties as <code>ex</code>, except for the addition of the <code>twoway_t</code> property and the exclusion of other interface-changing properties. The type <code>E1</code> satisfies the <code>TwoWayExecutor</code> requirements as follows:</p>
<ul>
<li>If <code>bulk_twoway_t::static_query_v&lt;Executor&gt;</code> is true, then <code>E1</code> implements member function <code>twoway_execute</code> in terms of the member function <code>bulk_twoway_execute</code> of the object <code>ex</code>, and <code>E1</code> has a member function <code>Executor require(bulk_twoway_t) const</code> that returns a copy of <code>ex</code>.</li>
<li>If <code>then_t::static_query_v&lt;Executor&gt;</code> is true, then <code>E1</code> implements member function <code>twoway_execute</code> in terms of the member function <code>then_execute</code> of the object <code>ex</code>, and <code>E1</code> has a member function <code>Executor require(then_t) const</code> that returns a copy of <code>ex</code>.</li>
<li>If <code>bulk_then_t::static_query_v&lt;Executor&gt;</code> is true, then <code>E1</code> implements member function <code>twoway_execute</code> in terms of the member function <code>bulk_then_execute</code> of the object <code>ex</code>, and <code>E1</code> has a member function <code>Executor require(bulk_then_t) const</code> that returns a copy of <code>ex</code>.</li>
<li>If <code>std::execution::oneway_t::static_query_v&lt;Executor&gt; &amp;&amp; adaptable_blocking_t::static_query_v&lt;Executor&gt;</code> is true, then <code>E1</code> implements member function <code>twoway_execute</code> in terms of the member function <code>execute</code> of the object <code>ex</code>, and <code>E1</code> has a member function <code>Executor require(std::execution::oneway_t) const</code> that returns a copy of <code>ex</code>.</li>
<li>If <code>std::execution::bulk_oneway_t::static_query_v&lt;Executor&gt; &amp;&amp; adaptable_blocking_t::static_query_v&lt;Executor&gt;</code> is true, then <code>E1</code> implements member function <code>twoway_execute</code> in terms of the member function <code>bulk_execute</code> of the object <code>ex</code>, and <code>E1</code> has a member function <code>Executor require(std::execution::bulk_oneway_t) const</code> that returns a copy of <code>ex</code>.</li>
</ul>
<p><em>Remarks:</em> This function shall not participate in overload resolution unless <code>twoway_t::template static_query_v&lt;Executor&gt;</code> is false and <code>bulk_twoway_t::static_query_v&lt;Executor&gt; || then_t::static_query_v&lt;Executor&gt; || bulk_then_t::static_query_v&lt;Executor&gt; || (std::execution::oneway_t::static_query_v&lt;Executor&gt; &amp;&amp; adaptable_blocking_t::static_query_v&lt;Executor&gt;) || (std::execution::bulk_oneway_t::static_query_v&lt;Executor&gt; &amp;&amp; adaptable_blocking_t::static_query_v&lt;Executor&gt;)</code> is true.</p>
<h4 id="twoway_t-polymorphic-wrapper"><span class="header-section-number">1.4.1.2</span> <code>twoway_t</code> polymorphic wrapper</h4>
<p>In addition to conforming to the specification for polymorphic executor wrappers, the nested class template <code>twoway_t::polymorphic_executor_type</code> provides the following member functions:</p>
<pre><code>template &lt;class... SupportableProperties&gt;
class polymorphic_executor_type
{
public:
  template&lt;class Executor&gt;
    polymorphic_executor_type(Executor e);

  template&lt;class Executor&gt;
    polymorphic_executor_type&amp; operator=(Executor e);

  template&lt;class Function&gt;
    std::experimental::future&lt;result_of_t&lt;decay_t&lt;Function&gt;()&gt;&gt;
      twoway_execute(Function&amp;&amp; f) const
};</code></pre>
<p><code>twoway_t::polymorphic_executor_type</code> satisfies the <code>TwoWayExecutor</code> requirements.</p>
<pre><code>template&lt;class Executor&gt;
  polymorphic_executor_type(Executor e);</code></pre>
<p><em>Remarks:</em> This function shall not participate in overload resolution unless:</p>
<ul>
<li><code>can_require_v&lt;Executor, twoway_t&gt;</code>.</li>
<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>
<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>
<li>and <code>can_query_v&lt;Executor, P&gt;</code>, if <code>P::is_requirable == false</code> and <code>P::is_preferable == false</code>, where <code>P</code> is each property in <code>SupportableProperties...</code>.</li>
</ul>
<p><em>Effects:</em> <code>*this</code> targets a copy of <code>e1</code>, where <code>e1</code> is the result of <code>execution::require(e, twoway)</code>.</p>
<pre><code>template&lt;class Executor&gt;
  polymorphic_executor_type&amp; operator=(Executor e);</code></pre>
<p><em>Requires:</em> As for <code>template&lt;class Executor&gt; polymorphic_executor_type(Executor e)</code>.</p>
<p><em>Effects:</em> <code>polymorphic_executor_type(std::move(e)).swap(*this)</code>.</p>
<p><em>Returns:</em> <code>*this</code>.</p>
<pre><code>template&lt;class Function&gt;
  std::experimental::future&lt;result_of_t&lt;decay_t&lt;Function&gt;()&gt;&gt;
    twoway_execute(Function&amp;&amp; f) const</code></pre>
<p><em>Remarks:</em> This function shall not participate in overload resolution unless: * <code>CONTAINS_PROPERTY(execution::twoway_t, SupportableProperties)</code>, * and <code>CONTAINS_PROPERTY(execution::single_t, SupportableProperties)</code>.</p>
<p><em>Effects:</em> Performs <code>e.twoway_execute(f2)</code>, where:</p>
<ul>
<li><code>e</code> is the target object of <code>*this</code>;</li>
<li><code>f1</code> is the result of <code>DECAY_COPY(std::forward&lt;Function&gt;(f))</code>;</li>
<li><code>f2</code> is a function object of unspecified type that, when invoked as <code>f2()</code>, performs <code>f1()</code>.</li>
</ul>
<p><em>Returns:</em> A future, whose shared state is made ready when the future returned by <code>e.twoway_execute(f2)</code> is made ready, containing the result of <code>f1()</code> or any exception thrown by <code>f1()</code>. [<em>Note:</em> <code>e2.twoway_execute(f2)</code> may return any future type that satisfies the Future requirements, and not necessarily <code>std::experimental::future</code>. One possible implementation approach is for the polymorphic wrapper to attach a continuation to the inner future via that object's <code>then()</code> member function. When invoked, this continuation stores the result in the outer future's associated shared and makes that shared state ready. <em>--end note</em>]</p>
<h4 id="then_t-customization-points"><span class="header-section-number">1.4.1.3</span> <code>then_t</code> customization points</h4>
<p>In addition to conforming to the above specification, the <code>then_t</code> property provides the following customization:</p>
<pre><code>struct then_t
{
  template&lt;class Executor&gt;
    friend see-below require(Executor ex, then_t);
};</code></pre>
<p>This customization point returns an executor that satisfies the <code>then_t</code> requirements by adapting the native functionality of an executor that does not satisfy the <code>then_t</code> requirements.</p>
<pre><code>template&lt;class Executor&gt;
  friend see-below require(Executor ex, then_t);</code></pre>
<p><em>Returns:</em> A value <code>e1</code> of type <code>E1</code> that holds a copy of <code>ex</code>. <code>E1</code> has member functions <code>require</code> and <code>query</code> that forward to the corresponding members of the copy of <code>ex</code>, if present. <code>e1</code> has the same properties as <code>ex</code>, except for the addition of the <code>then_t</code> property and the exclusion of other interface-changing properties. The type <code>E1</code> satisfies the <code>ThenExecutor</code> requirements by implementing member function <code>then_execute</code> in terms of the member function <code>bulk_then_execute</code> of the object <code>ex</code>, and <code>E1</code> has a member function <code>Executor require(bulk_then_t) const</code> that returns a copy of <code>ex</code>.</p>
<p><em>Remarks:</em> This function shall not participate in overload resolution unless <code>then_t::static_query_v&lt;Executor&gt;</code> is false and <code>bulk_then_t::static_query_v&lt;Executor&gt;</code> is true.</p>
<h4 id="then_t-polymorphic-wrapper"><span class="header-section-number">1.4.1.4</span> <code>then_t</code> polymorphic wrapper</h4>
<p>TODO</p>
<h4 id="bulk_twoway_t-customization-points"><span class="header-section-number">1.4.1.5</span> <code>bulk_twoway_t</code> customization points</h4>
<p>In addition to conforming to the specification for polymorphic executor wrappers, the nested class template <code>bulk_twoway_t::polymorphic_executor_type</code> provides the following member functions:</p>
<pre><code>struct bulk_twoway_t
{
  template&lt;class Executor&gt;
    friend see-below require(Executor ex, bulk_twoway_t);
};</code></pre>
<p>This customization point returns an executor that satisfies the <code>bulk_twoway_t</code> requirements by adapting the native functionality of an executor that does not satisfy the <code>bulk_twoway_t</code> requirements.</p>
<pre><code>template&lt;class Executor&gt;
  friend see-below require(Executor ex, bulk_twoway_t);</code></pre>
<p><em>Returns:</em> A value <code>e1</code> of type <code>E1</code> that holds a copy of <code>ex</code>. <code>E1</code> has member functions <code>require</code> and <code>query</code> that forward to the corresponding members of the copy of <code>ex</code>, if present. For some type <code>T</code>, the type yielded by <code>executor_future_t&lt;E1, T&gt;</code> is <code>executor_future_t&lt;Executor, T&gt;</code> if <code>bulk_then_t::static_query_v&lt;Executor&gt;</code> is true; otherwise, it is <code>std::experimental::future&lt;T&gt;</code>. <code>e1</code> has the same properties as <code>ex</code>, except for the addition of the <code>bulk_twoway_t</code> property and the exclusion of other interface-changing properties. The type <code>E1</code> satisfies the <code>BulkTwoWayExecutor</code> requirements as follows:</p>
<ul>
<li>If <code>twoway_t::static_query_v&lt;Executor&gt;</code> is true, then <code>E1</code> implements member function <code>bulk_twoway_execute</code> in terms of the member function <code>twoway_execute</code> of the object <code>ex</code>, and <code>E1</code> has a member function <code>Executor require(twoway_t) const</code> that returns a copy of <code>ex</code>.</li>
<li>If <code>bulk_then_t::static_query_v&lt;Executor&gt;</code> is true, then <code>E1</code> implements member function <code>bulk_twoway_execute</code> in terms of the member function <code>bulk_then_execute</code> of the object <code>ex</code>, and <code>E1</code> has a member function <code>Executor require(bulk_then_t) const</code> that returns a copy of <code>ex</code>.</li>
<li>If <code>then_t::static_query_v&lt;Executor&gt;</code> is true, then <code>E1</code> implements member function <code>bulk_twoway_execute</code> in terms of the member function <code>then_execute</code> of the object <code>ex</code>, and <code>E1</code> has a member function <code>Executor require(then_t) const</code> that returns a copy of <code>ex</code>.</li>
<li>If <code>std::execution::bulk_oneway_t::static_query_v&lt;Executor&gt; &amp;&amp; adaptable_blocking_t::static_query_v&lt;Executor&gt;</code> is true, then <code>E1</code> implements member function <code>bulk_twoway_execute</code> in terms of the member function <code>bulk_execute</code> of the object <code>ex</code>, and <code>E1</code> has a member function <code>Executor require(std::execution::bulk_oneway_t) const</code> that returns a copy of <code>ex</code>.</li>
<li>If <code>std::execution::oneway_t::static_query_v&lt;Executor&gt; &amp;&amp; adaptable_blocking_t::static_query_v&lt;Executor&gt;</code> is true, then <code>E1</code> implements member function <code>bulk_twoway_execute</code> in terms of the member function <code>execute</code> of the object <code>ex</code>, and <code>E1</code> has a member function <code>Executor require(std::execution::oneway_t) const</code> that returns a copy of <code>ex</code>.</li>
</ul>
<p><em>Remarks:</em> This function shall not participate in overload resolution unless <code>bulk_twoway_t::template static_query_v&lt;Executor&gt;</code> is false and <code>twoway_t::static_query_v&lt;Executor&gt; || bulk_then_t::static_query_v&lt;Executor&gt; || then_t::static_query_v&lt;Executor&gt; || (std::execution::bulk_oneway_t::static_query_v&lt;Executor&gt; &amp;&amp; adaptable_blocking_t::static_query_v&lt;Executor&gt;) || (std::execution::oneway_t::static_query_v&lt;Executor&gt; &amp;&amp; adaptable_blocking_t::static_query_v&lt;Executor&gt;)</code> is true.</p>
<h4 id="bulk_twoway_t-polymorphic-wrapper"><span class="header-section-number">1.4.1.6</span> <code>bulk_twoway_t</code> polymorphic wrapper</h4>
<p>In addition to conforming to the above specification, the nested class template <code>bulk_twoway_t::polymorphic_executor_type</code> has the following member function to satisfy the <code>BulkTwoWayExecutor</code> requirements.</p>
<pre><code>template &lt;class... SupportableProperties&gt;
class polymorphic_executor_type
{
public:
  template&lt;class Executor&gt;
    polymorphic_executor_type(Executor e);

  template&lt;class Executor&gt;
    polymorphic_executor_type&amp; operator=(Executor e);

  template&lt;class Function, class ResultFactory, class SharedFactory&gt;
    std::experimental::future&lt;result_of_t&lt;decay_t&lt;ResultFactory&gt;()&gt;&gt;
      bulk_twoway_execute(Function&amp;&amp; f, size_t n, ResultFactory&amp;&amp; rf, SharedFactory&amp;&amp; sf) const;
};</code></pre>
<p><code>bulk_twoway_t::polymorphic_executor_type</code> satisfies the <code>BulkTwoWayExecutor</code> requirements.</p>
<pre><code>template&lt;class Executor&gt;
  polymorphic_executor_type(Executor e);</code></pre>
<p><em>Remarks:</em> This function shall not participate in overload resolution unless:</p>
<ul>
<li><code>can_require_v&lt;Executor, bulk_twoway_t&gt;</code>.</li>
<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>
<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>
<li>and <code>can_query_v&lt;Executor, P&gt;</code>, if <code>P::is_requirable == false</code> and <code>P::is_preferable == false</code>, where <code>P</code> is each property in <code>SupportableProperties...</code>.</li>
</ul>
<p><em>Effects:</em> <code>*this</code> targets a copy of <code>e1</code>, where <code>e1</code> is the result of <code>execution::require(e, bulk_twoway)</code>.</p>
<pre><code>template&lt;class Executor&gt;
  polymorphic_executor_type&amp; operator=(Executor e);</code></pre>
<p><em>Requires:</em> As for <code>template&lt;class Executor&gt; polymorphic_executor_type(Executor e)</code>.</p>
<p><em>Effects:</em> <code>polymorphic_executor_type(std::move(e)).swap(*this)</code>.</p>
<p><em>Returns:</em> <code>*this</code>.</p>
<pre><code>template&lt;class Function, class ResultFactory, class SharedFactory&gt;
  std::experimental::future&lt;result_of_t&lt;decay_t&lt;ResultFactory&gt;()&gt;&gt;
    void bulk_twoway_execute(Function&amp;&amp; f, size_t n, ResultFactory&amp;&amp; rf, SharedFactory&amp;&amp; sf) const;</code></pre>
<p><em>Remarks:</em> This function shall not participate in overload resolution unless: * <code>CONTAINS_PROPERTY(execution::twoway_t, SupportableProperties)</code>, * and <code>CONTAINS_PROPERTY(execution::bulk_t, SupportableProperties)</code>.</p>
<p><em>Effects:</em> Performs <code>e.bulk_twoway_execute(f2, n, rf2, sf2)</code>, where:</p>
<ul>
<li><code>e</code> is the target object of <code>*this</code>;</li>
<li><code>rf1</code> is the result of <code>DECAY_COPY(std::forward&lt;ResultFactory&gt;(rf))</code>;</li>
<li><code>rf2</code> is a function object of unspecified type that, when invoked as <code>rf2()</code>, performs <code>rf1()</code>;</li>
<li><code>sf1</code> is the result of <code>DECAY_COPY(std::forward&lt;SharedFactory&gt;(rf))</code>;</li>
<li><code>sf2</code> is a function object of unspecified type that, when invoked as <code>sf2()</code>, performs <code>sf1()</code>;</li>
<li>if <code>decltype(rf1())</code> is non-void, <code>r1</code> is the result of <code>rf1()</code>;</li>
<li>if <code>decltype(rf2())</code> is non-void, <code>r2</code> is the result of <code>rf2()</code>;</li>
<li><code>s1</code> is the result of <code>sf1()</code>;</li>
<li><code>s2</code> is the result of <code>sf2()</code>;</li>
<li><code>f1</code> is the result of <code>DECAY_COPY(std::forward&lt;Function&gt;(f))</code>;</li>
<li>if <code>decltype(rf1())</code> is non-void and <code>decltype(rf2())</code> is non-void, <code>f2</code> is a function object of unspecified type that, when invoked as <code>f2(i, r2, s2)</code>, performs <code>f1(i, r1, s1)</code>, where <code>i</code> is a value of type <code>size_t</code>.</li>
<li>if <code>decltype(rf1())</code> is non-void and <code>decltype(rf2())</code> is void, <code>f2</code> is a function object of unspecified type that, when invoked as <code>f2(i, s2)</code>, performs <code>f1(i, r1, s1)</code>, where <code>i</code> is a value of type <code>size_t</code>.</li>
<li>if <code>decltype(rf1())</code> is void and <code>decltype(rf2())</code> is non-void, <code>f2</code> is a function object of unspecified type that, when invoked as <code>f2(i, r2, s2)</code>, performs <code>f1(i, s1)</code>, where <code>i</code> is a value of type <code>size_t</code>.</li>
<li>if <code>decltype(rf1())</code> is void and <code>decltype(rf2())</code> is void, <code>f2</code> is a function object of unspecified type that, when invoked as <code>f2(i, s2)</code>, performs <code>f1(i, s1)</code>, where <code>i</code> is a value of type <code>size_t</code>.</li>
</ul>
<p><em>Returns:</em> A future, whose shared state is made ready when the future returned by <code>e.bulk_twoway_execute(f2, n, rf2, sf2)</code> is made ready, containing the result in <code>r1</code> (if <code>decltype(rf1())</code> is non-void) or any exception thrown by an invocation<code>f1</code>. [<em>Note:</em> <code>e.bulk_twoway_execute(f2)</code> may return any future type that satisfies the Future requirements, and not necessarily <code>std::experimental::future</code>. One possible implementation approach is for the polymorphic wrapper to attach a continuation to the inner future via that object's <code>then()</code> member function. When invoked, this continuation stores the result in the outer future's associated shared and makes that shared state ready. <em>--end note</em>]</p>
<h4 id="bulk_then_t-customization-points"><span class="header-section-number">1.4.1.7</span> <code>bulk_then_t</code> customization points</h4>
<p>In addition to conforming to the above specification, the <code>bulk_then_t</code> property provides the following customization:</p>
<pre><code>struct bulk_then_t
{
  template&lt;class Executor&gt;
    friend see-below require(Executor ex, bulk_then_t);
};</code></pre>
<p>This customization point returns an executor that satisfies the <code>bulk_then_t</code> requirements by adapting the native functionality of an executor that does not satisfy the <code>bulk_then_t</code> requirements.</p>
<pre><code>template&lt;class Executor&gt;
  friend see-below require(Executor ex, bulk_then_t);</code></pre>
<p><em>Returns:</em> A value <code>e1</code> of type <code>E1</code> that holds a copy of <code>ex</code>. <code>E1</code> has member functions <code>require</code> and <code>query</code> that forward to the corresponding members of the copy of <code>ex</code>, if present. <code>e1</code> has the same properties as <code>ex</code>, except for the addition of the <code>bulk_then_t</code> property and the exclusion of other interface-changing properties. The type <code>E1</code> satisfies the <code>ThenExecutor</code> requirements by implementing member function <code>bulk_then_execute</code> in terms of the member function <code>then_execute</code> of the object <code>ex</code>, and <code>E1</code> has a member function <code>Executor require(then_t) const</code> that returns a copy of <code>ex</code>.</p>
<p><em>Remarks:</em> This function shall not participate in overload resolution unless <code>bulk_then_t::static_query_v&lt;Executor&gt;</code> is false and <code>then_t::static_query_v&lt;Executor&gt;</code> is true.</p>
<h4 id="bulk_then_t-polymorphic-wrapper"><span class="header-section-number">1.4.1.8</span> <code>bulk_then_t</code> polymorphic wrapper</h4>
<p>TODO</p>
<h2 id="executor-type-traits"><span class="header-section-number">1.5</span> Executor type traits</h2>
<h3 id="determining-that-a-type-satisfies-executor-type-requirements"><span class="header-section-number">1.5.1</span> Determining that a type satisfies executor type requirements</h3>
<pre><code>template&lt;class T&gt; struct is_twoway_executor;
template&lt;class T&gt; struct is_then_executor;
template&lt;class T&gt; struct is_bulk_twoway_executor;
template&lt;class T&gt; struct is_bulk_then_executor;</code></pre>
<p>This sub-clause contains templates that may be used to query the properties of a type at compile time. Each of these templates is a UnaryTypeTrait (C++Std [meta.rqmts]) with a BaseCharacteristic of <code>true_type</code> if the corresponding condition is true, otherwise <code>false_type</code>.</p>
<table style="width:94%;">
<colgroup>
<col width="40%" />
<col width="30%" />
<col width="23%" />
</colgroup>
<thead>
<tr class="header">
<th align="left">Template</th>
<th align="left">Condition</th>
<th align="left">Preconditions</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td align="left"><code>template&lt;class T&gt;</code> <br/><code>struct is_twoway_executor</code></td>
<td align="left"><code>T</code> meets the syntactic requirements for <code>TwoWayExecutor</code>.</td>
<td align="left"><code>T</code> is a complete type.</td>
</tr>
<tr class="even">
<td align="left"><code>template&lt;class T&gt;</code> <br/><code>struct is_then_executor</code></td>
<td align="left"><code>T</code> meets the syntactic requirements for <code>ThenExecutor</code>.</td>
<td align="left"><code>T</code> is a complete type.</td>
</tr>
<tr class="odd">
<td align="left"><code>template&lt;class T&gt;</code> <br/><code>struct is_bulk_twoway_executor</code></td>
<td align="left"><code>T</code> meets the syntactic requirements for <code>BulkTwoWayExecutor</code>.</td>
<td align="left"><code>T</code> is a complete type.</td>
</tr>
<tr class="even">
<td align="left"><code>template&lt;class T&gt;</code> <br/><code>struct is_bulk_then_executor</code></td>
<td align="left"><code>T</code> meets the syntactic requirements for <code>BulkThenExecutor</code>.</td>
<td align="left"><code>T</code> is a complete type.</td>
</tr>
</tbody>
</table>
<h3 id="associated-future-type"><span class="header-section-number">1.5.2</span> Associated future type</h3>
<pre><code>template&lt;class Executor, class T&gt;
struct executor_future
{
  using type = decltype(execution::require(declval&lt;const Executor&amp;&gt;(), execution::twoway).twoway_execute(declval&lt;T(*)()&gt;()));
};</code></pre>
