<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" lang="" xml:lang="">
<head>
  <meta charset="utf-8" />
  <meta name="generator" content="pandoc" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes" />
  <title>A Unified Executors Proposal for C++ | P0443R13</title>
  <style>
    code{white-space: pre-wrap;}
    span.smallcaps{font-variant: small-caps;}
    span.underline{text-decoration: underline;}
    div.column{display: inline-block; vertical-align: top; width: 50%;}
    div.hanging-indent{margin-left: 1.5em; text-indent: -1.5em;}
    ul.task-list{list-style: none;}
    pre > code.sourceCode { white-space: pre; position: relative; }
    pre > code.sourceCode > span { display: inline-block; line-height: 1.25; }
    pre > code.sourceCode > span:empty { height: 1.2em; }
    code.sourceCode > span { color: inherit; text-decoration: inherit; }
    div.sourceCode { margin: 1em 0; }
    pre.sourceCode { margin: 0; }
    @media screen {
    div.sourceCode { overflow: auto; }
    }
    @media print {
    pre > code.sourceCode { white-space: pre-wrap; }
    pre > code.sourceCode > span { text-indent: -5em; padding-left: 5em; }
    }
    pre.numberSource code
      { counter-reset: source-line 0; }
    pre.numberSource code > span
      { position: relative; left: -4em; counter-increment: source-line; }
    pre.numberSource code > span > a:first-child::before
      { content: counter(source-line);
        position: relative; left: -1em; text-align: right; vertical-align: baseline;
        border: none; display: inline-block;
        -webkit-touch-callout: none; -webkit-user-select: none;
        -khtml-user-select: none; -moz-user-select: none;
        -ms-user-select: none; user-select: none;
        padding: 0 4px; width: 4em;
        color: #aaaaaa;
      }
    pre.numberSource { margin-left: 3em; border-left: 1px solid #aaaaaa;  padding-left: 4px; }
    div.sourceCode
      {   }
    @media screen {
    pre > code.sourceCode > span > a:first-child::before { text-decoration: underline; }
    }
    code span.al { color: #ff0000; } /* Alert */
    code span.an { color: #008000; } /* Annotation */
    code span.at { } /* Attribute */
    code span.bu { } /* BuiltIn */
    code span.cf { color: #0000ff; } /* ControlFlow */
    code span.ch { color: #008080; } /* Char */
    code span.cn { } /* Constant */
    code span.co { color: #008000; } /* Comment */
    code span.cv { color: #008000; } /* CommentVar */
    code span.do { color: #008000; } /* Documentation */
    code span.er { color: #ff0000; font-weight: bold; } /* Error */
    code span.ex { } /* Extension */
    code span.im { } /* Import */
    code span.in { color: #008000; } /* Information */
    code span.kw { color: #0000ff; } /* Keyword */
    code span.op { } /* Operator */
    code span.ot { color: #ff4000; } /* Other */
    code span.pp { color: #ff4000; } /* Preprocessor */
    code span.sc { color: #008080; } /* SpecialChar */
    code span.ss { color: #008080; } /* SpecialString */
    code span.st { color: #008080; } /* String */
    code span.va { } /* Variable */
    code span.vs { color: #008080; } /* VerbatimString */
    code span.wa { color: #008000; font-weight: bold; } /* Warning */
  </style>
  <!--[if lt IE 9]>
    <script src="//cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv-printshiv.min.js"></script>
  <![endif]-->
</head>
<body>
<header id="title-block-header">
<h1 class="title">A Unified Executors Proposal for C++ | P0443R13</h1>
</header>
<table style="width:81%;">
<colgroup>
<col style="width: 27%" />
<col style="width: 52%" />
</colgroup>
<tbody>
<tr class="odd">
<td style="text-align: left;">Title:</td>
<td style="text-align: left;">A Unified Executors Proposal for C++</td>
</tr>
<tr class="even">
<td style="text-align: left;">Authors:</td>
<td style="text-align: left;">Jared Hoberock, jhoberock@nvidia.com</td>
</tr>
<tr class="odd">
<td style="text-align: left;"></td>
<td style="text-align: left;">Michael Garland, mgarland@nvidia.com</td>
</tr>
<tr class="even">
<td style="text-align: left;"></td>
<td style="text-align: left;">Chris Kohlhoff, chris@kohlhoff.com</td>
</tr>
<tr class="odd">
<td style="text-align: left;"></td>
<td style="text-align: left;">Chris Mysen, mysen@google.com</td>
</tr>
<tr class="even">
<td style="text-align: left;"></td>
<td style="text-align: left;">Carter Edwards, hcedwar@sandia.gov</td>
</tr>
<tr class="odd">
<td style="text-align: left;"></td>
<td style="text-align: left;">Gordon Brown, gordon@codeplay.com</td>
</tr>
<tr class="even">
<td style="text-align: left;"></td>
<td style="text-align: left;">David Hollman, dshollm@sandia.gov</td>
</tr>
<tr class="odd">
<td style="text-align: left;"></td>
<td style="text-align: left;">Lee Howes, lwh@fb.com</td>
</tr>
<tr class="even">
<td style="text-align: left;"></td>
<td style="text-align: left;">Kirk Shoop, kirkshoop@fb.com</td>
</tr>
<tr class="odd">
<td style="text-align: left;"></td>
<td style="text-align: left;">Lewis Baker, lbaker@fb.com</td>
</tr>
<tr class="even">
<td style="text-align: left;"></td>
<td style="text-align: left;">Eric Niebler, eniebler@fb.com</td>
</tr>
<tr class="odd">
<td style="text-align: left;">Other Contributors:</td>
<td style="text-align: left;">Hans Boehm, hboehm@google.com</td>
</tr>
<tr class="even">
<td style="text-align: left;"></td>
<td style="text-align: left;">Thomas Heller, thom.heller@gmail.com</td>
</tr>
<tr class="odd">
<td style="text-align: left;"></td>
<td style="text-align: left;">Bryce Lelbach, brycelelbach@gmail.com</td>
</tr>
<tr class="even">
<td style="text-align: left;"></td>
<td style="text-align: left;">Hartmut Kaiser, hartmut.kaiser@gmail.com</td>
</tr>
<tr class="odd">
<td style="text-align: left;"></td>
<td style="text-align: left;">Bryce Lelbach, brycelelbach@gmail.com</td>
</tr>
<tr class="even">
<td style="text-align: left;"></td>
<td style="text-align: left;">Gor Nishanov, gorn@microsoft.com</td>
</tr>
<tr class="odd">
<td style="text-align: left;"></td>
<td style="text-align: left;">Thomas Rodgers, rodgert@twrodgers.com</td>
</tr>
<tr class="even">
<td style="text-align: left;"></td>
<td style="text-align: left;">Michael Wong, michael@codeplay.com</td>
</tr>
<tr class="odd">
<td style="text-align: left;">Document Number:</td>
<td style="text-align: left;">P0443R13</td>
</tr>
<tr class="even">
<td style="text-align: left;">Date:</td>
<td style="text-align: left;">2020-03-02</td>
</tr>
<tr class="odd">
<td style="text-align: left;">Audience:</td>
<td style="text-align: left;">SG1 - Concurrency and Parallelism, LEWG</td>
</tr>
<tr class="even">
<td style="text-align: left;">Reply-to:</td>
<td style="text-align: left;">sg1-exec@googlegroups.com</td>
</tr>
<tr class="odd">
<td style="text-align: left;">Abstract:</td>
<td style="text-align: left;">This paper proposes <a href="#proposed-wording">a programming model</a> for executors, which are modular components for creating execution, and senders, which are lazy descriptions of execution.</td>
</tr>
</tbody>
</table>
<section id="design-document" data-number="1">
<h1 data-number="1"><span class="header-section-number">1</span> Design Document</h1>
<section id="motivation" data-number="1.1">
<h2 data-number="1.1"><span class="header-section-number">1.1</span> Motivation</h2>
<p>When we imagine the future of C++ programs, we envision elegant compositions of networked, asynchronous parallel computations accelerated by diverse hardware, ranging from tiny mobile devices to giant supercomputers. In the present, hardware diversity is greater than ever, but C++ programmers lack satisfying parallel programming tools for them. Industrial-strength concurrency primitives like <code>std::thread</code> and <code>std::atomic</code> are powerful but hazardous. <code>std::async</code> and <code>std::future</code> suffer from well-known problems. And the standard algorithms library, though parallelized, remains inflexible and non-composable.</p>
<p>To address these temporary challenges and build toward the future, C++ must lay a foundation for controlling program execution. First, <strong>C++ must provide flexible facilities to control where and when work happens.</strong> This paper proposes a design for those facilities. After <a href="#appendix-executors-bibilography">much discussion and collaboration</a>, SG1 adopted this design by universal consensus at the Cologne meeting in 2019.</p>
</section>
<section id="usage-example" data-number="1.2">
<h2 data-number="1.2"><span class="header-section-number">1.2</span> Usage Example</h2>
<p>This proposal defines requirements for two key components of execution: a work execution interface and a representation of work and their interrelationships. Respectively, these are <strong>executors</strong> and <strong>senders and receivers</strong>:</p>
<div class="sourceCode" id="cb1"><pre class="sourceCode p0443"><code class="sourceCode p0443syntax"><span id="cb1-1"><a href="#cb1-1"></a>// make P0443 APIs in namespace std::execution available</span>
<span id="cb1-2"><a href="#cb1-2"></a>using namespace std::execution;</span>
<span id="cb1-3"><a href="#cb1-3"></a></span>
<span id="cb1-4"><a href="#cb1-4"></a>// get an executor from somewhere, e.g. a thread pool</span>
<span id="cb1-5"><a href="#cb1-5"></a>std::<span class="kw">static_thread_pool</span> pool(16);</span>
<span id="cb1-6"><a href="#cb1-6"></a><span class="kw">executor</span> auto ex = pool.<span class="kw">executor</span>();</span>
<span id="cb1-7"><a href="#cb1-7"></a></span>
<span id="cb1-8"><a href="#cb1-8"></a>// use the executor to describe where some high-level library should execute its work</span>
<span id="cb1-9"><a href="#cb1-9"></a>perform_business_logic(ex);</span>
<span id="cb1-10"><a href="#cb1-10"></a></span>
<span id="cb1-11"><a href="#cb1-11"></a>// alternatively, use primitive P0443 APIs directly</span>
<span id="cb1-12"><a href="#cb1-12"></a></span>
<span id="cb1-13"><a href="#cb1-13"></a>// immediately submit work to the pool</span>
<span id="cb1-14"><a href="#cb1-14"></a><span class="kw">execute</span>(ex, []{ std::cout &lt;&lt; &quot;Hello world from the thread pool!&quot;; });</span>
<span id="cb1-15"><a href="#cb1-15"></a></span>
<span id="cb1-16"><a href="#cb1-16"></a>// immediately submit work to the pool and require this thread to block until completion</span>
<span id="cb1-17"><a href="#cb1-17"></a><span class="kw">execute</span>(std::require(ex, <span class="kw">blocking</span>.<span class="kw">always</span>), foo);</span>
<span id="cb1-18"><a href="#cb1-18"></a></span>
<span id="cb1-19"><a href="#cb1-19"></a>// describe a chain of dependent work to submit later</span>
<span id="cb1-20"><a href="#cb1-20"></a><span class="kw">sender</span> auto begin    = <span class="kw">schedule</span>(ex);</span>
<span id="cb1-21"><a href="#cb1-21"></a><span class="kw">sender</span> auto hi_again = <span class="st">then</span>(begin, []{ std::cout &lt;&lt; &quot;Hi again! Have an int.&quot;; return 13; });</span>
<span id="cb1-22"><a href="#cb1-22"></a><span class="kw">sender</span> auto work     = <span class="st">then</span>(hi_again, [](int arg) { return arg + 42; });</span>
<span id="cb1-23"><a href="#cb1-23"></a></span>
<span id="cb1-24"><a href="#cb1-24"></a>// prints the final result</span>
<span id="cb1-25"><a href="#cb1-25"></a><span class="kw">receiver</span> auto print_result = <span class="st">as_receiver</span>([](int arg) { std::cout &lt;&lt; &quot;Received &quot; &lt;&lt; std::endl; });</span>
<span id="cb1-26"><a href="#cb1-26"></a></span>
<span id="cb1-27"><a href="#cb1-27"></a>// submit the work for execution on the pool by combining with the receiver </span>
<span id="cb1-28"><a href="#cb1-28"></a><span class="kw">submit</span>(work, print_result);</span>
<span id="cb1-29"><a href="#cb1-29"></a></span>
<span id="cb1-30"><a href="#cb1-30"></a>// Blue: proposed by P0443. Teal: possible extensions.</span></code></pre></div>
</section>
<section id="executors-execute-work" data-number="1.3">
<h2 data-number="1.3"><span class="header-section-number">1.3</span> Executors Execute Work</h2>
<p>As lightweight handles, executors impose uniform access to execution contexts.</p>
<p>Executors provide a uniform interface for work creation by abstracting underlying resources where work physically executes. The previous code example’s underlying resource was a thread pool. Other examples include SIMD units, GPU runtimes, or simply the current thread. In general, we call such resources <strong>execution contexts</strong>. As lightweight handles, executors impose uniform access to execution contexts. Uniformity enables control over where work executes, even when it is executed indirectly behind library interfaces.</p>
<p>The basic executor interface is the <code>execute</code> function through which clients execute work:</p>
<div class="sourceCode" id="cb2"><pre class="sourceCode p0443"><code class="sourceCode p0443syntax"><span id="cb2-1"><a href="#cb2-1"></a>// obtain an executor</span>
<span id="cb2-2"><a href="#cb2-2"></a><span class="kw">executor</span> auto ex = ...</span>
<span id="cb2-3"><a href="#cb2-3"></a></span>
<span id="cb2-4"><a href="#cb2-4"></a>// define our work as a nullary invocable</span>
<span id="cb2-5"><a href="#cb2-5"></a>invocable auto work = []{ cout &lt;&lt; &quot;My work&quot; &lt;&lt; endl; };</span>
<span id="cb2-6"><a href="#cb2-6"></a></span>
<span id="cb2-7"><a href="#cb2-7"></a>// execute our work via the execute customization point</span>
<span id="cb2-8"><a href="#cb2-8"></a><span class="kw">execute</span>(ex, work);</span></code></pre></div>
<p>On its own, <code>execute</code> is a primitive “fire-and-forget”-style interface. It accepts a single nullary invocable, and returns nothing to identify or interact with the work it creates. In this way, it trades convenience for universality. As a consequence, we expect most programmers to interact with executors via more convenient higher-level libraries, our envisioned asynchronous STL being such an example.</p>
<p>Consider how <code>std::async</code> could be extended to interoperate with executors enabling client control over execution:</p>
<div class="sourceCode" id="cb3"><pre class="sourceCode p0443"><code class="sourceCode p0443syntax"><span id="cb3-1"><a href="#cb3-1"></a>template&lt;class Executor, class F, class Args...&gt;</span>
<span id="cb3-2"><a href="#cb3-2"></a>future&lt;invoke_result_t&lt;F,Args...&gt;&gt; async(const Executor&amp; ex, F&amp;&amp; f, Args&amp;&amp;... args) {</span>
<span id="cb3-3"><a href="#cb3-3"></a>  // package up the work</span>
<span id="cb3-4"><a href="#cb3-4"></a>  packaged_task work(forward&lt;F&gt;(f), forward&lt;Args&gt;(args)...);</span>
<span id="cb3-5"><a href="#cb3-5"></a></span>
<span id="cb3-6"><a href="#cb3-6"></a>  // get the future</span>
<span id="cb3-7"><a href="#cb3-7"></a>  auto result = work.get_future();</span>
<span id="cb3-8"><a href="#cb3-8"></a></span>
<span id="cb3-9"><a href="#cb3-9"></a>  // execute work on the given executor</span>
<span id="cb3-10"><a href="#cb3-10"></a>  execution::<span class="kw">execute</span>(ex, move(work));</span>
<span id="cb3-11"><a href="#cb3-11"></a></span>
<span id="cb3-12"><a href="#cb3-12"></a>  return result;</span>
<span id="cb3-13"><a href="#cb3-13"></a>}</span></code></pre></div>
<p>The benefit of such an extension is that a client can select from among multiple thread pools to control exactly which pool <code>std::async</code> uses simply by providing a corresponding executor. Inconveniences of work packaging and submission become the library’s responsibility.</p>
<p><strong>Authoring executors.</strong> Programmers author custom executor types by defining a type with an <code>execute</code> function. Consider the implementation of an executor whose <code>execute</code> function executes the client’s work “inline”:</p>
<div class="sourceCode" id="cb4"><pre class="sourceCode p0443"><code class="sourceCode p0443syntax"><span id="cb4-1"><a href="#cb4-1"></a>struct inline_executor {</span>
<span id="cb4-2"><a href="#cb4-2"></a>  // define execute</span>
<span id="cb4-3"><a href="#cb4-3"></a>  template&lt;class F&gt;</span>
<span id="cb4-4"><a href="#cb4-4"></a>  void <span class="kw">execute</span>(F&amp;&amp; f) const noexcept {</span>
<span id="cb4-5"><a href="#cb4-5"></a>    std::invoke(std::forward&lt;F&gt;(f));</span>
<span id="cb4-6"><a href="#cb4-6"></a>  }</span>
<span id="cb4-7"><a href="#cb4-7"></a></span>
<span id="cb4-8"><a href="#cb4-8"></a>  // enable comparisons</span>
<span id="cb4-9"><a href="#cb4-9"></a>  auto operator&lt;=&gt;(const inline_executor&amp;) const = default;</span>
<span id="cb4-10"><a href="#cb4-10"></a>};</span></code></pre></div>
<p>Additionally, a comparison function determines whether two executor objects refer to the same underlying resource and therefore execute with equivalent semantics. Concepts <code>executor</code> and <code>executor_of</code> summarize these requirements. The former validates executors in isolation; the latter, when both executor and work are available.</p>
<p><strong>Executor customization</strong> can accelerate execution or introduce novel behavior. The previous example demonstrated custom execution at the granularity of a new executor type, but finer-grained and coarser-grained customization techniques are also possible. These are <strong>executor properties</strong> and <strong>control structures</strong>, respectively.</p>
<p><strong>Executor properties</strong> communicate optional behavioral requirements beyond the minimal contract of <code>execute</code>, and this proposal specifies several. We expect expert implementors to impose these requirements beneath higher-level abstractions. In principle, optional, dynamic data members or function parameters could communicate these requirements, but C++ requires the ability to introduce customization at compile time. Moreover, optional parameters lead to <a href="https://wg21.link/P2033">combinatorially many function variants</a>.</p>
<p>Instead, statically-actionable properties factor such requirements and thereby avoid a combinatorial explosion of executor APIs. For example, consider the requirement to execute blocking work with priority. An unscalable design might embed these options into the <code>execute</code> interface by multiplying individual factors into separate functions: <code>execute</code>, <code>blocking_execute</code>, <code>execute_with_priority</code>, <code>blocking_execute_with_priority</code>, etc.</p>
<p>Executors avoid this unscalable situation by adopting <a href="https://wg21.link/P1393">P1393</a>’s properties design based on <code>require</code> and <code>prefer</code>:</p>
<div class="sourceCode" id="cb5"><pre class="sourceCode p0443"><code class="sourceCode p0443syntax"><span id="cb5-1"><a href="#cb5-1"></a>// obtain an executor</span>
<span id="cb5-2"><a href="#cb5-2"></a><span class="kw">executor</span> auto ex = ...;</span>
<span id="cb5-3"><a href="#cb5-3"></a></span>
<span id="cb5-4"><a href="#cb5-4"></a>// require the execute operation to block</span>
<span id="cb5-5"><a href="#cb5-5"></a><span class="kw">executor</span> auto blocking_ex = std::require(ex, execution::<span class="kw">blocking</span>.<span class="kw">always</span>);</span>
<span id="cb5-6"><a href="#cb5-6"></a></span>
<span id="cb5-7"><a href="#cb5-7"></a>// prefer to execute with a particular priority p</span>
<span id="cb5-8"><a href="#cb5-8"></a><span class="kw">executor</span> auto blocking_ex_with_priority = std::prefer(blocking_ex, execution::<span class="st">priority</span>(p));</span>
<span id="cb5-9"><a href="#cb5-9"></a></span>
<span id="cb5-10"><a href="#cb5-10"></a>// execute my blocking, possibly prioritized work</span>
<span id="cb5-11"><a href="#cb5-11"></a>execution::<span class="kw">execute</span>(blocking_ex_with_priority, work);</span></code></pre></div>
<p>Each application of <code>require</code> or <code>prefer</code> transforms an executor into one with the requested property. In this example, if <code>ex</code> cannot be transformed into a blocking executor, the call to <code>require</code> will fail to compile. <code>prefer</code> is a weaker request used to communicate hints and consequently always succeeds because it may ignore the request.</p>
<p>Consider a version of <code>std::async</code> which <em>never</em> blocks the caller:</p>
<div class="sourceCode" id="cb6"><pre class="sourceCode p0443"><code class="sourceCode p0443syntax"><span id="cb6-1"><a href="#cb6-1"></a>template&lt;<span class="kw">executor</span> E, class F, class... Args&gt;</span>
<span id="cb6-2"><a href="#cb6-2"></a>auto really_async(const E&amp; ex, F&amp;&amp; f, Args&amp;&amp;... args) {</span>
<span id="cb6-3"><a href="#cb6-3"></a>  using namespace execution;</span>
<span id="cb6-4"><a href="#cb6-4"></a></span>
<span id="cb6-5"><a href="#cb6-5"></a>  // package up the work</span>
<span id="cb6-6"><a href="#cb6-6"></a>  packaged_task work(forward&lt;F&gt;(f), forward&lt;Args&gt;(args)...);</span>
<span id="cb6-7"><a href="#cb6-7"></a></span>
<span id="cb6-8"><a href="#cb6-8"></a>  // get the future</span>
<span id="cb6-9"><a href="#cb6-9"></a>  auto result = work.get_future();</span>
<span id="cb6-10"><a href="#cb6-10"></a></span>
<span id="cb6-11"><a href="#cb6-11"></a>  // execute the nonblocking work on the given executor</span>
<span id="cb6-12"><a href="#cb6-12"></a>  <span class="kw">execute</span>(require(ex, <span class="kw">blocking</span>.<span class="kw">never</span>), move(work));</span>
<span id="cb6-13"><a href="#cb6-13"></a></span>
<span id="cb6-14"><a href="#cb6-14"></a>  return result;</span>
<span id="cb6-15"><a href="#cb6-15"></a>}</span></code></pre></div>
<p>Such an enhancement could address a well-known hazard of <code>std::async</code>:</p>
<div class="sourceCode" id="cb7"><pre class="sourceCode p0443"><code class="sourceCode p0443syntax"><span id="cb7-1"><a href="#cb7-1"></a>// confusingly, always blocks in the returned but discarded future&#39;s destructor</span>
<span id="cb7-2"><a href="#cb7-2"></a>std::async(foo);</span>
<span id="cb7-3"><a href="#cb7-3"></a></span>
<span id="cb7-4"><a href="#cb7-4"></a>// *never* blocks</span>
<span id="cb7-5"><a href="#cb7-5"></a>really_async(foo);</span></code></pre></div>
<p><strong>Control structures</strong> permit customizations at a higher level of abstraction by allowing executors to “hook” them and is useful when an efficient implementation is possible on a particular execution context. The first such control structure this proposal defines is <code>bulk_execute</code>, which creates a group of function invocations in a single operation. This pattern permits a wide range of efficient implementations and is of fundamental importance to C++ programs and the standard library.</p>
<p>By default, <code>bulk_execute</code> invokes <code>execute</code> repeatedly, but repeatedly executing individual work items is inefficient at scale. Consequently, many platforms provide APIs that explicitly and efficiently execute bulk work. In such cases, a custom <code>bulk_execute</code> avoids inefficient platform interactions via direct access to these accelerated bulk APIs while also optimizing the use of scalar APIs.</p>
<p><code>bulk_execute</code> receives an invocable and an invocation count. Consider a possible implementation:</p>
<div class="sourceCode" id="cb8"><pre class="sourceCode p0443"><code class="sourceCode p0443syntax"><span id="cb8-1"><a href="#cb8-1"></a>struct simd_executor : inline_executor { // first, satisfy executor requirements via inheritance</span>
<span id="cb8-2"><a href="#cb8-2"></a>  template&lt;class F&gt;</span>
<span id="cb8-3"><a href="#cb8-3"></a>  simd_sender <span class="kw">bulk_execute</span>(F f, size_t n) const {</span>
<span id="cb8-4"><a href="#cb8-4"></a>    #pragma simd</span>
<span id="cb8-5"><a href="#cb8-5"></a>    for(size_t i = 0; i != n; ++i) {</span>
<span id="cb8-6"><a href="#cb8-6"></a>      std::invoke(f, i);</span>
<span id="cb8-7"><a href="#cb8-7"></a>    }</span>
<span id="cb8-8"><a href="#cb8-8"></a></span>
<span id="cb8-9"><a href="#cb8-9"></a>    return {};</span>
<span id="cb8-10"><a href="#cb8-10"></a>  }</span>
<span id="cb8-11"><a href="#cb8-11"></a>};</span></code></pre></div>
<p>To accelerate <code>bulk_execute</code>, <code>simd_executor</code> uses a SIMD loop.</p>
<p><code>bulk_execute</code> should be used in cases where multiple pieces of work are available at once:</p>
<div class="sourceCode" id="cb9"><pre class="sourceCode p0443"><code class="sourceCode p0443syntax"><span id="cb9-1"><a href="#cb9-1"></a>template&lt;class Executor, class F, class Range&gt;</span>
<span id="cb9-2"><a href="#cb9-2"></a>void my_for_each(const Executor&amp; ex, F f, Range rng) {</span>
<span id="cb9-3"><a href="#cb9-3"></a>  // request bulk execution, receive a sender</span>
<span id="cb9-4"><a href="#cb9-4"></a>  <span class="kw">sender</span> auto s = execution::<span class="kw">bulk_execute</span>(ex, [=](size_t i) {</span>
<span id="cb9-5"><a href="#cb9-5"></a>    f(rng[i]);</span>
<span id="cb9-6"><a href="#cb9-6"></a>  }, std::ranges::size(rng));</span>
<span id="cb9-7"><a href="#cb9-7"></a></span>
<span id="cb9-8"><a href="#cb9-8"></a>  // initiate execution and wait for it to complete</span>
<span id="cb9-9"><a href="#cb9-9"></a>  execution::<span class="st">sync_wait</span>(s);</span>
<span id="cb9-10"><a href="#cb9-10"></a>}</span></code></pre></div>
<p><code>simd_executor</code>’s particular <code>bulk_execute</code> implementation executes “eagerly”, but <code>bulk_execute</code>’s semantics do not require it. As <code>my_for_each</code> demonstrates, unlike <code>execute</code>, <code>bulk_execute</code> is an example of a “lazy” operation whose execution may be optionally postponed. The token this <code>bulk_execute</code> returns is an example of a sender a client may use to initiate execution or otherwise interact with the work. For example, calling <code>sync_wait</code> on the sender ensures that the bulk work completes before the caller continues. Senders and receivers are the subject of the next section.</p>
</section>
<section id="senders-and-receivers-represent-work" data-number="1.4">
<h2 data-number="1.4"><span class="header-section-number">1.4</span> Senders and Receivers Represent Work</h2>
<p>The <code>executor</code> concept addresses a basic need of executing a single operation in a specified execution context. The expressive power of <code>executor</code> is limited, however: since <code>execute</code> returns <code>void</code> instead of a handle to the just-scheduled work, the <code>executor</code> abstraction gives no generic way to chain operations and thereby propagate values, errors, and cancellation signals downstream; no way to handle scheduling errors occurring between when work submission and execution; and no convenient way to control the allocation and lifetime of state associated with an operation.</p>
<p>Without such controls, it is not possible to define Generic (in the Stepanov sense) asynchronous algorithms that compose efficiently with sensible default implementations. To fill this gap, this paper proposes two related abstractions, <code>sender</code> and <code>receiver</code>, concretely motivated below.</p>
<section id="generic-async-algorithm-example-retry" data-number="1.4.1">
<h3 data-number="1.4.1"><span class="header-section-number">1.4.1</span> Generic async algorithm example: <code>retry</code></h3>
<p><code>retry</code> is the kind of Generic algorithm senders and receivers enable. It has simple semantics: schedule work on an execution context; if the execution succeeds, done; otherwise, if the user requests cancellation, done; otherwise, if a scheduling error occurs, try again.</p>
<div class="sourceCode" id="cb10"><pre class="sourceCode p0443"><code class="sourceCode p0443syntax"><span id="cb10-1"><a href="#cb10-1"></a>template&lt;invocable Fn&gt;</span>
<span id="cb10-2"><a href="#cb10-2"></a>void <span class="st">retry</span>(<span class="kw">executor_of</span>&lt;Fn&gt; auto ex, Fn fn) {</span>
<span id="cb10-3"><a href="#cb10-3"></a>  // ???</span>
<span id="cb10-4"><a href="#cb10-4"></a>}</span></code></pre></div>
<p>Executors alone prohibit a generic implementation because they lack a portable way to intercept and react to scheduling errors. Later we show how this algorithm might look when implemented with senders and receivers.</p>
</section>
<section id="goal-an-asynchronous-stl" data-number="1.4.2">
<h3 data-number="1.4.2"><span class="header-section-number">1.4.2</span> Goal: an asynchronous STL</h3>
<p>Suitably chosen concepts driving the definition of Generic async algorithms like <code>retry</code> streamline the creation of efficient, asynchronous graphs of work. Here is some sample syntax for the sorts of async programs we envision (borrowed from <a href="http://wg21.link/P1897">P1897</a>):</p>
<div class="sourceCode" id="cb11"><pre class="sourceCode p0443"><code class="sourceCode p0443syntax"><span id="cb11-1"><a href="#cb11-1"></a><span class="kw">sender</span> auto s = <span class="st">just</span>(3) |                                  // produce &#39;3&#39; immediately</span>
<span id="cb11-2"><a href="#cb11-2"></a>                <span class="st">via</span>(scheduler1) |                          // transition context</span>
<span id="cb11-3"><a href="#cb11-3"></a>                <span class="st">then</span>([](int a){return a+1;}) |             // chain continuation</span>
<span id="cb11-4"><a href="#cb11-4"></a>                <span class="st">then</span>([](int a){return a*2;}) |             // chain another continuation</span>
<span id="cb11-5"><a href="#cb11-5"></a>                <span class="st">via</span>(scheduler2) |                          // transition context</span>
<span id="cb11-6"><a href="#cb11-6"></a>                <span class="st">handle_error</span>([](auto e){return <span class="st">just</span>(3);}); // with default value on errors</span>
<span id="cb11-7"><a href="#cb11-7"></a>int r = <span class="st">sync_wait</span>(s);                                      // wait for the result</span></code></pre></div>
<p>It should be possible to replace <code>just(3)</code> with a call to any asynchronous API whose return type satisfies the correct concept and maintain this program’s correctness. Generic algorithms like <code>when_all</code> and <code>when_any</code> would permit users to express fork/join concurrency in their DAGs. As with STL’s <code>iterator</code> abstraction, the cost of satisfying the conceptual requirements are offset by the expressivity of a large reusable and composable library of algorithms.</p>
</section>
<section id="current-techniques" data-number="1.4.3">
<h3 data-number="1.4.3"><span class="header-section-number">1.4.3</span> Current techniques</h3>
<p>There are many techniques for creating chains of dependent asynchronous execution. Ordinary callbacks have enjoyed success in C++ and elsewhere for years. Modern codebases have switched to variations of future abstractions that support continuations (e.g., <code>std::experimental::future::then</code>). In C++20 and beyond, we could imagine standardizing on coroutines, so that launching an async operation returns an awaitable. Each of these approaches has strengths and weaknesses.</p>
<p><strong>Futures</strong>, as traditionally realized, require the dynamic allocation and management of a shared state, synchronization, and typically type-erasure of work and continuation. Many of these costs are inherent in the nature of “future” as a handle to an operation that is already scheduled for execution. These expenses rule out the future abstraction for many uses and makes it a poor choice for a basis of a Generic mechanism.</p>
<p><strong>Coroutines</strong> suffer many of the same problems but can avoid synchronizing when chaining dependent work because they typically start suspended. In many cases, coroutine frames require unavoidable dynamic allocation. Consequently, coroutines in embedded or heterogeneous environments require great attention to detail. Neither are coroutines good candidates for cancellation because the early and safe termination of coordinating coroutines requires unsatisfying solutions. On the one hand, exceptions are inefficient and disallowed in many environments. Alternatively, clumsy <em>ad hoc</em> mechanisms, whereby <code>co_yield</code> returns a status code, hinder correctness. <a href="http://wg21.link/P1662">P1662</a> provides a complete discussion.</p>
<p><strong>Callbacks</strong> are the simplest, most powerful, and most efficient mechanism for creating chains of work, but suffer problems of their own. Callbacks must propagate either errors or values. This simple requirement yields many different interface possibilities, but the lack of a standard obstructs Generic design. Additionally, few of these possibilities accomodate cancellation signals when the user requests upstream work to stop and clean up.</p>
</section>
</section>
<section id="receiver-sender-and-scheduler" data-number="1.5">
<h2 data-number="1.5"><span class="header-section-number">1.5</span> Receiver, sender, and scheduler</h2>
<p>With the preceding as motivation, we introduce primitives to address the needs of Generic asynchronous programming in the presence of value, error, and cancellation propagation.</p>
<section id="receiver" data-number="1.5.1">
<h3 data-number="1.5.1"><span class="header-section-number">1.5.1</span> Receiver</h3>
<p>A <code>receiver</code> is simply a callback with a particular interface and semantics. Unlike a traditional callback which uses function-call syntax and a single signature handling both success and error cases, a receiver has three separate channels for value, error, and “done” (aka cancelled).</p>
<p>These channels are specified as customization points, and a type <code>R</code> modeling <code>receiver_of&lt;R,Ts...&gt;</code> supports them:</p>
<div class="sourceCode" id="cb12"><pre class="sourceCode p0443"><code class="sourceCode p0443syntax"><span id="cb12-1"><a href="#cb12-1"></a>std::execution::<span class="kw">set_value</span>(r, ts...); // signal success, but set_value itself may fail</span>
<span id="cb12-2"><a href="#cb12-2"></a>std::execution::<span class="kw">set_error</span>(r, ep);    // signal error (ep is std::exception_ptr), never fails</span>
<span id="cb12-3"><a href="#cb12-3"></a>std::execution::<span class="kw">set_done</span>(r);         // signal stopped, never fails</span></code></pre></div>
<p>Exactly one of the three functions must be called on a <code>receiver</code> before it is destroyed. Each of these interfaces is considered “terminal”. That is, a particular receiver may assume that if one is called, no others ever will be. The one exception being if <code>set_value</code> exits with an exception, the receiver is not yet complete. Consequently, another function must be called on it before it is destroyed. After a failed call to <code>set_value</code>, correctness requires a subsequent call either to <code>set_error</code> or <code>set_done</code>; a receiver need not guarantee that a second call to <code>set_value</code> is well-formed. Collectively, these requirements are the “<em>receiver contract</em>”.</p>
<p>Although <code>receiver</code>’s interface appears novel at first glance, it remains just a callback. Moreover, <code>receiver</code>’s novelty disappears when recognizing that <code>std::promise</code>’s <code>set_value</code> and <code>set_exception</code> provide essentially the same interface. This choice of interface and semantics, along with <code>sender</code>, facilitate the Generic implementation of many useful async algorithms like <code>retry</code>.</p>
</section>
<section id="sender" data-number="1.5.2">
<h3 data-number="1.5.2"><span class="header-section-number">1.5.2</span> Sender</h3>
<p>A <code>sender</code> represents work that has not been scheduled for execution yet, to which one must add a continuation (a <code>receiver</code>) and then “launch”, or enqueue for execution. A sender’s duty to its connected receiver is to fulfill the <em>receiver contract</em> by ensuring that one of the three <code>receiver</code> functions returns normally.</p>
<p>Earlier versions of this paper fused these two operations — attach a continuation and launch for execution — into the single operation <code>submit</code>. This paper proposes to split <code>submit</code> into a <code>connect</code> step that packages a <code>sender</code> and a <code>receiver</code> into an operation state, and a <code>start</code> step that logically starts the operation and schedules the receiver completion-signalling methods to be called when the operation completes.</p>
<div class="sourceCode" id="cb13"><pre class="sourceCode p0443"><code class="sourceCode p0443syntax"><span id="cb13-1"><a href="#cb13-1"></a>// P0443R12</span>
<span id="cb13-2"><a href="#cb13-2"></a>std::execution::<span class="kw">submit</span>(snd, rec);</span>
<span id="cb13-3"><a href="#cb13-3"></a></span>
<span id="cb13-4"><a href="#cb13-4"></a>// P0443R13</span>
<span id="cb13-5"><a href="#cb13-5"></a>auto state = std::execution::<span class="kw">connect</span>(snd, rec);</span>
<span id="cb13-6"><a href="#cb13-6"></a>// ... later</span>
<span id="cb13-7"><a href="#cb13-7"></a>std::execution::<span class="kw">start</span>(state);</span></code></pre></div>
<p>This split offers interesting opportunities for optimization, and <a href="#appendix-a-note-on-coroutines">harmonizes senders with coroutines</a>.</p>
<p>The <code>sender</code> concept itself places no requirements on the execution context on which a sender’s work executes. Instead, specific models of the <code>sender</code> concept may offer stronger guarantees about the context from which the receiver’s methods will be invoked. This is particularly true of the senders created by a <code>scheduler</code>.</p>
</section>
<section id="scheduler" data-number="1.5.3">
<h3 data-number="1.5.3"><span class="header-section-number">1.5.3</span> Scheduler</h3>
<p>Many generic async algorithms create multiple execution agents on the same execution context. Therefore, it is insufficient to parameterize these algorithms with a single-shot sender completing in a known context. Rather, it makes sense to pass these algorithms a factory of single-shot senders. Such a factory is called a “<code>scheduler</code>”, and it has a single basis operation: <code>schedule</code>:</p>
<div class="sourceCode" id="cb14"><pre class="sourceCode p0443"><code class="sourceCode p0443syntax"><span id="cb14-1"><a href="#cb14-1"></a><span class="kw">sender</span> auto s = std::execution::<span class="kw">schedule</span>(sched);</span>
<span id="cb14-2"><a href="#cb14-2"></a>// OK, s is a single-shot sender of void that completes in sched&#39;s execution context</span></code></pre></div>
<p>Like executors, schedulers act as handles to an execution context. Unlike executors, schedulers submit execution lazily, but a single type may simultaneously model both concepts. We envision that subsumptions of the <code>scheduler</code> concept will add the ability to postpone or cancel execution until after some time period has elapsed.</p>
</section>
</section>
<section id="senders-receivers-and-generic-algorithms" data-number="1.6">
<h2 data-number="1.6"><span class="header-section-number">1.6</span> Senders, receivers, and generic algorithms</h2>
<p>Useful concepts constrain generic algorithms while allowing default implementations via those concepts’ basis operations. Below, we show how these <code>sender</code> and <code>receiver</code> provide efficient default implementations of common async algorithms. We envision that most generic async algorithms will be implemented as taking a sender and returning a sender whose <code>connect</code> method wraps its receiver an adaptor that implements the algorithm’s logic. The <code>then</code> algorithm below, which chains a continuation function on a <code>sender</code>, is a simple demonstration.</p>
<section id="algorithm-then" data-number="1.6.1">
<h3 data-number="1.6.1"><span class="header-section-number">1.6.1</span> Algorithm <code>then</code></h3>
<p>The following code implements a <code>then</code> algorithm that, like <code>std::experimental::future::then</code>, schedules a function to be applied to the result of an asynchronous operation when available. This code demonstrates how an algorithm can adapt receivers to codify the algorithm’s logic.</p>
<div class="sourceCode" id="cb15"><pre class="sourceCode p0443"><code class="sourceCode p0443syntax"><span id="cb15-1"><a href="#cb15-1"></a>template&lt;<span class="kw">receiver</span> R, class F&gt;</span>
<span id="cb15-2"><a href="#cb15-2"></a>struct _then_receiver : R { // for exposition, inherit set_error and set_done from R</span>
<span id="cb15-3"><a href="#cb15-3"></a>    F f_;</span>
<span id="cb15-4"><a href="#cb15-4"></a></span>
<span id="cb15-5"><a href="#cb15-5"></a>    // Customize set_value by invoking the callable and passing the result to the base class</span>
<span id="cb15-6"><a href="#cb15-6"></a>    template&lt;class... As&gt;</span>
<span id="cb15-7"><a href="#cb15-7"></a>      requires <span class="kw">receiver_of</span>&lt;R, invoke_result_t&lt;F, As...&gt;&gt;</span>
<span id="cb15-8"><a href="#cb15-8"></a>    void <span class="kw">set_value</span>(Args&amp;&amp;... args) &amp;&amp; noexcept(/*...*/) {</span>
<span id="cb15-9"><a href="#cb15-9"></a>        ::<span class="kw">set_value</span>((R&amp;&amp;) *this, invoke((F&amp;&amp;) f_, (As&amp;&amp;) as...));</span>
<span id="cb15-10"><a href="#cb15-10"></a>    }</span>
<span id="cb15-11"><a href="#cb15-11"></a></span>
<span id="cb15-12"><a href="#cb15-12"></a>    // Not shown: handle the case when the callable returns void</span>
<span id="cb15-13"><a href="#cb15-13"></a>};</span>
<span id="cb15-14"><a href="#cb15-14"></a></span>
<span id="cb15-15"><a href="#cb15-15"></a>template&lt;<span class="kw">sender</span> S, class F&gt;</span>
<span id="cb15-16"><a href="#cb15-16"></a>struct _then_sender : _sender_base {</span>
<span id="cb15-17"><a href="#cb15-17"></a>    S s_;</span>
<span id="cb15-18"><a href="#cb15-18"></a>    F f_;</span>
<span id="cb15-19"><a href="#cb15-19"></a></span>
<span id="cb15-20"><a href="#cb15-20"></a>    template&lt;<span class="kw">receiver</span> R&gt;</span>
<span id="cb15-21"><a href="#cb15-21"></a>      requires <span class="kw">sender_to</span>&lt;S, _then_receiver&lt;R, F&gt;&gt;</span>
<span id="cb15-22"><a href="#cb15-22"></a>    state_t&lt;S, _then_receiver&lt;R, F&gt;&gt; <span class="kw">connect</span>(R r) &amp;&amp; {</span>
<span id="cb15-23"><a href="#cb15-23"></a>        return ::<span class="kw">connect</span>((S&amp;&amp;)s_, _then_receiver&lt;R, F&gt;{(R&amp;&amp;)r, (F&amp;&amp;)f_});</span>
<span id="cb15-24"><a href="#cb15-24"></a>    }</span>
<span id="cb15-25"><a href="#cb15-25"></a>};</span>
<span id="cb15-26"><a href="#cb15-26"></a></span>
<span id="cb15-27"><a href="#cb15-27"></a>template&lt;<span class="kw">sender</span> S, class F&gt;</span>
<span id="cb15-28"><a href="#cb15-28"></a><span class="kw">sender</span> auto <span class="st">then</span>(S s, F f) {</span>
<span id="cb15-29"><a href="#cb15-29"></a>    return _then_sender{{}, (S&amp;&amp;)s, (F&amp;&amp;)f};</span>
<span id="cb15-30"><a href="#cb15-30"></a>}</span></code></pre></div>
<p>Given some asynchronous, <code>sender</code>-returning API <code>async_foo</code>, a user of <code>then</code> can execute some code once the async result is available:</p>
<div class="sourceCode" id="cb16"><pre class="sourceCode p0443"><code class="sourceCode p0443syntax"><span id="cb16-1"><a href="#cb16-1"></a><span class="kw">sender</span> auto s = <span class="st">then</span>(async_foo(args...), [](auto result) {/* stuff... */});</span></code></pre></div>
<p>This builds a composed asynchronous operation. When the user wants to schedule this operation for execution, they would <code>connect</code> a receiver, and then call <code>start</code> on the resulting operation state.</p>
<p>Scheduling work on an execution context can also be done with <code>then</code>. Given a <code>static_thread_pool</code> object <code>pool</code> that satisfied the <code>scheduler</code> concept, a user may do the following:</p>
<div class="sourceCode" id="cb17"><pre class="sourceCode p0443"><code class="sourceCode p0443syntax"><span id="cb17-1"><a href="#cb17-1"></a><span class="kw">sender</span> auto s = <span class="st">then</span>(</span>
<span id="cb17-2"><a href="#cb17-2"></a>    std::execution::<span class="kw">schedule</span>( pool ),</span>
<span id="cb17-3"><a href="#cb17-3"></a>    []{ std::printf(&quot;hello world&quot;); } );</span></code></pre></div>
<p>This creates a <code>sender</code> that, when submitted, will call <code>printf</code> from a thread in the thread pool.</p>
<p>There exist heterogeneous computing environments that are unable to execute arbitrary code. For those, an implementation of <code>then</code> as shown above would either not work or would incur the cost of a transition to the host in order to execute the unknown code. Therefore, <code>then</code> itself and several other fundamental algorithmic primitives, would themselves need to be customizable on a per-execution context basis.</p>
<p>A full working example of <code>then</code> can be found here: <a href="https://godbolt.org/z/dafqM-">https://godbolt.org/z/dafqM-</a></p>
</section>
<section id="algorithm-retry" data-number="1.6.2">
<h3 data-number="1.6.2"><span class="header-section-number">1.6.2</span> Algorithm <code>retry</code></h3>
<p>As described above, the idea of <code>retry</code> is to retry the async operation on failure, but not on success or cancellation. Key to a correct generic implementation of <code>retry</code> is the ability to distinguish the error case from the cancelled case.</p>
<p>As with the <code>then</code> algorithm, the <code>retry</code> algorithm places the logic of the algorithm into a custom receiver to which the sender to be retried is <code>connect</code>-ed. That custom receiver has <code>set_value</code> and <code>set_done</code> members that simply pass their signals through unmodified. The <code>set_error</code> member, on the other hand, reconstructs the operation state in-place by making another call to <code>connect</code> with the original sender and a new instance of the custom receiver. That new operation state is then <code>start</code>-ed again, which effectively causes the original sender to be retried.</p>
<p><a href="#appendix-the-retry-algorithm">The appendix</a> lists the source of the <code>retry</code> algorithm. Note that the signature of the retry algorithm is simply:</p>
<div class="sourceCode" id="cb18"><pre class="sourceCode p0443"><code class="sourceCode p0443syntax"><span id="cb18-1"><a href="#cb18-1"></a><span class="kw">sender</span> auto <span class="st">retry</span>(<span class="kw">sender</span> auto s);</span></code></pre></div>
<p>That is, it is not parameterized on an execution context on which to retry the operation. That is because we can assume the existence of a function <code>on</code> which schedules a sender for execution on a specified execution context:</p>
<div class="sourceCode" id="cb19"><pre class="sourceCode p0443"><code class="sourceCode p0443syntax"><span id="cb19-1"><a href="#cb19-1"></a><span class="kw">sender</span> auto <span class="st">on</span>(<span class="kw">sender</span> auto s, <span class="kw">scheduler</span> auto sched);</span></code></pre></div>
<p>Given these two functions, a user can simply do <code>retry(on(s, sched))</code> to retry an operation on a particular execution context.</p>
</section>
<section id="toward-an-asynchronous-stl" data-number="1.6.3">
<h3 data-number="1.6.3"><span class="header-section-number">1.6.3</span> Toward an asynchronous STL</h3>
<p>The algorithms <code>then</code> and <code>retry</code> are only two of many interesting Generic asynchronous algorithms that are expressible in terms of senders and receivers. Two other important algorithms are <code>on</code> and <code>via</code>, the former which schedules a sender for execution on a particular <code>scheduler</code>, and the latter which causes a sender’s <em>continuations</em> to be run on a particular <code>scheduler</code>. In this way, chains of asynchronous computation can be created that transition from one execution context to another.</p>
<p>Other important algorithms are <code>when_all</code> and <code>when_any</code>, encapsulating fork/join semantics. With these algorithms and others, entire DAGs of async computation can be created and executed. <code>when_any</code> can in turn be used to implement a generic <code>timeout</code> algorithm, together with a sender that sleeps for a duration and then sends a “done” signal, and so these algorithms compose.</p>
<p>In short, sender/receiver permits a rich set of Generic asynchronous algorithms to sit alongside Stepanov’s sequence algorithms in the STL. Asynchronous APIs that return senders would be usable with these Generic algorithms, increasing reusability. <a href="http://wg21.link/P1897">P1897</a> suggest an initial set of these algorithms.</p>
</section>
</section>
<section id="summary" data-number="1.7">
<h2 data-number="1.7"><span class="header-section-number">1.7</span> Summary</h2>
<p>We envision a future when C++ programmers can express asynchronous, parallel execution of work on diverse hardware resources through elegant standard interfaces. This proposal provides a foundation for flexible execution and is our initial step towards that goal. <strong>Executors</strong> represent hardware resources that execute work. <strong>Senders and receivers</strong> represent lazily-constructed asynchronous DAGs of work. These primitives empower programmers to control where and when work happens.</p>
</section>
</section>
<section id="proposed-wording" data-number="2">
<h1 data-number="2"><span class="header-section-number">2</span> Proposed Wording</h1>
<section id="execution-support-library" data-number="2.1">
<h2 data-number="2.1"><span class="header-section-number">2.1</span> Execution Support Library</h2>
<section id="general" data-number="2.1.1">
<h3 data-number="2.1.1"><span class="header-section-number">2.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>
</section>
<section id="header-execution-synopsis" data-number="2.1.2">
<h3 data-number="2.1.2"><span class="header-section-number">2.1.2</span> Header <code>&lt;execution&gt;</code> synopsis</h3>
<pre><code>namespace std {
namespace execution {

  // Exception types:

  extern runtime_error const invocation-error; // exposition only
  struct receiver_invocation_error : runtime_error, nested_exception {
    receiver_invocation_error() noexcept
      : runtime_error(invocation-error), nested_exception() {}
  };

  // Invocable archetype

  using invocable_archetype = unspecified;

  // Customization points:

  inline namespace unspecified{
    inline constexpr unspecified set_value = unspecified;

    inline constexpr unspecified set_done = unspecified;

    inline constexpr unspecified set_error = unspecified;

    inline constexpr unspecified execute = unspecified;

    inline constexpr unspecified connect = unspecified;

    inline constexpr unspecified start = unspecified;

    inline constexpr unspecified submit = unspecified;

    inline constexpr unspecified schedule = unspecified;

    inline constexpr unspecified bulk_execute = unspecified;
  }

  template&lt;class S, class R&gt;
    using connect_result_t = invoke_result_t&lt;decltype(connect), S, R&gt;;

  template&lt;class, class&gt; struct as-receiver; // exposition only

  template&lt;class, class&gt; struct as-invocable; // exposition only

  // Concepts:

  template&lt;class T, class E = exception_ptr&gt;
    concept receiver = see-below;

  template&lt;class T, class... An&gt;
    concept receiver_of = see-below;

  template&lt;class R, class... An&gt;
    inline constexpr bool is_nothrow_receiver_of_v =
      receiver_of&lt;R, An...&gt; &amp;&amp;
      is_nothrow_invocable_v&lt;decltype(set_value), R, An...&gt;;

  template&lt;class O&gt;
    concept operation_state = see-below;

  template&lt;class S&gt;
    concept sender = see-below;

  template&lt;class S&gt;
    concept typed_sender = see-below;

  template&lt;class S, class R&gt;
    concept sender_to = see-below;

  template&lt;class S&gt;
    concept scheduler = see-below;

  template&lt;class E&gt;
    concept executor = see-below;

  template&lt;class E, class F&gt;
    concept executor_of = see-below;

  // Sender and receiver utilities type
  namespace unspecified { struct sender_base {}; }
  using unspecified::sender_base;

  template&lt;class S&gt; struct sender_traits;

  // Associated execution context property:

  struct context_t;

  constexpr context_t context;

  // 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 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... SupportableProperties&gt; class any_executor;

  template&lt;class Property&gt; struct prefer_only;

} // namespace execution
} // namespace std</code></pre>
</section>
</section>
<section id="requirements" data-number="2.2">
<h2 data-number="2.2"><span class="header-section-number">2.2</span> Requirements</h2>
<section id="protoallocator-requirements" data-number="2.2.1">
<h3 data-number="2.2.1"><span class="header-section-number">2.2.1</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>
</section>
<section id="invocable-archetype" data-number="2.2.2">
<h3 data-number="2.2.2"><span class="header-section-number">2.2.2</span> Invocable archetype</h3>
<p>The name <code>execution::invocable_archetype</code> is an implementation-defined type such that <code>invocable&lt;execution::invocable_archetype&amp;&gt;</code> is <code>true</code>.</p>
<p>A program that creates an instance of <code>execution::invocable_archetype</code> is ill-formed.</p>
</section>
<section id="customization-points" data-number="2.2.3">
<h3 data-number="2.2.3"><span class="header-section-number">2.2.3</span> Customization points</h3>
<section id="executionset_value" data-number="2.2.3.1">
<h4 data-number="2.2.3.1"><span class="header-section-number">2.2.3.1</span> <code>execution::set_value</code></h4>
<p>The name <code>execution::set_value</code> denotes a customization point object. The expression <code>execution::set_value(R, Vs...)</code> for some subexpressions <code>R</code> and <code>Vs...</code> is expression-equivalent to:</p>
<ul>
<li><p><code>R.set_value(Vs...)</code>, if that expression is valid. If the function selected does not send the value(s) <code>Vs...</code> to the receiver <code>R</code>’s value channel, the program is ill-formed with no diagnostic required.</p></li>
<li><p>Otherwise, <code>set_value(R, Vs...)</code>, if that expression is valid, with overload resolution performed in a context that includes the declaration</p>
<pre><code>  void set_value();</code></pre>
<p>and that does not include a declaration of <code>execution::set_value</code>. If the function selected by overload resolution does not send the value(s) <code>Vs...</code> to the receiver <code>R</code>’s value channel, the program is ill-formed with no diagnostic required.</p></li>
<li><p>Otherwise, <code>execution::set_value(R, Vs...)</code> is ill-formed.</p></li>
</ul>
<p>[<em>Editorial note:</em> We should probably define what “send the value(s) <code>Vs...</code> to the receiver <code>R</code>’s value channel” means more carefully. <em>–end editorial note</em>]</p>
</section>
<section id="executionset_done" data-number="2.2.3.2">
<h4 data-number="2.2.3.2"><span class="header-section-number">2.2.3.2</span> <code>execution::set_done</code></h4>
<p>The name <code>execution::set_done</code> denotes a customization point object. The expression <code>execution::set_done(R)</code> for some subexpression <code>R</code> is expression-equivalent to:</p>
<ul>
<li><p><code>R.set_done()</code>, if that expression is valid. If the function selected does not signal the receiver <code>R</code>’s done channel, the program is ill-formed with no diagnostic required.</p></li>
<li><p>Otherwise, <code>set_done(R)</code>, if that expression is valid, with overload resolution performed in a context that includes the declaration</p>
<pre><code>  void set_done();</code></pre>
<p>and that does not include a declaration of <code>execution::set_done</code>. If the function selected by overload resolution does not signal the receiver <code>R</code>’s done channel, the program is ill-formed with no diagnostic required.</p></li>
<li><p>Otherwise, <code>execution::set_done(R)</code> is ill-formed.</p></li>
</ul>
<p>[<em>Editorial note:</em> We should probably define what “signal receiver <code>R</code>’s done channel” means more carefully. <em>–end editorial note</em>]</p>
</section>
<section id="executionset_error" data-number="2.2.3.3">
<h4 data-number="2.2.3.3"><span class="header-section-number">2.2.3.3</span> <code>execution::set_error</code></h4>
<p>The name <code>execution::set_error</code> denotes a customization point object. The expression <code>execution::set_error(R, E)</code> for some subexpressions <code>R</code> and <code>E</code> are expression-equivalent to:</p>
<ul>
<li><p><code>R.set_error(E)</code>, if that expression is valid. If the function selected does not send the error <code>E</code> to the receiver <code>R</code>’s error channel, the program is ill-formed with no diagnostic required.</p></li>
<li><p>Otherwise, <code>set_error(R, E)</code>, if that expression is valid, with overload resolution performed in a context that includes the declaration</p>
<pre><code>  void set_error();</code></pre>
<p>and that does not include a declaration of <code>execution::set_error</code>. If the function selected by overload resolution does not send the error <code>E</code> to the receiver <code>R</code>’s error channel, the program is ill-formed with no diagnostic required.</p></li>
<li><p>Otherwise, <code>execution::set_error(R, E)</code> is ill-formed.</p></li>
</ul>
<p>[<em>Editorial note:</em> We should probably define what “send the error <code>E</code> to the receiver <code>R</code>’s error channel” means more carefully. <em>–end editorial note</em>]</p>
</section>
<section id="executionexecute" data-number="2.2.3.4">
<h4 data-number="2.2.3.4"><span class="header-section-number">2.2.3.4</span> <code>execution::execute</code></h4>
<p>The name <code>execution::execute</code> denotes a customization point object.</p>
<p>For some subexpressions <code>e</code> and <code>f</code>, let <code>E</code> be a type such that <code>decltype((e))</code> is <code>E</code> and let <code>F</code> be a type such that <code>decltype((f))</code> is <code>F</code>. The expression <code>execution::execute(e, f)</code> is ill-formed if <code>F</code> does not model <code>invocable</code>, or if <code>E</code> does not model either <code>executor</code> or <code>sender</code>. Otherwise, it is expression-equivalent to:</p>
<ul>
<li><p><code>e.execute(f)</code>, if that expression is valid. If the function selected does not execute the function object <code>f</code> on the executor <code>e</code>, the program is ill-formed with no diagnostic required.</p></li>
<li><p>Otherwise, <code>execute(e, f)</code>, if that expression is valid, with overload resolution performed in a context that includes the declaration</p>
<pre><code>  void execute();</code></pre>
<p>and that does not include a declaration of <code>execution::execute</code>. If the function selected by overload resolution does not execute the function object <code>f</code> on the executor <code>e</code>, the program is ill-formed with no diagnostic required.</p></li>
<li><p>Otherwise, if <code>F</code> is not an instance of <em><code>as-invocable</code></em><code>&lt;</code><em><code>R</code></em><code>, E&gt;</code> for some type <em><code>R</code></em>, and <code>invocable&lt;remove_cvref_t&lt;F&gt;&amp;&gt; &amp;&amp; sender_to&lt;E,</code><em><code>as-receiver</code></em><code>&lt;remove_cvref_t&lt;F&gt;, E&gt;&gt;</code> is <code>true</code>, <code>execution::submit(e,</code><em><code>as-receiver</code></em><code>&lt;remove_cvref_t&lt;F&gt;, E&gt;{std::forward&lt;F&gt;(f)})</code>, where <em><code>as-receiver</code></em> is some implementation-defined class template equivalent to:</p>
<pre><code>  template&lt;class F, class&gt;
  struct as-receiver {
    F f_;
    void set_value() noexcept(is_nothrow_invocable_v&lt;F&amp;&gt;) {
      invoke(f_);
    }
    [[noreturn]] void set_error(std::exception_ptr) noexcept {
      terminate();
    }
    void set_done() noexcept {}
  };</code></pre></li>
</ul>
<p>[<em>Editorial note:</em> We should probably define what “execute the function object <code>F</code> on the executor <code>E</code>” means more carefully. <em>–end editorial note</em>]</p>
</section>
<section id="executionconnect" data-number="2.2.3.5">
<h4 data-number="2.2.3.5"><span class="header-section-number">2.2.3.5</span> <code>execution::connect</code></h4>
<p>The name <code>execution::connect</code> denotes a customization point object. For some subexpressions <code>s</code> and <code>r</code>, let <code>S</code> be a type such that <code>decltype((s))</code> is <code>S</code> and let <code>R</code> be a type such that <code>decltype((r))</code> is <code>R</code>. The expression <code>execution::connect(s, r)</code> is expression-equivalent to:</p>
<ul>
<li><p><code>s.connect(r)</code>, if that expression is valid, if its type satisfies <code>operation_state</code>, and if <code>S</code> satisfies <code>sender</code>.</p></li>
<li><p>Otherwise, <code>connect(s, r)</code>, if that expression is valid, if its type satisfies <code>operation_state</code>, and if <code>S</code> satisfies <code>sender</code>, with overload resolution performed in a context that includes the declaration</p>
<pre><code>  void connect();</code></pre>
<p>and that does not include a declaration of <code>execution::connect</code>.</p></li>
<li><p>Otherwise, <em><code>as-operation</code></em><code>{s, r}</code>, if <code>r</code> is not an instance of <em><code>as-receiver</code></em><code>&lt;</code><em><code>F</code></em><code>, S&gt;</code> for some type <em><code>F</code></em>, and if <code>receiver_of&lt;R&gt; &amp;&amp;</code> <em><code>executor-of-impl</code></em><code>&lt;remove_cvref_t&lt;S&gt;,</code><em><code>as-invocable</code></em><code>&lt;remove_cvref_t&lt;R&gt;, S&gt;&gt;</code> is <code>true</code>, where <em><code>as-operation</code></em> is an implementation-defined class equivalent to</p>
<pre><code>  struct as-operation {
    remove_cvref_t&lt;S&gt; e_;
    remove_cvref_t&lt;R&gt; r_;
    void start() noexcept try {
      execution::execute(std::move(e_), as-invocable&lt;remove_cvref_t&lt;R&gt;, S&gt;{r_});
    } catch(...) {
      execution::set_error(std::move(r_), current_exception());
    }
  };</code></pre>
<p>and <em><code>as-invocable</code></em> is a class template equivalent to the following:</p>
<pre><code>  template&lt;class R, class&gt;
  struct as-invocable {
    R* r_;
    explicit as-invocable(R&amp; r) noexcept
      : r_(std::addressof(r)) {}
    as-invocable(as-invocable &amp;&amp; other) noexcept
      : r_(std::exchange(other.r_, nullptr)) {}
    ~as-invocable() {
      if(r_)
        execution::set_done(std::move(*r_));
    }
    void operator()() &amp; noexcept try {
      execution::set_value(std::move(*r_));
      r_ = nullptr;
    } catch(...) {
      execution::set_error(std::move(*r_), current_exception());
      r_ = nullptr;
    }
  };</code></pre></li>
<li><p>Otherwise, <code>execution::connect(s, r)</code> is ill-formed.</p></li>
</ul>
</section>
<section id="executionstart" data-number="2.2.3.6">
<h4 data-number="2.2.3.6"><span class="header-section-number">2.2.3.6</span> <code>execution::start</code></h4>
<p>The name <code>execution::start</code> denotes a customization point object. The expression <code>execution::start(O)</code> for some lvalue subexpression <code>O</code> is expression-equivalent to:</p>
<ul>
<li><p><code>O.start()</code>, if that expression is valid.</p></li>
<li><p>Otherwise, <code>start(O)</code>, if that expression is valid, with overload resolution performed in a context that includes the declaration</p>
<pre><code>  void start();</code></pre>
<p>and that does not include a declaration of <code>execution::start</code>.</p></li>
<li><p>Otherwise, <code>execution::start(O)</code> is ill-formed.</p></li>
</ul>
</section>
<section id="executionsubmit" data-number="2.2.3.7">
<h4 data-number="2.2.3.7"><span class="header-section-number">2.2.3.7</span> <code>execution::submit</code></h4>
<p>The name <code>execution::submit</code> denotes a customization point object.</p>
<p>For some subexpressions <code>s</code> and <code>r</code>, let <code>S</code> be a type such that <code>decltype((s))</code> is <code>S</code> and let <code>R</code> be a type such that <code>decltype((r))</code> is <code>R</code>. The expression <code>execution::submit(s, r)</code> is ill-formed if <code>sender_to&lt;S, R&gt;</code> is not <code>true</code>. Otherwise, it is expression-equivalent to:</p>
<ul>
<li><p><code>s.submit(r)</code>, if that expression is valid and <code>S</code> models <code>sender</code>. If the function selected does not submit the receiver object <code>r</code> via the sender <code>s</code>, the program is ill-formed with no diagnostic required.</p></li>
<li><p>Otherwise, <code>submit(s, r)</code>, if that expression is valid and <code>S</code> models <code>sender</code>, with overload resolution performed in a context that includes the declaration</p>
<pre><code>  void submit();</code></pre>
<p>and that does not include a declaration of <code>execution::submit</code>. If the function selected by overload resolution does not submit the receiver object <code>r</code> via the sender <code>s</code>, the program is ill-formed with no diagnostic required.</p></li>
<li><p>Otherwise, <code>execution::start((new</code><em><code>submit-receiver</code></em><code>&lt;S, R&gt;{s,r})-&gt;state_)</code>, where <em><code>submit-receiver</code></em> is an implementation-defined class template equivalent to</p>
<pre><code>  template&lt;class S, class R&gt;
  struct submit-receiver {
    struct wrap {
      submit-receiver * p_;
      template&lt;class...As&gt;
        requires receiver_of&lt;R, As...&gt;
      void set_value(As&amp;&amp;... as) &amp;&amp; noexcept(is_nothrow_receiver_of_v&lt;R, As...&gt;) {
        execution::set_value(std::move(p_-&gt;r_), (As&amp;&amp;) as...);
        delete p_;
      }
      template&lt;class E&gt;
        requires receiver&lt;R, E&gt;
      void set_error(E&amp;&amp; e) &amp;&amp; noexcept {
        execution::set_error(std::move(p_-&gt;r_), (E&amp;&amp;) e);
        delete p_;
      }
      void set_done() &amp;&amp; noexcept {
        execution::set_done(std::move(p_-&gt;r_));
        delete p_;
      }
    };
    remove_cvref_t&lt;R&gt; r_;
    connect_result_t&lt;S, wrap&gt; state_;
    submit-receiver(S&amp;&amp; s, R&amp;&amp; r)
      : r_((R&amp;&amp;) r)
      , state_(execution::connect((S&amp;&amp;) s, wrap{this})) {}
  };</code></pre></li>
</ul>
</section>
<section id="executionschedule" data-number="2.2.3.8">
<h4 data-number="2.2.3.8"><span class="header-section-number">2.2.3.8</span> <code>execution::schedule</code></h4>
<p>The name <code>execution::schedule</code> denotes a customization point object. For some subexpression <code>s</code>, let <code>S</code> be a type such that <code>decltype((s))</code> is <code>S</code>. The expression <code>execution::schedule(s)</code> is expression-equivalent to:</p>
<ul>
<li><p><code>s.schedule()</code>, if that expression is valid and its type models <code>sender</code>.</p></li>
<li><p>Otherwise, <code>schedule(s)</code>, if that expression is valid and its type models <code>sender</code> with overload resolution performed in a context that includes the declaration</p>
<pre><code>  void schedule();</code></pre>
<p>and that does not include a declaration of <code>execution::schedule</code>.</p></li>
<li><p>Otherwise, <em><code>as-sender</code></em><code>&lt;remove_cvref_t&lt;S&gt;&gt;{s}</code> if <code>S</code> satisfies <code>executor</code>, where <em><code>as-sender</code></em> is an implementation-defined class template equivalent to</p>
<pre><code>  template&lt;class E&gt;
  struct as-sender {
  private:
    E ex_;
  public:
    template&lt;template&lt;class...&gt; class Tuple, template&lt;class...&gt; class Variant&gt;
      using value_types = Variant&lt;Tuple&lt;&gt;&gt;;
    template&lt;template&lt;class...&gt; class Variant&gt;
      using error_types = Variant&lt;std::exception_ptr&gt;;
    static constexpr bool sends_done = true;

    explicit as-sender(E e) noexcept
      : ex_((E&amp;&amp;) e) {}
    template&lt;class R&gt;
      requires receiver_of&lt;R&gt;
    connect_result_t&lt;E, R&gt; connect(R&amp;&amp; r) &amp;&amp; {
      return execution::connect((E&amp;&amp;) ex_, (R&amp;&amp;) r);
    }
    template&lt;class R&gt;
      requires receiver_of&lt;R&gt;
    connect_result_t&lt;const E &amp;, R&gt; connect(R&amp;&amp; r) const &amp; {
      return execution::connect(ex_, (R&amp;&amp;) r);
    }
  };</code></pre></li>
<li><p>Otherwise, <code>execution::schedule(s)</code> is ill-formed.</p></li>
</ul>
</section>
<section id="executionbulk_execute" data-number="2.2.3.9">
<h4 data-number="2.2.3.9"><span class="header-section-number">2.2.3.9</span> <code>execution::bulk_execute</code></h4>
<p>The name <code>execution::bulk_execute</code> denotes a customization point object. If <code>is_convertible_v&lt;N, size_t&gt;</code> is true, then the expression <code>execution::bulk_execute(S, F, N)</code> for some subexpressions <code>S</code>, <code>F</code>, and <code>N</code> is expression-equivalent to:</p>
<ul>
<li><p><code>S.bulk_execute(F, N)</code>, if that expression is valid. If the function selected does not execute <code>N</code> invocations of the function object <code>F</code> on the executor <code>S</code> in bulk with forward progress guarantee <code>std::query(S, execution::bulk_guarantee)</code>, and the result of that function does not model <code>sender&lt;void&gt;</code>, the program is ill-formed with no diagnostic required.</p></li>
<li><p>Otherwise, <code>bulk_execute(S, F, N)</code>, if that expression is valid, with overload resolution performed in a context that includes the declaration</p>
<pre><code>  void bulk_execute();</code></pre>
<p>and that does not include a declaration of <code>execution::bulk_execute</code>. If the function selected by overload resolution does not execute <code>N</code> invocations of the function object <code>F</code> on the executor <code>S</code> in bulk with forward progress guarantee <code>std::query(E, execution::bulk_guarantee)</code>, and the result of that function does not model <code>sender&lt;void&gt;</code>, the program is ill-formed with no diagnostic required.</p></li>
<li><p>Otherwise, if the types <code>F</code> and <code>executor_index_t&lt;remove_cvref_t&lt;S&gt;&gt;</code> model <code>invocable</code> and if <code>std::query(S, execution::bulk_guarantee)</code> equals <code>execution::bulk_guarantee.unsequenced</code>, then</p>
<ul>
<li>Evaluates <code>DECAY_COPY(std::forward&lt;decltype(F)&gt;(F))</code> on the calling thread to create a function object <code>cf</code>. [<em>Note:</em> Additional copies of <code>cf</code> may subsequently be created. <em>–end note.</em>]</li>
<li>For each value of <code>i</code> in <code>N</code>, <code>cf(i)</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>.</li>
<li>May block pending completion of one or more invocations of <code>cf</code>.</li>
<li>Synchronizes with (C++Std [intro.multithread]) the invocations of <code>cf</code>.</li>
</ul></li>
<li><p>Otherwise, <code>execution::bulk_execute(S, F, N)</code> is ill-formed.</p></li>
</ul>
<p>[<em>Editorial note:</em> We should probably define what “execute <code>N</code> invocations of the function object <code>F</code> on the executor <code>S</code> in bulk” means more carefully. <em>–end editorial note</em>]</p>
</section>
</section>
<section id="concepts-receiver-and-receiver_of" data-number="2.2.4">
<h3 data-number="2.2.4"><span class="header-section-number">2.2.4</span> Concepts <code>receiver</code> and <code>receiver_of</code></h3>
<p>A receiver represents the continuation of an asynchronous operation. An asynchronous operation may complete with a (possibly empty) set of values, an error, or it may be cancelled. A receiver has three principal operations corresponding to the three ways an asynchronous operation may complete: <code>set_value</code>, <code>set_error</code>, and <code>set_done</code>. These are collectively known as a receiver’s <em>completion-signal operations</em>.</p>
<pre><code>    template&lt;class T, class E = exception_ptr&gt;
    concept receiver =
      move_constructible&lt;remove_cvref_t&lt;T&gt;&gt; &amp;&amp;
      constructible_from&lt;remove_cvref_t&lt;T&gt;, T&gt; &amp;&amp;
      requires(remove_cvref_t&lt;T&gt;&amp;&amp; t, E&amp;&amp; e) {
        { execution::set_done(std::move(t)) } noexcept;
        { execution::set_error(std::move(t), (E&amp;&amp;) e) } noexcept;
      };

    template&lt;class T, class... An&gt;
    concept receiver_of =
      receiver&lt;T&gt; &amp;&amp;
      requires(remove_cvref_t&lt;T&gt;&amp;&amp; t, An&amp;&amp;... an) {
        execution::set_value(std::move(t), (An&amp;&amp;) an...);
      };</code></pre>
<p>The receiver’s completion-signal operations have semantic requirements that are collectively known as the <em>receiver contract</em>, described below:</p>
<ul>
<li><p>None of a receiver’s completion-signal operations shall be invoked before <code>execution::start</code> has been called on the operation state object that was returned by <code>execution::connect</code> to connect that receiver to a sender.</p></li>
<li><p>Once <code>execution::start</code> has been called on the operation state object, exactly one of the receiver’s completion-signal operations shall complete non-exceptionally before the receiver is destroyed.</p></li>
<li><p>If <code>execution::set_value</code> exits with an exception, it is still valid to call <code>execution::set_error</code> or <code>execution::set_done</code> on the receiver.</p></li>
</ul>
<p>Once one of a receiver’s completion-signal operations has completed non-exceptionally, the receiver contract has been satisfied.</p>
</section>
<section id="concept-operation_state" data-number="2.2.5">
<h3 data-number="2.2.5"><span class="header-section-number">2.2.5</span> Concept <code>operation_state</code></h3>
<pre><code>    template&lt;class O&gt;
      concept operation_state =
        destructible&lt;O&gt; &amp;&amp;
        is_object_v&lt;O&gt; &amp;&amp;
        requires (O&amp; o) {
          { execution::start(o) } noexcept;
        };</code></pre>
<p>An object whose type satisfies <code>operation_state</code> represents the state of an asynchronous operation. It is the result of calling <code>execution::connect</code> with a <code>sender</code> and a <code>receiver</code>.</p>
<p><code>execution::start</code> may be called on an <code>operation_state</code> object at most once. Once <code>execution::start</code> has been invoked, the caller shall ensure that the start of a non-exceptional invocation of one of the receiver’s completion-signalling operations strongly happens before [intro.multithread] the call to the <code>operation_state</code> destructor.</p>
<p>The start of the invocation of <code>execution::start</code> shall strongly happen before [intro.multithread] the invocation of one of the three receiver operations.</p>
<p><code>execution::start</code> may or may not block pending the successful transfer of execution to one of the three receiver operations.</p>
</section>
<section id="concepts-sender-and-sender_to" data-number="2.2.6">
<h3 data-number="2.2.6"><span class="header-section-number">2.2.6</span> Concepts <code>sender</code> and <code>sender_to</code></h3>
<p>XXX TODO The <code>sender</code> and <code>sender_to</code> concepts…</p>
<pre><code>    template&lt;class S&gt;
      concept sender =
        move_constructible&lt;remove_cvref_t&lt;S&gt;&gt; &amp;&amp;
        !requires {
          typename sender_traits&lt;remove_cvref_t&lt;S&gt;&gt;::__unspecialized; // exposition only
        };

    template&lt;class S, class R&gt;
      concept sender_to =
        sender&lt;S&gt; &amp;&amp;
        receiver&lt;R&gt; &amp;&amp;
        requires (S&amp;&amp; s, R&amp;&amp; r) {
          execution::connect((S&amp;&amp;) s, (R&amp;&amp;) r);
        };</code></pre>
<p>None of these operations shall introduce data races as a result of concurrent invocations of those functions from different threads.</p>
<p>A sender 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>
</section>
<section id="concept-typed_sender" data-number="2.2.7">
<h3 data-number="2.2.7"><span class="header-section-number">2.2.7</span> Concept <code>typed_sender</code></h3>
<p>A sender is <em>typed</em> if it declares what types it sends through a receiver’s channels. The <code>typed_sender</code> concept is defined as:</p>
<pre><code>    template&lt;template&lt;template&lt;class...&gt; class Tuple, template&lt;class...&gt; class Variant&gt; class&gt;
      struct has-value-types; // exposition only

    template&lt;template&lt;class...&gt; class Variant&gt;
      struct has-error-types; // exposition only

    template&lt;class S&gt;
      concept has-sender-types = // exposition only
        requires {
          typename has-value-types&lt;S::template value_types&gt;;
          typename has-error-types&lt;S::template error_types&gt;;
          typename bool_constant&lt;S::sends_done&gt;;
        };

    template&lt;class S&gt;
      concept typed_sender =
        sender&lt;S&gt; &amp;&amp;
        has-sender-types&lt;sender_traits&lt;remove_cvref_t&lt;S&gt;&gt;&gt;;</code></pre>
</section>
<section id="concept-scheduler" data-number="2.2.8">
<h3 data-number="2.2.8"><span class="header-section-number">2.2.8</span> Concept <code>scheduler</code></h3>
<p>XXX TODO The <code>scheduler</code> concept…</p>
<pre><code>    template&lt;class S&gt;
      concept scheduler =
        copy_constructible&lt;remove_cvref_t&lt;S&gt;&gt; &amp;&amp;
        equality_comparable&lt;remove_cvref_t&lt;S&gt;&gt; &amp;&amp;
        requires(E&amp;&amp; e) {
          execution::schedule((E&amp;&amp;)e);
        };</code></pre>
<p>None of a scheduler’s copy constructor, destructor, equality comparison, or <code>swap</code> operation shall exit via an exception.</p>
<p>None of these operations, nor an scheduler type’s <code>schedule</code> function, or associated query functions 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 scheduler 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 scheduler 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 scheduler 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 scheduler type’s destructor shall not block pending completion of any receivers submitted to the sender objects returned from <code>schedule</code>. [<em>Note:</em> The ability to wait for completion of submitted function objects may be provided by the execution context that produced the scheduler. <em>–end note</em>]</p>
<p>In addition to the above requirements, type <code>S</code> models <code>scheduler</code> only if it satisfies the requirements in the Table below.</p>
<p>In the Table below,</p>
<ul>
<li><code>s</code> denotes a (possibly const) scheduler object of type <code>S</code>,</li>
<li><code>N</code> denotes a type that models <code>sender</code>, and</li>
<li><code>n</code> denotes a sender object of type <code>N</code></li>
</ul>
<table>
<colgroup>
<col style="width: 25%" />
<col style="width: 27%" />
<col style="width: 46%" />
</colgroup>
<thead>
<tr class="header">
<th>Expression</th>
<th>Return Type</th>
<th>Operational semantics</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td><code>execution::schedule(s)</code></td>
<td><code>N</code></td>
<td>Evaluates <code>execution::schedule(s)</code> on the calling thread to create <code>N</code>.</td>
</tr>
</tbody>
</table>
<p><code>execution::start(o)</code>, where <code>o</code> is the result of a call to <code>execution::connect(N, r)</code> for some receiver object <code>r</code>, is required to eagerly submit <code>r</code> for execution on an execution agent that <code>s</code> creates for it. Let <code>rc</code> be <code>r</code> or an object created by copy or move construction from <code>r</code>. The semantic constraints on the <code>sender</code> <code>N</code> returned from a scheduler <code>s</code>’s <code>schedule</code> function are as follows:</p>
<ul>
<li><p>If <code>rc</code>’s <code>set_error</code> function is called in response to a submission error, scheduling error, or other internal error, let <code>E</code> be an expression that refers to that error if <code>set_error(rc, E)</code> is well-formed; otherwise, let <code>E</code> be an <code>exception_ptr</code> that refers to that error. [ <em>Note:</em> <code>E</code> could be the result of calling <code>current_exception</code> or <code>make_exception_ptr</code> — <em>end note</em> ] The scheduler calls <code>set_error(rc, E)</code> on an unspecified weakly-parallel execution agent ([ <em>Note:</em> An invocation of <code>set_error</code> on a receiver is required to be <code>noexcept</code> — <em>end note</em>]), and</p></li>
<li><p>If <code>rc</code>’s <code>set_error</code> function is called in response to an exception that propagates out of the invocation of <code>set_value</code> on <code>rc</code>, let <code>E</code> be <code>make_exception_ptr(receiver_invocation_error{})</code> invoked from within a catch clause that has caught the exception. The executor calls <code>set_error(rc, E)</code> on an unspecified weakly-parallel execution agent, and</p></li>
<li><p>A call to <code>set_done(rc)</code> is made on an unspecified weakly-parallel execution agent ([ <em>Note:</em> An invocation of a receiver’s <code>set_done</code> function is required to be <code>noexcept</code> — <em>end note</em> ]).</p></li>
</ul>
<p>[ Note: The senders returned from a scheduler’s <code>schedule</code> function have wide discretion when deciding which of the three receiver functions to call upon submission. — <em>end note</em> ]</p>
</section>
<section id="concepts-executor-and-executor_of" data-number="2.2.9">
<h3 data-number="2.2.9"><span class="header-section-number">2.2.9</span> Concepts <code>executor</code> and <code>executor_of</code></h3>
<p>XXX TODO The <code>executor</code> and <code>executor_of</code> concepts…</p>
<p>Let <em><code>executor-of-impl</code></em> be the exposition-only concept</p>
<pre><code>    template&lt;class E, class F&gt;
      concept executor-of-impl =
        invocable&lt;remove_cvref_t&lt;F&gt;&amp;&gt; &amp;&amp;
        constructible_from&lt;remove_cvref_t&lt;F&gt;, F&gt; &amp;&amp;
        move_constructible&lt;remove_cvref_t&lt;F&gt;&gt; &amp;&amp;
        copy_constructible&lt;E&gt; &amp;&amp;
        is_nothrow_copy_constructible_v&lt;E&gt; &amp;&amp;
        equality_comparable&lt;E&gt; &amp;&amp;
        requires(const E&amp; e, F&amp;&amp; f) {
          execution::execute(e, (F&amp;&amp;)f);
        };</code></pre>
<p>Then,</p>
<pre><code>    template&lt;class E&gt;
      concept executor =
        executor-of-impl&lt;E, execution::invocable_archetype&gt;;

    template&lt;class E, class F&gt;
      concept executor_of =
        executor&lt;E&gt; &amp;&amp;
        executor-of-impl&lt;E, F&gt;;</code></pre>
<p>Neither of an executor’s equality comparison or <code>swap</code> operation shall exit via an exception.</p>
<p>None of an executor type’s copy constructor, destructor, equality comparison, <code>swap</code> function, <code>execute</code> function, or associated <code>query</code> functions 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>std::query(x1,p) == std::query(x2,p)</code> for every property <code>p</code> where both <code>std::query(x1,p)</code> and <code>std::query(x2,p)</code> are well-formed and result in a non-void type that is <code>equality_comparable</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>std::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>
<p>In addition to the above requirements, types <code>E</code> and <code>F</code> model <code>executor_of</code> only if they satisfy the requirements of the Table below.</p>
<p>In the Table below,</p>
<ul>
<li><code>e</code> denotes a (possibly const) executor object of type <code>E</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 of type <code>F&amp;&amp;</code> invocable as <code>cf()</code> and where <code>decay_t&lt;F&gt;</code> models <code>move_constructible</code>.</li>
</ul>
<table>
<colgroup>
<col style="width: 25%" />
<col style="width: 27%" />
<col style="width: 46%" />
</colgroup>
<thead>
<tr class="header">
<th>Expression</th>
<th>Return Type</th>
<th>Operational semantics</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td><code>execution::execute(e, f)</code></td>
<td><code>void</code></td>
<td>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 is implementation-defined. The forward progress guarantee of the associated execution agent(s) is implementation-defined. <em>–end note.</em>]</td>
</tr>
</tbody>
</table>
<p>[<em>Editorial note:</em> The operational semantics of <code>execution::execute</code> should be specified with the <code>execution::execute</code> CPO rather than the <code>executor</code> concept. <em>–end note.</em>]</p>
</section>
<section id="sender-and-receiver-traits" data-number="2.2.10">
<h3 data-number="2.2.10"><span class="header-section-number">2.2.10</span> Sender and receiver traits</h3>
<section id="class-template-sender_traits" data-number="2.2.10.1">
<h4 data-number="2.2.10.1"><span class="header-section-number">2.2.10.1</span> Class template <code>sender_traits</code></h4>
<p>XXX TODO The class template<code>sender_traits</code>…</p>
<p>The class template <code>sender_traits</code> can be used to query information about a <code>sender</code>; in particular, what values and errors it sends through a receiver’s value and error channel, and whether or not it ever calls <code>set_done</code> on a receiver.</p>
<p>The primary <code>sender_traits&lt;S&gt;</code> class template is defined as if inheriting from an implementation-defined class template <em><code>sender-traits-base</code></em><code>&lt;S&gt;</code> defined as follows:</p>
<ul>
<li><p>Let <em><code>has-sender-types</code></em> be an implementation-defined concept equivalent to:</p>
<pre><code>  template&lt;template&lt;template&lt;class...&gt; class, template&lt;class...&gt; class&gt; class&gt;
    struct has-value-types ; // exposition only

  template&lt;template&lt;template&lt;class...&gt; class&gt; class&gt;
    struct has-error-types ; // exposition only

  template&lt;class S&gt;
    concept has-sender-types =
      requires {
        typename has-value-types &lt;S::template value_types&gt;;
        typename has-error-types &lt;S::template error_types&gt;;
        typename bool_constant&lt;S::sends_done&gt;;
      };</code></pre>
<p>If <em><code>has-sender-types</code></em><code>&lt;S&gt;</code> is true, then <em><code>sender-traits-base</code></em> is equivalent to:</p>
<pre><code>  template&lt;class S&gt;
    struct sender-traits-base {
      template&lt;template&lt;class...&gt; class Tuple, template&lt;class...&gt; class Variant&gt;
        using value_types = typename S::template value_types&lt;Tuple, Variant&gt;;

      template&lt;template&lt;class...&gt; class Variant&gt;
        using error_types = typename S::template error_types&lt;Variant&gt;;

      static constexpr bool sends_done = S::sends_done;
    };</code></pre></li>
<li><p>Otherwise, let <em><code>void-receiver</code></em> be an implementation-defined class type equivalent to</p>
<pre><code>  struct void-receiver { // exposition only
    void set_value() noexcept;
    void set_error(exception_ptr) noexcept;
    void set_done() noexcept;
  };</code></pre>
<p>If <em><code>executor-of-impl</code></em><code>&lt;S,</code><em><code>as-invocable</code></em><code>&lt;</code><em><code>void-receiver</code></em><code>, S&gt;&gt;</code> is <code>true</code>, then <em><code>sender-traits-base</code></em> is equivalent to</p>
<pre><code>  template&lt;class S&gt;
    struct sender-traits-base {
      template&lt;template&lt;class...&gt; class Tuple, template&lt;class...&gt; class Variant&gt;
        using value_types = Variant&lt;Tuple&lt;&gt;&gt;;

      template&lt;template&lt;class...&gt; class Variant&gt;
        using error_types = Variant&lt;exception_ptr&gt;;

      static constexpr bool sends_done = true;
    };</code></pre></li>
<li><p>Otherwise, if <code>derived_from&lt;S, sender_base&gt;</code> is <code>true</code>, then <em><code>sender-traits-base</code></em> is equivalent to</p>
<pre><code>  template&lt;class S&gt;
    struct sender-traits-base {};</code></pre></li>
<li><p>Otherwise, <em><code>sender-traits-base</code></em> is equivalent to</p>
<pre><code>  template&lt;class S&gt;
    struct sender-traits-base {
      using __unspecialized = void; // exposition only
    };</code></pre></li>
</ul>
<p>Because a sender may send one set of types or another to a receiver based on some runtime condition, <code>sender_traits</code> may provide a nested <code>value_types</code> template that is parameterized on a tuple-like class template and a variant-like class template that are used to hold the result.</p>
<p>[<em>Example:</em> If a sender type <code>S</code> sends types <code>As...</code> or <code>Bs...</code> to a receiver’s value channel, it may specialize <code>sender_traits</code> such that <code>typename sender_traits&lt;S&gt;::value_types&lt;tuple, variant&gt;</code> names the type <code>variant&lt;tuple&lt;As...&gt;, tuple&lt;Bs...&gt;&gt;</code> – <em>end example</em>]</p>
<p>Because a sender may send one or another type of error types to a receiver, <code>sender_traits</code> may provide a nested <code>error_types</code> template that is parameterized on a variant-like class template that is used to hold the result.</p>
<p>[<em>Example:</em> If a sender type <code>S</code> sends error types <code>exception_ptr</code> or <code>error_code</code> to a receiver’s error channel, it may specialize <code>sender_traits</code> such that <code>typename sender_traits&lt;S&gt;::error_types&lt;variant&gt;</code> names the type <code>variant&lt;exception_ptr, error_code&gt;</code> – <em>end example</em>]</p>
<p>A sender type can signal that it never calls <code>set_done</code> on a receiver by specializing <code>sender_traits</code> such that <code>sender_traits&lt;S&gt;::sends_done</code> is <code>false</code>; conversely, it may set <code>sender_traits&lt;S&gt;::sends_done</code> to <code>true</code> to indicate that it does call <code>set_done</code> on a receiver.</p>
<p>Users may specialize <code>sender_traits</code> on program-defined types.</p>
</section>
</section>
<section id="query-only-properties" data-number="2.2.11">
<h3 data-number="2.2.11"><span class="header-section-number">2.2.11</span> Query-only properties</h3>
<section id="associated-execution-context-property" data-number="2.2.11.1">
<h4 data-number="2.2.11.1"><span class="header-section-number">2.2.11.1</span> Associated execution context property</h4>
<pre><code>struct context_t
{
  template &lt;class T&gt;
    static constexpr bool is_applicable_property_v = executor&lt;T&gt;;

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

  using polymorphic_query_result_type = any;

  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>std::query(e, context_t)</code>, where <code>e</code> is an executor, shall not change between invocations.</p>
</section>
<section id="polymorphic-executor-wrappers" data-number="2.2.11.2">
<h4 data-number="2.2.11.2"><span class="header-section-number">2.2.11.2</span> Polymorphic executor wrappers</h4>
<p>The <code>any_executor</code> class template provides polymorphic wrappers for executors.</p>
<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>
<pre><code>template &lt;class... SupportableProperties&gt;
class any_executor
{
public:
  // construct / copy / destroy:

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

  any_executor&amp; operator=(const any_executor&amp; e) noexcept;
  any_executor&amp; operator=(any_executor&amp;&amp; e) noexcept;
  any_executor&amp; operator=(nullptr_t) noexcept;
  template&lt;executor Executor&gt;
    any_executor&amp; operator=(Executor e);

  ~any_executor();

  // any_executor modifiers:

  void swap(any_executor&amp; other) noexcept;

  // any_executor operations:

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

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

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

  // any_executor capacity:

  explicit operator bool() const noexcept;

  // any_executor target access:

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

// any_executor comparisons:

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

// any_executor specialized algorithms:

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

template &lt;class Property, class... SupportableProperties&gt;
any_executor prefer(const any_executor&lt;SupportableProperties&gt;&amp; e, Property p);</code></pre>
<p>The <code>any_executor</code> class satisfies the <code>executor</code> concept requirements.</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>any_executor</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>
<section id="any_executor-constructors" data-number="2.2.11.2.1">
<h5 data-number="2.2.11.2.1"><span class="header-section-number">2.2.11.2.1</span> <code>any_executor</code> constructors</h5>
<pre><code>any_executor() noexcept;</code></pre>
<p><em>Postconditions:</em> <code>!*this</code>.</p>
<pre><code>any_executor(nullptr_t) noexcept;</code></pre>
<p><em>Postconditions:</em> <code>!*this</code>.</p>
<pre><code>any_executor(const any_executor&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>any_executor(any_executor&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;
  any_executor(any_executor&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;
  any_executor(any_executor&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>
<pre><code>template&lt;executor Executor&gt;
  any_executor(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, 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>e</code>.</p>
</section>
<section id="any_executor-assignment" data-number="2.2.11.2.2">
<h5 data-number="2.2.11.2.2"><span class="header-section-number">2.2.11.2.2</span> <code>any_executor</code> assignment</h5>
<pre><code>any_executor&amp; operator=(const any_executor&amp; e) noexcept;</code></pre>
<p><em>Effects:</em> <code>any_executor(e).swap(*this)</code>.</p>
<p><em>Returns:</em> <code>*this</code>.</p>
<pre><code>any_executor&amp; operator=(any_executor&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>any_executor&amp; operator=(nullptr_t) noexcept;</code></pre>
<p><em>Effects:</em> <code>any_executor(nullptr).swap(*this)</code>.</p>
<p><em>Returns:</em> <code>*this</code>.</p>
<pre><code>template&lt;executor Executor&gt;
  any_executor&amp; operator=(Executor e);</code></pre>
<p><em>Requires:</em> As for <code>template&lt;executor Executor&gt; any_executor(Executor e)</code>.</p>
<p><em>Effects:</em> <code>any_executor(std::move(e)).swap(*this)</code>.</p>
<p><em>Returns:</em> <code>*this</code>.</p>
</section>
<section id="any_executor-destructor" data-number="2.2.11.2.3">
<h5 data-number="2.2.11.2.3"><span class="header-section-number">2.2.11.2.3</span> <code>any_executor</code> destructor</h5>
<pre><code>~any_executor();</code></pre>
<p><em>Effects:</em> If <code>*this != nullptr</code>, releases shared ownership of, or destroys, the target of <code>*this</code>.</p>
</section>
<section id="any_executor-modifiers" data-number="2.2.11.2.4">
<h5 data-number="2.2.11.2.4"><span class="header-section-number">2.2.11.2.4</span> <code>any_executor</code> modifiers</h5>
<pre><code>void swap(any_executor&amp; other) noexcept;</code></pre>
<p><em>Effects:</em> Interchanges the targets of <code>*this</code> and <code>other</code>.</p>
</section>
<section id="any_executor-operations" data-number="2.2.11.2.5">
<h5 data-number="2.2.11.2.5"><span class="header-section-number">2.2.11.2.5</span> <code>any_executor</code> operations</h5>
<pre><code>template &lt;class Property&gt;
any_executor 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>std::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>std::query(e, p)</code> is well-formed, <code>static_cast&lt;Property::polymorphic_query_result_type&gt;(std::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>
<pre><code>template&lt;class Function&gt;
  void execute(Function&amp;&amp; f) const;</code></pre>
<p><em>Effects:</em> Performs <code>execution::execute(e, 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>
</section>
<section id="any_executor-capacity" data-number="2.2.11.2.6">
<h5 data-number="2.2.11.2.6"><span class="header-section-number">2.2.11.2.6</span> <code>any_executor</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>
</section>
<section id="any_executor-target-access" data-number="2.2.11.2.7">
<h5 data-number="2.2.11.2.7"><span class="header-section-number">2.2.11.2.7</span> <code>any_executor</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;executor Executor&gt; Executor* target() noexcept;
template&lt;executor 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>
</section>
<section id="any_executor-comparisons" data-number="2.2.11.2.8">
<h5 data-number="2.2.11.2.8"><span class="header-section-number">2.2.11.2.8</span> <code>any_executor</code> comparisons</h5>
<pre><code>template&lt;class... SupportableProperties&gt;
bool operator==(const any_executor&lt;SupportableProperties...&gt;&amp; a, const any_executor&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 any_executor&lt;SupportableProperties...&gt;&amp; e, nullptr_t) noexcept;
template&lt;class... SupportableProperties&gt;
bool operator==(nullptr_t, const any_executor&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 any_executor&lt;SupportableProperties...&gt;&amp; a, const any_executor&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 any_executor&lt;SupportableProperties...&gt;&amp; e, nullptr_t) noexcept;
template&lt;class... SupportableProperties&gt;
bool operator!=(nullptr_t, const any_executor&lt;SupportableProperties...&gt;&amp; e) noexcept;</code></pre>
<p><em>Returns:</em> <code>(bool) e</code>.</p>
</section>
<section id="any_executor-specialized-algorithms" data-number="2.2.11.2.9">
<h5 data-number="2.2.11.2.9"><span class="header-section-number">2.2.11.2.9</span> <code>any_executor</code> specialized algorithms</h5>
<pre><code>template&lt;class... SupportableProperties&gt;
void swap(any_executor&lt;SupportableProperties...&gt;&amp; a, any_executor&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;
any_executor prefer(const any_executor&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>std::prefer(e, p)</code>, where <code>e</code> is the target object of <code>*this</code>.</p>
</section>
</section>
</section>
<section id="behavioral-properties" data-number="2.2.12">
<h3 data-number="2.2.12"><span class="header-section-number">2.2.12</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
{
  template &lt;class T&gt;
    static constexpr bool is_applicable_property_v = executor&lt;T&gt;;

  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 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 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>std::query(e1, p1)</code> and a subsequent invocation <code>std::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>std::require(e1, p2)</code> or <code>std::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(std::query(ex, std::declval&lt;const S::Nk&gt;())));</code></pre>
<p><em>Returns:</em> <code>std::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>
<section id="blocking-properties" data-number="2.2.12.1">
<h4 data-number="2.2.12.1"><span class="header-section-number">2.2.12.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>
<colgroup>
<col style="width: 40%" />
<col style="width: 37%" />
<col style="width: 21%" />
</colgroup>
<thead>
<tr class="header">
<th>Nested Property Type</th>
<th>Nested Property Object Name</th>
<th>Requirements</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td><code>blocking_t::possibly_t</code></td>
<td><code>blocking.possibly</code></td>
<td>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><code>blocking_t::always_t</code></td>
<td><code>blocking.always</code></td>
<td>Invocation of an executor’s execution function shall block until completion of all invocations of submitted function object.</td>
</tr>
<tr class="odd">
<td><code>blocking_t::never_t</code></td>
<td><code>blocking.never</code></td>
<td>Invocation of an executor’s execution function shall not block pending completion of the invocations of the submitted function object.</td>
</tr>
</tbody>
</table>
<section id="blocking_talways_t-customization-points" data-number="2.2.12.1.1">
<h5 data-number="2.2.12.1.1"><span class="header-section-number">2.2.12.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
{
  static constexpr bool is_requirable = true;
  static constexpr bool is_preferable = false;

  template &lt;class T&gt;
    static constexpr bool is_applicable_property_v = executor&lt;T&gt;;

  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>. <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>std::query(e1,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>
</section>
</section>
<section id="properties-to-indicate-if-blocking-and-directionality-may-be-adapted" data-number="2.2.12.2">
<h4 data-number="2.2.12.2"><span class="header-section-number">2.2.12.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>std::require</code>.</p>
<p><code>blocking_adaptation_t</code> provides nested property types and objects as described below.</p>
<table style="width:100%;">
<colgroup>
<col style="width: 35%" />
<col style="width: 45%" />
<col style="width: 19%" />
</colgroup>
<thead>
<tr class="header">
<th>Nested Property Type</th>
<th>Nested Property Object Name</th>
<th>Requirements</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td><code>blocking_adaptation_t::disallowed_t</code></td>
<td><code>blocking_adaptation.disallowed</code></td>
<td>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><code>blocking_adaptation_t::allowed_t</code></td>
<td><code>blocking_adaptation.allowed</code></td>
<td>The <code>require</code> customization point may adapt the executor to add the <code>blocking_t::always_t</code> property.</td>
</tr>
</tbody>
</table>
<section id="blocking_adaptation_tallowed_t-customization-points" data-number="2.2.12.2.1">
<h5 data-number="2.2.12.2.1"><span class="header-section-number">2.2.12.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
{
  static constexpr bool is_requirable = true;
  static constexpr bool is_preferable = false;

  template &lt;class T&gt;
    static constexpr bool is_applicable_property_v = executor&lt;T&gt;;

  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>. 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>
</section>
</section>
<section id="properties-to-indicate-if-submitted-tasks-represent-continuations" data-number="2.2.12.3">
<h4 data-number="2.2.12.3"><span class="header-section-number">2.2.12.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 style="width:100%;">
<colgroup>
<col style="width: 35%" />
<col style="width: 45%" />
<col style="width: 19%" />
</colgroup>
<thead>
<tr class="header">
<th>Nested Property Type</th>
<th>Nested Property Object Name</th>
<th>Requirements</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td><code>relationship_t::fork_t</code></td>
<td><code>relationship.fork</code></td>
<td>Function objects submitted through the executor do not represent continuations of the caller.</td>
</tr>
<tr class="even">
<td><code>relationship_t::continuation_t</code></td>
<td><code>relationship.continuation</code></td>
<td>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>
</section>
<section id="properties-to-indicate-likely-task-submission-in-the-future" data-number="2.2.12.4">
<h4 data-number="2.2.12.4"><span class="header-section-number">2.2.12.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 style="width: 34%" />
<col style="width: 45%" />
<col style="width: 19%" />
</colgroup>
<thead>
<tr class="header">
<th>Nested Property Type</th>
<th>Nested Property Object Name</th>
<th>Requirements</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td><code>outstanding_work_t::untracked_t</code></td>
<td><code>outstanding_work.untracked</code></td>
<td>The existence of the executor object does not indicate any likely future submission of a function object.</td>
</tr>
<tr class="even">
<td><code>outstanding_work_t::tracked_t</code></td>
<td><code>outstanding_work.tracked</code></td>
<td>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>
</section>
<section id="properties-for-bulk-execution-guarantees" data-number="2.2.12.5">
<h4 data-number="2.2.12.5"><span class="header-section-number">2.2.12.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 style="width:100%;">
<colgroup>
<col style="width: 35%" />
<col style="width: 45%" />
<col style="width: 19%" />
</colgroup>
<thead>
<tr class="header">
<th>Nested Property Type</th>
<th>Nested Property Object Name</th>
<th>Requirements</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td><code>bulk_guarantee_t::unsequenced_t</code></td>
<td><code>bulk_guarantee.unsequenced</code></td>
<td>Execution agents within the same bulk execution may be parallelized and vectorized.</td>
</tr>
<tr class="even">
<td><code>bulk_guarantee_t::sequenced_t</code></td>
<td><code>bulk_guarantee.sequenced</code></td>
<td>Execution agents within the same bulk execution may not be parallelized.</td>
</tr>
<tr class="odd">
<td><code>bulk_guarantee_t::parallel_t</code></td>
<td><code>bulk_guarantee.parallel</code></td>
<td>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>
</section>
<section id="properties-for-mapping-of-execution-on-to-threads" data-number="2.2.12.6">
<h4 data-number="2.2.12.6"><span class="header-section-number">2.2.12.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 style="width: 34%" />
<col style="width: 45%" />
<col style="width: 19%" />
</colgroup>
<thead>
<tr class="header">
<th>Nested Property Type</th>
<th>Nested Property Object Name</th>
<th>Requirements</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td><code>mapping_t::thread_t</code></td>
<td><code>mapping.thread</code></td>
<td>Execution agents are mapped onto threads of execution.</td>
</tr>
<tr class="even">
<td><code>mapping_t::new_thread_t</code></td>
<td><code>mapping.new_thread</code></td>
<td>Each execution agent is mapped onto a new thread of execution.</td>
</tr>
<tr class="odd">
<td><code>mapping_t::other_t</code></td>
<td><code>mapping.other</code></td>
<td>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>
</section>
</section>
<section id="properties-for-customizing-memory-allocation" data-number="2.2.13">
<h3 data-number="2.2.13"><span class="header-section-number">2.2.13</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
{
    template &lt;class T&gt;
      static constexpr bool is_applicable_property_v = executor&lt;T&gt;;

    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>
<colgroup>
<col style="width: 32%" />
<col style="width: 22%" />
<col style="width: 45%" />
</colgroup>
<thead>
<tr class="header">
<th>Property</th>
<th>Notes</th>
<th>Requirements</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td><code>allocator_t&lt;ProtoAllocator&gt;</code></td>
<td>Result of <code>allocator_t&lt;void&gt;::operator(OtherProtoAllocator)</code>.</td>
<td>The executor shall use the encapsulated allocator to allocate any memory required to store the submitted function object.</td>
</tr>
<tr class="even">
<td><code>allocator_t&lt;void&gt;</code></td>
<td>Specialisation of <code>allocator_t&lt;ProtoAllocator&gt;</code>.</td>
<td>The executor 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>std::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>std::query(E, P)</code> shall satisfy the <code>ProtoAllocator</code> requirements; * the result of the expression <code>std::query(E, P)</code> shall be the allocator currently established in the executor <code>E</code>; and * the expression <code>std::query(E, allocator_t&lt;void&gt;{})</code> shall also be well formed and have the same result as <code>std::query(E, P)</code>.</p>
<section id="allocator_t-members" data-number="2.2.13.1">
<h4 data-number="2.2.13.1"><span class="header-section-number">2.2.13.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>
</section>
</section>
</section>
<section id="executor-type-traits" data-number="2.3">
<h2 data-number="2.3"><span class="header-section-number">2.3</span> Executor type traits</h2>
<section id="associated-shape-type" data-number="2.3.1">
<h3 data-number="2.3.1"><span class="header-section-number">2.3.1</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(std::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>
</section>
<section id="associated-index-type" data-number="2.3.2">
<h3 data-number="2.3.2"><span class="header-section-number">2.3.2</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(std::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>
</section>
</section>
<section id="polymorphic-executor-support" data-number="2.4">
<h2 data-number="2.4"><span class="header-section-number">2.4</span> Polymorphic executor support</h2>
<section id="class-bad_executor" data-number="2.4.1">
<h3 data-number="2.4.1"><span class="header-section-number">2.4.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>
<section id="bad_executor-constructors" data-number="2.4.1.1">
<h4 data-number="2.4.1.1"><span class="header-section-number">2.4.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>
</section>
</section>
<section id="struct-prefer_only" data-number="2.4.2">
<h3 data-number="2.4.2"><span class="header-section-number">2.4.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, class Callback&gt;
void do_async_work(
    Executor ex,
    Callback callback)
{
  if (try_work() == done)
  {
    // Work completed immediately, invoke callback.
    std::require(ex,
        execution::single,
        execution::oneway,
      ).execute(callback);
  }
  else
  {
    // Perform work in background. Track outstanding work.
    start_background_work(
        std::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>std::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.
    std::require(ex,
        execution::single,
        execution::oneway,
      ).execute(callback);
  }
  else
  {
    // Perform work in background. Track outstanding work.
    start_background_work(
        std::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>std::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(std::prefer(std::move(ex), std::declval&lt;const InnerProperty&gt;())))
      -&gt; decltype(std::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(std::query(ex, std::declval&lt;const InnerProperty&gt;())))
      -&gt; decltype(std::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(std::prefer(std::move(ex), std::declval&lt;const InnerProperty&gt;())))
    -&gt; decltype(std::prefer(std::move(ex), std::declval&lt;const InnerProperty&gt;()));</code></pre>
<p><em>Returns:</em> <code>std::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>std::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(std::query(ex, std::declval&lt;const InnerProperty&gt;())))
    -&gt; decltype(std::query(ex, std::declval&lt;const InnerProperty&gt;()));</code></pre>
<p><em>Returns:</em> <code>std::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>std::query(ex, p.property)</code> is well-formed.</p>
</section>
</section>
<section id="thread-pools" data-number="2.5">
<h2 data-number="2.5"><span class="header-section-number">2.5</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>
<section id="header-thread_pool-synopsis" data-number="2.5.1">
<h3 data-number="2.5.1"><span class="header-section-number">2.5.1</span> Header <code>&lt;thread_pool&gt;</code> synopsis</h3>
<pre><code>namespace std {

  class static_thread_pool;

} // namespace std</code></pre>
</section>
<section id="class-static_thread_pool" data-number="2.5.2">
<h3 data-number="2.5.2"><span class="header-section-number">2.5.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 scheduler_type = see-below;
    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 schedulers from 
    // standard contexts.
    scheduler_type scheduler() noexcept;

    // 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, scheduler and sender, 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>scheduler</code>, <code>executor</code>, <code>attach</code>, <code>wait</code>, and <code>stop</code>, and the associated schedulers’, senders` and 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>
<section id="types" data-number="2.5.2.1">
<h4 data-number="2.5.2.1"><span class="header-section-number">2.5.2.1</span> Types</h4>
<pre><code>using scheduler_type = see-below;</code></pre>
<p>A scheduler type conforming to the specification for <code>static_thread_pool</code> scheduler types described below.</p>
<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>
</section>
<section id="construction-and-destruction" data-number="2.5.2.2">
<h4 data-number="2.5.2.2"><span class="header-section-number">2.5.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>
</section>
<section id="worker-management" data-number="2.5.2.3">
<h4 data-number="2.5.2.3"><span class="header-section-number">2.5.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>
</section>
<section id="scheduler-creation" data-number="2.5.2.4">
<h4 data-number="2.5.2.4"><span class="header-section-number">2.5.2.4</span> Scheduler creation</h4>
<pre><code>scheduler_type scheduler() noexcept;</code></pre>
<p><em>Returns:</em> A scheduler that may be used to create sender objects that may be used to submit receiver objects to the thread pool. The returned scheduler has the following properties already established:</p>
<ul>
<li><code>execution::allocator</code></li>
<li><code>execution::allocator(std::allocator&lt;void&gt;())</code></li>
</ul>
</section>
<section id="executor-creation" data-number="2.5.2.5">
<h4 data-number="2.5.2.5"><span class="header-section-number">2.5.2.5</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>
</section>
</section>
<section id="static_thread_pool-scheduler-types" data-number="2.5.3">
<h3 data-number="2.5.3"><span class="header-section-number">2.5.3</span> <code>static_thread_pool</code> scheduler types</h3>
<p>All scheduler types accessible through <code>static_thread_pool::scheduler()</code>, and subsequent invocations of the member function <code>require</code>, conform to the following specification.</p>
<pre><code>class C
{
  public:

    // types:

    using sender_type = see-below;

    // 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;

    // scheduler operations:

    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;

    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>
<section id="constructors" data-number="2.5.3.1">
<h4 data-number="2.5.3.1"><span class="header-section-number">2.5.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>
</section>
<section id="assignment" data-number="2.5.3.2">
<h4 data-number="2.5.3.2"><span class="header-section-number">2.5.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>
</section>
<section id="operations" data-number="2.5.3.3">
<h4 data-number="2.5.3.3"><span class="header-section-number">2.5.3.3</span> Operations</h4>
<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 scheduler 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 scheduler object are identical to those 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>
</section>
<section id="comparisons" data-number="2.5.3.4">
<h4 data-number="2.5.3.4"><span class="header-section-number">2.5.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>
</section>
<section id="static_thread_pool-scheduler-functions" data-number="2.5.3.5">
<h4 data-number="2.5.3.5"><span class="header-section-number">2.5.3.5</span> <code>static_thread_pool</code> scheduler functions</h4>
<p>In addition to conforming to the above specification, <code>static_thread_pool</code> schedulers shall conform to the following specification.</p>
<pre><code>class C
{
  public:
    sender_type schedule() noexcept;
};</code></pre>
<p><code>C</code> is a type satisfying the <code>scheduler</code> requirements.</p>
</section>
<section id="sender-creation" data-number="2.5.3.6">
<h4 data-number="2.5.3.6"><span class="header-section-number">2.5.3.6</span> Sender creation</h4>
<pre><code>  sender_type schedule() noexcept;</code></pre>
<p><em>Returns:</em> A sender that may be used to submit function objects to the thread pool. The returned sender 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>
</section>
</section>
<section id="static_thread_pool-sender-types" data-number="2.5.4">
<h3 data-number="2.5.4"><span class="header-section-number">2.5.4</span> <code>static_thread_pool</code> sender types</h3>
<p>All sender types accessible through <code>static_thread_pool::scheduler().schedule()</code>, and subsequent invocations of the member function <code>require</code>, conform to the following specification.</p>
<pre><code>class C
{
  public:

    // 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;

    // sender 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>
<section id="constructors-1" data-number="2.5.4.1">
<h4 data-number="2.5.4.1"><span class="header-section-number">2.5.4.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>
</section>
<section id="assignment-1" data-number="2.5.4.2">
<h4 data-number="2.5.4.2"><span class="header-section-number">2.5.4.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>
</section>
<section id="operations-1" data-number="2.5.4.3">
<h4 data-number="2.5.4.3"><span class="header-section-number">2.5.4.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 sender 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 sender object. All other properties of the returned sender 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 sender 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 sender 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 sender, 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>
</section>
<section id="comparisons-1" data-number="2.5.4.4">
<h4 data-number="2.5.4.4"><span class="header-section-number">2.5.4.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>
</section>
<section id="static_thread_pool-sender-execution-functions" data-number="2.5.4.5">
<h4 data-number="2.5.4.5"><span class="header-section-number">2.5.4.5</span> <code>static_thread_pool</code> sender execution functions</h4>
<p>In addition to conforming to the above specification, <code>static_thread_pool</code> <code>scheduler</code>s’ senders shall conform to the following specification.</p>
<pre><code>class C
{
  public:
    template&lt;template&lt;class...&gt; class Tuple, template&lt;class...&gt; class Variant&gt;
      using value_types = Variant&lt;Tuple&lt;&gt;&gt;;
    template&lt;template&lt;class...&gt; class Variant&gt;
      using error_types = Variant&lt;&gt;;
    static constexpr bool sends_done = true;

    template&lt;receiver_of R&gt;
      see-below connect(R&amp;&amp; r) const;
};</code></pre>
<p><code>C</code> is a type satisfying the <code>typed_sender</code> requirements.</p>
<pre><code>template&lt;receiver_of R&gt;
  see-below connect(R&amp;&amp; r) const;</code></pre>
<p><em>Returns:</em> An object whose type satisfies the <code>operation_state</code> concept.</p>
<p><em>Effects:</em> When <code>execution::start</code> is called on the returned operation state, the receiver <code>r</code> is submitted for execution on the <code>static_thread_pool</code> according to the the properties established for <code>*this</code>. let <code>e</code> be an object of type <code>exception_ptr</code>; then <code>static_thread_pool</code> will evaluate one of <code>execution::set_value(r)</code>, <code>execution::set_error(r, e)</code>, or <code>execution::set_done(r)</code>.</p>
</section>
</section>
<section id="static_thread_pool-executor-types" data-number="2.5.5">
<h3 data-number="2.5.5"><span class="header-section-number">2.5.5</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:

    using shape_type = size_t;
    using index_type = size_t;

    // 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>
<section id="constructors-2" data-number="2.5.5.1">
<h4 data-number="2.5.5.1"><span class="header-section-number">2.5.5.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>
</section>
<section id="assignment-2" data-number="2.5.5.2">
<h4 data-number="2.5.5.2"><span class="header-section-number">2.5.5.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>
</section>
<section id="operations-2" data-number="2.5.5.3">
<h4 data-number="2.5.5.3"><span class="header-section-number">2.5.5.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>
</section>
<section id="comparisons-2" data-number="2.5.5.4">
<h4 data-number="2.5.5.4"><span class="header-section-number">2.5.5.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>
</section>
<section id="static_thread_pool-executor-execution-functions" data-number="2.5.5.5">
<h4 data-number="2.5.5.5"><span class="header-section-number">2.5.5.5</span> <code>static_thread_pool</code> executor execution functions</h4>
<p>In addition to conforming to the above specification, <code>static_thread_pool</code> executors shall conform to the following specification.</p>
<pre><code>class C
{
  public:
    template&lt;class Function&gt;
      void execute(Function&amp;&amp; f) const;

    template&lt;class Function&gt;
      void bulk_execute(Function&amp;&amp; f, size_t n) const;
};</code></pre>
<p><code>C</code> is a type satisfying the <code>Executor</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 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>
<pre><code>template&lt;class Function&gt;
  void bulk_execute(Function&amp;&amp; f, size_t n) 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 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>
</section>
</section>
</section>
<section id="changelog" data-number="2.6">
<h2 data-number="2.6"><span class="header-section-number">2.6</span> Changelog</h2>
<section id="revision-12" data-number="2.6.1">
<h3 data-number="2.6.1"><span class="header-section-number">2.6.1</span> Revision 12</h3>
<p>Introduced introductory design discussion which replaces the obsolete <a href="https://wg21.link/P0761">P0761</a>. No normative changes.</p>
</section>
<section id="revision-11" data-number="2.6.2">
<h3 data-number="2.6.2"><span class="header-section-number">2.6.2</span> Revision 11</h3>
<p>As directed by SG1 at the 2019-07 Cologne meeting, we have implemented the following changes suggested by P1658 and P1660 which incorporate “lazy” execution:</p>
<ul>
<li>Eliminated all interface-changing properties.</li>
<li>Introduced <code>set_value</code>, <code>set_error</code>, <code>set_done</code>, <code>execute</code>, <code>submit</code>, and <code>bulk_execute</code> customization point objects.</li>
<li>Introduced <code>executor</code>, <code>executor_of</code>, <code>receiver</code>, <code>receiver_of</code>, <code>sender</code>, <code>sender_to</code>, <code>typed_sender</code>, and <code>scheduler</code> concepts.</li>
<li>Renamed polymorphic executor to <code>any_executor</code>.</li>
<li>Introduced <code>invocable_archetype</code>.</li>
<li>Eliminated <code>OneWayExecutor</code> and <code>BulkOneWayExecutor</code> requirements.</li>
<li>Eliminated <code>is_executor</code>, <code>is_oneway_executor</code>, and <code>is_bulk_oneway_executor</code> type traits.</li>
<li>Eliminated interface-changing properties from <code>any_executor</code>.</li>
</ul>
</section>
<section id="revision-10" data-number="2.6.3">
<h3 data-number="2.6.3"><span class="header-section-number">2.6.3</span> Revision 10</h3>
<p>As directed by LEWG at the 2018-11 San Diego meeting, we have migrated the property customization mechanism to namespace <code>std</code> and moved all of the details of its specification to a separate paper, <a href="http://wg21.link/P1393">P1393</a>. This change also included the introduction of a separate customization point for interface-enforcing properties, <code>require_concept</code>. The generalization also necessitated the introduction of <code>is_applicable_property_v</code> in the properties paper, which in turn led to the introduction of <code>is_executor_v</code> to express the applicability of properties in this paper.</p>
</section>
<section id="revision-9" data-number="2.6.4">
<h3 data-number="2.6.4"><span class="header-section-number">2.6.4</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>
</section>
<section id="revision-8" data-number="2.6.5">
<h3 data-number="2.6.5"><span class="header-section-number">2.6.5</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>
</section>
<section id="revision-7" data-number="2.6.6">
<h3 data-number="2.6.6"><span class="header-section-number">2.6.6</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>
</section>
<section id="revision-6" data-number="2.6.7">
<h3 data-number="2.6.7"><span class="header-section-number">2.6.7</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>
</section>
<section id="revision-5" data-number="2.6.8">
<h3 data-number="2.6.8"><span class="header-section-number">2.6.8</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>
</section>
<section id="revision-4" data-number="2.6.9">
<h3 data-number="2.6.9"><span class="header-section-number">2.6.9</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>
</section>
<section id="revision-3" data-number="2.6.10">
<h3 data-number="2.6.10"><span class="header-section-number">2.6.10</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>
</section>
<section id="revision-2" data-number="2.6.11">
<h3 data-number="2.6.11"><span class="header-section-number">2.6.11</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>
</section>
<section id="revision-1" data-number="2.6.12">
<h3 data-number="2.6.12"><span class="header-section-number">2.6.12</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>
</section>
<section id="revision-0" data-number="2.6.13">
<h3 data-number="2.6.13"><span class="header-section-number">2.6.13</span> Revision 0</h3>
<ul>
<li>Initial design</li>
</ul>
</section>
</section>
<section id="appendix-executors-bibilography" data-number="2.7">
<h2 data-number="2.7"><span class="header-section-number">2.7</span> Appendix: Executors Bibilography</h2>
<table>
<colgroup>
<col style="width: 35%" />
<col style="width: 58%" />
<col style="width: 5%" />
</colgroup>
<thead>
<tr class="header">
<th style="text-align: left;">Paper</th>
<th style="text-align: left;">Notes</th>
<th style="text-align: left;">Date introduced</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td style="text-align: left;"><a href="https://wg21.link/N3378">N3378 - A preliminary proposal for work executors</a><br />
<a href="https://wg21.link/N3562">N3562 - Executors and schedulers, revision 1</a><br />
<a href="https://wg21.link/N3371">N3731 - Executors and schedulers, revision 2</a><br />
<a href="https://wg21.link/N3785">N3785 - Executors and schedulers, revision 3</a><br />
<a href="https://wg21.link/N4143">N4143 - Executors and schedulers, revision 4</a><br />
<a href="https://wg21.link/N4414">N4414 - Executors and schedulers, revision 5</a><br />
<a href="https://wg21.link/P0008">P0008 - C++ Executors</a></td>
<td style="text-align: left;">Initial executors proposal from Google, based on an abstract base class.</td>
<td style="text-align: left;">2012-02-24</td>
</tr>
<tr class="even">
<td style="text-align: left;"><a href="https://wg21.link/N4046">N4046 - Executors and Asynchronous Operations</a></td>
<td style="text-align: left;">Initial executors proposal from Kohlhoff, based on extensions to ASIO.</td>
<td style="text-align: left;">2014-05-26</td>
</tr>
<tr class="odd">
<td style="text-align: left;"><a href="https://wg21.link/N4406">N4406 - Parallel Algorithms Need Executors</a><br />
<a href="https://wg21.link/P0058">P0058 - An interface for abstracting execution</a></td>
<td style="text-align: left;">Initial executors proposal from Nvidia, based on a traits class.</td>
<td style="text-align: left;">2015-04-10</td>
</tr>
<tr class="even">
<td style="text-align: left;"><a href="https://wg21.link/P0285">P0285 - Using customization points to unify executors</a></td>
<td style="text-align: left;">Proposes unifying various competing executors proposals via customization points.</td>
<td style="text-align: left;">2016-02-14</td>
</tr>
<tr class="odd">
<td style="text-align: left;"><a href="https://wg21.link/P0443">P0443 - A Unified Executors Proposal for C++</a></td>
<td style="text-align: left;">This proposal.</td>
<td style="text-align: left;">2016-10-17</td>
</tr>
<tr class="even">
<td style="text-align: left;"><a href="https://wg21.link/P0688">P0688 - A Proposal to Simplify the Executors Design</a></td>
<td style="text-align: left;">Proposes simplifying this proposal’s APIs using properties.</td>
<td style="text-align: left;">2017-06-19</td>
</tr>
<tr class="odd">
<td style="text-align: left;"><a href="https://wg21.link/P0761">P0761 - Executors Design Document</a></td>
<td style="text-align: left;">Describes the design of this proposal circa 2017.</td>
<td style="text-align: left;">2017-07-31</td>
</tr>
<tr class="even">
<td style="text-align: left;"><a href="https://wg21.link/P1055">P1055 - A Modest Executor Proposal</a></td>
<td style="text-align: left;">Initial executors proposal from Facebook, based on lazy execution.</td>
<td style="text-align: left;">2018-04-26</td>
</tr>
<tr class="odd">
<td style="text-align: left;"><a href="https://wg21.link/P1194">P1194 - The Compromise Executors Proposal: A lazy simplification of P0443</a></td>
<td style="text-align: left;">Initial proposal to integrate senders and receivers into this proposal.</td>
<td style="text-align: left;">2018-10-08</td>
</tr>
<tr class="even">
<td style="text-align: left;"><a href="https://wg21.link/P1232">P1232 - Integrating executors with the standard library through customization</a></td>
<td style="text-align: left;">Proposes to allow executors to customize standard algorithms directly.</td>
<td style="text-align: left;">2018-10-08</td>
</tr>
<tr class="odd">
<td style="text-align: left;"><a href="https://wg21.link/P1244">P1244 - Dependent Execution for a Unified Executors Proposal for C++</a></td>
<td style="text-align: left;">Vestigal futures-based dependent execution functionality excised from later revisions of this proposal.</td>
<td style="text-align: left;">2018-10-08</td>
</tr>
<tr class="even">
<td style="text-align: left;"><a href="https://wg21.link/P1341">P1341 - Unifying asynchronous APIs in C++ standard Library</a></td>
<td style="text-align: left;">Proposes enhancements making senders awaitable.</td>
<td style="text-align: left;">2018-11-25</td>
</tr>
<tr class="odd">
<td style="text-align: left;"><a href="https://wg21.link/P1393">P1393 - A General Property Customization Mechanism</a></td>
<td style="text-align: left;">Standalone paper proposing the property customization used by P0443 executors.</td>
<td style="text-align: left;">2019-01-13</td>
</tr>
<tr class="even">
<td style="text-align: left;"><a href="https://wg21.link/P1677">P1677 - Cancellation is serendipitous-success</a></td>
<td style="text-align: left;">Motivates the need for <code>done</code> in addition to <code>error</code>.</td>
<td style="text-align: left;">2019-05-18</td>
</tr>
<tr class="odd">
<td style="text-align: left;"><a href="https://wg21.link/P1678">P1678 - Callbacks and Composition</a></td>
<td style="text-align: left;">Argues for callbacks/receivers as a universal design pattern in the standard library.</td>
<td style="text-align: left;">2019-05-18</td>
</tr>
<tr class="even">
<td style="text-align: left;"><a href="https://wg21.link/P1525">P1525 - One-Way execute is a Poor Basis Operation</a></td>
<td style="text-align: left;">Identifies deficiencies of <code>execute</code> as a basis operation.</td>
<td style="text-align: left;">2019-06-17</td>
</tr>
<tr class="odd">
<td style="text-align: left;"><a href="https://wg21.link/P1658">P1658 - Suggestions for Consensus on Executors</a></td>
<td style="text-align: left;">Suggests progress-making changes to this proposal circa 2019.</td>
<td style="text-align: left;">2019-06-17</td>
</tr>
<tr class="even">
<td style="text-align: left;"><a href="https://wg21.link/P1660">P1660 - A Compromise Executor Design Sketch</a></td>
<td style="text-align: left;">Proposes concrete changes to this proposal along the lines of <a href="https://wg21.link/P1525">P1525</a>, <a href="https://wg21.link/P1658">P1658</a>, and <a href="https://wg21.link/P1738">P1738</a>.</td>
<td style="text-align: left;">2019-06-17</td>
</tr>
<tr class="odd">
<td style="text-align: left;"><a href="https://wg21.link/P1738">P1738 - The Executor Concept Hierarchy Needs a Single Root</a></td>
<td style="text-align: left;">Identifies problems caused by a multi-root executor concept hierarchy.</td>
<td style="text-align: left;">2019-06-17</td>
</tr>
<tr class="even">
<td style="text-align: left;"><a href="https://wg21.link/P1897">P1897 - Towards C++23 executors: A proposal for an initial set of algorithms</a></td>
<td style="text-align: left;">Initial proposal for a set of customizable sender algorithms.</td>
<td style="text-align: left;">2019-10-06</td>
</tr>
<tr class="odd">
<td style="text-align: left;"><a href="https://wg21.link/P1898">P1898 - Forward progress delegation for executors</a></td>
<td style="text-align: left;">Proposes a model of forward progress for executors and asynchronous graphs of work.</td>
<td style="text-align: left;">2019-10-06</td>
</tr>
<tr class="even">
<td style="text-align: left;"><a href="https://wg21.link/P2006">P2006 - Splitting submit() into connect()/start()</a></td>
<td style="text-align: left;">Proposes refactoring <code>submit</code> into more fundamental <code>connect</code> and <code>start</code> sender operations.</td>
<td style="text-align: left;">2020-01-13</td>
</tr>
<tr class="odd">
<td style="text-align: left;"><a href="https://wg21.link/P2033">P2033 - History of Executor Properties</a></td>
<td style="text-align: left;">Documents the evolution of <a href="https://wg21.link/P1393">P1393</a>’s property system, especially as it relates to executors.</td>
<td style="text-align: left;">2020-01-13</td>
</tr>
</tbody>
</table>
</section>
<section id="appendix-a-note-on-coroutines" data-number="2.8">
<h2 data-number="2.8"><span class="header-section-number">2.8</span> Appendix: A note on coroutines</h2>
<p><a href="http://wg21.link/P1341">P1341</a> leverages the structural similarities between coroutines and the sender/receiver abstraction to give a class of senders a standard-provided <code>operator co_await</code>. The end result is that a sender, simply by dint of being a sender, can be <code>co_await</code>-ed in a coroutine. With the refinement of sender/receiver that was proposed in <a href="https://wg21.link/P2006">P2006</a> — namely, the splitting of <code>submit</code> into <code>connect</code>/<code>start</code> — that automatic adaptation from sender-to-awaitable is allocation- and synchronization-free.</p>
</section>
<section id="appendix-the-retry-algorithm" data-number="2.9">
<h2 data-number="2.9"><span class="header-section-number">2.9</span> Appendix: The <code>retry</code> Algorithm</h2>
<p>Below is an implementation of a simple <code>retry</code> algorithm in terms of <code>sender</code>/<code>receiver</code>. This algorithm is Generic in the sense that it will retry any multi-shot asynchronous operation that satisfies the <code>sender</code> concept. More accurately, it takes any deferred async operation and wraps it so that when it is executed, it will retry the wrapped operation until it either succeeds or is cancelled.</p>
<p>Full working code can be found here: <a href="https://godbolt.org/z/nm6GmH">https://godbolt.org/z/nm6GmH</a></p>
<div class="sourceCode" id="cb161"><pre class="sourceCode p0443"><code class="sourceCode p0443syntax"><span id="cb161-1"><a href="#cb161-1"></a>// _conv needed so we can emplace construct non-movable types into</span>
<span id="cb161-2"><a href="#cb161-2"></a>// a std::optional.</span>
<span id="cb161-3"><a href="#cb161-3"></a>template&lt;invocable F&gt;</span>
<span id="cb161-4"><a href="#cb161-4"></a>    requires std::is_nothrow_move_constructible_v&lt;F&gt;</span>
<span id="cb161-5"><a href="#cb161-5"></a>struct _conv {</span>
<span id="cb161-6"><a href="#cb161-6"></a>    F f_;</span>
<span id="cb161-7"><a href="#cb161-7"></a>    explicit _conv(F f) noexcept : f_((F&amp;&amp;) f) {}</span>
<span id="cb161-8"><a href="#cb161-8"></a>    operator invoke_result_t&lt;F&gt;() &amp;&amp; {</span>
<span id="cb161-9"><a href="#cb161-9"></a>        return ((F&amp;&amp;) f_)();</span>
<span id="cb161-10"><a href="#cb161-10"></a>    }</span>
<span id="cb161-11"><a href="#cb161-11"></a>};</span>
<span id="cb161-12"><a href="#cb161-12"></a></span>
<span id="cb161-13"><a href="#cb161-13"></a>// pass through set_value and set_error, but retry the operation</span>
<span id="cb161-14"><a href="#cb161-14"></a>// from set_error.</span>
<span id="cb161-15"><a href="#cb161-15"></a>template&lt;class O, class R&gt;</span>
<span id="cb161-16"><a href="#cb161-16"></a>struct _retry_receiver {</span>
<span id="cb161-17"><a href="#cb161-17"></a>    O* o_;</span>
<span id="cb161-18"><a href="#cb161-18"></a>    explicit _retry_receiver(O* o): o_(o) {}</span>
<span id="cb161-19"><a href="#cb161-19"></a>    template&lt;class... As&gt;</span>
<span id="cb161-20"><a href="#cb161-20"></a>        requires <span class="kw">receiver_of</span>&lt;R, As...&gt;</span>
<span id="cb161-21"><a href="#cb161-21"></a>    void <span class="kw">set_value</span>(As&amp;&amp;... as) &amp;&amp;</span>
<span id="cb161-22"><a href="#cb161-22"></a>        noexcept(is_nothrow_receiver_of_v&lt;R, As...&gt;) {</span>
<span id="cb161-23"><a href="#cb161-23"></a>        ::<span class="kw">set_value</span>(std::move(o_-&gt;r_), (As&amp;&amp;) as...);</span>
<span id="cb161-24"><a href="#cb161-24"></a>    }</span>
<span id="cb161-25"><a href="#cb161-25"></a>    void <span class="kw">set_error</span>(auto&amp;&amp;) &amp;&amp; noexcept {</span>
<span id="cb161-26"><a href="#cb161-26"></a>        o_-&gt;_retry(); // This causes the op to be retried</span>
<span id="cb161-27"><a href="#cb161-27"></a>    }</span>
<span id="cb161-28"><a href="#cb161-28"></a>    void <span class="kw">set_done</span>() &amp;&amp; noexcept {</span>
<span id="cb161-29"><a href="#cb161-29"></a>        ::<span class="kw">set_done</span>(std::move(o_-&gt;r_));</span>
<span id="cb161-30"><a href="#cb161-30"></a>    }</span>
<span id="cb161-31"><a href="#cb161-31"></a>};</span>
<span id="cb161-32"><a href="#cb161-32"></a></span>
<span id="cb161-33"><a href="#cb161-33"></a>template&lt;<span class="kw">sender</span> S&gt;</span>
<span id="cb161-34"><a href="#cb161-34"></a>struct _retry_sender : <span class="kw">sender_base</span> {</span>
<span id="cb161-35"><a href="#cb161-35"></a>    S s_;</span>
<span id="cb161-36"><a href="#cb161-36"></a>    explicit _retry_sender(S s): s_((S&amp;&amp;) s) {}</span>
<span id="cb161-37"><a href="#cb161-37"></a></span>
<span id="cb161-38"><a href="#cb161-38"></a>    // Hold the nested operation state in an optional so we can</span>
<span id="cb161-39"><a href="#cb161-39"></a>    // re-construct and re-start it when the operation fails.</span>
<span id="cb161-40"><a href="#cb161-40"></a>    template&lt;<span class="kw">receiver</span> R&gt;</span>
<span id="cb161-41"><a href="#cb161-41"></a>    struct _op {</span>
<span id="cb161-42"><a href="#cb161-42"></a>        S s_;</span>
<span id="cb161-43"><a href="#cb161-43"></a>        R r_;</span>
<span id="cb161-44"><a href="#cb161-44"></a>        std::optional&lt;state_t&lt;S&amp;, _retry_receiver&lt;_op, R&gt;&gt;&gt; o_;</span>
<span id="cb161-45"><a href="#cb161-45"></a></span>
<span id="cb161-46"><a href="#cb161-46"></a>        _op(S s, R r): s_((S&amp;&amp;)s), r_((R&amp;&amp;)r), o_{_connect()} {}</span>
<span id="cb161-47"><a href="#cb161-47"></a>        _op(_op&amp;&amp;) = delete;</span>
<span id="cb161-48"><a href="#cb161-48"></a></span>
<span id="cb161-49"><a href="#cb161-49"></a>        auto _connect() noexcept {</span>
<span id="cb161-50"><a href="#cb161-50"></a>            return _conv{[this] {</span>
<span id="cb161-51"><a href="#cb161-51"></a>                return ::<span class="kw">connect</span>(s_, _retry_receiver&lt;_op, R&gt;{this});</span>
<span id="cb161-52"><a href="#cb161-52"></a>            }};</span>
<span id="cb161-53"><a href="#cb161-53"></a>        }</span>
<span id="cb161-54"><a href="#cb161-54"></a>        void _retry() noexcept try {</span>
<span id="cb161-55"><a href="#cb161-55"></a>            o_.emplace(_connect()); // potentially throwing</span>
<span id="cb161-56"><a href="#cb161-56"></a>            ::<span class="kw">start</span>(std::move(*o_));</span>
<span id="cb161-57"><a href="#cb161-57"></a>        } catch(...) {</span>
<span id="cb161-58"><a href="#cb161-58"></a>            ::<span class="kw">set_error</span>((R&amp;&amp;) r_, std::current_exception());</span>
<span id="cb161-59"><a href="#cb161-59"></a>        }</span>
<span id="cb161-60"><a href="#cb161-60"></a>        void <span class="kw">start</span>() &amp;&amp; noexcept {</span>
<span id="cb161-61"><a href="#cb161-61"></a>            ::<span class="kw">start</span>(std::move(*o_));</span>
<span id="cb161-62"><a href="#cb161-62"></a>        }</span>
<span id="cb161-63"><a href="#cb161-63"></a>    };</span>
<span id="cb161-64"><a href="#cb161-64"></a></span>
<span id="cb161-65"><a href="#cb161-65"></a>    template&lt;<span class="kw">receiver</span> R&gt;</span>
<span id="cb161-66"><a href="#cb161-66"></a>        requires <span class="kw">sender_to</span>&lt;S&amp;, _retry_receiver&lt;_op&lt;R&gt;, R&gt;&gt;</span>
<span id="cb161-67"><a href="#cb161-67"></a>    auto <span class="kw">connect</span>(R r) &amp;&amp; -&gt; _op&lt;R&gt; {</span>
<span id="cb161-68"><a href="#cb161-68"></a>        return _op&lt;R&gt;{(S&amp;&amp;) s_, (R&amp;&amp;) r};</span>
<span id="cb161-69"><a href="#cb161-69"></a>    }</span>
<span id="cb161-70"><a href="#cb161-70"></a>};</span>
<span id="cb161-71"><a href="#cb161-71"></a></span>
<span id="cb161-72"><a href="#cb161-72"></a>template&lt;<span class="kw">sender</span> S&gt;</span>
<span id="cb161-73"><a href="#cb161-73"></a><span class="kw">sender</span> auto <span class="st">retry</span>(S s) {</span>
<span id="cb161-74"><a href="#cb161-74"></a>    return _retry_sender{(S&amp;&amp;)s};</span>
<span id="cb161-75"><a href="#cb161-75"></a>}</span></code></pre></div>
</section>
</section>
</body>
</html>
