<table style="width:79%;">
<colgroup>
<col width="27%" />
<col width="51%" />
</colgroup>
<tbody>
<tr class="odd">
<td align="left">Title:</td>
<td align="left">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">P0443R9</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 proposes a programming model for executors, which are modular components for creating execution. The design of this proposal is described in paper <a href="https://wg21.link/P0761">P0761</a>.</td>
</tr>
</tbody>
</table>
<h2 id="changelog"><span class="header-section-number">0.1</span> Changelog</h2>
<h3 id="revision-9"><span class="header-section-number">0.1.1</span> Revision 9</h3>
<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. This paper contains material related to one-way execution which the authors hope to standardize with C++20 as suggested by the Bellevue poll. <a href="http://wg21.link/P1244">P1244</a> contains remaining material related to dependent execution. We expect P1244 to evolve as committee consensus builds around a design for dependent execution.</p>
<p>This revision also contains bug fixes to the <code>allocator_t</code> property which were originally scheduled for Revision 7 but were inadvertently omitted.</p>
<h3 id="revision-8"><span class="header-section-number">0.1.2</span> Revision 8</h3>
<p>Revision 8 of this proposal makes interface-changing properties such as <code>oneway</code> mutually exclusive in order to simplify implementation requirements for executor adaptors such as polymorphic executors. Additionally, this revision clarifies wording regarding execution agent lifetime.</p>
<h3 id="revision-7"><span class="header-section-number">0.1.3</span> Revision 7</h3>
<p>Revision 7 of this proposal corrects wording bugs discovered by the authors after Revision 6's publication.</p>
<ul>
<li>Enhanced <code>static_query_v</code> to result in a default property value for executors which do not provide a <code>query</code> function for the property of interest</li>
<li>Revise <code>then_execute</code> and <code>bulk_then_execute</code>'s operational semantics to allow user functions to handle incoming exceptions thrown by preceding execution agents</li>
<li>Introduce <code>exception_arg</code> to disambiguate the user function's exceptional overload from its nonexceptional overload in <code>then_execute</code> and <code>bulk_then_execute</code></li>
</ul>
<h3 id="revision-6"><span class="header-section-number">0.1.4</span> Revision 6</h3>
<p>Revision 6 of this proposal corrects bugs and omissions discovered by the authors after Revision 5's publication, and introduces an enhancement improving the safety of the design.</p>
<ul>
<li>Enforce mutual exclusion of behavioral properties via the type system instead of via convention</li>
<li>Introduce missing <code>execution::require</code> adaptations</li>
<li>Allow executors to opt-out of invoking factory functions when appropriate</li>
<li>Various bug fixes and corrections</li>
</ul>
<h3 id="revision-5"><span class="header-section-number">0.1.5</span> Revision 5</h3>
<p>Revision 5 of this proposal responds to feedback requested during the 2017 Albuquerque ISO C++ Standards Committee meeting and introduces changes which allow properties to better interoperate with polymorphic executor wrappers and also simplify <code>execution::require</code>'s behavior.</p>
<ul>
<li>Defined general property type requirements</li>
<li>Elaborated specification of standard property types</li>
<li>Simplified <code>execution::require</code>'s specification</li>
<li>Enhanced polymorphic executor wrapper
<ul>
<li>Templatized <code>execution::executor&lt;SupportableProperties...&gt;</code></li>
<li>Introduced <code>prefer_only</code> property adaptor</li>
</ul></li>
<li>Responded to Albuquerque feedback
<ul>
<li>From SG1
<ul>
<li>Execution contexts are now optional properties of executors</li>
<li>Eliminated ill-specified caller-agent forward progress properties</li>
<li>Elaborated <code>Future</code>'s requirements to incorporate forward progress</li>
<li>Reworded operational semantics of execution functions to use similar language as the blocking properties</li>
<li>Elaborated <code>static_thread_pool</code>'s specification to guarantee that threads in the bool boost-block their work</li>
<li>Elaborated operational semantics of execution functions to note that forward progress guarantees are specific to the concrete executor type</li>
</ul></li>
<li>From LEWG
<ul>
<li>Eliminated named <code>BaseExecutor</code> concept</li>
<li>Simplified general executor requirements</li>
<li>Enhanced the <code>OneWayExecutor</code> introductory paragraph</li>
<li>Eliminated <code>has_*_member</code> type traits</li>
</ul></li>
</ul></li>
<li>Minor changes
<ul>
<li>Renamed TS namespace from <code>concurrency_v2</code> to <code>executors_v1</code></li>
<li>Introduced <code>static_query_v</code> enabling static queries</li>
<li>Eliminated unused <code>property_value</code> trait</li>
<li>Eliminated the names <code>allocator_wrapper_t</code> and <code>default_allocator</code></li>
</ul></li>
</ul>
<h3 id="revision-4"><span class="header-section-number">0.1.6</span> Revision 4</h3>
<ul>
<li>Specified the guarantees implied by <code>bulk_sequenced_execution</code>, <code>bulk_parallel_execution</code>, and <code>bulk_unsequenced_execution</code></li>
</ul>
<h3 id="revision-3"><span class="header-section-number">0.1.7</span> Revision 3</h3>
<ul>
<li>Introduced <code>execution::query()</code> for executor property introspection</li>
<li>Simplified the design of <code>execution::prefer()</code></li>
<li><code>oneway</code>, <code>twoway</code>, <code>single</code>, and <code>bulk</code> are now <code>require()</code>-only properties</li>
<li>Introduced properties allowing executors to opt into adaptations that add blocking semantics</li>
<li>Introduced properties describing the forward progress relationship between caller and agents</li>
<li>Various minor improvements to existing functionality based on prototyping</li>
</ul>
<h3 id="revision-2"><span class="header-section-number">0.1.8</span> Revision 2</h3>
<ul>
<li>Separated wording from explanatory prose, now contained in paper <a href="https://wg21.link/P0761">P0761</a></li>
<li>Applied the simplification proposed by paper <a href="https://wg21.link/P0688">P0688</a></li>
</ul>
<h3 id="revision-1"><span class="header-section-number">0.1.9</span> Revision 1</h3>
<ul>
<li>Executor category simplification</li>
<li>Specified executor customization points in detail</li>
<li>Introduced new fine-grained executor type traits
<ul>
<li>Detectors for execution functions</li>
<li>Traits for introspecting cross-cutting concerns
<ul>
<li>Introspection of mapping of agents to threads</li>
<li>Introspection of execution function blocking behavior</li>
</ul></li>
</ul></li>
<li>Allocator support for single agent execution functions</li>
<li>Renamed <code>thread_pool</code> to <code>static_thread_pool</code></li>
<li>New introduction</li>
</ul>
<h3 id="revision-0"><span class="header-section-number">0.1.10</span> Revision 0</h3>
<ul>
<li>Initial design</li>
</ul>
<h1 id="proposed-wording"><span class="header-section-number">1</span> Proposed Wording</h1>
<h2 id="execution-support-library"><span class="header-section-number">1.1</span> Execution Support Library</h2>
<h3 id="general"><span class="header-section-number">1.1.1</span> General</h3>
<p>This Clause describes components supporting execution of function objects [function.objects].</p>
<p><em>(The following definition appears in working draft N4762 [thread.req.lockable.general])</em></p>
<blockquote>
<p>An <em>execution agent</em> is an entity such as a thread that may perform work in parallel with other execution agents. [<em>Note:</em> Implementations or users may introduce other kinds of agents such as processes or thread-pool tasks. <em>--end note</em>] The calling agent is determined by context; e.g., the calling thread that contains the call, and so on.</p>
</blockquote>
<p>An execution agent invokes a function object within an <em>execution context</em> such as the calling thread or thread-pool. An <em>executor</em> submits a function object to an execution context to be invoked by an execution agent within that execution context. [<em>Note:</em> Invocation of the function object may be inlined such as when the execution context is the calling thread, or may be scheduled such as when the execution context is a thread-pool with task scheduler. <em>--end note</em>] An executor may submit a function object with <em>execution properties</em> that specify how the submission and invocation of the function object interacts with the submitting thread and execution context, including forward progress guarantees [intro.progress].</p>
<p>For the intent of this library and extensions to this library, the <em>lifetime of an execution agent</em> begins before the function object is invoked and ends after this invocation completes, either normally or having thrown an exception.</p>
<h3 id="header-execution-synopsis"><span class="header-section-number">1.1.2</span> Header <code>&lt;execution&gt;</code> synopsis</h3>
<pre><code>namespace std {
namespace execution {

  // Customization points:

  namespace {
    constexpr unspecified require = unspecified;
    constexpr unspecified prefer = unspecified;
    constexpr unspecified query = unspecified;
  }

  // Customization point type traits:

  template&lt;class Executor, class... Properties&gt; struct can_require;
  template&lt;class Executor, class... Properties&gt; struct can_prefer;
  template&lt;class Executor, class Property&gt; struct can_query;

  template&lt;class Executor, class... Properties&gt;
    constexpr bool can_require_v = can_require&lt;Executor, Properties...&gt;::value;
  template&lt;class Executor, class... Properties&gt;
    constexpr bool can_prefer_v = can_prefer&lt;Executor, Properties...&gt;::value;
  template&lt;class Executor, class Property&gt;
    constexpr bool can_query_v = can_query&lt;Executor, Property&gt;::value;

  // Associated execution context property:

  struct context_t;

  constexpr context_t context;

  // Interface-changing properties:

  struct oneway_t;
  struct bulk_oneway_t;

  constexpr oneway_t oneway;
  constexpr bulk_oneway_t bulk_oneway;

  // Blocking properties:

  struct blocking_t;

  constexpr blocking_t blocking;

  // Properties to allow adaptation of blocking and directionality:

  struct blocking_adaptation_t;

  constexpr blocking_adaptation_t blocking_adaptation;

  // Properties to indicate if submitted tasks represent continuations:

  struct relationship_t;

  constexpr relationship_t relationship;

  // Properties to indicate likely task submission in the future:

  struct outstanding_work_t;

  constexpr outstanding_work_t outstanding_work;

  // Properties for bulk execution guarantees:

  struct bulk_guarantee_t;

  constexpr bulk_guarantee_t bulk_guarantee;

  // Properties for mapping of execution on to threads:

  struct mapping_t;

  constexpr mapping_t mapping;

  // Memory allocation properties:

  template &lt;typename ProtoAllocator&gt;
  struct allocator_t;

  constexpr allocator_t&lt;void&gt; allocator;

  // Executor type traits:

  template&lt;class Executor&gt; struct is_oneway_executor;
  template&lt;class Executor&gt; struct is_bulk_oneway_executor;

  template&lt;class Executor&gt; constexpr bool is_oneway_executor_v = is_oneway_executor&lt;Executor&gt;::value;
  template&lt;class Executor&gt; constexpr bool is_bulk_oneway_executor_v = is_bulk_oneway_executor&lt;Executor&gt;::value;

  template&lt;class Executor&gt; struct executor_shape;
  template&lt;class Executor&gt; struct executor_index;

  template&lt;class Executor&gt; using executor_shape_t = typename executor_shape&lt;Executor&gt;::type;
  template&lt;class Executor&gt; using executor_index_t = typename executor_index&lt;Executor&gt;::type;

  // Polymorphic executor support:

  class bad_executor;

  template &lt;class InterfaceProperty, class... SupportableProperties&gt;
    using executor = typename InterfaceProperty::template
      polymorphic_executor_type&lt;InterfaceProperty, SupportableProperties...&gt;;
  template&lt;class Property&gt; struct prefer_only;

} // namespace execution
} // namespace std</code></pre>
<h2 id="requirements"><span class="header-section-number">1.2</span> Requirements</h2>
<h3 id="customization-point-objects"><span class="header-section-number">1.2.1</span> Customization point objects</h3>
<p><em>(The following text has been adapted from the draft Ranges Technical Specification.)</em></p>
<p>A <em>customization point object</em> is a function object (C++ Std, [function.objects]) with a literal class type that interacts with user-defined types while enforcing semantic requirements on that interaction.</p>
<p>The type of a customization point object shall satisfy the requirements of <code>CopyConstructible</code> (C++Std [copyconstructible]) and <code>Destructible</code> (C++Std [destructible]).</p>
<p>All instances of a specific customization point object type shall be equal.</p>
<p>Let <code>t</code> be a (possibly const) customization point object of type <code>T</code>, and <code>args...</code> be a parameter pack expansion of some parameter pack <code>Args...</code>. The customization point object <code>t</code> shall be invocable as <code>t(args...)</code> when the types of <code>Args...</code> meet the requirements specified in that customization point object's definition. Otherwise, <code>T</code> shall not have a function call operator that participates in overload resolution.</p>
<p>Each customization point object type constrains its return type to satisfy some particular type requirements.</p>
<p>The library defines several named customization point objects. In every translation unit where such a name is defined, it shall refer to the same instance of the customization point object.</p>
<p>[<em>Note:</em> Many of the customization points objects in the library evaluate function call expressions with an unqualified name which results in invoking a user-defined function found by argument dependent name lookup (C++Std [basic.lookup.argdep]). To preclude such an expression resulting in invoking an unconstrained functions with the same name in namespace <code>std</code>, customization point objects specify that lookup for these expressions is performed in a context that includes deleted overloads matching the signatures of overloads defined in namespace <code>std</code>. When the deleted overloads are viable, user-defined overloads must be more specialized (C++Std [temp.func.order]) to be used by a customization point object. <em>--end note</em>]</p>
<h3 id="protoallocator-requirements"><span class="header-section-number">1.2.2</span> <code>ProtoAllocator</code> requirements</h3>
<p>A type <code>A</code> meets the <code>ProtoAllocator</code> requirements if <code>A</code> is <code>CopyConstructible</code> (C++Std [copyconstructible]), <code>Destructible</code> (C++Std [destructible]), and <code>allocator_traits&lt;A&gt;::rebind_alloc&lt;U&gt;</code> meets the allocator requirements (C++Std [allocator.requirements]), where <code>U</code> is an object type. [<em>Note:</em> For example, <code>std::allocator&lt;void&gt;</code> meets the proto-allocator requirements but not the allocator requirements. <em>--end note</em>] No comparison operator, copy operation, move operation, or swap operation on these types shall exit via an exception.</p>
<h3 id="general-requirements-on-executors"><span class="header-section-number">1.2.3</span> General requirements on executors</h3>
<p>An executor type shall satisfy the requirements of <code>CopyConstructible</code> (C++Std [copyconstructible]), <code>Destructible</code> (C++Std [destructible]), and <code>EqualityComparable</code> (C++Std [equalitycomparable]).</p>
<p>None of these concepts' operations, nor an executor type's swap operations, shall exit via an exception.</p>
<p>None of these concepts' operations, nor an executor type's associated execution functions, associated query functions, or other member functions defined in executor type requirements, shall introduce data races as a result of concurrent invocations of those functions from different threads.</p>
<p>For any two (possibly const) values <code>x1</code> and <code>x2</code> of some executor type <code>X</code>, <code>x1 == x2</code> shall return <code>true</code> only if <code>x1.query(p) == x2.query(p)</code> for every property <code>p</code> where both <code>x1.query(p)</code> and <code>x2.query(p)</code> are well-formed and result in a non-void type that is <code>EqualityComparable</code> (C++Std [equalitycomparable]). [<em>Note:</em> The above requirements imply that <code>x1 == x2</code> returns <code>true</code> if <code>x1</code> and <code>x2</code> can be interchanged with identical effects. An executor may conceptually contain additional properties which are not exposed by a named property type that can be observed via <code>execution::query</code>; in this case, it is up to the concrete executor implementation to decide if these properties affect equality. Returning <code>false</code> does not necessarily imply that the effects are not identical. <em>--end note</em>]</p>
<p>An executor type's destructor shall not block pending completion of the submitted function objects. [<em>Note:</em> The ability to wait for completion of submitted function objects may be provided by the associated execution context. <em>--end note</em>]</p>
<h3 id="onewayexecutor-requirements"><span class="header-section-number">1.2.4</span> <code>OneWayExecutor</code> requirements</h3>
<p>A type <code>X</code> satisfies the <code>OneWayExecutor</code> requirements if it satisfies the general requirements on executors, as well as the requirements in the Table below.</p>
<p>[<em>Note:</em> <code>OneWayExecutor</code>s provides fire-and-forget semantics without a channel for awaiting the completion of a submitted function object and obtaining its result. <em>--end note</em>]</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>
</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.execute(f)</code></td>
<td align="left"><code>void</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 invocation. <br/> Synchronizes with [intro.multithread] the invocation of <code>f</code>. <br/>Shall not propagate any exception thrown by the function object or any other function submitted to the executor. [<em>Note:</em> The treatment of exceptions thrown by one-way submitted functions and the forward progress guarantee of the associated execution agent(s) are implementation defined. <em>--end note.</em>]</td>
</tr>
</tbody>
</table>
<h3 id="bulkonewayexecutor-requirements"><span class="header-section-number">1.2.5</span> <code>BulkOneWayExecutor</code> requirements</h3>
<p>The <code>BulkOneWayExecutor</code> requirements specify requirements for executors which submit a function object to be invoked multiple times without a channel for awaiting the completion of the submitted function object invocations and obtaining their result. [<em>Note:</em> That is, the executor provides fire-and-forget semantics. <em>--end note</em>]</p>
<p>A type <code>X</code> satisfies the <code>BulkOneWayExecutor</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>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><code>f</code> denotes a function object of type <code>F&amp;&amp;</code> invocable as <code>cf(i, s)</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_execute(f, n, sf)</code></td>
<td align="left"><code>void</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>cf(i,s)</code> (or 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>. <code>sf()</code> will be invoked at most once to produce value <code>s</code> before any invocation of <code>cf</code>. <br/> May block pending completion of one or more invocations of <code>cf</code>. <br/> Synchronizes with (C++Std [intro.multithread]) the invocations of <code>f</code>. <br/> Shall not propagate any exception thrown by <code>cf</code> or any other function submitted to the executor. [<em>Note:</em> The treatment of exceptions thrown by bulk one-way submitted functions and the forward progress guarantee of the associated execution agent(s) are implementation defined. <em>--end note.</em>]</td>
</tr>
</tbody>
</table>
<h2 id="executor-customization-points"><span class="header-section-number">1.3</span> Executor customization points</h2>
<p><em>Executor customization points</em> are functions which adapt an executor's properties. Executor customization points enable uniform use of executors in generic contexts.</p>
<p>When an executor customization point named <em>NAME</em> invokes a free execution function of the same name, overload resolution is performed in a context that includes the declaration <code>void</code> <em>NAME</em><code>(auto&amp;... args) = delete;</code>, where <code>sizeof...(args)</code> is the arity of the free execution function. This context also does not include a declaration of the executor customization point.</p>
<p>[<em>Note:</em> This provision allows executor customization points to invoke the executor's free, non-member execution function of the same name without recursion. <em>--end note</em>]</p>
<p>Whenever <code>std::execution::</code><em>NAME</em><code>(</code><em>ARGS</em><code>)</code> is a valid expression, that expression satisfies the syntactic requirements for the free execution function named <em>NAME</em> with arity <code>sizeof...(</code><em>ARGS</em><code>)</code> with that free execution function's semantics.</p>
<h3 id="require"><span class="header-section-number">1.3.1</span> <code>require</code></h3>
<pre><code>namespace {
  constexpr unspecified require = unspecified;
}</code></pre>
<p>The name <code>require</code> denotes a customization point. The effect of the expression <code>std::execution::require(E, P0, Pn...)</code> for some expressions <code>E</code> and <code>P0</code>, and where <code>Pn...</code> represents <code>N</code> expressions (where <code>N</code> is 0 or more), is equivalent to:</p>
<ul>
<li><p>If <code>N == 0</code>, <code>P0::is_requirable</code> is true, and the expression <code>decay_t&lt;decltype(P0)&gt;::template static_query_v&lt;decay_t&lt;decltype(E)&gt;&gt; == decay_t&lt;decltype(P0)&gt;::value()</code> is a well-formed constant expression with value <code>true</code>, <code>E</code>.</p></li>
<li><p>If <code>N == 0</code>, <code>P0::is_requirable</code> is true, and the expression <code>(E).require(P0)</code> is well-formed, <code>(E).require(P0)</code>.</p></li>
<li><p>If <code>N == 0</code>, <code>P0::is_requirable</code> is true, and the expression <code>require(E, P0)</code> is well-formed, <code>require(E, P0)</code>.</p></li>
<li><p>If <code>N &gt; 0</code> and the expression <code>std::execution::require( std::execution::require(E, P0), Pn...)</code> is well formed, <code>std::execution::require( std::execution::require(E, P0), Pn...)</code>.</p></li>
<li><p>Otherwise, <code>std::execution::require(E, P0, Pn...)</code> is ill-formed.</p></li>
</ul>
<h3 id="prefer"><span class="header-section-number">1.3.2</span> <code>prefer</code></h3>
<pre><code>namespace {
  constexpr unspecified prefer = unspecified;
}</code></pre>
<p>The name <code>prefer</code> denotes a customization point. The effect of the expression <code>std::execution::prefer(E, P0, Pn...)</code> for some expressions <code>E</code> and <code>P0</code>, and where <code>Pn...</code> represents <code>N</code> expressions (where <code>N</code> is 0 or more), is equivalent to:</p>
<ul>
<li><p>If <code>N == 0</code>, <code>P0::is_preferable</code> is true, and the expression <code>decay_t&lt;decltype(P0)&gt;::template static_query_v&lt;decay_t&lt;decltype(E)&gt;&gt; == decay_t&lt;decltype(P0)&gt;::value()</code> is a well-formed constant expression with value <code>true</code>, <code>E</code>.</p></li>
<li><p>If <code>N == 0</code>, <code>P0::is_preferable</code> is true, and the expression <code>(E).require(P0)</code> is well-formed, <code>(E).require(P0)</code>.</p></li>
<li><p>If <code>N == 0</code>, <code>P0::is_preferable</code> is true, and the expression <code>prefer(E, P0)</code> is well-formed, <code>prefer(E, P0)</code>.</p></li>
<li><p>If <code>N == 0</code> and <code>P0::is_preferable</code> is true, <code>E</code>.</p></li>
<li><p>If <code>N &gt; 0</code> and the expression <code>std::execution::prefer( std::execution::prefer(E, P0), Pn...)</code> is well formed, <code>std::execution::prefer( std::execution::prefer(E, P0), Pn...)</code>.</p></li>
<li><p>Otherwise, <code>std::execution::require(E, P0, Pn...)</code> is ill-formed.</p></li>
</ul>
<h3 id="query"><span class="header-section-number">1.3.3</span> <code>query</code></h3>
<pre><code>namespace {
  constexpr unspecified query = unspecified;
}</code></pre>
<p>The name <code>query</code> denotes a customization point. The effect of the expression <code>std::execution::query(E, P)</code> for some expressions <code>E</code> and <code>P</code> is equivalent to:</p>
<ul>
<li><p>If the expression <code>decay_t&lt;decltype(P)&gt;::template static_query_v&lt;decay_t&lt;decltype(E)&gt;&gt;</code> is a well-formed constant expression, <code>decay_t&lt;decltype(P)&gt;::template static_query_v&lt;decay_t&lt;decltype(E)&gt;&gt;</code>.</p></li>
<li><p>If the expression <code>(E).query(P)</code> is well-formed, <code>(E).query(P)</code>.</p></li>
<li><p>If the expression <code>query(E, P)</code> is well-formed, <code>query(E, P)</code>.</p></li>
<li><p>Otherwise, <code>std::execution::query(E, P)</code> is ill-formed.</p></li>
</ul>
<h3 id="customization-point-type-traits"><span class="header-section-number">1.3.4</span> Customization point type traits</h3>
<pre><code>template&lt;class Executor, class... Properties&gt; struct can_require;
template&lt;class Executor, class... Properties&gt; struct can_prefer;
template&lt;class Executor, class Property&gt; struct can_query;</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 can_require</code></td>
<td align="left">The expression <code>std::execution::require( declval&lt;const Executor&gt;(), declval&lt;Properties&gt;()...)</code> is well formed.</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 can_prefer</code></td>
<td align="left">The expression <code>std::execution::prefer( declval&lt;const Executor&gt;(), declval&lt;Properties&gt;()...)</code> is well formed.</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 can_query</code></td>
<td align="left">The expression <code>std::execution::query( declval&lt;const Executor&gt;(), declval&lt;Property&gt;())</code> is well formed.</td>
<td align="left"><code>T</code> is a complete type.</td>
</tr>
</tbody>
</table>
<h2 id="executor-properties"><span class="header-section-number">1.4</span> Executor properties</h2>
<h3 id="in-general"><span class="header-section-number">1.4.1</span> In general</h3>
<p>An executor's behavior in generic contexts is determined by a set of executor properties, and each executor property imposes certain requirements on the executor.</p>
<p>Given an existing executor, a related executor with different properties may be created by invoking the <code>require</code> member or non-member functions. These functions behave according the Table below. In the Table below, <code>x</code> denotes a (possibly const) executor object of type <code>X</code>, and <code>p</code> denotes a (possibly const) property object.</p>
<p>[<em>Note:</em> As a general design note properties which define a mutually exclusive pair, that describe an enabled or non-enabled behaviour follow the convention of having the same property name for both with the <code>not_</code> prefix to the property for the non-enabled behaviour. <em>--end note</em>]</p>
<table style="width:33%;">
<colgroup>
<col width="18%" />
<col width="15%" />
</colgroup>
<thead>
<tr class="header">
<th align="left">Expression</th>
<th align="left">Comments</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td align="left"><code>x.require(p)</code> <br/> <code>require(x,p)</code></td>
<td align="left">Returns an executor object with the requested property <code>p</code> added to the set. All other properties of the returned executor are identical to those of <code>x</code>, except where those properties are described below as being mutually exclusive to <code>p</code>. In this case, the mutually exclusive properties are implicitly removed from the set associated with the returned executor. <br/> <br/> The expression is ill formed if an executor is unable to add the requested property.</td>
</tr>
</tbody>
</table>
<p>The current value of an executor's properties can be queried by invoking the <code>query</code> function. This function behaves according the Table below. In the Table below, <code>x</code> denotes a (possibly const) executor object of type <code>X</code>, and <code>p</code> denotes a (possibly const) property object.</p>
<table style="width:33%;">
<colgroup>
<col width="18%" />
<col width="15%" />
</colgroup>
<thead>
<tr class="header">
<th align="left">Expression</th>
<th align="left">Comments</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td align="left"><code>x.query(p)</code></td>
<td align="left">Returns the current value of the requested property <code>p</code>. The expression is ill formed if an executor is unable to return the requested property.</td>
</tr>
</tbody>
</table>
<h3 id="requirements-on-properties"><span class="header-section-number">1.4.2</span> Requirements on properties</h3>
<p>A property type <code>P</code> shall provide:</p>
<ul>
<li>A nested constant expression named <code>is_requirable</code> of type <code>bool</code>, usable as <code>P::is_requirable</code>.</li>
<li>A nested constant expression named <code>is_preferable</code> of type <code>bool</code>, usable as <code>P::is_preferable</code>.</li>
</ul>
<p>[<em>Note:</em> These constants are used to determine whether the property can be used with the <code>require</code> and <code>prefer</code> customization points, respectively. <em>--end note</em>]</p>
<p>A property type <code>P</code> may provide a nested type <code>polymorphic_query_result_type</code> that satisfies the <code>CopyConstructible</code> and <code>Destructible</code> requirements. 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. [<em>Note:</em> When present, this type allows the property to be used with a polymorphic <code>executor</code> wrapper. <em>--end note</em>]</p>
<p>A property type <code>P</code> may provide:</p>
<ul>
<li>A nested variable template <code>static_query_v</code>, usable as <code>P::static_query_v&lt;Executor&gt;</code>. This may be conditionally present.</li>
<li>A member function <code>value()</code>.</li>
</ul>
<p>If both <code>static_query_v</code> and <code>value()</code> are present, they shall return the same type and this type shall satisfy the <code>EqualityComparable</code> requirements.</p>
<p>[<em>Note:</em> These are used to determine whether invoking <code>require</code> would result in an identity transformation. <em>--end note</em>]</p>
<h3 id="query-only-properties"><span class="header-section-number">1.4.3</span> Query-only properties</h3>
<h4 id="associated-execution-context-property"><span class="header-section-number">1.4.3.1</span> Associated execution context property</h4>
<pre><code>struct context_t
{
  static constexpr bool is_requirable = false;
  static constexpr bool is_preferable = false;

  using polymorphic_query_result_type = any; // TODO: alternatively consider void*, or simply omitting the type.

  template&lt;class Executor&gt;
    static constexpr decltype(auto) static_query_v
      = Executor::query(context_t());
};</code></pre>
<p>The <code>context_t</code> property can be used only with <code>query</code>, which returns the execution context associated with the executor.</p>
<p>The value returned from <code>execution::query(e, context_t)</code>, where <code>e</code> is an executor, shall not change between invocations.</p>
<h3 id="interface-changing-properties"><span class="header-section-number">1.4.4</span> Interface-changing properties</h3>
<pre><code>struct oneway_t;
struct bulk_oneway_t;</code></pre>
<p>The interface-changing properties conform to the following specification:</p>
<pre><code>struct S
{
  static constexpr bool is_requirable = true;
  static constexpr bool is_preferable = false;

  template&lt;class... SupportableProperties&gt; 
    class polymorphic_executor_type;

  using polymorphic_query_result_type = bool;

  template&lt;class Executor&gt;
    static constexpr bool static_query_v
      = see-below;

  static constexpr bool value() const { return true; }
};</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>oneway_t</code></td>
<td align="left">The executor type satisfies the <code>OneWayExecutor</code> requirements.</td>
</tr>
<tr class="even">
<td align="left"><code>bulk_oneway_t</code></td>
<td align="left">The executor type satisfies the <code>BulkOneWayExecutor</code> requirements.</td>
</tr>
</tbody>
</table>
<p><code>S::static_query_v&lt;Executor&gt;</code> is true if and only if <code>Executor</code> fulfills <code>S</code>'s requirements.</p>
<p>The <code>oneway_t</code> and <code>bulk_oneway_t</code> properties are mutually exclusive.</p>
<h4 id="polymorphic-wrappers"><span class="header-section-number">1.4.4.1</span> Polymorphic wrappers</h4>
<p>In several places in this section the operation <code>CONTAINS_PROPERTY(p, pn)</code> is used. All such uses mean <code>std::disjunction_v&lt;std::is_same&lt;p, pn&gt;...&gt;</code>.</p>
<p>In several places in this section the operation <code>FIND_CONVERTIBLE_PROPERTY(p, pn)</code> is used. All such uses mean the first type <code>P</code> in the parameter pack <code>pn</code> for which <code>std::is_convertible_v&lt;p, P&gt;</code> is <code>true</code>. If no such type <code>P</code> exists, the operation <code>FIND_CONVERTIBLE_PROPERTY(p, pn)</code> is ill-formed.</p>
<p>The nested class template <code>S::polymorphic_executor_type</code> conforms to the following specification.</p>
<pre><code>template &lt;class... SupportableProperties&gt;
class polymorphic_executor_type
{
public:
  // construct / copy / destroy:

  polymorphic_executor_type() noexcept;
  polymorphic_executor_type(nullptr_t) noexcept;
  polymorphic_executor_type(const polymorphic_executor_type&amp; e) noexcept;
  polymorphic_executor_type(polymorphic_executor_type&amp;&amp; e) noexcept;
  template&lt;class... OtherSupportableProperties&gt;
    polymorphic_executor_type(polymorphic_executor_type&lt;OtherSupportableProperties...&gt; e);
  template&lt;class... OtherSupportableProperties&gt;
    polymorphic_executor_type(polymorphic_executor_type&lt;OtherSupportableProperties...&gt; e) = delete;

  polymorphic_executor_type&amp; operator=(const polymorphic_executor_type&amp; e) noexcept;
  polymorphic_executor_type&amp; operator=(polymorphic_executor_type&amp;&amp; e) noexcept;
  polymorphic_executor_type&amp; operator=(nullptr_t) noexcept;

  ~polymorphic_executor_type();

  // polymorphic_executor_type modifiers:

  void swap(polymorphic_executor_type&amp; other) noexcept;

  // polymorphic_executor_type operations:

  template &lt;class Property&gt;
  polymorphic_executor_type require(Property) const;

  template &lt;class Property&gt;
  typename Property::polymorphic_query_result_type query(Property) const;

  // polymorphic_executor_type capacity:

  explicit operator bool() const noexcept;

  // polymorphic_executor_type target access:

  const type_info&amp; target_type() const noexcept;
  template&lt;class Executor&gt; Executor* target() noexcept;
  template&lt;class Executor&gt; const Executor* target() const noexcept;

  // polymorphic_executor_type casts:

  template&lt;class... OtherSupportableProperties&gt;
    polymorphic_executor_type&lt;OtherSupportableProperties...&gt; static_executor_cast() const;
};

// polymorphic_executor_type comparisons:

template &lt;class... SupportableProperties&gt;
bool operator==(const polymorphic_executor_type&lt;SupportableProperties...&gt;&amp; a, const polymorphic_executor_type&lt;SupportableProperties...&gt;&amp; b) noexcept;
template &lt;class... SupportableProperties&gt;
bool operator==(const polymorphic_executor_type&lt;SupportableProperties...&gt;&amp; e, nullptr_t) noexcept;
template &lt;class... SupportableProperties&gt;
bool operator==(nullptr_t, const polymorphic_executor_type&lt;SupportableProperties...&gt;&amp; e) noexcept;
template &lt;class... SupportableProperties&gt;
bool operator!=(const polymorphic_executor_type&lt;SupportableProperties...&gt;&amp; a, const polymorphic_executor_type&lt;SupportableProperties...&gt;&amp; b) noexcept;
template &lt;class... SupportableProperties&gt;
bool operator!=(const polymorphic_executor_type&lt;SupportableProperties...&gt;&amp; e, nullptr_t) noexcept;
template &lt;class... SupportableProperties&gt;
bool operator!=(nullptr_t, const polymorphic_executor_type&lt;SupportableProperties...&gt;&amp; e) noexcept;

// polymorphic_executor_type specialized algorithms:

template &lt;class... SupportableProperties&gt;
void swap(polymorphic_executor_type&lt;SupportableProperties...&gt;&amp; a, polymorphic_executor_type&lt;SupportableProperties...&gt;&amp; b) noexcept;

template &lt;class Property, class... SupportableProperties&gt;
polymorphic_executor_type prefer(const polymorphic_executor_type&lt;SupportableProperties&gt;&amp; e, Property p);</code></pre>
<p>The <code>polymorphic_executor_type</code> class satisfies the general requirements on executors.</p>
<p>[<em>Note:</em> To meet the <code>noexcept</code> requirements for executor copy constructors and move constructors, implementations may share a target between two or more <code>polymorphic_executor_type</code> objects. <em>--end note</em>]</p>
<p>Each property type in the <code>SupportableProperties...</code> pack shall provide a nested type <code>polymorphic_query_result_type</code>.</p>
<p>The <em>target</em> is the executor object that is held by the wrapper.</p>
<h5 id="polymorphic_executor_type-constructors"><span class="header-section-number">1.4.4.1.1</span> <code>polymorphic_executor_type</code> constructors</h5>
<pre><code>polymorphic_executor_type() noexcept;</code></pre>
<p><em>Postconditions:</em> <code>!*this</code>.</p>
<pre><code>polymorphic_executor_type(nullptr_t) noexcept;</code></pre>
<p><em>Postconditions:</em> <code>!*this</code>.</p>
<pre><code>polymorphic_executor_type(const polymorphic_executor_type&amp; e) noexcept;</code></pre>
<p><em>Postconditions:</em> <code>!*this</code> if <code>!e</code>; otherwise, <code>*this</code> targets <code>e.target()</code> or a copy of <code>e.target()</code>.</p>
<pre><code>polymorphic_executor_type(polymorphic_executor_type&amp;&amp; e) noexcept;</code></pre>
<p><em>Effects:</em> If <code>!e</code>, <code>*this</code> has no target; otherwise, moves <code>e.target()</code> or move-constructs the target of <code>e</code> into the target of <code>*this</code>, leaving <code>e</code> in a valid state with an unspecified value.</p>
<pre><code>template&lt;class... OtherSupportableProperties&gt;
  polymorphic_executor_type(polymorphic_executor_type&lt;OtherSupportableProperties...&gt; e);</code></pre>
<p><em>Remarks:</em> This function shall not participate in overload resolution unless: * <code>CONTAINS_PROPERTY(p, OtherSupportableProperties)</code> , where <code>p</code> is each property in <code>SupportableProperties...</code>.</p>
<p><em>Effects:</em> <code>*this</code> targets a copy of <code>e</code> initialized with <code>std::move(e)</code>.</p>
<pre><code>template&lt;class... OtherSupportableProperties&gt;
  polymorphic_executor_type(polymorphic_executor_type&lt;OtherSupportableProperties...&gt; e) = delete;</code></pre>
<p><em>Remarks:</em> This function shall not participate in overload resolution unless <code>CONTAINS_PROPERTY(p, OtherSupportableProperties)</code> is <code>false</code> for some property <code>p</code> in <code>SupportableProperties...</code>.</p>
<h5 id="polymorphic_executor_type-assignment"><span class="header-section-number">1.4.4.1.2</span> <code>polymorphic_executor_type</code> assignment</h5>
<pre><code>polymorphic_executor_type&amp; operator=(const polymorphic_executor_type&amp; e) noexcept;</code></pre>
<p><em>Effects:</em> <code>polymorphic_executor_type(e).swap(*this)</code>.</p>
<p><em>Returns:</em> <code>*this</code>.</p>
<pre><code>polymorphic_executor_type&amp; operator=(polymorphic_executor_type&amp;&amp; e) noexcept;</code></pre>
<p><em>Effects:</em> Replaces the target of <code>*this</code> with the target of <code>e</code>, leaving <code>e</code> in a valid state with an unspecified value.</p>
<p><em>Returns:</em> <code>*this</code>.</p>
<pre><code>polymorphic_executor_type&amp; operator=(nullptr_t) noexcept;</code></pre>
<p><em>Effects:</em> <code>polymorphic_executor_type(nullptr).swap(*this)</code>.</p>
<p><em>Returns:</em> <code>*this</code>.</p>
<h5 id="polymorphic_executor_type-destructor"><span class="header-section-number">1.4.4.1.3</span> <code>polymorphic_executor_type</code> destructor</h5>
<pre><code>~polymorphic_executor_type();</code></pre>
<p><em>Effects:</em> If <code>*this != nullptr</code>, releases shared ownership of, or destroys, the target of <code>*this</code>.</p>
<h5 id="polymorphic_executor_type-modifiers"><span class="header-section-number">1.4.4.1.4</span> <code>polymorphic_executor_type</code> modifiers</h5>
<pre><code>void swap(polymorphic_executor_type&amp; other) noexcept;</code></pre>
<p><em>Effects:</em> Interchanges the targets of <code>*this</code> and <code>other</code>.</p>
<h5 id="polymorphic_executor_type-operations"><span class="header-section-number">1.4.4.1.5</span> <code>polymorphic_executor_type</code> operations</h5>
<pre><code>template &lt;class Property&gt;
polymorphic_executor_type require(Property p) const;</code></pre>
<p><em>Remarks:</em> This function shall not participate in overload resolution unless <code>FIND_CONVERTIBLE_PROPERTY(Property, SupportableProperties)::is_requirable</code> is well-formed and has the value <code>true</code>.</p>
<p><em>Returns:</em> A polymorphic wrapper whose target is the result of <code>execution::require(e, p)</code>, where <code>e</code> is the target object of <code>*this</code>.</p>
<pre><code>template &lt;class Property&gt;
typename Property::polymorphic_query_result_type query(Property p) const;</code></pre>
<p><em>Remarks:</em> This function shall not participate in overload resolution unless <code>FIND_CONVERTIBLE_PROPERTY(Property, SupportableProperties)</code> is well-formed.</p>
<p><em>Returns:</em> If <code>polymorphic_executor_type::query(e, p)</code> is well-formed, <code>static_cast&lt;Property::polymorphic_query_result_type&gt;(polymorphic_executor_type::query(e, p))</code>, where <code>e</code> is the target object of <code>*this</code>. Otherwise, <code>Property::polymorphic_query_result_type{}</code>.</p>
<h5 id="polymorphic_executor_type-capacity"><span class="header-section-number">1.4.4.1.6</span> <code>polymorphic_executor_type</code> capacity</h5>
<pre><code>explicit operator bool() const noexcept;</code></pre>
<p><em>Returns:</em> <code>true</code> if <code>*this</code> has a target, otherwise <code>false</code>.</p>
<h5 id="polymorphic_executor_type-target-access"><span class="header-section-number">1.4.4.1.7</span> <code>polymorphic_executor_type</code> target access</h5>
<pre><code>const type_info&amp; target_type() const noexcept;</code></pre>
<p><em>Returns:</em> If <code>*this</code> has a target of type <code>T</code>, <code>typeid(T)</code>; otherwise, <code>typeid(void)</code>.</p>
<pre><code>template&lt;class Executor&gt; Executor* target() noexcept;
template&lt;class Executor&gt; const Executor* target() const noexcept;</code></pre>
<p><em>Returns:</em> If <code>target_type() == typeid(Executor)</code> a pointer to the stored executor target; otherwise a null pointer value.</p>
<h5 id="polymorphic_executor_type-comparisons"><span class="header-section-number">1.4.4.1.8</span> <code>polymorphic_executor_type</code> comparisons</h5>
<pre><code>template&lt;class... SupportableProperties&gt;
bool operator==(const polymorphic_executor_type&lt;SupportableProperties...&gt;&amp; a, const polymorphic_executor_type&lt;SupportableProperties...&gt;&amp; b) noexcept;</code></pre>
<p><em>Returns:</em></p>
<ul>
<li><code>true</code> if <code>!a</code> and <code>!b</code>;</li>
<li><code>true</code> if <code>a</code> and <code>b</code> share a target;</li>
<li><code>true</code> if <code>e</code> and <code>f</code> are the same type and <code>e == f</code>, where <code>e</code> is the target of <code>a</code> and <code>f</code> is the target of <code>b</code>;</li>
<li>otherwise <code>false</code>.</li>
</ul>
<pre><code>template&lt;class... SupportableProperties&gt;
bool operator==(const polymorphic_executor_type&lt;SupportableProperties...&gt;&amp; e, nullptr_t) noexcept;
template&lt;class... SupportableProperties&gt;
bool operator==(nullptr_t, const polymorphic_executor_type&lt;SupportableProperties...&gt;&amp; e) noexcept;</code></pre>
<p><em>Returns:</em> <code>!e</code>.</p>
<pre><code>template&lt;class... SupportableProperties&gt;
bool operator!=(const polymorphic_executor_type&lt;SupportableProperties...&gt;&amp; a, const polymorphic_executor_type&lt;SupportableProperties...&gt;&amp; b) noexcept;</code></pre>
<p><em>Returns:</em> <code>!(a == b)</code>.</p>
<pre><code>template&lt;class... SupportableProperties&gt;
bool operator!=(const polymorphic_executor_type&lt;SupportableProperties...&gt;&amp; e, nullptr_t) noexcept;
template&lt;class... SupportableProperties&gt;
bool operator!=(nullptr_t, const polymorphic_executor_type&lt;SupportableProperties...&gt;&amp; e) noexcept;</code></pre>
<p><em>Returns:</em> <code>(bool) e</code>.</p>
<h5 id="polymorphic_executor_type-specialized-algorithms"><span class="header-section-number">1.4.4.1.9</span> <code>polymorphic_executor_type</code> specialized algorithms</h5>
<pre><code>template&lt;class... SupportableProperties&gt;
void swap(polymorphic_executor_type&lt;SupportableProperties...&gt;&amp; a, polymorphic_executor_type&lt;SupportableProperties...&gt;&amp; b) noexcept;</code></pre>
<p><em>Effects:</em> <code>a.swap(b)</code>.</p>
<pre><code>template &lt;class Property, class... SupportableProperties&gt;
polymorphic_executor_type prefer(const polymorphic_executor_type&lt;SupportableProperties...&gt;&amp; e, Property p);</code></pre>
<p><em>Remarks:</em> This function shall not participate in overload resolution unless <code>FIND_CONVERTIBLE_PROPERTY(Property, SupportableProperties)::is_preferable</code> is well-formed and has the value <code>true</code>.</p>
<p><em>Returns:</em> A polymorphic wrapper whose target is the result of <code>execution::prefer(e, p)</code>, where <code>e</code> is the target object of <code>*this</code>.</p>
<h5 id="polymorphic_executor_type-casts"><span class="header-section-number">1.4.4.1.10</span> <code>polymorphic_executor_type</code> casts</h5>
<pre><code>template&lt;class... OtherSupportableProperties&gt;
  polymorphic_executor_type&lt;OtherSupportableProperties...&gt; static_executor_cast() const;</code></pre>
<p><em>Requires:</em> The target object was first inserted into a polymorphic wrapper (whether via the wrapper's constructor or assignment operator) whose template parameters included the parameters in <code>OtherSupportableProperties</code>.</p>
<p><em>Returns:</em> A polymorphic wrapper whose target is <code>e</code>.</p>
<h4 id="oneway_t-customization-points"><span class="header-section-number">1.4.4.2</span> <code>oneway_t</code> customization points</h4>
<p>In addition to conforming to the above specification for interface-changing properties, the <code>oneway_t</code> property provides the following customization:</p>
<pre><code>struct oneway_t
{
  template&lt;class Executor&gt;
    friend see-below require(Executor ex, oneway_t);
};</code></pre>
<p>This customization point returns an executor that satisfies the <code>oneway_t</code> requirements by adapting the native functionality of an executor that does not satisfy the <code>oneway_t</code> requirements.</p>
<pre><code>template&lt;class Executor&gt;
  friend see-below require(Executor ex, oneway_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>oneway_t</code> property and the exclusion of other interface-changing properties. The type <code>E1</code> satisfies the <code>OneWayExecutor</code> requirements by implementing member function <code>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(bulk_oneway_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>oneway_t::static_query_v&lt;Executor&gt;</code> is false and <code>bulk_oneway_t::static_query_v&lt;Executor&gt;</code> is true.</p>
<h4 id="oneway_t-polymorphic-wrapper"><span class="header-section-number">1.4.4.3</span> <code>oneway_t</code> polymorphic wrapper</h4>
<p>In addition to conforming to the above specification for polymorphic wrappers, the nested class template <code>oneway_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;
    void execute(Function&amp;&amp; f) const;
};</code></pre>
<p><code>oneway_t::polymorphic_executor_type</code> satisfies the <code>OneWayExecutor</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, oneway_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, oneway)</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;
  void execute(Function&amp;&amp; f) const;</code></pre>
<p><em>Effects:</em> Performs <code>e.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>
<h4 id="bulk_oneway_t-customization-points"><span class="header-section-number">1.4.4.4</span> <code>bulk_oneway_t</code> customization points</h4>
<p>In addition to conforming to the above specification for interface-changing properties, the <code>oneway_t</code> property provides the following customization:</p>
<pre><code>struct bulk_oneway_t
{
  template&lt;class Executor&gt;
    friend see-below require(Executor ex, bulk_oneway_t);
};</code></pre>
<p>This customization point returns an executor that satisfies the <code>bulk_oneway_t</code> requirements by adapting the native functionality of an executor that does not satisfy the <code>bulk_oneway_t</code> requirements.</p>
<pre><code>template&lt;class Executor&gt;
  friend see-below require(Executor ex, bulk_oneway_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_oneway_t</code> property and the exclusion of other interface-changing properties. The type <code>E1</code> satisfies the <code>BulkOneWayExecutor</code> requirements by implementing member function <code>bulk_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(oneway_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_oneway_t::static_query_v&lt;Executor&gt;</code> is false and <code>oneway_t::static_query_v&lt;Executor&gt;</code> is true.</p>
<h4 id="bulk_oneway_t-polymorphic-wrapper"><span class="header-section-number">1.4.4.5</span> <code>bulk_oneway_t</code> polymorphic wrapper</h4>
<p>In addition to conforming to the above specification for polymorphic wrappers, the nested class template <code>bulk_oneway_t::polymorphic_executor_type</code> has 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, class SharedFactory&gt;
    void bulk_execute(Function&amp;&amp; f, size_t n, SharedFactory&amp;&amp; sf) const;
};</code></pre>
<p><code>bulk_oneway_t::polymorphic_executor_type</code> satisfies the <code>BulkOneWayExecutor</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_oneway_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_oneway)</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 SharedFactory&gt;
  void bulk_execute(Function&amp;&amp; f, size_t n, SharedFactory&amp;&amp; sf) const;</code></pre>
<p><em>Effects:</em> Performs <code>e.bulk_execute(f2, n, sf2)</code>, where:</p>
<ul>
<li><code>e</code> is the target object of <code>*this</code>;</li>
<li><code>sf1</code> is the result of <code>DECAY_COPY(std::forward&lt;SharedFactory&gt;(sf))</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><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><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>
<h3 id="behavioral-properties"><span class="header-section-number">1.4.5</span> Behavioral properties</h3>
<p>Behavioral properties define a set of mutually-exclusive nested properties describing executor behavior.</p>
<p>Unless otherwise specified, behavioral property types <code>S</code>, their nested property types <code>S::N</code><em>i</em>, and nested property objects <code>S::n</code><em>i</em> conform to the following specification:</p>
<pre><code>struct S
{
  static constexpr bool is_requirable = false;
  static constexpr bool is_preferable = false;
  using polymorphic_query_result_type = S;

  template&lt;class Executor&gt;
    static constexpr auto static_query_v
      = see-below;

  template&lt;class Executor&gt;
  friend constexpr S query(const Executor&amp; ex, const Property&amp; p) noexcept(see-below);

  friend constexpr bool operator==(const S&amp; a, const S&amp; b);
  friend constexpr bool operator!=(const S&amp; a, const S&amp; b) { return !operator==(a, b); }

  constexpr S();

  struct N1
  {
    static constexpr bool is_requirable = true;
    static constexpr bool is_preferable = true;
    using polymorphic_query_result_type = S;

    template&lt;class Executor&gt;
      static constexpr auto static_query_v
        = see-below;

    static constexpr S value() { return S(N1()); }
  };

  static constexpr n1;

  constexpr S(const N1);

  ...

  struct NN
  {
    static constexpr bool is_requirable = true;
    static constexpr bool is_preferable = true;
    using polymorphic_query_result_type = S;

    template&lt;class Executor&gt;
      static constexpr auto static_query_v
        = see-below;

    static constexpr S value() { return S(NN()); }
  };

  static constexpr nN;

  constexpr S(const NN);
};</code></pre>
<p>Queries for the value of an executor's behavioral property shall not change between invocations unless the executor is assigned another executor with a different value of that behavioral property.</p>
<p><code>S()</code> and <code>S(S::E</code><em>i</em><code>())</code> are all distinct values of <code>S</code>. [<em>Note:</em> This means they compare unequal. <em>--end note.</em>]</p>
<p>The value returned from <code>execution::query(e1, p1)</code> and a subsequent invocation <code>execution::query(e1, p1)</code>, where</p>
<ul>
<li><code>p1</code> is an instance of <code>S</code> or <code>S::E</code><em>i</em>, and</li>
<li><code>e2</code> is the result of <code>execution::require(e1, p2)</code> or <code>execution::prefer(e1, p2)</code>,</li>
</ul>
<p>shall compare equal unless</p>
<ul>
<li><code>p2</code> is an instance of <code>S::E</code><em>i</em>, and</li>
<li><code>p1</code> and <code>p2</code> are different types.</li>
</ul>
<p>The value of the expression <code>S::N1::static_query_v&lt;Executor&gt;</code> is</p>
<ul>
<li><code>Executor::query(S::N1())</code>, if that expression is a well-formed expression;</li>
<li>ill-formed if <code>declval&lt;Executor&gt;().query(S::N1())</code> is well-formed;</li>
<li>ill-formed if <code>can_query_v&lt;Executor,S::N</code><em>i</em><code>&gt;</code> is <code>true</code> for any <code>1 &lt;</code> <em>i</em> <code>&lt;= N</code>;</li>
<li>otherwise <code>S::N1()</code>.</li>
</ul>
<p>[<em>Note:</em> These rules automatically enable the <code>S::N1</code> property by default for executors which do not provide a <code>query</code> function for properties <code>S::N</code><em>i</em>. <em>--end note</em>]</p>
<p>The value of the expression <code>S::N</code><em>i</em><code>::static_query_v&lt;Executor&gt;</code>, for all <code>1 &lt;</code> <em>i</em> <code>&lt;= N</code>, is</p>
<ul>
<li><code>Executor::query(S::N</code><em>i</em><code>())</code>, if that expression is a well-formed constant expression;</li>
<li>otherwise ill-formed.</li>
</ul>
<p>The value of the expression <code>S::static_query_v&lt;Executor&gt;</code> is</p>
<ul>
<li><code>Executor::query(S())</code>, if that expression is a well-formed constant expression;</li>
<li>otherwise, ill-formed if <code>declval&lt;Executor&gt;().query(S())</code> is well-formed;</li>
<li>otherwise, <code>S::N</code><em>i</em><code>::static_query_v&lt;Executor&gt;</code> for the least <em>i</em> <code>&lt;= N</code> for which this expression is a well-formed constant expression;</li>
<li>otherwise ill-formed.</li>
</ul>
<p>[<em>Note:</em> These rules automatically enable the <code>S::N1</code> property by default for executors which do not provide a <code>query</code> function for properties <code>S</code> or <code>S::N</code><em>i</em>. <em>--end note</em>]</p>
<p>Let <em>k</em> be the least value of <em>i</em> for which <code>can_query_v&lt;Executor,S::N</code><em>i</em><code>&gt;</code> is true, if such a value of <em>i</em> exists.</p>
<pre><code>template&lt;class Executor&gt;
  friend constexpr S query(const Executor&amp; ex, const Property&amp; p) noexcept(noexcept(execution::query(ex, std::declval&lt;const S::Nk&gt;())));</code></pre>
<p><em>Returns:</em> <code>execution::query(ex, S::N</code><em>k</em><code>())</code>.</p>
<p><em>Remarks:</em> This function shall not participate in overload resolution unless <code>is_same_v&lt;Property,S&gt; &amp;&amp; can_query_v&lt;Executor,S::N</code><em>i</em><code>&gt;</code> is true for at least one <code>S::N</code><em>i</em>`.</p>
<pre><code>bool operator==(const S&amp; a, const S&amp; b);</code></pre>
<p><em>Returns:</em> <code>true</code> if <code>a</code> and <code>b</code> were constructed from the same constructor; <code>false</code>, otherwise.</p>
<h4 id="blocking-properties"><span class="header-section-number">1.4.5.1</span> Blocking properties</h4>
<p>The <code>blocking_t</code> property describes what guarantees executors provide about the blocking behavior of their execution functions.</p>
<p><code>blocking_t</code> provides nested property types and objects as described below.</p>
<table style="width:93%;">
<colgroup>
<col width="37%" />
<col width="34%" />
<col width="20%" />
</colgroup>
<thead>
<tr class="header">
<th align="left">Nested Property Type</th>
<th align="left">Nested Property Object Name</th>
<th align="left">Requirements</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td align="left"><code>blocking_t::possibly_t</code></td>
<td align="left"><code>blocking_t::possibly</code></td>
<td align="left">Invocation of an executor's execution function may block pending completion of one or more invocations of the submitted function object.</td>
</tr>
<tr class="even">
<td align="left"><code>blocking_t::always_t</code></td>
<td align="left"><code>blocking_t::always</code></td>
<td align="left">Invocation of an executor's execution function shall block until completion of all invocations of submitted function object.</td>
</tr>
<tr class="odd">
<td align="left"><code>blocking_t::never_t</code></td>
<td align="left"><code>blocking_t::never</code></td>
<td align="left">Invocation of an executor's execution function shall not block pending completion of the invocations of the submitted function object.</td>
</tr>
</tbody>
</table>
<h5 id="blocking_talways_t-customization-points"><span class="header-section-number">1.4.5.1.1</span> <code>blocking_t::always_t</code> customization points</h5>
<p>In addition to conforming to the above specification, the <code>blocking_t::always_t</code> property provides the following customization:</p>
<pre><code>struct always_t
{
  template&lt;class Executor&gt;
    friend see-below require(Executor ex, blocking_t::always_t);
};</code></pre>
<p>If the executor has the <code>blocking_adaptation_t::allowed_t</code> property, this customization uses an adapter to implement the <code>blocking_t::always_t</code> property.</p>
<pre><code>template&lt;class Executor&gt;
  friend see-below require(Executor ex, blocking_t::always_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>. If <code>Executor</code> satisfies the <code>OneWayExecutor</code> requirements, <code>E1</code> shall satisfy the <code>OneWayExecutor</code> requirements by providing member functions <code>require</code>, <code>query</code>, and <code>execute</code> that forward to the corresponding member functions of the copy of <code>ex</code>. If <code>Executor</code> satisfies the <code>BulkOneWayExecutor</code> requirements, <code>E1</code> shall satisfy the <code>BulkOneWayExecutor</code> requirements by providing member functions <code>require</code>, <code>query</code>, and <code>bulk_execute</code> that forward to the corresponding member functions of the copy of <code>ex</code>. In addition, <code>E1</code> provides an overload of <code>require</code> such that <code>e1.require(blocking.always)</code> returns a copy of <code>e1</code>, an overload of <code>query</code> such that <code>e1.query(blocking)</code> returns <code>blocking.always</code>, and functions <code>execute</code> and <code>bulk_execute</code> shall block the calling thread until the submitted functions have finished execution. <code>e1</code> has the same executor properties as <code>ex</code>, except for the addition of the <code>blocking_t::always_t</code> property, and removal of <code>blocking_t::never_t</code> and <code>blocking_t::possibly_t</code> properties if present.</p>
<p><em>Remarks:</em> This function shall not participate in overload resolution unless <code>blocking_adaptation_t::static_query_v&lt;Executor&gt;</code> is <code>blocking_adaptation.allowed</code>.</p>
<h4 id="properties-to-indicate-if-blocking-and-directionality-may-be-adapted"><span class="header-section-number">1.4.5.2</span> Properties to indicate if blocking and directionality may be adapted</h4>
<p>The <code>blocking_adaptation_t</code> property allows or disallows blocking or directionality adaptation via <code>execution::require</code>.</p>
<p><code>blocking_adaptation_t</code> provides nested property types and objects as described below.</p>
<table>
<colgroup>
<col width="35%" />
<col width="44%" />
<col width="19%" />
</colgroup>
<thead>
<tr class="header">
<th align="left">Nested Property Type</th>
<th align="left">Nested Property Object Name</th>
<th align="left">Requirements</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td align="left"><code>blocking_adaptation_t::disallowed_t</code></td>
<td align="left"><code>blocking_adaptation::disallowed</code></td>
<td align="left">The <code>require</code> customization point may not adapt the executor to add the <code>blocking_t::always_t</code> property.</td>
</tr>
<tr class="even">
<td align="left"><code>blocking_adaptation_t::allowed_t</code></td>
<td align="left"><code>blocking_adaptation::allowed</code></td>
<td align="left">The <code>require</code> customization point may adapt the executor to add the <code>blocking_t::always_t</code> property.</td>
</tr>
</tbody>
</table>
<h5 id="blocking_adaptation_tallowed_t-customization-points"><span class="header-section-number">1.4.5.2.1</span> <code>blocking_adaptation_t::allowed_t</code> customization points</h5>
<p>In addition to conforming to the above specification, the <code>blocking_adaptation_t::allowed_t</code> property provides the following customization:</p>
<pre><code>struct allowed_t
{
  template&lt;class Executor&gt;
    friend see-below require(Executor ex, blocking_adaptation_t::allowed_t);
};</code></pre>
<p>This customization uses an adapter to implement the <code>blocking_adaptation_t::allowed_t</code> property.</p>
<pre><code>template&lt;class Executor&gt;
  friend see-below require(Executor ex, blocking_adaptation_t::allowed_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>. If <code>Executor</code> satisfies the <code>OneWayExecutor</code> requirements, <code>E1</code> shall satisfy the <code>OneWayExecutor</code> requirements by providing member functions <code>require</code>, <code>query</code>, and <code>execute</code> that forward to the corresponding member functions of the copy of <code>ex</code>. If <code>Executor</code> satisfies the <code>BulkOneWayExecutor</code> requirements, <code>E1</code> shall satisfy the <code>BulkOneWayExecutor</code> requirements by providing member functions <code>require</code>, <code>query</code>, and <code>bulk_execute</code> that forward to the corresponding member functions of the copy of <code>ex</code>. In addition, <code>blocking_adaptation_t::static_query_v&lt;E1&gt;</code> is <code>blocking_adaptation.allowed</code>, and <code>e1.require(blocking_adaptation.disallowed)</code> yields a copy of <code>ex</code>. <code>e1</code> has the same executor properties as <code>ex</code>, except for the addition of the <code>blocking_adaptation_t::allowed_t</code> property.</p>
<h4 id="properties-to-indicate-if-submitted-tasks-represent-continuations"><span class="header-section-number">1.4.5.3</span> Properties to indicate if submitted tasks represent continuations</h4>
<p>The <code>relationship_t</code> property allows users of executors to indicate that submitted tasks represent continuations.</p>
<p><code>relationship_t</code> provides nested property types and objects as indicated below.</p>
<table>
<colgroup>
<col width="35%" />
<col width="44%" />
<col width="19%" />
</colgroup>
<thead>
<tr class="header">
<th align="left">Nested Property Type</th>
<th align="left">Nested Property Object Name</th>
<th align="left">Requirements</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td align="left"><code>relationship_t::fork_t</code></td>
<td align="left"><code>relationship_t::fork</code></td>
<td align="left">Function objects submitted through the executor do not represent continuations of the caller.</td>
</tr>
<tr class="even">
<td align="left"><code>relationship_t::continuation_t</code></td>
<td align="left"><code>relationship_t::continuation</code></td>
<td align="left">Function objects submitted through the executor represent continuations of the caller. Invocation of the submitted function object may be deferred until the caller completes.</td>
</tr>
</tbody>
</table>
<h4 id="properties-to-indicate-likely-task-submission-in-the-future"><span class="header-section-number">1.4.5.4</span> Properties to indicate likely task submission in the future</h4>
<p>The <code>outstanding_work_t</code> property allows users of executors to indicate that task submission is likely in the future.</p>
<p><code>outstanding_work_t</code> provides nested property types and objects as indicated below.</p>
<table>
<colgroup>
<col width="34%" />
<col width="45%" />
<col width="20%" />
</colgroup>
<thead>
<tr class="header">
<th align="left">Nested Property Type</th>
<th align="left">Nested Property Object Name</th>
<th align="left">Requirements</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td align="left"><code>outstanding_work_t::untracked_t</code></td>
<td align="left"><code>outstanding_work::untracked</code></td>
<td align="left">The existence of the executor object does not indicate any likely future submission of a function object.</td>
</tr>
<tr class="even">
<td align="left"><code>outstanding_work_t::tracked_t</code></td>
<td align="left"><code>outstanding_work::tracked</code></td>
<td align="left">The existence of the executor object represents an indication of likely future submission of a function object. The executor or its associated execution context may choose to maintain execution resources in anticipation of this submission.</td>
</tr>
</tbody>
</table>
<p>[<em>Note:</em> The <code>outstanding_work_t::tracked_t</code> and <code>outstanding_work_t::untracked_t</code> properties are used to communicate to the associated execution context intended future work submission on the executor. The intended effect of the properties is the behavior of execution context's facilities for awaiting outstanding work; specifically whether it considers the existance of the executor object with the <code>outstanding_work_t::tracked_t</code> property enabled outstanding work when deciding what to wait on. However this will be largely defined by the execution context implementation. It is intended that the execution context will define its wait facilities and on-destruction behaviour and provide an interface for querying this. An initial work towards this is included in P0737r0. <em>--end note</em>]</p>
<h4 id="properties-for-bulk-execution-guarantees"><span class="header-section-number">1.4.5.5</span> Properties for bulk execution guarantees</h4>
<p>Bulk execution guarantee properties communicate the forward progress and ordering guarantees of execution agents associated with the bulk execution.</p>
<p><code>bulk_guarantee_t</code> provides nested property types and objects as indicated below.</p>
<table>
<colgroup>
<col width="35%" />
<col width="44%" />
<col width="19%" />
</colgroup>
<thead>
<tr class="header">
<th align="left">Nested Property Type</th>
<th align="left">Nested Property Object Name</th>
<th align="left">Requirements</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td align="left"><code>bulk_guarantee_t::unsequenced_t</code></td>
<td align="left"><code>bulk_guarantee_t::unsequenced</code></td>
<td align="left">Execution agents within the same bulk execution may be parallelized and vectorized.</td>
</tr>
<tr class="even">
<td align="left"><code>bulk_guarantee_t::sequenced_t</code></td>
<td align="left"><code>bulk_guarantee_t::sequenced</code></td>
<td align="left">Execution agents within the same bulk execution may not be parallelized.</td>
</tr>
<tr class="odd">
<td align="left"><code>bulk_guarantee_t::parallel_t</code></td>
<td align="left"><code>bulk_guarantee_t::parallel</code></td>
<td align="left">Execution agents within the same bulk execution may be parallelized.</td>
</tr>
</tbody>
</table>
<p>Execution agents associated with the <code>bulk_guarantee_t::unsequenced_t</code> property may invoke the function object in an unordered fashion. Any such invocations in the same thread of execution are unsequenced with respect to each other. [<em>Note:</em> This means that multiple execution agents may be interleaved on a single thread of execution, which overrides the usual guarantee from [intro.execution] that function executions do not interleave with one another. <em>--end note</em>]</p>
<p>Execution agents associated with the <code>bulk_guarantee_t::sequenced_t</code> property invoke the function object in sequence in lexicographic order of their indices.</p>
<p>Execution agents associated with the <code>bulk_guarantee_t::parallel_t</code> property invoke the function object with a parallel forward progress guarantee. Any such invocations in the same thread of execution are indeterminately sequenced with respect to each other. [<em>Note:</em> It is the caller's responsibility to ensure that the invocation does not introduce data races or deadlocks. <em>--end note</em>]</p>
<p>[<em>Editorial note:</em> The descriptions of these properties were ported from [algorithms.parallel.user]. The intention is that a future standard will specify execution policy behavior in terms of the fundamental properties of their associated executors. We did not include the accompanying code examples from [algorithms.parallel.user] because the examples seem easier to understand when illustrated by <code>std::for_each</code>. <em>--end editorial note</em>]</p>
<h4 id="properties-for-mapping-of-execution-on-to-threads"><span class="header-section-number">1.4.5.6</span> Properties for mapping of execution on to threads</h4>
<p>The <code>mapping_t</code> property describes what guarantees executors provide about the mapping of execution agents onto threads of execution.</p>
<p><code>mapping_t</code> provides nested property types and objects as indicated below.</p>
<table>
<colgroup>
<col width="34%" />
<col width="45%" />
<col width="20%" />
</colgroup>
<thead>
<tr class="header">
<th align="left">Nested Property Type</th>
<th align="left">Nested Property Object Name</th>
<th align="left">Requirements</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td align="left"><code>mapping_t::thread_t</code></td>
<td align="left"><code>mapping::thread</code></td>
<td align="left">Execution agents are mapped onto threads of execution.</td>
</tr>
<tr class="even">
<td align="left"><code>mapping_t::new_thread_t</code></td>
<td align="left"><code>mapping::new_thread</code></td>
<td align="left">Each execution agent is mapped onto a new thread of execution.</td>
</tr>
<tr class="odd">
<td align="left"><code>mapping_t::other_t</code></td>
<td align="left"><code>mapping::other</code></td>
<td align="left">Mapping of each execution agent is implementation-defined.</td>
</tr>
</tbody>
</table>
<p>[<em>Note:</em> A mapping of an execution agent onto a thread of execution implies the execution agent runs as-if on a <code>std::thread</code>. Therefore, the facilities provided by <code>std::thread</code>, such as thread-local storage, are available. <code>mapping_t::new_thread_t</code> provides stronger guarantees, in particular that thread-local storage will not be shared between execution agents. <em>--end note</em>]</p>
<h3 id="properties-for-customizing-memory-allocation"><span class="header-section-number">1.4.6</span> Properties for customizing memory allocation</h3>
<pre><code>template &lt;typename ProtoAllocator&gt;
struct allocator_t;</code></pre>
<p>The <code>allocator_t</code> property conforms to the following specification:</p>
<pre><code>template &lt;typename ProtoAllocator&gt;
struct allocator_t
{
    static constexpr bool is_requirable = true;
    static constexpr bool is_preferable = true;

    template&lt;class Executor&gt;
    static constexpr auto static_query_v
      = Executor::query(allocator_t);

    template &lt;typename OtherProtoAllocator&gt;
    allocator_t&lt;OtherProtoAllocator&gt; operator()(const OtherProtoAllocator &amp;a) const;

    static constexpr ProtoAllocator value() const;

private:
    ProtoAllocator a_; // exposition only
};</code></pre>
<table style="width:47%;">
<colgroup>
<col width="15%" />
<col width="11%" />
<col width="20%" />
</colgroup>
<thead>
<tr class="header">
<th align="left">Property</th>
<th align="left">Notes</th>
<th align="left">Requirements</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td align="left"><code>allocator_t&lt;ProtoAllocator&gt;</code></td>
<td align="left">Result of <code>allocator_t&lt;void&gt;::operator(OtherProtoAllocator)</code>.</td>
<td align="left">For executor types that satisfy the <code>OneWayExecutor</code> requirements, the executor implementation shall use the encapsulated allocator to allocate any memory required to store the submitted function object.</td>
</tr>
<tr class="even">
<td align="left"><code>allocator_t&lt;void&gt;</code></td>
<td align="left">Specialisation of <code>allocator_t&lt;ProtoAllocator&gt;</code>.</td>
<td align="left">For executor types that satisfy the <code>OneWayExecutor</code> requirements, the executor implementation shall use an implementation defined default allocator to allocate any memory required to store the submitted function object.</td>
</tr>
</tbody>
</table>
<p>If the expression <code>execution::query(E, P)</code> is well formed, where <code>P</code> is an object of type <code>allocator_t&lt;ProtoAllocator&gt;</code>, then: * the type of the expression <code>execution::query(E, P)</code> shall satisfy the <code>ProtoAllocator</code> requirements; * the result of the expression <code>execution::query(E, P)</code> shall be the allocator currently established in the executor <code>E</code>; and * the expression <code>execution::query(E, allocator_t&lt;void&gt;{})</code> shall also be well formed and have the same result as <code>execution::query(E, P)</code>.</p>
<h4 id="allocator_t-members"><span class="header-section-number">1.4.6.1</span> <code>allocator_t</code> members</h4>
<pre><code>template &lt;typename OtherProtoAllocator&gt;
allocator_t&lt;OtherProtoAllocator&gt; operator()(const OtherProtoAllocator &amp;a) const;</code></pre>
<p><em>Returns:</em> An allocator object whose exposition-only member <code>a_</code> is initialized as <code>a_(a)</code>.</p>
<p><em>Remarks:</em> This function shall not participate in overload resolution unless <code>ProtoAllocator</code> is <code>void</code>.</p>
<p>[<em>Note:</em> It is permitted for <code>a</code> to be an executor's implementation-defined default allocator and, if so, the default allocator may also be established within an executor by passing the result of this function to <code>require</code>. <em>--end note</em>]</p>
<pre><code>static constexpr ProtoAllocator value() const;</code></pre>
<p><em>Returns:</em> The exposition-only member <code>a_</code>.</p>
<p><em>Remarks:</em> This function shall not participate in overload resolution unless <code>ProtoAllocator</code> is not <code>void</code>.</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_oneway_executor;
template&lt;class T&gt; struct is_bulk_oneway_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_oneway_executor</code></td>
<td align="left"><code>T</code> meets the syntactic requirements for <code>OneWayExecutor</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_oneway_executor</code></td>
<td align="left"><code>T</code> meets the syntactic requirements for <code>BulkOneWayExecutor</code>.</td>
<td align="left"><code>T</code> is a complete type.</td>
</tr>
</tbody>
</table>
<h3 id="associated-shape-type"><span class="header-section-number">1.5.2</span> Associated shape type</h3>
<pre><code>template&lt;class Executor&gt;
struct executor_shape
{
  private:
    // exposition only
    template&lt;class T&gt;
    using helper = typename T::shape_type;

  public:
    using type = std::experimental::detected_or_t&lt;
      size_t, helper, decltype(execution::require(declval&lt;const Executor&amp;&gt;(), execution::bulk))
    &gt;;

    // exposition only
    static_assert(std::is_integral_v&lt;type&gt;, &quot;shape type must be an integral type&quot;);
};</code></pre>
<h3 id="associated-index-type"><span class="header-section-number">1.5.3</span> Associated index type</h3>
<pre><code>template&lt;class Executor&gt;
struct executor_index
{
  private:
    // exposition only
    template&lt;class T&gt;
    using helper = typename T::index_type;

  public:
    using type = std::experimental::detected_or_t&lt;
      executor_shape_t&lt;Executor&gt;, helper, decltype(execution::require(declval&lt;const Executor&amp;&gt;(), execution::bulk))
    &gt;;

    // exposition only
    static_assert(std::is_integral_v&lt;type&gt;, &quot;index type must be an integral type&quot;);
};</code></pre>
<h2 id="polymorphic-executor-support"><span class="header-section-number">1.6</span> Polymorphic executor support</h2>
<h3 id="class-bad_executor"><span class="header-section-number">1.6.1</span> Class <code>bad_executor</code></h3>
<p>An exception of type <code>bad_executor</code> is thrown by polymorphic executor member functions <code>execute</code> and <code>bulk_execute</code> when the executor object has no target.</p>
<pre><code>class bad_executor : public exception
{
public:
  // constructor:
  bad_executor() noexcept;
};</code></pre>
<h4 id="bad_executor-constructors"><span class="header-section-number">1.6.1.1</span> <code>bad_executor</code> constructors</h4>
<pre><code>bad_executor() noexcept;</code></pre>
<p><em>Effects:</em> Constructs a <code>bad_executor</code> object.</p>
<p><em>Postconditions:</em> <code>what()</code> returns an implementation-defined NTBS.</p>
<h3 id="struct-prefer_only"><span class="header-section-number">1.6.2</span> Struct <code>prefer_only</code></h3>
<p>The <code>prefer_only</code> struct is a property adapter that disables the <code>is_requirable</code> value.</p>
<p>[<em>Example:</em></p>
<p>Consider a generic function that performs some task immediately if it can, and otherwise asynchronously in the background.</p>
<pre><code>template&lt;class Executor&gt;
void do_async_work(
    Executor ex,
    Callback callback)
{
  if (try_work() == done)
  {
    // Work completed immediately, invoke callback.
    execution::require(ex,
        execution::single,
        execution::oneway,
      ).execute(callback);
  }
  else
  {
    // Perform work in background. Track outstanding work.
    start_background_work(
        execution::prefer(ex,
          execution::outstanding_work.tracked),
        callback);
  }
}</code></pre>
<p>This function can be used with an inline executor which is defined as follows:</p>
<pre><code>struct inline_executor
{
  constexpr bool operator==(const inline_executor&amp;) const noexcept
  {
    return true;
  }

  constexpr bool operator!=(const inline_executor&amp;) const noexcept
  {
    return false;
  }

  template&lt;class Function&gt; void execute(Function f) const noexcept
  {
    f();
  }
};</code></pre>
<p>as, in the case of an unsupported property, invocation of <code>execution::prefer</code> will fall back to an identity operation.</p>
<p>The polymorphic <code>executor</code> wrapper should be able to simply swap in, so that we could change <code>do_async_work</code> to the non-template function:</p>
<pre><code>void do_async_work(
    executor&lt;
      execution::single,
      execution::oneway,
      execution::outstanding_work_t::tracked_t&gt; ex,
    std::function&lt;void()&gt; callback)
{
  if (try_work() == done)
  {
    // Work completed immediately, invoke callback.
    execution::require(ex,
        execution::single,
        execution::oneway,
      ).execute(callback);
  }
  else
  {
    // Perform work in background. Track outstanding work.
    start_background_work(
        execution::prefer(ex,
          execution::outstanding_work.tracked),
        callback);
  }
}</code></pre>
<p>with no change in behavior or semantics.</p>
<p>However, if we simply specify <code>execution::outstanding_work.tracked</code> in the <code>executor</code> template parameter list, we will get a compile error due to the <code>executor</code> template not knowing that <code>execution::outstanding_work.tracked</code> is intended for use with <code>prefer</code> only. At the point of construction from an <code>inline_executor</code> called <code>ex</code>, <code>executor</code> will try to instantiate implementation templates that perform the ill-formed <code>execution::require(ex, execution::outstanding_work.tracked)</code>.</p>
<p>The <code>prefer_only</code> adapter addresses this by turning off the <code>is_requirable</code> attribute for a specific property. It would be used in the above example as follows:</p>
<pre><code>void do_async_work(
    executor&lt;
      execution::single,
      execution::oneway,
      prefer_only&lt;execution::outstanding_work_t::tracked_t&gt;&gt; ex,
    std::function&lt;void()&gt; callback)
{
  ...
}</code></pre>
<p><em>-- end example</em>]</p>
<pre><code>template&lt;class InnerProperty&gt;
struct prefer_only
{
  InnerProperty property;

  static constexpr bool is_requirable = false;
  static constexpr bool is_preferable = InnerProperty::is_preferable;

  using polymorphic_query_result_type = see-below; // not always defined

  template&lt;class Executor&gt;
    static constexpr auto static_query_v = see-below; // not always defined

  constexpr prefer_only(const InnerProperty&amp; p);

  constexpr auto value() const
    noexcept(noexcept(std::declval&lt;const InnerProperty&gt;().value()))
      -&gt; decltype(std::declval&lt;const InnerProperty&gt;().value());

  template&lt;class Executor, class Property&gt;
  friend auto prefer(Executor ex, const Property&amp; p)
    noexcept(noexcept(execution::prefer(std::move(ex), std::declval&lt;const InnerProperty&gt;())))
      -&gt; decltype(execution::prefer(std::move(ex), std::declval&lt;const InnerProperty&gt;()));

  template&lt;class Executor, class Property&gt;
  friend constexpr auto query(const Executor&amp; ex, const Property&amp; p)
    noexcept(noexcept(execution::query(ex, std::declval&lt;const InnerProperty&gt;())))
      -&gt; decltype(execution::query(ex, std::declval&lt;const InnerProperty&gt;()));
};</code></pre>
<p>If <code>InnerProperty::polymorphic_query_result_type</code> is valid and denotes a type, the template instantiation <code>prefer_only&lt;InnerProperty&gt;</code> defines a nested type <code>polymorphic_query_result_type</code> as a synonym for <code>InnerProperty::polymorphic_query_result_type</code>.</p>
<p>If <code>InnerProperty::static_query_v</code> is a variable template and <code>InnerProperty::static_query_v&lt;E&gt;</code> is well formed for some executor type <code>E</code>, the template instantiation <code>prefer_only&lt;InnerProperty&gt;</code> defines a nested variable template <code>static_query_v</code> as a synonym for <code>InnerProperty::static_query_v</code>.</p>
<pre><code>constexpr prefer_only(const InnerProperty&amp; p);</code></pre>
<p><em>Effects:</em> Initializes <code>property</code> with <code>p</code>.</p>
<pre><code>constexpr auto value() const
  noexcept(noexcept(std::declval&lt;const InnerProperty&gt;().value()))
    -&gt; decltype(std::declval&lt;const InnerProperty&gt;().value());</code></pre>
<p><em>Returns:</em> <code>property.value()</code>.</p>
<p><em>Remarks:</em> Shall not participate in overload resolution unless the expression <code>property.value()</code> is well-formed.</p>
<pre><code>template&lt;class Executor, class Property&gt;
friend auto prefer(Executor ex, const Property&amp; p)
  noexcept(noexcept(execution::prefer(std::move(ex), std::declval&lt;const InnerProperty&gt;())))
    -&gt; decltype(execution::prefer(std::move(ex), std::declval&lt;const InnerProperty&gt;()));</code></pre>
<p><em>Returns:</em> <code>execution::prefer(std::move(ex), p.property)</code>.</p>
<p><em>Remarks:</em> Shall not participate in overload resolution unless <code>std::is_same_v&lt;Property, prefer_only&gt;</code> is <code>true</code>, and the expression <code>execution::prefer(std::move(ex), p.property)</code> is well-formed.</p>
<pre><code>template&lt;class Executor, class Property&gt;
friend constexpr auto query(const Executor&amp; ex, const Property&amp; p)
  noexcept(noexcept(execution::query(ex, std::declval&lt;const InnerProperty&gt;())))
    -&gt; decltype(execution::query(ex, std::declval&lt;const InnerProperty&gt;()));</code></pre>
<p><em>Returns:</em> <code>execution::query(ex, p.property)</code>.</p>
<p><em>Remarks:</em> Shall not participate in overload resolution unless <code>std::is_same_v&lt;Property, prefer_only&gt;</code> is <code>true</code>, and the expression <code>execution::query(ex, p.property)</code> is well-formed.</p>
<h2 id="thread-pools"><span class="header-section-number">1.7</span> Thread pools</h2>
<p>Thread pools manage execution agents which run on threads without incurring the overhead of thread creation and destruction whenever such agents are needed.</p>
<h3 id="header-thread_pool-synopsis"><span class="header-section-number">1.7.1</span> Header <code>&lt;thread_pool&gt;</code> synopsis</h3>
<pre><code>namespace std {

  class static_thread_pool;

} // namespace std</code></pre>
<h3 id="class-static_thread_pool"><span class="header-section-number">1.7.2</span> Class <code>static_thread_pool</code></h3>
<p><code>static_thread_pool</code> is a statically-sized thread pool which may be explicitly grown via thread attachment. The <code>static_thread_pool</code> is expected to be created with the use case clearly in mind with the number of threads known by the creator. As a result, no default constructor is considered correct for arbitrary use cases and <code>static_thread_pool</code> does not support any form of automatic resizing.</p>
<p><code>static_thread_pool</code> presents an effectively unbounded input queue and the execution functions of <code>static_thread_pool</code>'s associated executors do not block on this input queue.</p>
<p>[<em>Note:</em> Because <code>static_thread_pool</code> represents work as parallel execution agents, situations which require concurrent execution properties are not guaranteed correctness. <em>--end note.</em>]</p>
<pre><code>class static_thread_pool
{
  public:
    using executor_type = see-below;
    
    // construction/destruction
    explicit static_thread_pool(std::size_t num_threads);
    
    // nocopy
    static_thread_pool(const static_thread_pool&amp;) = delete;
    static_thread_pool&amp; operator=(const static_thread_pool&amp;) = delete;

    // stop accepting incoming work and wait for work to drain
    ~static_thread_pool();

    // attach current thread to the thread pools list of worker threads
    void attach();

    // signal all work to complete
    void stop();

    // wait for all threads in the thread pool to complete
    void wait();

    // placeholder for a general approach to getting executors from 
    // standard contexts.
    executor_type executor() noexcept;
};</code></pre>
<p>For an object of type <code>static_thread_pool</code>, <em>outstanding work</em> is defined as the sum of:</p>
<ul>
<li><p>the number of existing executor objects associated with the <code>static_thread_pool</code> for which the <code>execution::outstanding_work.tracked</code> property is established;</p></li>
<li><p>the number of function objects that have been added to the <code>static_thread_pool</code> via the <code>static_thread_pool</code> executor, but not yet invoked; and</p></li>
<li><p>the number of function objects that are currently being invoked within the <code>static_thread_pool</code>.</p></li>
</ul>
<p>The <code>static_thread_pool</code> member functions <code>executor</code>, <code>attach</code>, <code>wait</code>, and <code>stop</code>, and the associated executors' copy constructors and member functions, do not introduce data races as a result of concurrent invocations of those functions from different threads of execution.</p>
<p>A <code>static_thread_pool</code>'s threads run execution agents with forward progress guarantee delegation. [<em>Note:</em> Forward progress is delegated to an execution agent for its lifetime. Because <code>static_thread_pool</code> guarantees only parallel forward progress to running execution agents; <em>i.e.</em>, execution agents which have run the first step of the function object. <em>--end note</em>]</p>
<h4 id="types"><span class="header-section-number">1.7.2.1</span> Types</h4>
<pre><code>using executor_type = see-below;</code></pre>
<p>An executor type conforming to the specification for <code>static_thread_pool</code> executor types described below.</p>
<h4 id="construction-and-destruction"><span class="header-section-number">1.7.2.2</span> Construction and destruction</h4>
<pre><code>static_thread_pool(std::size_t num_threads);</code></pre>
<p><em>Effects:</em> Constructs a <code>static_thread_pool</code> object with <code>num_threads</code> threads of execution, as if by creating objects of type <code>std::thread</code>.</p>
<pre><code>~static_thread_pool();</code></pre>
<p><em>Effects:</em> Destroys an object of class <code>static_thread_pool</code>. Performs <code>stop()</code> followed by <code>wait()</code>.</p>
<h4 id="worker-management"><span class="header-section-number">1.7.2.3</span> Worker management</h4>
<pre><code>void attach();</code></pre>
<p><em>Effects:</em> Adds the calling thread to the pool such that this thread is used to execute submitted function objects. [<em>Note:</em> Threads created during thread pool construction, or previously attached to the pool, will continue to be used for function object execution. <em>--end note</em>] Blocks the calling thread until signalled to complete by <code>stop()</code> or <code>wait()</code>, and then blocks until all the threads created during <code>static_thread_pool</code> object construction have completed. (NAMING: a possible alternate name for this function is <code>join()</code>.)</p>
<pre><code>void stop();</code></pre>
<p><em>Effects:</em> Signals the threads in the pool to complete as soon as possible. If a thread is currently executing a function object, the thread will exit only after completion of that function object. Invocation of <code>stop()</code> returns without waiting for the threads to complete. Subsequent invocations to attach complete immediately.</p>
<pre><code>void wait();</code></pre>
<p><em>Effects:</em> If not already stopped, signals the threads in the pool to complete once the outstanding work is <code>0</code>. Blocks the calling thread (C++Std [defns.block]) until all threads in the pool have completed, without executing submitted function objects in the calling thread. Subsequent invocations of <code>attach()</code> complete immediately.</p>
<p><em>Synchronization:</em> The completion of each thread in the pool synchronizes with (C++Std [intro.multithread]) the corresponding successful <code>wait()</code> return.</p>
<h4 id="executor-creation"><span class="header-section-number">1.7.2.4</span> Executor creation</h4>
<pre><code>executor_type executor() noexcept;</code></pre>
<p><em>Returns:</em> An executor that may be used to submit function objects to the thread pool. The returned executor has the following properties already established:</p>
<ul>
<li><code>execution::oneway</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::allocator</code></li>
<li><code>execution::allocator(std::allocator&lt;void&gt;())</code></li>
</ul>
<h3 id="static_thread_pool-executor-types"><span class="header-section-number">1.7.3</span> <code>static_thread_pool</code> executor types</h3>
<p>All executor types accessible through <code>static_thread_pool::executor()</code>, and subsequent invocations of the member function <code>require</code>, conform to the following specification.</p>
<pre><code>class C
{
  public:
    // types:

    typedef std::size_t shape_type;
    typedef std::size_t index_type;

    // construct / copy / destroy:

    C(const C&amp; other) noexcept;
    C(C&amp;&amp; other) noexcept;

    C&amp; operator=(const C&amp; other) noexcept;
    C&amp; operator=(C&amp;&amp; other) noexcept;

    // executor operations:

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

    static constexpr execution::bulk_guarantee_t query(execution::bulk_guarantee_t::parallel_t) const;
    static constexpr execution::mapping_t query(execution::mapping_t::thread_t) const;
    execution::blocking_t query(execution::blocking_t) const;
    execution::relationship_t query(execution::relationship_t) const;
    execution::outstanding_work_t query(execution::outstanding_work_t) const;
    see-below query(execution::context_t) const noexcept;
    see-below query(execution::allocator_t&lt;void&gt;) const noexcept;
    template&lt;class ProtoAllocator&gt;
    see-below query(execution::allocator_t&lt;ProtoAllocator&gt;) const noexcept;

    bool running_in_this_thread() const noexcept;
};

bool operator==(const C&amp; a, const C&amp; b) noexcept;
bool operator!=(const C&amp; a, const C&amp; b) noexcept;</code></pre>
<p>Objects of type <code>C</code> are associated with a <code>static_thread_pool</code>.</p>
<h4 id="constructors"><span class="header-section-number">1.7.3.1</span> Constructors</h4>
<pre><code>C(const C&amp; other) noexcept;</code></pre>
<p><em>Postconditions:</em> <code>*this == other</code>.</p>
<pre><code>C(C&amp;&amp; other) noexcept;</code></pre>
<p><em>Postconditions:</em> <code>*this</code> is equal to the prior value of <code>other</code>.</p>
<h4 id="assignment"><span class="header-section-number">1.7.3.2</span> Assignment</h4>
<pre><code>C&amp; operator=(const C&amp; other) noexcept;</code></pre>
<p><em>Postconditions:</em> <code>*this == other</code>.</p>
<p><em>Returns:</em> <code>*this</code>.</p>
<pre><code>C&amp; operator=(C&amp;&amp; other) noexcept;</code></pre>
<p><em>Postconditions:</em> <code>*this</code> is equal to the prior value of <code>other</code>.</p>
<p><em>Returns:</em> <code>*this</code>.</p>
<h4 id="operations"><span class="header-section-number">1.7.3.3</span> Operations</h4>
<pre><code>see-below require(execution::blocking_t::never_t) const;
see-below require(execution::blocking_t::possibly_t) const;
see-below require(execution::blocking_t::always_t) const;
see-below require(execution::relationship_t::continuation_t) const;
see-below require(execution::relationship_t::fork_t) const;
see-below require(execution::outstanding_work_t::tracked_t) const;
see-below require(execution::outstanding_work_t::untracked_t) const;</code></pre>
<p><em>Returns:</em> An executor object of an unspecified type conforming to these specifications, associated with the same thread pool 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><code>see-below require(const execution::allocator_t&lt;void&gt;&amp; a) const;</code></pre>
<p><em>Returns:</em> <code>require(execution::allocator(x))</code>, where <code>x</code> is an implementation-defined default allocator.</p>
<pre><code>template&lt;class ProtoAllocator&gt;
  see-below require(const execution::allocator_t&lt;ProtoAllocator&gt;&amp; a) const;</code></pre>
<p><em>Returns:</em> An executor object of an unspecified type conforming to these specifications, associated with the same thread pool 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.alloc</code>. All other properties of the returned executor object are identical to those of <code>*this</code>.</p>
<pre><code>static constexpr execution::bulk_guarantee_t query(execution::bulk_guarantee_t) const;</code></pre>
<p><em>Returns:</em> <code>execution::bulk_guarantee.parallel</code></p>
<pre><code>static constexpr execution::mapping_t query(execution::mapping_t) const;</code></pre>
<p><em>Returns:</em> <code>execution::mapping.thread</code>.</p>
<pre><code>execution::blocking_t query(execution::blocking_t) const;
execution::relationship_t query(execution::relationship_t) const;
execution::outstanding_work_t query(execution::outstanding_work_t) const;</code></pre>
<p><em>Returns:</em> The value of the given property of <code>*this</code>.</p>
<pre><code>static_thread_pool&amp; query(execution::context_t) const noexcept;</code></pre>
<p><em>Returns:</em> A reference to the associated <code>static_thread_pool</code> object.</p>
<pre><code>see-below query(execution::allocator_t&lt;void&gt;) const noexcept;
see-below query(execution::allocator_t&lt;ProtoAllocator&gt;) const noexcept;</code></pre>
<p><em>Returns:</em> The allocator object associated with the executor, with type and value as either previously established by the <code>execution::allocator_t&lt;ProtoAllocator&gt;</code> property or the implementation defined default allocator established by the <code>execution::allocator_t&lt;void&gt;</code> property.</p>
<pre><code>bool running_in_this_thread() const noexcept;</code></pre>
<p><em>Returns:</em> <code>true</code> if the current thread of execution is a thread that was created by or attached to the associated <code>static_thread_pool</code> object.</p>
<h4 id="comparisons"><span class="header-section-number">1.7.3.4</span> Comparisons</h4>
<pre><code>bool operator==(const C&amp; a, const C&amp; b) noexcept;</code></pre>
<p><em>Returns:</em> <code>true</code> if <code>&amp;a.query(execution::context) == &amp;b.query(execution::context)</code> and <code>a</code> and <code>b</code> have identical properties, otherwise <code>false</code>.</p>
<pre><code>bool operator!=(const C&amp; a, const C&amp; b) noexcept;</code></pre>
<p><em>Returns:</em> <code>!(a == b)</code>.</p>
<h4 id="static_thread_pool-executor-types-with-the-executiononeway-property"><span class="header-section-number">1.7.3.5</span> <code>static_thread_pool</code> executor types with the <code>execution::oneway</code> property</h4>
<p>In addition to conforming to the above specification, <code>static_thread_pool</code> executors having the <code>execution::oneway</code> property established shall conform to the following specification.</p>
<pre><code>class C
{
  public:
    template&lt;class Function&gt;
      void execute(Function&amp;&amp; f) const;
};</code></pre>
<p><code>C</code> is a type satisfying the <code>OneWayExecutor</code> requirements.</p>
<pre><code>template&lt;class Function&gt;
  void execute(Function&amp;&amp; f) const;</code></pre>
<p><em>Effects:</em> Submits the function <code>f</code> for execution on the <code>static_thread_pool</code> according to the <code>OneWayExecutor</code> requirements and the properties established for <code>*this</code>. If the submitted function <code>f</code> exits via an exception, the <code>static_thread_pool</code> invokes <code>std::terminate()</code>.</p>
<h4 id="static_thread_pool-executor-types-with-the-executionbulk_oneway-property"><span class="header-section-number">1.7.3.6</span> <code>static_thread_pool</code> executor types with the <code>execution::bulk_oneway</code> property</h4>
<p>In addition to conforming to the above specification, <code>static_thread_pool</code> executors having the <code>execution::bulk_oneway</code> property established shall conform to the following specification.</p>
<pre><code>class C
{
  public:
    template&lt;class Function, class SharedFactory&gt;
      void bulk_execute(Function&amp;&amp; f, size_t n, SharedFactory&amp;&amp; sf) const;
};</code></pre>
<p><code>C</code> is a type satisfying the <code>BulkOneWayExecutor</code> requirements.</p>
<pre><code>template&lt;class Function, class SharedFactory&gt;
  void bulk_execute(Function&amp;&amp; f, size_t n, SharedFactory&amp;&amp; sf) const;</code></pre>
<p><em>Effects:</em> Submits the function <code>f</code> for bulk execution on the <code>static_thread_pool</code> according to the <code>BulkOneWayExecutor</code> requirements and the properties established for <code>*this</code>. If the submitted function <code>f</code> exits via an exception, the <code>static_thread_pool</code> invokes <code>std::terminate()</code>.</p>
