<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" lang xml:lang>
<head>
  <meta charset="utf-8" />
  <meta name="generator" content="mpark/wg21" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes" />
  <meta name="dcterms.date" content="2020-05-16" />
  <title>Towards C++23 executors: A proposal for an initial set of algorithms</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%;}
  </style>
  <style>
code.sourceCode > span { display: inline-block; line-height: 1.25; }
code.sourceCode > span { color: inherit; text-decoration: inherit; }
code.sourceCode > span:empty { height: 1.2em; }
.sourceCode { overflow: visible; }
code.sourceCode { white-space: pre; position: relative; }
div.sourceCode { margin: 1em 0; }
pre.sourceCode { margin: 0; }
@media screen {
div.sourceCode { overflow: auto; }
}
@media print {
code.sourceCode { white-space: pre-wrap; }
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
  {  background-color: #f6f8fa; }
@media screen {
code.sourceCode > span > a:first-child::before { text-decoration: underline; }
}
code span. { } /* Normal */
code span.al { color: #ff0000; } /* Alert */
code span.an { } /* Annotation */
code span.at { } /* Attribute */
code span.bn { color: #9f6807; } /* BaseN */
code span.bu { color: #9f6807; } /* BuiltIn */
code span.cf { color: #00607c; } /* ControlFlow */
code span.ch { color: #9f6807; } /* Char */
code span.cn { } /* Constant */
code span.co { color: #008000; font-style: italic; } /* Comment */
code span.cv { color: #008000; font-style: italic; } /* CommentVar */
code span.do { color: #008000; } /* Documentation */
code span.dt { color: #00607c; } /* DataType */
code span.dv { color: #9f6807; } /* DecVal */
code span.er { color: #ff0000; font-weight: bold; } /* Error */
code span.ex { } /* Extension */
code span.fl { color: #9f6807; } /* Float */
code span.fu { } /* Function */
code span.im { } /* Import */
code span.in { color: #008000; } /* Information */
code span.kw { color: #00607c; } /* Keyword */
code span.op { color: #af1915; } /* Operator */
code span.ot { } /* Other */
code span.pp { color: #6f4e37; } /* Preprocessor */
code span.re { } /* RegionMarker */
code span.sc { color: #9f6807; } /* SpecialChar */
code span.ss { color: #9f6807; } /* SpecialString */
code span.st { color: #9f6807; } /* String */
code span.va { } /* Variable */
code span.vs { color: #9f6807; } /* VerbatimString */
code span.wa { color: #008000; font-weight: bold; } /* Warning */
code.diff {color: #898887}
code.diff span.va {color: #006e28}
code.diff span.st {color: #bf0303}
  </style>
  <style type="text/css">
body {
margin: 5em;
font-family: serif;

hyphens: auto;
line-height: 1.35;
}
div.wrapper {
max-width: 60em;
margin: auto;
}
ul {
list-style-type: none;
padding-left: 2em;
margin-top: -0.2em;
margin-bottom: -0.2em;
}
a {
text-decoration: none;
color: #4183C4;
}
a.hidden_link {
text-decoration: none;
color: inherit;
}
li {
margin-top: 0.6em;
margin-bottom: 0.6em;
}
h1, h2, h3, h4 {
position: relative;
line-height: 1;
}
a.self-link {
position: absolute;
top: 0;
left: calc(-1 * (3.5rem - 26px));
width: calc(3.5rem - 26px);
height: 2em;
text-align: center;
border: none;
transition: opacity .2s;
opacity: .5;
font-family: sans-serif;
font-weight: normal;
font-size: 83%;
}
a.self-link:hover { opacity: 1; }
a.self-link::before { content: "§"; }
ul > li:before {
content: "\2014";
position: absolute;
margin-left: -1.5em;
}
:target { background-color: #C9FBC9; }
:target .codeblock { background-color: #C9FBC9; }
:target ul { background-color: #C9FBC9; }
.abbr_ref { float: right; }
.folded_abbr_ref { float: right; }
:target .folded_abbr_ref { display: none; }
:target .unfolded_abbr_ref { float: right; display: inherit; }
.unfolded_abbr_ref { display: none; }
.secnum { display: inline-block; min-width: 35pt; }
.header-section-number { display: inline-block; min-width: 35pt; }
.annexnum { display: block; }
div.sourceLinkParent {
float: right;
}
a.sourceLink {
position: absolute;
opacity: 0;
margin-left: 10pt;
}
a.sourceLink:hover {
opacity: 1;
}
a.itemDeclLink {
position: absolute;
font-size: 75%;
text-align: right;
width: 5em;
opacity: 0;
}
a.itemDeclLink:hover { opacity: 1; }
span.marginalizedparent {
position: relative;
left: -5em;
}
li span.marginalizedparent { left: -7em; }
li ul > li span.marginalizedparent { left: -9em; }
li ul > li ul > li span.marginalizedparent { left: -11em; }
li ul > li ul > li ul > li span.marginalizedparent { left: -13em; }
div.footnoteNumberParent {
position: relative;
left: -4.7em;
}
a.marginalized {
position: absolute;
font-size: 75%;
text-align: right;
width: 5em;
}
a.enumerated_item_num {
position: relative;
left: -3.5em;
display: inline-block;
margin-right: -3em;
text-align: right;
width: 3em;
}
div.para { margin-bottom: 0.6em; margin-top: 0.6em; text-align: justify; }
div.section { text-align: justify; }
div.sentence { display: inline; }
span.indexparent {
display: inline;
position: relative;
float: right;
right: -1em;
}
a.index {
position: absolute;
display: none;
}
a.index:before { content: "⟵"; }

a.index:target {
display: inline;
}
.indexitems {
margin-left: 2em;
text-indent: -2em;
}
div.itemdescr {
margin-left: 3em;
}
.bnf {
font-family: serif;
margin-left: 40pt;
margin-top: 0.5em;
margin-bottom: 0.5em;
}
.ncbnf {
font-family: serif;
margin-top: 0.5em;
margin-bottom: 0.5em;
margin-left: 40pt;
}
.ncsimplebnf {
font-family: serif;
font-style: italic;
margin-top: 0.5em;
margin-bottom: 0.5em;
margin-left: 40pt;
background: inherit; 
}
span.textnormal {
font-style: normal;
font-family: serif;
white-space: normal;
display: inline-block;
}
span.rlap {
display: inline-block;
width: 0px;
}
span.descr { font-style: normal; font-family: serif; }
span.grammarterm { font-style: italic; }
span.term { font-style: italic; }
span.terminal { font-family: monospace; font-style: normal; }
span.nonterminal { font-style: italic; }
span.tcode { font-family: monospace; font-style: normal; }
span.textbf { font-weight: bold; }
span.textsc { font-variant: small-caps; }
a.nontermdef { font-style: italic; font-family: serif; }
span.emph { font-style: italic; }
span.techterm { font-style: italic; }
span.mathit { font-style: italic; }
span.mathsf { font-family: sans-serif; }
span.mathrm { font-family: serif; font-style: normal; }
span.textrm { font-family: serif; }
span.textsl { font-style: italic; }
span.mathtt { font-family: monospace; font-style: normal; }
span.mbox { font-family: serif; font-style: normal; }
span.ungap { display: inline-block; width: 2pt; }
span.textit { font-style: italic; }
span.texttt { font-family: monospace; }
span.tcode_in_codeblock { font-family: monospace; font-style: normal; }
span.phantom { color: white; }

span.math { font-style: normal; }
span.mathblock {
display: block;
margin-left: auto;
margin-right: auto;
margin-top: 1.2em;
margin-bottom: 1.2em;
text-align: center;
}
span.mathalpha {
font-style: italic;
}
span.synopsis {
font-weight: bold;
margin-top: 0.5em;
display: block;
}
span.definition {
font-weight: bold;
display: block;
}
.codeblock {
margin-left: 1.2em;
line-height: 127%;
}
.outputblock {
margin-left: 1.2em;
line-height: 127%;
}
div.itemdecl {
margin-top: 2ex;
}
code.itemdeclcode {
white-space: pre;
display: block;
}
span.textsuperscript {
vertical-align: super;
font-size: smaller;
line-height: 0;
}
.footnotenum { vertical-align: super; font-size: smaller; line-height: 0; }
.footnote {
font-size: small;
margin-left: 2em;
margin-right: 2em;
margin-top: 0.6em;
margin-bottom: 0.6em;
}
div.minipage {
display: inline-block;
margin-right: 3em;
}
div.numberedTable {
text-align: center;
margin: 2em;
}
div.figure {
text-align: center;
margin: 2em;
}
table {
border: 1px solid black;
border-collapse: collapse;
margin-left: auto;
margin-right: auto;
margin-top: 0.8em;
text-align: left;
hyphens: none; 
}
td, th {
padding-left: 1em;
padding-right: 1em;
vertical-align: top;
}
td.empty {
padding: 0px;
padding-left: 1px;
}
td.left {
text-align: left;
}
td.right {
text-align: right;
}
td.center {
text-align: center;
}
td.justify {
text-align: justify;
}
td.border {
border-left: 1px solid black;
}
tr.rowsep, td.cline {
border-top: 1px solid black;
}
tr.even, tr.odd {
border-bottom: 1px solid black;
}
tr.capsep {
border-top: 3px solid black;
border-top-style: double;
}
tr.header {
border-bottom: 3px solid black;
border-bottom-style: double;
}
th {
border-bottom: 1px solid black;
}
span.centry {
font-weight: bold;
}
div.table {
display: block;
margin-left: auto;
margin-right: auto;
text-align: center;
width: 90%;
}
span.indented {
display: block;
margin-left: 2em;
margin-bottom: 1em;
margin-top: 1em;
}
ol.enumeratea { list-style-type: none; background: inherit; }
ol.enumerate { list-style-type: none; background: inherit; }

code.sourceCode > span { display: inline; }

div#refs p { padding-left: 32px; text-indent: -32px; }
</style>
  <link href="data:image/vnd.microsoft.icon;base64,AAABAAIAEBAAAAEAIABoBAAAJgAAACAgAAABACAAqBAAAI4EAAAoAAAAEAAAACAAAAABACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////AIJEAACCRAAAgkQAAIJEAACCRAAAgkQAVoJEAN6CRADegkQAWIJEAACCRAAAgkQAAIJEAACCRAAA////AP///wCCRAAAgkQAAIJEAACCRAAsgkQAvoJEAP+CRAD/gkQA/4JEAP+CRADAgkQALoJEAACCRAAAgkQAAP///wD///8AgkQAAIJEABSCRACSgkQA/IJEAP99PQD/dzMA/3czAP99PQD/gkQA/4JEAPyCRACUgkQAFIJEAAD///8A////AHw+AFiBQwDqgkQA/4BBAP9/PxP/uZd6/9rJtf/bybX/upd7/39AFP+AQQD/gkQA/4FDAOqAQgBc////AP///wDKklv4jlEa/3o7AP+PWC//8+3o///////////////////////z7un/kFox/35AAP+GRwD/mVYA+v///wD///8A0Zpk+NmibP+0d0T/8evj///////+/fv/1sKz/9bCs//9/fr//////+/m2/+NRwL/nloA/5xYAPj///8A////ANKaZPjRmGH/5cKh////////////k149/3UwAP91MQD/lmQ//86rhv+USg3/m1YA/5hSAP+bVgD4////AP///wDSmmT4zpJY/+/bx///////8+TV/8mLT/+TVx//gkIA/5lVAP+VTAD/x6B//7aEVv/JpH7/s39J+P///wD///8A0ppk+M6SWP/u2sf///////Pj1f/Nj1T/2KFs/8mOUv+eWhD/lEsA/8aee/+0glT/x6F7/7J8Rvj///8A////ANKaZPjRmGH/48Cf///////+/v7/2qt//82PVP/OkFX/37KJ/86siv+USg7/mVQA/5hRAP+bVgD4////AP///wDSmmT40ppk/9CVXP/69O////////7+/v/x4M//8d/P//7+/f//////9u7n/6tnJf+XUgD/nFgA+P///wD///8A0ppk+NKaZP/RmWL/1qNy//r07///////////////////////+vXw/9akdP/Wnmn/y5FY/6JfFvj///8A////ANKaZFTSmmTo0ppk/9GYYv/Ql1//5cWm//Hg0P/x4ND/5cWm/9GXYP/RmGH/0ppk/9KaZOjVnmpY////AP///wDSmmQA0ppkEtKaZI7SmmT60ppk/9CWX//OkVb/zpFW/9CWX//SmmT/0ppk/NKaZJDSmmQS0ppkAP///wD///8A0ppkANKaZADSmmQA0ppkKtKaZLrSmmT/0ppk/9KaZP/SmmT/0ppkvNKaZCrSmmQA0ppkANKaZAD///8A////ANKaZADSmmQA0ppkANKaZADSmmQA0ppkUtKaZNzSmmTc0ppkVNKaZADSmmQA0ppkANKaZADSmmQA////AP5/AAD4HwAA4AcAAMADAACAAQAAgAEAAIABAACAAQAAgAEAAIABAACAAQAAgAEAAMADAADgBwAA+B8AAP5/AAAoAAAAIAAAAEAAAAABACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////AP///wCCRAAAgkQAAIJEAACCRAAAgkQAAIJEAACCRAAAgkQAAIJEAACCRAAAgkQAAIJEAAyCRACMgkQA6oJEAOqCRACQgkQAEIJEAACCRAAAgkQAAIJEAACCRAAAgkQAAIJEAACCRAAAgkQAAIJEAACCRAAA////AP///wD///8A////AIJEAACCRAAAgkQAAIJEAACCRAAAgkQAAIJEAACCRAAAgkQAAIJEAACCRABigkQA5oJEAP+CRAD/gkQA/4JEAP+CRADqgkQAZoJEAACCRAAAgkQAAIJEAACCRAAAgkQAAIJEAACCRAAAgkQAAIJEAAD///8A////AP///wD///8AgkQAAIJEAACCRAAAgkQAAIJEAACCRAAAgkQAAIJEAACCRAA4gkQAwoJEAP+CRAD/gkQA/4JEAP+CRAD/gkQA/4JEAP+CRAD/gkQAxIJEADyCRAAAgkQAAIJEAACCRAAAgkQAAIJEAACCRAAAgkQAAP///wD///8A////AP///wCCRAAAgkQAAIJEAACCRAAAgkQAAIJEAACCRAAWgkQAmIJEAP+CRAD/gkQA/4JEAP+CRAD/gkQA/4JEAP+CRAD/gkQA/4JEAP+CRAD/gkQA/4JEAJyCRAAYgkQAAIJEAACCRAAAgkQAAIJEAACCRAAA////AP///wD///8A////AIJEAACCRAAAgkQAAIJEAACCRAAAgkQAdIJEAPCCRAD/gkQA/4JEAP+CRAD/gkQA/4JEAP+CRAD/gkQA/4JEAP+CRAD/gkQA/4JEAP+CRAD/gkQA/4JEAPSCRAB4gkQAAIJEAACCRAAAgkQAAIJEAAD///8A////AP///wD///8AgkQAAIJEAACCRAAAgkQASoJEANKCRAD/gkQA/4JEAP+CRAD/g0YA/39AAP9zLgD/bSQA/2shAP9rIQD/bSQA/3MuAP9/PwD/g0YA/4JEAP+CRAD/gkQA/4JEAP+CRADUgkQAToJEAACCRAAAgkQAAP///wD///8A////AP///wB+PwAAgkUAIoJEAKiCRAD/gkQA/4JEAP+CRAD/hEcA/4BBAP9sIwD/dTAA/5RfKv+viF7/vp56/76ee/+wiF7/lWAr/3YxAP9sIwD/f0AA/4RHAP+CRAD/gkQA/4JEAP+CRAD/gkQArIJEACaBQwAA////AP///wD///8A////AIBCAEBzNAD6f0EA/4NFAP+CRAD/gkQA/4VIAP92MwD/bSUA/6N1Tv/ezsL/////////////////////////////////38/D/6V3Uv9uJgD/dTEA/4VJAP+CRAD/gkQA/4JEAP+BQwD/fUAA/4FDAEj///8A////AP///wD///8AzJRd5qBlKf91NgD/dDUA/4JEAP+FSQD/cy4A/3YyAP/PuKP//////////////////////////////////////////////////////9K7qP94NQD/ciwA/4VJAP+CRAD/fkEA/35BAP+LSwD/mlYA6v///wD///8A////AP///wDdpnL/4qx3/8KJUv+PUhf/cTMA/3AsAP90LgD/4dK+/////////////////////////////////////////////////////////////////+TYxf91MAD/dTIA/31CAP+GRwD/llQA/6FcAP+gWwD8////AP///wD///8A////ANGZY/LSm2X/4ap3/92mcP+wdT3/byQA/8mwj////////////////////////////////////////////////////////////////////////////+LYxv9zLgP/jUoA/59bAP+hXAD/nFgA/5xYAPL///8A////AP///wD///8A0ppk8tKaZP/RmWL/1p9q/9ubXv/XqXj////////////////////////////7+fD/vZyG/6BxS/+gcUr/vJuE//r37f//////////////////////3MOr/5dQBf+dVQD/nVkA/5xYAP+cWAD/nFgA8v///wD///8A////AP///wDSmmTy0ppk/9KaZP/SmWP/yohJ//jo2P//////////////////////4NTG/4JDFf9lGAD/bSQA/20kAP9kGAD/fz8S/+Xb0f//////5NG9/6txN/+LOgD/m1QA/51aAP+cWAD/m1cA/5xYAP+cWADy////AP///wD///8A////ANKaZPLSmmT/0ppk/8+TWf/Unmv//v37//////////////////////+TWRr/VwsA/35AAP+ERgD/g0UA/4JGAP9lHgD/kFga/8KXX/+TRwD/jT4A/49CAP+VTQD/n10A/5xYAP+OQQD/lk4A/55cAPL///8A////AP///wD///8A0ppk8tKaZP/SmmT/y4tO/92yiP//////////////////////8NnE/8eCQP+rcTT/ez0A/3IyAP98PgD/gEMA/5FSAP+USwD/jj8A/5lUAP+JNwD/yqV2/694Mf+HNQD/jkAA/82rf/+laBj/jT4A8v///wD///8A////AP///wDSmmTy0ppk/9KaZP/LiUr/4byY///////////////////////gupX/0I5P/+Wuev/Lklz/l1sj/308AP+QSwD/ol0A/59aAP+aVQD/k0oA/8yoh///////+fXv/6pwO//Lp3v///////Pr4f+oay7y////AP///wD///8A////ANKaZPLSmmT/0ppk/8uJSv/hvJj//////////////////////+G7l//Jhkb/0ppk/96nc//fqXX/x4xO/6dkFP+QSQD/llEA/5xXAP+USgD/yaOA///////38uv/qG05/8ijdv//////8efb/6ZpLPL///8A////AP///wD///8A0ppk8tKaZP/SmmT/zIxO/9yxh///////////////////////7dbA/8iEQf/Sm2X/0Zlj/9ScZv/eqHf/2KJv/7yAQf+XTgD/iToA/5lSAP+JNgD/yKFv/611LP+HNQD/jT8A/8qmeP+kZRT/jT4A8v///wD///8A////AP///wDSmmTy0ppk/9KaZP/Pk1n/1J5q//78+//////////////////+/fv/1aFv/8iEQv/Tm2b/0ppl/9GZY//Wn2z/1pZc/9eldf/Bl2b/kUcA/4w9AP+OQAD/lUwA/59eAP+cWQD/jT8A/5ZOAP+eXADy////AP///wD///8A////ANKaZPLSmmT/0ppk/9KZY//KiEn/8d/P///////////////////////47+f/05tm/8iCP//KiEj/yohJ/8eCP//RmGH//vfy///////n1sP/rXQ7/4k4AP+TTAD/nVoA/5xYAP+cVwD/nFgA/5xYAPL///8A////AP///wD///8A0ppk8tKaZP/SmmT/0ptl/8uLTf/aq37////////////////////////////+/fz/6c2y/961jv/etY7/6Myx//78+v//////////////////////3MWv/5xXD/+ORAD/mFQA/51ZAP+cWAD/nFgA8v///wD///8A////AP///wDSmmTy0ppk/9KaZP/SmmT/0ppk/8mFRP/s1b//////////////////////////////////////////////////////////////////////////////+PD/0JFU/7NzMv+WUQD/kUsA/5tXAP+dWQDy////AP///wD///8A////ANKaZP/SmmT/0ppk/9KaZP/Sm2X/z5NZ/8yMT//z5NX/////////////////////////////////////////////////////////////////9Ofa/8yNUP/UmGH/36p5/8yTWv+qaSD/kksA/5ROAPz///8A////AP///wD///8A0ppk5NKaZP/SmmT/0ppk/9KaZP/TnGf/zY9T/82OUv/t1sD//////////////////////////////////////////////////////+7Yw//OkFX/zI5R/9OcZ//SmmP/26V0/9ymdf/BhUf/ol8R6P///wD///8A////AP///wDSmmQ80ppk9tKaZP/SmmT/0ppk/9KaZP/TnGj/zpFW/8qJSv/dson/8uHS//////////////////////////////////Lj0//etIv/y4lL/86QVf/TnGj/0ppk/9KaZP/RmWP/05xn/9ymdfjUnWdC////AP///wD///8A////ANKaZADSmmQc0ppkotKaZP/SmmT/0ppk/9KaZP/Tm2b/0Zli/8qJSf/NjlH/16Z3/+G8mP/myKr/5siq/+G8mP/Xp3f/zY5S/8qISf/RmGH/05tm/9KaZP/SmmT/0ppk/9KaZP/SmmSm0pljINWdaQD///8A////AP///wD///8A0ppkANKaZADSmmQA0ppkQtKaZMrSmmT/0ppk/9KaZP/SmmT/0ptl/9GYYf/Nj1P/y4lL/8qISP/KiEj/y4lK/82PU//RmGH/0ptl/9KaZP/SmmT/0ppk/9KaZP/SmmTO0ppkRtKaZADSmmQA0ppkAP///wD///8A////AP///wDSmmQA0ppkANKaZADSmmQA0ppkANKaZGzSmmTu0ppk/9KaZP/SmmT/0ppk/9KaZP/SmmT/0ppk/9KaZP/SmmT/0ppk/9KaZP/SmmT/0ppk/9KaZP/SmmTw0ppkcNKaZADSmmQA0ppkANKaZADSmmQA////AP///wD///8A////ANKaZADSmmQA0ppkANKaZADSmmQA0ppkANKaZBLSmmSQ0ppk/9KaZP/SmmT/0ppk/9KaZP/SmmT/0ppk/9KaZP/SmmT/0ppk/9KaZP/SmmT/0ppklNKaZBTSmmQA0ppkANKaZADSmmQA0ppkANKaZAD///8A////AP///wD///8A0ppkANKaZADSmmQA0ppkANKaZADSmmQA0ppkANKaZADSmmQy0ppkutKaZP/SmmT/0ppk/9KaZP/SmmT/0ppk/9KaZP/SmmT/0ppkvtKaZDbSmmQA0ppkANKaZADSmmQA0ppkANKaZADSmmQA0ppkAP///wD///8A////AP///wDSmmQA0ppkANKaZADSmmQA0ppkANKaZADSmmQA0ppkANKaZADSmmQA0ppkXNKaZODSmmT/0ppk/9KaZP/SmmT/0ppk5NKaZGDSmmQA0ppkANKaZADSmmQA0ppkANKaZADSmmQA0ppkANKaZADSmmQA////AP///wD///8A////ANKaZADSmmQA0ppkANKaZADSmmQA0ppkANKaZADSmmQA0ppkANKaZADSmmQA0ppkBtKaZIbSmmTo0ppk6tKaZIrSmmQK0ppkANKaZADSmmQA0ppkANKaZADSmmQA0ppkANKaZADSmmQA0ppkANKaZAD///8A////AP/8P///+B///+AH//+AAf//AAD//AAAP/AAAA/gAAAHwAAAA8AAAAPAAAADwAAAA8AAAAPAAAADwAAAA8AAAAPAAAADwAAAA8AAAAPAAAADwAAAA8AAAAPAAAADwAAAA+AAAAfwAAAP/AAAP/8AAP//gAH//+AH///4H////D//" rel="icon" />
  <!--[if lt IE 9]>
    <script src="//cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv-printshiv.min.js"></script>
  <![endif]-->
  
</head>
<body>
<div class="wrapper">
<header id="title-block-header">
<h1 class="title" style="text-align:center">Towards C++23 executors: A proposal for an initial set of algorithms</h1>

<table style="border:none;float:right">
  <tr>
    <td>Document #: </td>
    <td>P1897R3</td>
  </tr>
  <tr>
    <td>Date: </td>
    <td>2020-05-16</td>
  </tr>
  <tr>
    <td style="vertical-align:top">Project: </td>
    <td>Programming Language C++<br>
      SG1<br>
    </td>
  </tr>
  <tr>
    <td style="vertical-align:top">Reply-to: </td>
    <td>
      Lee Howes<br>&lt;<a href="mailto:lwh@fb.com" class="email">lwh@fb.com</a>&gt;<br>
    </td>
  </tr>
</table>

</header>
<div style="clear:both">
<h1 id="changelog"><span class="header-section-number">1</span> Changelog<a href="#changelog" class="self-link"></a></h1>
<h2 id="differences-between-r2-and-r3"><span class="header-section-number">1.1</span> Differences between R2 and R3<a href="#differences-between-r2-and-r3" class="self-link"></a></h2>
<ul>
<li>Rename <code>just_via</code> to <code>just_on</code>.</li>
<li>Rename <code>via</code> to <code>on</code>.</li>
<li>Add <code>ensure_started</code>.</li>
<li>Add note on the feedback about <code>indexed_for</code> in Prague. Removed <code>indexed_for</code> from the paper of initial algorithms.</li>
<li>Add <code>let_value</code>.</li>
<li>Tweaked <code>handle_error</code> wording to be more similar to <code>let_value</code>, and renamed <code>let_error</code> for naming consistency.</li>
<li>Updated to use P0443R13 as a baseline.</li>
<li>Improves the wording to be closer to mergable wording and less pseudowording.</li>
<li>Modified <code>sync_wait</code> to terminate on done rather than throwing.</li>
<li>Added notes to clarify execution context of completion of <code>on</code> and <code>just_on</code>.</li>
</ul>
<h2 id="differences-between-r1-and-r2"><span class="header-section-number">1.2</span> Differences between R1 and R2<a href="#differences-between-r1-and-r2" class="self-link"></a></h2>
<ul>
<li>Add <code>just_via</code> algorithm to allow type customization at the head of a work chain.</li>
<li>Add <code>when_all</code> to fill missing gap in the ability to join sender chains.</li>
<li>Add <code>indexed_for</code> based on feedback during the Belfast meeting to have a side-effecting algorithm.</li>
<li>Propose question on replacing <code>bulk_execute</code> with <code>indexed_for</code> for the Prague meeting.</li>
</ul>
<h2 id="differences-between-r0-and-r1"><span class="header-section-number">1.3</span> Differences between R0 and R1<a href="#differences-between-r0-and-r1" class="self-link"></a></h2>
<ul>
<li>Improve examples to be clearer, and fully expanded into function call form.</li>
<li>Add reference to range.adapter.</li>
<li>Remove <code>is_noexcept_sender</code>.</li>
<li>Remove <code>just_error</code>.</li>
<li>Clarified use of parameter packs of values and errors.</li>
<li>Removed confusing use of <code>on</code> in addition to <code>via</code> in the final example.</li>
</ul>
<h1 id="introduction"><span class="header-section-number">2</span> Introduction<a href="#introduction" class="self-link"></a></h1>
<p>In <span class="citation" data-cites="P0443R13">[<a href="#ref-P0443R13" role="doc-biblioref">P0443R13</a>]</span> we have included the fundamental principles described in <span class="citation" data-cites="P1660R0">[<a href="#ref-P1660R0" role="doc-biblioref">P1660R0</a>]</span>, and the fundamental requirement to customize algorithms. In recent discussions we have converged to an understanding of the <code>submit</code> operation on a <code>sender</code> and its more fundamental primitives <code>connect</code> and <code>start</code> supporting general interoperation between algorithms, and algorithm customization giving us full flexibility to optimize, to offload and to avoid synchronization in chains of mutually compatible algorithm customizations.</p>
<p>As a starting point, in <span class="citation" data-cites="P0443R13">[<a href="#ref-P0443R13" role="doc-biblioref">P0443R13</a>]</span> we only include a <code>bulk_execute</code> algorithm, that satisfies the core requirement to provide scalar and bulk execution. To make the C++23 solution completely practical, we should extend the set of algorithms, however. This paper suggests an expanded initial set that enables early useful work chains. This set is intended to act as a discussion focus for us to discuss one by one, and to analyze the finer constraints of the wording to make sure we do not over-constrain the design.</p>
<p>This paper does not attempt to propose the mapping of the C++20 parallel algorithms into an asynchronous environment. Once we have basic primitives, we can describe default implementations of the parallel algorithms, as well as <code>std::async</code>, in terms of these.</p>
<p>In the long run we expect to have a much wider set of algorithms, potentially covering the full set in the current C++20 parallel algorithms. The precise customization of these algorithms is open to discussion: they may be individually customized and individually defaulted, or they may be optionally individually customized but defaulted in a tree such that customizing one is known to accelerate dependencies. It is open to discussion how we achieve this and that is an independent topic, beyond the scope of this paper.</p>
<h2 id="summary"><span class="header-section-number">2.1</span> Summary<a href="#summary" class="self-link"></a></h2>
<p>Starting with <span class="citation" data-cites="P0443R13">[<a href="#ref-P0443R13" role="doc-biblioref">P0443R13</a>]</span> as a baseline we have the following customization points:</p>
<ul>
<li><code>connect(sender, receiver) -&gt; operation_state</code></li>
<li><code>start(operation_state) -&gt; void</code></li>
<li><code>submit(sender, receiver) -&gt; void</code></li>
<li><code>schedule(scheduler) -&gt; sender</code></li>
<li><code>execute(executor, invocable) -&gt; void</code></li>
<li><code>set_done</code></li>
<li><code>set_error</code></li>
<li><code>set_value</code></li>
</ul>
<p>and the following Concepts:</p>
<ul>
<li><code>scheduler</code></li>
<li><code>receiver</code></li>
<li><code>receiver_of</code></li>
<li><code>sender</code></li>
<li><code>sender_to</code></li>
<li><code>typed_sender</code></li>
<li><code>operation_state</code></li>
<li><code>executor</code></li>
<li><code>executor_of</code></li>
</ul>
<p>We propose immediately discussing the addition of the following algorithms:</p>
<ul>
<li><code>just(v...)</code>
<ul>
<li>returns a <code>sender</code> of the value(s) <code>v...</code></li>
</ul></li>
<li><code>just_on(sch, v...)</code>
<ul>
<li>a variant of the above that embeds the <code>on</code> algorithm</li>
</ul></li>
<li><code>on(s, sch)</code>
<ul>
<li>returns a sender that propagates the value or error from <code>s</code> on <code>sch</code>’s execution context</li>
</ul></li>
<li><code>sync_wait(s)</code>
<ul>
<li>blocks and returns a <code>T</code> of the value type of the sender, throwing on error, and terminates on done.</li>
</ul></li>
<li><code>when_all(s...)</code>
<ul>
<li>returns a sender that completes when all Senders <code>s...</code> complete, propagating all values <!--* `indexed_for(s, policy, rng, f)`
* returns a sender that applies `f` for each element of `rng` passing that element and the values from the incoming sender, completes when all `f`s complete propagating s's values onwards--></li>
</ul></li>
<li><code>transform(s, f)</code>
<ul>
<li>returns a sender that applies <code>f</code> to the value passed by <code>s</code>, or propagates errors or cancellation <!--* `bulk_transform(s, f)`
* returns a sender that applies `f` to each element in a range sent by `s`, or propagates errors or cancellation--></li>
</ul></li>
<li><code>let_value(s, f)</code>
<ul>
<li>Creates an async scope where the value propagated by <code>s</code> is available for the duration of another async operation produced by <code>f</code>. Error and cancellation signals propagate unmodified.</li>
</ul></li>
<li><code>let_error(s, f)</code>
<ul>
<li>Creates an async scope where an error propagated by <code>s</code> is available for the duration of another async operation produced by <code>f</code>. Value and cancellation propagate unmodified.</li>
</ul></li>
<li><code>ensure_started(s)</code>
<ul>
<li>Eagerly submits <code>s</code> and returns a sender that may be executing concurrently with surrounding code.</li>
</ul></li>
</ul>
<h2 id="examples"><span class="header-section-number">2.2</span> Examples<a href="#examples" class="self-link"></a></h2>
<h4 id="simple-example"><span class="header-section-number">2.2.0.1</span> Simple example<a href="#simple-example" class="self-link"></a></h4>
<p>A very simple example of applying a function to a propagated value and waiting for it.</p>
<div class="sourceCode" id="cb1"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb1-1"><a href="#cb1-1"></a><span class="kw">auto</span>  just_sender <span class="op">=</span>       <span class="co">// sender_to&lt;int&gt;</span></span>
<span id="cb1-2"><a href="#cb1-2"></a>  just<span class="op">(</span><span class="dv">3</span><span class="op">)</span>;</span>
<span id="cb1-3"><a href="#cb1-3"></a></span>
<span id="cb1-4"><a href="#cb1-4"></a><span class="kw">auto</span> transform_sender <span class="op">=</span>  <span class="co">// sender_to&lt;float&gt;</span></span>
<span id="cb1-5"><a href="#cb1-5"></a>  transform<span class="op">(</span></span>
<span id="cb1-6"><a href="#cb1-6"></a>    std<span class="op">::</span>move<span class="op">(</span>just_sender<span class="op">)</span>,</span>
<span id="cb1-7"><a href="#cb1-7"></a>    <span class="op">[](</span><span class="dt">int</span> a<span class="op">){</span><span class="cf">return</span> a<span class="op">+</span><span class="fl">0.5</span><span class="bu">f</span>;<span class="op">})</span>;</span>
<span id="cb1-8"><a href="#cb1-8"></a></span>
<span id="cb1-9"><a href="#cb1-9"></a><span class="dt">int</span> result <span class="op">=</span>              <span class="co">// value: 3.5</span></span>
<span id="cb1-10"><a href="#cb1-10"></a>  sync_wait<span class="op">(</span>std<span class="op">::</span>move<span class="op">(</span>transform_sender<span class="op">))</span>;</span></code></pre></div>
<p>In this very simple example we:</p>
<ul>
<li>start a chain with the value three</li>
<li>apply a function to the incoming value, adding 0.5 and returning a sender of a float.</li>
<li>block for the resulting value and assign the <code>float</code> value <code>3.5</code> to <code>result</code>.</li>
</ul>
<p>Using <code>operator|</code> as in ranges to remove the need to pass arguments around, we can represent this as:</p>
<div class="sourceCode" id="cb2"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb2-1"><a href="#cb2-1"></a><span class="dt">float</span> f <span class="op">=</span> sync_wait<span class="op">(</span></span>
<span id="cb2-2"><a href="#cb2-2"></a>  just<span class="op">(</span><span class="dv">3</span><span class="op">)</span> <span class="op">|</span> transform<span class="op">([](</span><span class="dt">int</span> a<span class="op">){</span><span class="cf">return</span> a<span class="op">+</span><span class="fl">0.5</span><span class="bu">f</span>;<span class="op">}))</span>;</span></code></pre></div>
<!--
#### Using indexed_for
We propose that indexed_for be the cleaned up version of bulk_execute, this shows how it fits into a work chain, with a parameter pack of inputs

```cpp
auto  just_sender =       // sender_to<int>
  just(std::vector<int>{3, 4, 5}, 10);

auto indexed_for_sender =  // sender_to<float>
  indexed_for(
    std::move(just_sender),
    std::execution::par,
    ranges::iota_view{3},
    [](size_t idx, std::vector<int>& vec, const int& i){
      vec[idx] = vec[idx] + i;
    });

auto transform_sender = transform(
  std::move(indexed_for_sender), [](vector<int> vec, int /*i*/){return vec;});

vector<int> result =       // value: {13, 14, 15}
  sync_wait(std::move(transform_sender));
```

In this less simple example we:

 * start a chain with a vector of three ints and an int
 * apply a function for each element in an index space, that receives the vector and int by reference and modifies the vector
 * specifies the most relaxed execution policy on which we guarantee the invocable and range access function to be safe to call
 * applies a transform to filter out the int result, demonstrating how indexed_for does not change the structure of the data
 * block for the resulting value and assign vector {13, 14, 15} to `result`

Using `operator|` as in ranges to remove the need to pass arguments around, we can represent this as:
```cpp
vector<int> result_vec = sync_wait(
  just(std::vector<int>{3, 4, 5}, 10) |
  indexed_for(
    std::execution::par,
    ranges::iota_view{3},
    [](size_t idx, vector<int>&vec, const int& i){vec[idx] = vec[idx] + i;}) |
  transform([](vector<int> vec, int /*i*/){return vec;}));
```
-->
<h4 id="using-when_all"><span class="header-section-number">2.2.0.2</span> Using when_all<a href="#using-when_all" class="self-link"></a></h4>
<p>when_all joins a list of incoming senders, propagating their values.</p>
<div class="sourceCode" id="cb3"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb3-1"><a href="#cb3-1"></a><span class="kw">auto</span>  just_sender <span class="op">=</span>       <span class="co">// sender_to&lt;int&gt;</span></span>
<span id="cb3-2"><a href="#cb3-2"></a>  just<span class="op">(</span>std<span class="op">::</span>vector<span class="op">&lt;</span><span class="dt">int</span><span class="op">&gt;{</span><span class="dv">3</span>, <span class="dv">4</span>, <span class="dv">5</span><span class="op">}</span>, <span class="dv">10</span><span class="op">)</span>;</span>
<span id="cb3-3"><a href="#cb3-3"></a><span class="kw">auto</span>  just_float_sender <span class="op">=</span>       <span class="co">// sender_to&lt;int&gt;</span></span>
<span id="cb3-4"><a href="#cb3-4"></a>  just<span class="op">(</span><span class="fl">20.0</span><span class="bu">f</span><span class="op">)</span>;</span>
<span id="cb3-5"><a href="#cb3-5"></a></span>
<span id="cb3-6"><a href="#cb3-6"></a><span class="kw">auto</span> when_all_sender <span class="op">=</span> when_all<span class="op">(</span></span>
<span id="cb3-7"><a href="#cb3-7"></a>  std<span class="op">::</span>move<span class="op">(</span>just_sender<span class="op">)</span>, std<span class="op">::</span>move<span class="op">(</span>just_float_sender<span class="op">))</span>;</span>
<span id="cb3-8"><a href="#cb3-8"></a></span>
<span id="cb3-9"><a href="#cb3-9"></a><span class="kw">auto</span> transform_sender<span class="op">(</span></span>
<span id="cb3-10"><a href="#cb3-10"></a>  std<span class="op">::</span>move<span class="op">(</span>when_all_sender<span class="op">)</span>,</span>
<span id="cb3-11"><a href="#cb3-11"></a>  <span class="op">[](</span>std<span class="op">::</span>vector<span class="op">&lt;</span><span class="dt">int</span><span class="op">&gt;</span> vec, <span class="dt">int</span> <span class="co">/*i*/</span>, <span class="dt">float</span> <span class="co">/*f*/</span><span class="op">)</span> <span class="op">{</span></span>
<span id="cb3-12"><a href="#cb3-12"></a>    <span class="cf">return</span> vec;</span>
<span id="cb3-13"><a href="#cb3-13"></a>  <span class="op">})</span></span>
<span id="cb3-14"><a href="#cb3-14"></a></span>
<span id="cb3-15"><a href="#cb3-15"></a>vector<span class="op">&lt;</span><span class="dt">int</span><span class="op">&gt;</span> result <span class="op">=</span>       <span class="co">// value: {3, 4, 5}</span></span>
<span id="cb3-16"><a href="#cb3-16"></a>  sync_wait<span class="op">(</span>std<span class="op">::</span>move<span class="op">(</span>transform_sender<span class="op">))</span>;</span></code></pre></div>
<p>This demonstrates simple joining of senders:</p>
<ul>
<li>start a chain with a pack of a vector and an int</li>
<li>start a second chain with a float</li>
<li>join the two to produce a pack of a vector, an int and a float</li>
<li>applies a transform to filter out the vector result</li>
<li>block for the resulting value and assign vector {3, 4, 5} to <code>result</code></li>
</ul>
<p>Using <code>operator|</code> as in ranges to remove the need to pass arguments around, we can represent this as:</p>
<div class="sourceCode" id="cb4"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb4-1"><a href="#cb4-1"></a>vector<span class="op">&lt;</span><span class="dt">int</span><span class="op">&gt;</span> result_vec <span class="op">=</span> sync_wait<span class="op">(</span></span>
<span id="cb4-2"><a href="#cb4-2"></a>  when_all<span class="op">(</span>just<span class="op">(</span>std<span class="op">::</span>vector<span class="op">&lt;</span><span class="dt">int</span><span class="op">&gt;{</span><span class="dv">3</span>, <span class="dv">4</span>, <span class="dv">5</span><span class="op">}</span>, <span class="dv">10</span><span class="op">)</span>, just<span class="op">(</span><span class="fl">20.0</span><span class="bu">f</span><span class="op">))</span> <span class="op">|</span></span>
<span id="cb4-3"><a href="#cb4-3"></a>  transform<span class="op">([](</span>vector<span class="op">&lt;</span><span class="dt">int</span><span class="op">&gt;</span> vec, <span class="dt">int</span> <span class="co">/*i*/</span>, <span class="dt">float</span> <span class="co">/*f*/</span><span class="op">){</span><span class="cf">return</span> vec;<span class="op">}))</span>;</span></code></pre></div>
<h4 id="with-exception"><span class="header-section-number">2.2.0.3</span> With exception<a href="#with-exception" class="self-link"></a></h4>
<p>A simple example showing how an exception that leaks out of a transform may propagate and be thrown from sync_wait.</p>
<div class="sourceCode" id="cb5"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb5-1"><a href="#cb5-1"></a><span class="dt">int</span> result <span class="op">=</span> <span class="dv">0</span>;</span>
<span id="cb5-2"><a href="#cb5-2"></a><span class="cf">try</span> <span class="op">{</span></span>
<span id="cb5-3"><a href="#cb5-3"></a>  <span class="kw">auto</span> just_sender <span class="op">=</span> just<span class="op">(</span><span class="dv">3</span><span class="op">)</span>;</span>
<span id="cb5-4"><a href="#cb5-4"></a>  <span class="kw">auto</span> on_sender <span class="op">=</span> on<span class="op">(</span>std<span class="op">::</span>move<span class="op">(</span>just_sender<span class="op">)</span>, scheduler1<span class="op">)</span>;</span>
<span id="cb5-5"><a href="#cb5-5"></a>  <span class="kw">auto</span> transform_sender <span class="op">=</span> transform<span class="op">(</span></span>
<span id="cb5-6"><a href="#cb5-6"></a>    std<span class="op">::</span>move<span class="op">(</span>on_sender<span class="op">)</span>,</span>
<span id="cb5-7"><a href="#cb5-7"></a>    <span class="op">[](</span><span class="dt">int</span> a<span class="op">){</span><span class="cf">throw</span> <span class="dv">2</span>;<span class="op">})</span>;</span>
<span id="cb5-8"><a href="#cb5-8"></a>  <span class="kw">auto</span> skipped_transform_sender <span class="op">=</span> transform<span class="op">(</span></span>
<span id="cb5-9"><a href="#cb5-9"></a>    std<span class="op">::</span>move<span class="op">(</span>transform_sender<span class="op">).</span></span>
<span id="cb5-10"><a href="#cb5-10"></a>    <span class="op">[](){</span><span class="cf">return</span> <span class="dv">3</span>;<span class="op">})</span>;</span>
<span id="cb5-11"><a href="#cb5-11"></a></span>
<span id="cb5-12"><a href="#cb5-12"></a>  result <span class="op">=</span> sync_wait<span class="op">(</span>std<span class="op">::</span>move<span class="op">(</span>skipped_transform_sender<span class="op">))</span>;</span>
<span id="cb5-13"><a href="#cb5-13"></a><span class="op">}</span> <span class="cf">catch</span><span class="op">(</span><span class="dt">int</span> a<span class="op">)</span> <span class="op">{</span></span>
<span id="cb5-14"><a href="#cb5-14"></a> result <span class="op">=</span> a;                                     <span class="co">// Assign 2 to result</span></span>
<span id="cb5-15"><a href="#cb5-15"></a><span class="op">}</span></span></code></pre></div>
<p>In this example we:</p>
<ul>
<li>start a chain with an int value <code>3</code></li>
<li>switch the context to one owned by scheduler1</li>
<li>apply a transformation to the value <code>3</code>, but this transform throws an exception rather than returning a transformed value</li>
<li>skip the final transform because there is an error propagating</li>
<li>block for the resulting value, seeing an exception thrown instead of a value returned</li>
<li>handle the exception</li>
</ul>
<p>As before, using <code>operator|</code> as in ranges to remove the need to pass arguments around, we can represent this more cleanly:</p>
<div class="sourceCode" id="cb6"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb6-1"><a href="#cb6-1"></a><span class="dt">int</span> result <span class="op">=</span> <span class="dv">0</span>;</span>
<span id="cb6-2"><a href="#cb6-2"></a><span class="cf">try</span> <span class="op">{</span></span>
<span id="cb6-3"><a href="#cb6-3"></a> result <span class="op">=</span> sync_wait<span class="op">(</span></span>
<span id="cb6-4"><a href="#cb6-4"></a>    just<span class="op">(</span><span class="dv">3</span><span class="op">)</span> <span class="op">|</span></span>
<span id="cb6-5"><a href="#cb6-5"></a>    on<span class="op">(</span>scheduler1<span class="op">)</span> <span class="op">|</span></span>
<span id="cb6-6"><a href="#cb6-6"></a>    transform<span class="op">([](</span><span class="dt">int</span> a<span class="op">){</span><span class="cf">throw</span> <span class="dv">2</span>;<span class="op">})</span> <span class="op">|</span></span>
<span id="cb6-7"><a href="#cb6-7"></a>    transform<span class="op">([](){</span><span class="cf">return</span> <span class="dv">3</span>;<span class="op">}))</span>;</span>
<span id="cb6-8"><a href="#cb6-8"></a><span class="op">}</span> <span class="cf">catch</span><span class="op">(</span><span class="dt">int</span> a<span class="op">)</span> <span class="op">{</span></span>
<span id="cb6-9"><a href="#cb6-9"></a> result <span class="op">=</span> a;                                     <span class="co">// Assign 2 to result</span></span>
<span id="cb6-10"><a href="#cb6-10"></a><span class="op">}</span></span></code></pre></div>
<h4 id="handle-an-exception"><span class="header-section-number">2.2.0.4</span> Handle an exception<a href="#handle-an-exception" class="self-link"></a></h4>
<p>Very similar to the above, we can handle an error mid-stream</p>
<div class="sourceCode" id="cb7"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb7-1"><a href="#cb7-1"></a><span class="kw">auto</span> just_sender <span class="op">=</span> just<span class="op">(</span><span class="dv">3</span><span class="op">)</span>;</span>
<span id="cb7-2"><a href="#cb7-2"></a><span class="kw">auto</span> via_sender <span class="op">=</span> on<span class="op">(</span>std<span class="op">::</span>move<span class="op">(</span>just_sender<span class="op">)</span>, scheduler1<span class="op">)</span>;</span>
<span id="cb7-3"><a href="#cb7-3"></a><span class="kw">auto</span> transform_sender <span class="op">=</span> transform<span class="op">(</span></span>
<span id="cb7-4"><a href="#cb7-4"></a>  std<span class="op">::</span>move<span class="op">(</span>via_sender<span class="op">)</span>,</span>
<span id="cb7-5"><a href="#cb7-5"></a>  <span class="op">[](</span><span class="dt">int</span> a<span class="op">){</span><span class="cf">throw</span> <span class="dv">2</span>;<span class="op">})</span>;</span>
<span id="cb7-6"><a href="#cb7-6"></a><span class="kw">auto</span> skipped_transform_sender <span class="op">=</span> transform<span class="op">(</span></span>
<span id="cb7-7"><a href="#cb7-7"></a>  std<span class="op">::</span>move<span class="op">(</span>transform_sender<span class="op">).</span></span>
<span id="cb7-8"><a href="#cb7-8"></a>  <span class="op">[](){</span><span class="cf">return</span> <span class="dv">3</span>;<span class="op">})</span>;</span>
<span id="cb7-9"><a href="#cb7-9"></a><span class="kw">auto</span> error_handling_sender <span class="op">=</span> let_error<span class="op">(</span></span>
<span id="cb7-10"><a href="#cb7-10"></a>  std<span class="op">::</span>move<span class="op">(</span>skipped_transform_sender<span class="op">)</span>,</span>
<span id="cb7-11"><a href="#cb7-11"></a>  <span class="op">[](</span>exception_ptr e<span class="op">){</span><span class="cf">return</span> just<span class="op">(</span><span class="dv">5</span><span class="op">)</span>;<span class="op">})</span>;</span>
<span id="cb7-12"><a href="#cb7-12"></a></span>
<span id="cb7-13"><a href="#cb7-13"></a><span class="kw">auto</span> result <span class="op">=</span> sync_wait<span class="op">(</span>std<span class="op">::</span>move<span class="op">(</span>error_handling_sender<span class="op">))</span>;</span></code></pre></div>
<p>In this example we:</p>
<ul>
<li>start a chain with an int value <code>3</code></li>
<li>switch the context to one owned by scheduler1</li>
<li>apply a transformation to the value <code>3</code>, but this transform throws an exception rather than returning a transformed value</li>
<li>skip the final transform because there is an error propagating</li>
<li>handle the error channel, applying an operation to an <code>exception_ptr</code> pointing to the value <code>2</code></li>
<li>in handling the error we return a sender that propagates the value <code>5</code>, thus recovering from the error</li>
<li>block for the resulting value, assigning <code>5</code> to <code>result</code></li>
</ul>
<p>As before, using <code>operator|</code> as in ranges to remove the need to pass arguments around, we can represent this more cleanly:</p>
<div class="sourceCode" id="cb8"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb8-1"><a href="#cb8-1"></a><span class="kw">auto</span> s <span class="op">=</span> ;</span>
<span id="cb8-2"><a href="#cb8-2"></a><span class="dt">int</span> result <span class="op">=</span> sync_wait<span class="op">(</span></span>
<span id="cb8-3"><a href="#cb8-3"></a>  just<span class="op">(</span><span class="dv">3</span><span class="op">)</span> <span class="op">|</span></span>
<span id="cb8-4"><a href="#cb8-4"></a>  on<span class="op">(</span>scheduler1<span class="op">)</span> <span class="op">|</span></span>
<span id="cb8-5"><a href="#cb8-5"></a>  transform<span class="op">([](</span><span class="dt">float</span> a<span class="op">){</span><span class="cf">throw</span> <span class="dv">2</span>;<span class="op">})</span> <span class="op">|</span></span>
<span id="cb8-6"><a href="#cb8-6"></a>  transform<span class="op">([](){</span><span class="cf">return</span> <span class="dv">3</span>;<span class="op">})</span> <span class="op">|</span></span>
<span id="cb8-7"><a href="#cb8-7"></a>  let_error<span class="op">([](</span><span class="kw">auto</span> e<span class="op">){</span></span>
<span id="cb8-8"><a href="#cb8-8"></a>   <span class="cf">return</span> just<span class="op">(</span><span class="dv">5</span><span class="op">)</span>;<span class="op">}))</span>;</span></code></pre></div>
<h1 id="impact-on-the-standard-library"><span class="header-section-number">3</span> Impact on the standard library<a href="#impact-on-the-standard-library" class="self-link"></a></h1>
<h2 id="sender-adapter-objects"><span class="header-section-number">3.1</span> Sender adapter objects<a href="#sender-adapter-objects" class="self-link"></a></h2>
<p>Taking inspiration from <a href="http://eel.is/c++draft/range.adaptor.object">range adaptors</a> define sender adapters.</p>
<p>Wording to be based on [range.adaptors] with the basic requirement that:</p>
<ul>
<li><code>operator|</code> be overloaded for the purpose of creating pipelines over senders</li>
<li>That the following are equivalent expressions:
<ul>
<li><code>algorithm(sender, args...)</code></li>
<li><code>algorithm(args...)(sender)</code></li>
<li><code>sender | algorithm(args...)</code></li>
</ul></li>
<li>that <code>algorithms(args...)</code> is a <em>sender adaptor closure object</em></li>
<li>TBD where sender adapters are declared</li>
</ul>
<p>Details below are in loosely approximated wording and should be made consistent with <span class="citation" data-cites="P0443R11">[<a href="#ref-P0443R11" role="doc-biblioref">P0443R11</a>]</span> and the standard itself when finalized. We choose this set of algorithms as a basic set to allow a range of realistic, though still limited, compositions to be written against executors.</p>
<!-- ## execution::is_noexcept_sender
### Summary
Queries whether the passed sender will ever propagate an error when treated as an r-value to `submit`.

### Signature

### Wording
The name `execution::is_noexcept_sender` denotes a customization point object.
The expression `execution::is_noexcept_sender(S)` for some subexpression `S` is expression-equivalent to:

 * `S.is_noexcept()`, if that expression is valid.
 * Otherwise, `is_noexcept_sender(S)`, if that expression is valid, with overload resolution performed in a context that includes the declaration
```
        template<class S>
          void is_noexcept_sender(S) = delete;
```
    and that does not include a declaration of `execution::is_noexcept_sender`.

 * Otherwise, `false`.

If possible, `is_noexcept_sender` should be `noexcept`.

If `execution::is_noexcept_sender(s)` returns true for a `sender` `s` then it is guaranteed that `s` will not call `error` on any `callback` `c` passed to `submit(s, c)`. -->
<h2 id="executionjust"><span class="header-section-number">3.2</span> execution::just<a href="#executionjust" class="self-link"></a></h2>
<h3 id="overview"><span class="header-section-number">3.2.1</span> Overview<a href="#overview" class="self-link"></a></h3>
<p><code>just</code> creates a <code>sender</code> that propagates a value inline to a submitted receiver.</p>
<p>Signature:</p>
<div class="sourceCode" id="cb9"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb9-1"><a href="#cb9-1"></a><span class="kw">template</span> <span class="op">&lt;</span><span class="kw">typename</span> T<span class="op">&gt;</span></span>
<span id="cb9-2"><a href="#cb9-2"></a><span class="kw">concept</span> moveable<span class="op">-</span>value <span class="op">=</span> <span class="co">// exposition only</span></span>
<span id="cb9-3"><a href="#cb9-3"></a>  move_constructible<span class="op">&lt;</span>remove_cvref_t<span class="op">&lt;</span>T<span class="op">&gt;&gt;</span> <span class="op">&amp;&amp;</span></span>
<span id="cb9-4"><a href="#cb9-4"></a>  constructible_from<span class="op">&lt;</span>remove_cvref_t<span class="op">&lt;</span>T<span class="op">&gt;</span>, T<span class="op">&gt;</span>;</span>
<span id="cb9-5"><a href="#cb9-5"></a></span>
<span id="cb9-6"><a href="#cb9-6"></a><span class="kw">template</span> <span class="op">&lt;</span>movable<span class="op">-</span>value<span class="op">...</span> Ts<span class="op">&gt;</span></span>
<span id="cb9-7"><a href="#cb9-7"></a>see<span class="op">-</span>below just<span class="op">(</span>Ts<span class="op">&amp;&amp;...</span> ts<span class="op">)</span> <span class="kw">noexcept</span><span class="op">(</span>see<span class="op">-</span>below<span class="op">)</span>;</span></code></pre></div>
<p><em>[ Example:</em></p>
<div class="sourceCode" id="cb10"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb10-1"><a href="#cb10-1"></a><span class="dt">int</span> r <span class="op">=</span> sync_wait<span class="op">(</span>just<span class="op">(</span><span class="dv">3</span><span class="op">))</span>;</span>
<span id="cb10-2"><a href="#cb10-2"></a><span class="co">// r==3</span></span></code></pre></div>
<p><em>- end example]</em></p>
<h3 id="wording"><span class="header-section-number">3.2.2</span> Wording<a href="#wording" class="self-link"></a></h3>
<p>The expression <code>execution::just(t...)</code> returns a sender, <code>s</code> wrapping the values <code>t...</code>.</p>
<!-- * If `t...` are nothrow movable then `execution::is_noexcept_sender(s)` shall be constexpr and return true.-->
<ul>
<li>When <code>execution::connect(s, r)</code> is called resulting in <code>operation_state</code> <code>o</code> containing <code>rCopy</code> with type <code>remove_cvref_t&lt;decltype(r)&gt;</code> and initialized with <code>r</code> and followed by <code>execution::start(o)</code> for some <code>r</code>, will call <code>execution::set_value(r, std::move(t)...)</code>, inline with the caller.</li>
<li>If moving of <code>t</code> throws, then will catch the exception and call <code>execution::set_error(r, e)</code> with the caught <code>exception_ptr</code>.</li>
</ul>
<h2 id="executionjust_on"><span class="header-section-number">3.3</span> execution::just_on<a href="#executionjust_on" class="self-link"></a></h2>
<h3 id="overview-1"><span class="header-section-number">3.3.1</span> Overview<a href="#overview-1" class="self-link"></a></h3>
<p><code>just_on</code> creates a <code>sender</code> that propagates a value to a submitted receiver on the execution context of a passed <code>scheduler</code>. Semantically equivalent to <code>on(just(t), s)</code> if <code>just_on</code> is not customized on <code>s</code>. Providing <code>just_on</code> offers an opportunity to directly customise the algorithm to control allocation of the value <code>t</code> at the head of a custom pipeline.</p>
<p>Signature:</p>
<div class="sourceCode" id="cb11"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb11-1"><a href="#cb11-1"></a><span class="kw">template</span> <span class="op">&lt;</span>execution<span class="op">::</span>scheduler Sch, movable<span class="op">-</span>value<span class="op">...</span> Ts<span class="op">&gt;</span></span>
<span id="cb11-2"><a href="#cb11-2"></a>see<span class="op">-</span>below just_on<span class="op">(</span>Sch sch, Ts<span class="op">&amp;&amp;...</span> ts<span class="op">)</span> <span class="kw">noexcept</span><span class="op">(</span>see<span class="op">-</span>below<span class="op">)</span>;</span></code></pre></div>
<p><em>[ Example:</em></p>
<div class="sourceCode" id="cb12"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb12-1"><a href="#cb12-1"></a>MyScheduler s;</span>
<span id="cb12-2"><a href="#cb12-2"></a><span class="dt">int</span> r <span class="op">=</span> sync_wait<span class="op">(</span>just_on<span class="op">(</span>s, <span class="dv">3</span><span class="op">))</span>;</span>
<span id="cb12-3"><a href="#cb12-3"></a><span class="co">// r==3</span></span></code></pre></div>
<p><em>- end example]</em></p>
<h3 id="wording-1"><span class="header-section-number">3.3.2</span> Wording<a href="#wording-1" class="self-link"></a></h3>
<p>The name <code>execution::just_on</code> denotes a customization point object. For some subexpressions <code>sch</code> and <code>ts...</code> let <code>Sch</code> be a type such that <code>decltype((sch))</code> is <code>Sch</code> and let <code>Ts...</code> be a pack of types such that <code>decltype((ts))...</code> is <code>Ts...</code>. The expression <code>execution::just_on(sch, ts...)</code> is expression-equivalent to:</p>
<ul>
<li><code>sch.just_on(ts...)</code> if that expression is valid and if <code>sch</code> satisfies <code>scheduler</code>.</li>
<li>Otherwise, <code>just_on(sch, ts...)</code>, if that expression is valid, if <code>sch</code> satisfies <code>scheduler</code> with overload resolution performed in a context that includes the declaration</li>
</ul>
<div class="sourceCode" id="cb13"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb13-1"><a href="#cb13-1"></a>   void just_on() = delete;</span></code></pre></div>
<p>and that does not include a declaration of <code>execution::just_on</code>. * Otherwise returns the result of the expression: <code>execution::on(execution::just(ts...), sch)</code></p>
<ul>
<li>Any customisation of <code>execution::just_on(sch, ts)</code> returning a <code>sender</code> <code>s</code> shall execute calls to <code>set_value</code>, <code>set_done</code> or <code>set_error</code> on a <code>receiver</code> connected to <code>s</code> on an execution context owned by <code>sch</code>.</li>
</ul>
<!--
## execution::just_error

### Summary
Returns a sender that propagates the passed error inline when `submit` is called.
This is useful for starting a chain of work.

### Wording
The expression `execution::just_error(e)` returns a sender, `s` wrapping the error `e`.

 * If `t` is nothrow movable then `execution::is_noexcept_sender(s)` shall be constexpr and return true.
 * When `execution::submit(s, r)` is called for some `r`, and r-value `s` will call `execution::set_error(r, std::move(t))`, inline with the caller.
 * When `execution::submit(s, r)` is called for some `r`, and l-value `s` will call `execution::set_error(r, t)`, inline with the caller.
 * If moving of `e` throws, then will catch the exception and call `execution::set_error(r, e)` with the caught `exception_ptr`. -->
<h2 id="executionsync_wait"><span class="header-section-number">3.4</span> execution::sync_wait<a href="#executionsync_wait" class="self-link"></a></h2>
<h3 id="overview-2"><span class="header-section-number">3.4.1</span> Overview<a href="#overview-2" class="self-link"></a></h3>
<p>Blocks the calling thread to wait for the passed sender to complete. Returns <code>T</code> when passed a <code>typed_sender</code> that sends a <code>T</code> on the value channel, where <code>T</code> may be <code>void</code>, throws if an exception is propagated and calls <code>std::terminate</code> on propagation of the <code>set_done()</code> signal.</p>
<div class="sourceCode" id="cb14"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb14-1"><a href="#cb14-1"></a><span class="kw">template</span> <span class="op">&lt;</span>execution<span class="op">::</span>typed_sender S<span class="op">&gt;</span></span>
<span id="cb14-2"><a href="#cb14-2"></a><span class="kw">auto</span> sync_wait<span class="op">(</span>S<span class="op">&amp;&amp;</span> s<span class="op">)</span> <span class="op">-&gt;</span> std<span class="op">::</span>sender_traits<span class="op">&lt;</span>S<span class="op">&gt;::</span>value_types;</span>
<span id="cb14-3"><a href="#cb14-3"></a><span class="kw">template</span> <span class="op">&lt;</span><span class="kw">class</span> ValueType, execution<span class="op">::</span>sender S<span class="op">&gt;</span></span>
<span id="cb14-4"><a href="#cb14-4"></a>ValueType sync_wait_r<span class="op">(</span>S<span class="op">&amp;&amp;</span> s<span class="op">)</span>;</span></code></pre></div>
<p><em>[ Example:</em></p>
<div class="sourceCode" id="cb15"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb15-1"><a href="#cb15-1"></a><span class="dt">int</span> r <span class="op">=</span> sync_wait<span class="op">(</span>just<span class="op">(</span><span class="dv">3</span><span class="op">))</span>;</span>
<span id="cb15-2"><a href="#cb15-2"></a><span class="dt">float</span> r <span class="op">=</span> sync_wait<span class="op">&lt;</span><span class="dt">float</span><span class="op">&gt;(</span>just<span class="op">(</span><span class="fl">3.5</span><span class="bu">f</span><span class="op">))</span>;</span>
<span id="cb15-3"><a href="#cb15-3"></a><span class="co">// r==3</span></span></code></pre></div>
<p><em>- end example]</em></p>
<h3 id="wording-2"><span class="header-section-number">3.4.2</span> Wording<a href="#wording-2" class="self-link"></a></h3>
<p>The name <code>execution::sync_wait</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::sync_wait(s)</code> is expression-equivalent to:</p>
<ul>
<li><code>s.sync_wait()</code> if that expression is valid and if <code>S</code> satisfies <code>sender</code>.</li>
<li>Otherwise, <code>sync_wait(s)</code>, if that expression is valid, if <code>S</code> satisfies <code>sender</code>, with overload resolution performed in a context that includes the declaration</li>
</ul>
<div class="sourceCode" id="cb16"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb16-1"><a href="#cb16-1"></a>     void sync_wait() = delete;</span></code></pre></div>
<p>and that does not include a declaration of <code>execution::sync_wait</code>.</p>
<ul>
<li><p>Otherwise constructs a <code>receiver</code>, <code>r</code> over an implementation-defined synchronization primitive and passes <code>r</code> to <code>execution::connect(s, r)</code> returning some <code>operation_state</code> <code>os</code>. Waits on the synchronization primitive to block on completion of <code>s</code>.</p>
<ul>
<li>If the operation completes by calling <code>set_value(r, t)</code> then <code>sync_wait()</code> will return a value, <code>x</code>, of type <code>remove_cvref_t&lt;decltype(t)&gt;</code>.</li>
<li>If the operation completes by calling <code>set_value(r)</code> then <code>sync_wait()</code> will return <code>void</code>.</li>
<li>If the operation completes by calling <code>set_error(r, e)</code> then <code>sync_wait()</code> calls <code>std::rethrow_exception(e)</code> if <code>decltype(e)</code> is <code>std::exception_ptr</code> or <code>throw e;</code> otherwise.</li>
<li>If the operation completes by calling <code>set_done(r)</code> then <code>sync_wait()</code> will call <code>std::terminate</code>.</li>
</ul></li>
</ul>
<!--
If `execution::is_noexcept_sender(S)` returns true at compile-time, and the return type `T` is nothrow movable, then `sync_wait` is noexcept.
Note that `sync_wait` requires `S` to propagate a single value type.
-->
<h2 id="executionon"><span class="header-section-number">3.5</span> execution::on<a href="#executionon" class="self-link"></a></h2>
<h3 id="overview-3"><span class="header-section-number">3.5.1</span> Overview<a href="#overview-3" class="self-link"></a></h3>
<p>Takes a <code>sender</code> and a <code>scheduler</code> and ensures that the <code>sender</code> operation is <code>connect</code>ed and <code>start</code>ed on the execution context associated with the <code>scheduler</code>, giving the programmer control over where the work encapsulated by <code>sender</code> is started.</p>
<div class="sourceCode" id="cb17"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb17-1"><a href="#cb17-1"></a><span class="kw">template</span> <span class="op">&lt;</span>execution<span class="op">::</span>sender S, execution<span class="op">::</span>scheduler Sch<span class="op">&gt;</span></span>
<span id="cb17-2"><a href="#cb17-2"></a>see<span class="op">-</span>below on<span class="op">(</span>S s, Sch sch<span class="op">)</span>;</span></code></pre></div>
<p><em>[ Example:</em></p>
<div class="sourceCode" id="cb18"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb18-1"><a href="#cb18-1"></a><span class="kw">auto</span> r <span class="op">=</span> sync_wait<span class="op">(</span>just<span class="op">(</span><span class="dv">3</span><span class="op">)</span> <span class="op">|</span> on<span class="op">(</span>my_scheduler<span class="op">{})</span> <span class="op">|</span> transform<span class="op">([](</span><span class="dt">int</span> v<span class="op">){</span><span class="cf">return</span> v<span class="op">+</span><span class="dv">1</span>;<span class="op">}))</span>;</span>
<span id="cb18-2"><a href="#cb18-2"></a><span class="co">// r==3</span></span></code></pre></div>
<p><em>- end example]</em></p>
<h3 id="wording-3"><span class="header-section-number">3.5.2</span> Wording<a href="#wording-3" class="self-link"></a></h3>
<p>The name <code>execution::on</code> denotes a customization point object. For some subexpressions <code>s</code> and <code>sch</code>, let <code>S</code> be a type such that <code>decltype((s))</code> is <code>S</code> and <code>Sch</code> be a type such that <code>decltype((sch))</code> is <code>Sch</code> The expression <code>execution::on(s, sch)</code> is expression-equivalent to:</p>
<ul>
<li><code>s.on(sch)</code> if that expression is valid, if <code>S</code> satisfies <code>sender</code></li>
<li>Otherwise, <code>on(s, sch)</code> if that expression is valid, and if <code>S</code> satisfies <code>sender</code> and if <code>Sch</code> satisfies <code>scheduler</code>, with overload resolution performed in a context that includes the declaration</li>
</ul>
<div class="sourceCode" id="cb19"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb19-1"><a href="#cb19-1"></a>     <span class="dt">void</span> on<span class="op">()</span> <span class="op">=</span> <span class="kw">delete</span>;</span></code></pre></div>
<p>and that does not include a declaration of <code>execution::on</code>.</p>
<ul>
<li><p>Otherwise:</p>
<ul>
<li>Constructs a <code>sender</code> <code>s2</code> such that when <code>connect</code> is called with some <code>receiver</code> <code>output_receiver</code> as <code>execution::connect(s2, output_receiver)</code> resulting in an <code>operation_state</code> <code>os</code> which is stored as a subobject of the parent <code>operation_state</code>:
<ul>
<li>Constructs a receiver, <code>r</code> and passes <code>r</code> to <code>execution::connect(s, r)</code> resulting in an operation state <code>ros</code>, which is stored as a subobject of <code>os</code> such that:</li>
<li>When <code>set_value</code>, <code>set_error</code> or <code>set_done</code> is called on <code>r</code>, the parameter is copied and stored as a subobject of a receiver <code>r2</code> and <code>execution::connect(execution::schedule(sch), std::move(r2))</code> results in an <code>operation_state</code> <code>os2</code> which is stored as a subobject of <code>os</code> such that:
<ul>
<li>When <code>set_value</code> is called on <code>r2</code>, <code>os2</code>’s destructor will be called, the stored value is forwarded to <code>output_receiver</code> on the appropriate choice of <code>set_value</code>, <code>set_error</code> or <code>set_done</code> to match the operation performed on <code>r</code>.</li>
<li>When <code>set_error</code> or <code>set_done</code> is called on <code>r2</code> the parameters propagate to <code>output_receiver</code>.</li>
</ul></li>
<li>If <code>connect</code> throws, the resulting exception is forwarded to <code>execution::set_error(output_receiver)</code>.</li>
<li>The destructor of <code>ros</code> is called.</li>
<li>If <code>connect</code> throws, the resulting exception is forwarded to <code>execution::set_error(output_receiver)</code>.</li>
<li>Calls <code>execution::start(os2)</code>.</li>
</ul></li>
<li>When <code>execution::start</code> is called on <code>os</code>, call <code>execution::start(ros)</code>.</li>
</ul></li>
<li><p>Otherwise, <code>execution::on(s, sch)</code> is ill-formed.</p></li>
<li><p>Any customisation of <code>execution::on(s, sch)</code> returning a <code>sender</code> <code>s2</code> shall execute calls to <code>set_value</code>, <code>set_done</code> or <code>set_error</code> on a <code>receiver</code> connected to <code>s2</code> on an execution context owned by <code>sch</code>.</p></li>
</ul>
<h2 id="executionwhen_all"><span class="header-section-number">3.6</span> execution::when_all<a href="#executionwhen_all" class="self-link"></a></h2>
<h3 id="overview-4"><span class="header-section-number">3.6.1</span> Overview<a href="#overview-4" class="self-link"></a></h3>
<p><code>when_all</code> combines a set of <em>non-void</em> <code>senders</code>, returning a <code>sender</code> that, on success, completes with the combined values of all incoming <code>sender</code>s. To make usage simpler, <code>when_all</code> is restricted to <code>typed_sender</code>s that each send only a single possible value type.</p>
<p>Signature:</p>
<div class="sourceCode" id="cb20"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb20-1"><a href="#cb20-1"></a><span class="kw">template</span> <span class="op">&lt;</span>execution<span class="op">::</span>typed_sender Ss<span class="op">...&gt;</span></span>
<span id="cb20-2"><a href="#cb20-2"></a>see<span class="op">-</span>below when_all<span class="op">(</span>Ss<span class="op">...</span> ss<span class="op">)</span>;</span></code></pre></div>
<p><em>[ Example:</em></p>
<div class="sourceCode" id="cb21"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb21-1"><a href="#cb21-1"></a><span class="kw">auto</span> r <span class="op">=</span></span>
<span id="cb21-2"><a href="#cb21-2"></a>  sync_wait<span class="op">(</span></span>
<span id="cb21-3"><a href="#cb21-3"></a>    transform<span class="op">(</span></span>
<span id="cb21-4"><a href="#cb21-4"></a>      when_all<span class="op">(</span>just<span class="op">(</span><span class="dv">3</span><span class="op">)</span> <span class="op">|</span> just<span class="op">(</span><span class="fl">1.2</span><span class="bu">f</span><span class="op">))</span>,</span>
<span id="cb21-5"><a href="#cb21-5"></a>      <span class="op">[](</span><span class="dt">int</span> a, <span class="dt">float</span> b<span class="op">){</span><span class="cf">return</span> a <span class="op">+</span> b;<span class="op">}))</span>;</span>
<span id="cb21-6"><a href="#cb21-6"></a><span class="co">// r==4.2</span></span></code></pre></div>
<p><em>- end example]</em></p>
<h3 id="wording-4"><span class="header-section-number">3.6.2</span> Wording<a href="#wording-4" class="self-link"></a></h3>
<p>The name <code>execution::when_all</code> denotes a customization point object. For some subexpression <code>ss...</code>, let <code>Ss...</code> be a list of types such that <code>decltype((ss))...</code> is <code>Ss...</code>. The expression <code>execution::when_all(ss...)</code> is expression-equivalent to:</p>
<ul>
<li><p><code>when_all(ss...)</code> if that expression is valid, and if each <code>Si</code> in <code>Ss</code> satisfies <code>typed_sender</code>, <code>sender_traits&lt;Si&gt;::value_types&lt;T&gt;</code> for some type <code>T</code> with overload resolution performed in a context that includes the declaration</p>
<div class="sourceCode" id="cb22"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb22-1"><a href="#cb22-1"></a>   <span class="dt">void</span> when_all<span class="op">()</span> <span class="op">=</span> <span class="kw">delete</span>;</span></code></pre></div>
<p>and that does not include a declaration of <code>execution::when_all</code>.</p></li>
<li><p>Otherwise, returns a <code>sender</code>, <code>s</code>, that, when <code>connect(s, output_receiver)</code> is called on the returned <code>sender</code>, for some <code>output_receiver</code>, constructs a <code>receiver</code> <code>ri</code> for each passed <code>sender</code>, <code>si</code> and calls <code>connect(si, ri)</code>, returning <code>operation_state</code> object <code>osi</code>. The <code>operation_state</code>s, <code>osi</code>, are stored as subobjects within the operation-state object returned from <code>connect(s, output_receiver)</code> such that:</p>
<ul>
<li>if <code>set_value(ti)</code> is called on all <code>ri</code>, for some single value <code>ti</code> for each <code>ri</code> will concatenate the list of values and call <code>set_value(output_receiver, t0..., t1..., tn...)</code>.</li>
<li>if <code>set_done()</code> is called on any <code>ri</code>, will call <code>set_done(output_receiver)</code>, discarding other results.</li>
<li>if <code>set_error(e)</code> is called on any <code>ri</code> will call <code>set_error(output_receiver, e)</code> for some <code>e</code>, discarding other results.</li>
</ul></li>
</ul>
<p>When <code>start</code> is called on the returned <code>sender</code>’s <code>operation_state</code>, call <code>execution::start(osi)</code> for each <code>operation_state</code> <code>osi</code>.</p>
<p><strong>Note:</strong> See <a href="#planned-developments">Planned Developments</a>.</p>
<!--
## execution::indexed_for

### Overview
`indexed_for` is a sender adapter that takes a `sender`, execution policy, a range and an invocable and returns a `sender` that propagates the input values but runs the invocable once for each element of the range, passing the input by non-const reference.

Signature:
```cpp
S<T...> indexed_for(
  S<T...>,
  execution_policy,
  range<Idx>,
  invocable<void(Idx, T&...));
```

where `S<T...>` represents implementation-defined sender types that send a value of type list `T...` in their value channels.
Note that in the general case there may be many types `T...` for a given `sender`, in which case the invocable may have to represent an overload set.

*[ Example:*
```cpp
int r = sync_wait(
  just(3) |
  indexed_for(
    std::execution::par,
    ranges::iota_view{6},
    [](int idx, int& v){v = v + idx;}));
// r==9
```

### Wording

The name `execution::indexed_for` denotes a customization point object.
The expression `execution::indexed_for(S, P, R, F)` for some subexpressions `S`, `P`, `R` and `F` is expression-equivalent to:

 * If `P` does not satisfy `std::is_execution_policy_v<P>`, then the expression is invalid.
 * If `R` does not satisfy either `range` then the expression is invalid.
 * If `P` is `std::execution::sequenced_policy` then `range` must satisfy `input_range` otherwise `range` must satisfy `random_access_range`.
 * If `F` does not satisfy `MoveConstructible` then the expression is invalid.
 * S.indexed_for(P, R, F), if that expression is valid.
 * Otherwise, `indexed_for(S, R, P, F)`, if that expression is valid with overload resolution performed in a context that includes the declaration
 ```
         template<class S, class R, class P, class F>
           void indexed_for(S, R, P, F) = delete;
 ```
   and that does not include a declaration of `execution::indexed_for`.

 * Otherwise constructs a receiver, `r` and passes that receiver to `execution::submit(S, r)`.

   * If `set_value` is called on `r` with some parameter pack `t...` then calls `F(idx, t...)` for each element `idx` in `R`.
     Once all complete calls `execution::set_value(output_receiver, v)`.

     * If any call to `set_value` throws an exception, then call `set_error(r, e)` with some exception from the set.

   * If `set_error(r, e)` is called, passes `e` to `execution::set_error(output_receiver, e)`.
   * If `set_done(r)` is called, calls `execution::set_done(output_receiver)`.

**Notes:**
 * If `P` is not `execution::seq` and `R` satisfies `random_access_range` then `indexed_for` may run the instances of `F` concurrently.
 * `P` represents a guarantee on the most relaxed execution policy `F` and the element access function of range `R`  are safe to run under, and hence the most parallel fashion in which the underlying `scheduler` may map instances of `F` to execution agents.
-->
<h2 id="executiontransform"><span class="header-section-number">3.7</span> execution::transform<a href="#executiontransform" class="self-link"></a></h2>
<h3 id="overview-5"><span class="header-section-number">3.7.1</span> Overview<a href="#overview-5" class="self-link"></a></h3>
<p><code>transform</code> is a sender adapter that takes a <code>sender</code> and an invocable and returns a <code>sender</code> that propagates the value resulting from calling the invocable on the value passed by the preceding <code>sender</code>.</p>
<p>Signature:</p>
<div class="sourceCode" id="cb23"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb23-1"><a href="#cb23-1"></a><span class="kw">template</span> <span class="op">&lt;</span>execution<span class="op">::</span>sender S, moveable<span class="op">-</span>value F<span class="op">&gt;</span></span>
<span id="cb23-2"><a href="#cb23-2"></a>  <span class="kw">requires</span> std<span class="op">::</span>invocable<span class="op">&lt;</span>F, sender_traits<span class="op">&lt;</span>S<span class="op">&gt;::</span><span class="kw">template</span> value_types<span class="op">&gt;</span></span>
<span id="cb23-3"><a href="#cb23-3"></a>see<span class="op">-</span>below transform<span class="op">(</span>S s, F f<span class="op">)</span>;</span></code></pre></div>
<p><em>[ Example:</em></p>
<div class="sourceCode" id="cb24"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb24-1"><a href="#cb24-1"></a><span class="dt">int</span> r <span class="op">=</span> sync_wait<span class="op">(</span>just<span class="op">(</span><span class="dv">3</span><span class="op">)</span> <span class="op">|</span> transform<span class="op">([](</span><span class="dt">int</span> v<span class="op">){</span><span class="cf">return</span> v<span class="op">+</span><span class="dv">1</span>;<span class="op">}))</span>;</span>
<span id="cb24-2"><a href="#cb24-2"></a><span class="co">// r==4</span></span></code></pre></div>
<p><em>- end example]</em></p>
<h3 id="wording-5"><span class="header-section-number">3.7.2</span> Wording<a href="#wording-5" class="self-link"></a></h3>
<p>The name <code>execution::transform</code> denotes a customization point object. For some subexpressions <code>s</code> and <code>f</code>, let <code>S</code> be a type such that <code>decltype((s))</code> is <code>S</code> and <code>decltype((f))</code> is <code>F</code>. The expression <code>execution::transform(s, f)</code> is expression-equivalent to:</p>
<ul>
<li><code>s.transform(f)</code> if that expression is valid, <code>s</code> satisfies <code>sender</code>.</li>
<li>Otherwise, <code>transform(S, F)</code>, if that expression is valid, <code>s</code> satisfies <code>sender</code> with overload resolution performed in a context that includes the declaration</li>
</ul>
<div class="sourceCode" id="cb25"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb25-1"><a href="#cb25-1"></a>   void transform() = delete;</span></code></pre></div>
<p>and that does not include a declaration of <code>execution::transform</code>.</p>
<ul>
<li><p>Otherwise constructs a <code>receiver</code>, <code>r</code> and passes that receiver to <code>execution::connect(s, r)</code> to return an <code>operation_state</code> <code>os</code> such that:</p>
<p>When some <code>output_receiver</code> has been passed to <code>connect</code> on the returned <code>sender</code> to return some <code>operation_state</code> <code>os2</code>:</p>
<ul>
<li>If <code>set_value(r, Ts... ts)</code> is called, calls <code>std::invoke(F, ts...)</code> and passes the result <code>v</code> to <code>execution::set_value(output_receiver, v)</code>.</li>
<li>If <code>f</code> throws, catches the exception and passes it to <code>execution::set_error(output_receiver, e)</code>.</li>
<li>If <code>set_error(r, e)</code> is called, passes <code>e</code> to <code>execution::set_error(output_receiver, e)</code>.</li>
<li>If <code>set_done(r)</code> is called, calls <code>execution::set_done(output_receiver)</code>.</li>
</ul></li>
</ul>
<p>When <code>start()</code> is called on <code>os2</code> calls <code>execution::start(os)</code>.</p>
<ul>
<li>Otherwise the expression <code>execution::transform(s, f)</code> is ill-formed.</li>
</ul>
<!--
If `execution::is_noexcept_sender(S)` returns true at compile-time, and `F(S1::value_types)` is marked `noexcept` and all entries in `S1::value_types` are nothrow movable, `execution::is_noexcept_sender(transform(S1, F))` should return `true` at compile time.
-->
<!--

## execution::bulk_transform

### Overview
`bulk_transform` is a sender adapter that takes a `sender` of a `range` of values and an invocable and returns a `sender` that executes the invocable for each element of the input range, and propagates the range of returned values.

Signature:
```cpp
S<range<T2>> bulk_transform(S<range<T>>, invocable<T2(T));
```

where `S<range<T>>` and `S<T2>` are implementation-defined types that is represent senders that send a value of type list `T` or `T2` respectively in their value channels.
Note that in the general case there may be many types `T` for a given `sender`, in which case the invocable may have to represent an overload set.

*[ Example:*
```cpp
std::vector<int> r = sync_wait(just(std::vector<int>{3, 4, 5}) | bulk_transform([](int v){return v+1;}));
// r=={4, 5, 6}
```
*- end example]*

Note: it is TBD how precisely we should represent the intermediate data types here. Intermediate vectors would require allocator support. Purely lazy ranges may be inadequate.

### Wording
The name `execution::bulk_transform` denotes a customization point object.
The expression `execution::bulk_transform(S, F)` for some subexpressions S and F is expression-equivalent to:

 * S.bulk_transform(F), if that expression is valid.
 * Otherwise, `bulk_transform(S, F)`, if that expression is valid with overload resolution performed in a context that includes the declaration
 ```
         template<class S, class F>
           void bulk_transform(S, F) = delete;
 ```
   and that does not include a declaration of `execution::bulk_transform`.

 * Otherwise constructs a receiver, `r`  and passes that receiver to `execution::submit(S, r)`.

   * If `S::value_type` does not model the concept `Range<T>` for some `T` the expression ill-formed.
   * If `set_value` is called on `r` with some parameter `input` applies the equivalent of `out = std::ranges::transform_view(input, F)` and passes the result `output` to `execution::set_value(output_receiver, v)`.
   * If `set_error(r, e)` is called, passes `e` to `execution::set_error(output_receiver, e)`.
   * If `set_done(r)` is called, calls `execution::set_done(output_receiver)`.
-->
<h2 id="executionlet_value"><span class="header-section-number">3.8</span> execution::let_value<a href="#executionlet_value" class="self-link"></a></h2>
<h3 id="overview-6"><span class="header-section-number">3.8.1</span> Overview<a href="#overview-6" class="self-link"></a></h3>
<p><code>let_value</code> is a sender adapter that takes a <code>sender</code> and an invocable and returns a <code>sender</code> that keeps the completion result of the incoming sender alive for the duration of the algorithm returned by the invocable and makes that value available to the invocable.</p>
<p>Signature:</p>
<div class="sourceCode" id="cb26"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb26-1"><a href="#cb26-1"></a><span class="kw">template</span><span class="op">&lt;</span><span class="kw">typename</span> F<span class="op">&gt;</span></span>
<span id="cb26-2"><a href="#cb26-2"></a><span class="kw">struct</span> is<span class="op">-</span>invocable<span class="op">-</span>with <span class="op">{</span></span>
<span id="cb26-3"><a href="#cb26-3"></a>  <span class="kw">template</span><span class="op">&lt;</span><span class="kw">typename</span><span class="op">...</span> Args<span class="op">&gt;</span></span>
<span id="cb26-4"><a href="#cb26-4"></a>  <span class="kw">using</span> apply <span class="op">=</span> std<span class="op">::</span>bool_constant<span class="op">&lt;(</span>std<span class="op">::</span>invocable<span class="op">&lt;</span>F, Args<span class="op">...&gt;</span> <span class="op">&amp;&amp;</span> <span class="op">...)&gt;</span>;</span>
<span id="cb26-5"><a href="#cb26-5"></a><span class="op">}</span>;</span>
<span id="cb26-6"><a href="#cb26-6"></a></span>
<span id="cb26-7"><a href="#cb26-7"></a><span class="kw">template</span><span class="op">&lt;</span>execution<span class="op">::</span>sender S, moveable<span class="op">-</span>value F<span class="op">&gt;</span></span>
<span id="cb26-8"><a href="#cb26-8"></a>  <span class="kw">requires</span> sender_traits<span class="op">&lt;</span>S<span class="op">&gt;::</span><span class="kw">template</span> value_types<span class="op">&lt;</span></span>
<span id="cb26-9"><a href="#cb26-9"></a>    is<span class="op">-</span>invocable<span class="op">-</span>with<span class="op">&lt;</span>F<span class="op">&gt;::</span><span class="kw">template</span> apply<span class="op">&gt;::</span>value</span>
<span id="cb26-10"><a href="#cb26-10"></a>see<span class="op">-</span>below let_value<span class="op">(</span>S s, F f<span class="op">)</span>;</span></code></pre></div>
<p>where <code>S&lt;T...&gt;</code> and <code>S&lt;T2&gt;</code> are implementation-defined types that is represent senders that send a value of type list <code>T...</code> or <code>T2</code> respectively in their value channels. Note that in the general case there may be many types <code>T...</code> for a given <code>sender</code>, in which case the invocable may have to represent an overload set.</p>
<p><em>[ Example:</em></p>
<div class="sourceCode" id="cb27"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb27-1"><a href="#cb27-1"></a><span class="dt">int</span> r <span class="op">=</span> sync_wait<span class="op">(</span></span>
<span id="cb27-2"><a href="#cb27-2"></a>  just<span class="op">(</span><span class="dv">3</span><span class="op">)</span> <span class="op">|</span></span>
<span id="cb27-3"><a href="#cb27-3"></a>  let_value<span class="op">([](</span><span class="dt">int</span><span class="op">&amp;</span> let_v<span class="op">){</span></span>
<span id="cb27-4"><a href="#cb27-4"></a>    <span class="cf">return</span> just<span class="op">(</span><span class="dv">4</span><span class="op">)</span> <span class="op">|</span> transform<span class="op">([&amp;](</span><span class="dt">int</span> v<span class="op">){</span><span class="cf">return</span> let_v <span class="op">+</span> v;<span class="op">})))</span>;</span>
<span id="cb27-5"><a href="#cb27-5"></a><span class="co">// r==7</span></span></code></pre></div>
<h3 id="wording-6"><span class="header-section-number">3.8.2</span> Wording<a href="#wording-6" class="self-link"></a></h3>
<p>The name <code>execution::let_value</code> denotes a customization point object. For some subexpressions <code>s</code> and <code>f</code>, let <code>S</code> be a type such that <code>decltype((s))</code> is <code>S</code> and <code>decltype((f))</code> is <code>F</code>. The expression <code>execution::let_value(s, f)</code> is expression-equivalent to:</p>
<ul>
<li>s.let_value(f), if that expression is valid, if <code>s</code> satisfies <code>sender</code> and <code>f</code> satisfies <code>invocable</code>.</li>
<li>Otherwise, <code>let_value(s, f)</code>, if that expression is valid,, if <code>s</code> satisfies <code>sender</code> and <code>f</code> satisfies <code>invocable</code> with overload resolution performed in a context that includes the declaration</li>
</ul>
<div class="sourceCode" id="cb28"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb28-1"><a href="#cb28-1"></a>    void let_value() = delete;</span></code></pre></div>
<p>and that does not include a declaration of <code>execution::let_value</code>. * Otherwise, returns a <code>sender</code>, <code>s2</code>, that, when <code>connect(s, output_receiver)</code> is called on <code>s2</code>, for some <code>output_receiver</code>, returning an <code>operation_state</code> <code>os2</code> which will be stored as a subobject of the parent <code>operation_state</code>, constructs a <code>receiver</code> <code>r</code> and passes that receiver to <code>connect(s, r)</code>, returning <code>operation_state</code> object <code>os</code> and stores <code>os</code> as a subobject of <code>os2</code>:</p>
<ul>
<li>If <code>set_value(r, ts...)</code> is called:
<ul>
<li>copies <code>ts...</code> into <code>os2</code> as subobjects <code>t2s...</code>, calls <code>std::invoke(f, t2s...)</code> to return some <code>invoke_result</code></li>
<li>calls <code>execution::connect(invoke_result, output_receiver)</code> resulting in some <code>operation_state</code> <code>os3</code>, stores <code>os3</code> as a subobject of <code>os2</code> and calls <code>execution::start(os3)</code>.</li>
<li>the destructor of <code>os2</code> must be sequenced after the completion of the operation represented by <code>invoke_result</code>.</li>
<li>If <code>f</code> or <code>connect()</code> throws, catches the exception and passes it to <code>set_error(output_receiver, e)</code>.</li>
</ul></li>
<li>If <code>set_error(r, e)</code> is called, passes <code>e</code> to <code>set_error(output_receiver, e)</code>.</li>
<li>If <code>set_done(r)</code> is called, calls <code>set_done(output_receiver)</code>.</li>
</ul>
<p>When <code>start</code> is called on <code>os2</code>, call <code>execution::start(os)</code>.</p>
<ul>
<li>Otherwise the expression <code>execution::let_value(s, f)</code> is ill-formed.</li>
</ul>
<h2 id="executionlet_error"><span class="header-section-number">3.9</span> execution::let_error<a href="#executionlet_error" class="self-link"></a></h2>
<h3 id="overview-7"><span class="header-section-number">3.9.1</span> Overview<a href="#overview-7" class="self-link"></a></h3>
<p><code>let_error</code> is a sender adapter that takes a <code>sender</code> and an invocable and returns a <code>sender</code> that, on error propagation, keeps the error result of the incoming sender alive for the duration of the <code>sender</code> returned by the invocable and makes that value available to the invocable.</p>
<p>Signature:</p>
<div class="sourceCode" id="cb29"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb29-1"><a href="#cb29-1"></a><span class="kw">template</span><span class="op">&lt;</span><span class="kw">typename</span> F<span class="op">&gt;</span></span>
<span id="cb29-2"><a href="#cb29-2"></a><span class="kw">struct</span> is<span class="op">-</span>invocable<span class="op">-</span>with <span class="op">{</span></span>
<span id="cb29-3"><a href="#cb29-3"></a>  <span class="kw">template</span><span class="op">&lt;</span><span class="kw">typename</span><span class="op">...</span> Args<span class="op">&gt;</span></span>
<span id="cb29-4"><a href="#cb29-4"></a>  <span class="kw">using</span> apply <span class="op">=</span> std<span class="op">::</span>bool_constant<span class="op">&lt;(</span>std<span class="op">::</span>invocable<span class="op">&lt;</span>F, Args<span class="op">...&gt;</span> <span class="op">&amp;&amp;</span> <span class="op">...)&gt;</span>;</span>
<span id="cb29-5"><a href="#cb29-5"></a><span class="op">}</span>;</span>
<span id="cb29-6"><a href="#cb29-6"></a></span>
<span id="cb29-7"><a href="#cb29-7"></a><span class="kw">template</span><span class="op">&lt;</span>execution<span class="op">::</span>sender S, moveable<span class="op">-</span>value F<span class="op">&gt;</span></span>
<span id="cb29-8"><a href="#cb29-8"></a>  <span class="kw">requires</span> sender_traits<span class="op">&lt;</span>S<span class="op">&gt;::</span><span class="kw">template</span> error_types<span class="op">&lt;</span></span>
<span id="cb29-9"><a href="#cb29-9"></a>    is<span class="op">-</span>invocable<span class="op">-</span>with<span class="op">&lt;</span>F<span class="op">&gt;::</span><span class="kw">template</span> apply<span class="op">&gt;::</span>value</span>
<span id="cb29-10"><a href="#cb29-10"></a>see<span class="op">-</span>below let_error<span class="op">(</span>S s, F f<span class="op">)</span>;</span></code></pre></div>
<p><em>[ Example:</em></p>
<div class="sourceCode" id="cb30"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb30-1"><a href="#cb30-1"></a><span class="dt">float</span> r <span class="op">=</span> sync_wait<span class="op">(</span></span>
<span id="cb30-2"><a href="#cb30-2"></a>  just<span class="op">(</span><span class="dv">3</span><span class="op">)</span> <span class="op">|</span></span>
<span id="cb30-3"><a href="#cb30-3"></a>  transform<span class="op">([](</span><span class="dt">int</span> v<span class="op">){</span><span class="cf">throw</span> <span class="fl">2.0</span><span class="bu">f</span>;<span class="op">})</span> <span class="op">|</span></span>
<span id="cb30-4"><a href="#cb30-4"></a>  let_error<span class="op">([](</span><span class="dt">float</span> e<span class="op">){</span><span class="cf">return</span> just<span class="op">(</span>e<span class="op">+</span><span class="dv">1</span><span class="op">)</span>;<span class="op">}))</span>;</span>
<span id="cb30-5"><a href="#cb30-5"></a><span class="co">// r==3.0f</span></span></code></pre></div>
<h3 id="wording-7"><span class="header-section-number">3.9.2</span> Wording<a href="#wording-7" class="self-link"></a></h3>
<p>The name <code>execution::let_error</code> denotes a customization point object. For some subexpressions <code>s</code> and <code>f</code>, let <code>S</code> be a type such that <code>decltype((s))</code> is <code>S</code> and <code>decltype((f))</code> is <code>F</code>. The expression <code>execution::let_error(s, f)</code> is expression-equivalent to:</p>
<ul>
<li>s.let_error(f), if that expression is valid, if <code>s</code> satisfies <code>sender</code>.</li>
<li>Otherwise, <code>let_error(s, f)</code>, if that expression is valid,, if <code>s</code> satisfies <code>sender</code> with overload resolution performed in a context that includes the declaration</li>
</ul>
<div class="sourceCode" id="cb31"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb31-1"><a href="#cb31-1"></a>    void let_error() = delete;</span></code></pre></div>
<p>and that does not include a declaration of <code>execution::let_error</code>.</p>
<ul>
<li><p>Otherwise, returns a <code>sender</code>, <code>s2</code>, that, when <code>connect(s, output_receiver)</code> is called on <code>s2</code>, for some <code>output_receiver</code>, returning an <code>operation_state</code> <code>os2</code>, constructs a <code>receiver</code> <code>r</code> and passes that receiver to <code>connect(s, r)</code>, returning <code>operation_state</code> object <code>os</code> and stores <code>os</code> as a subobject of <code>os2</code>:</p>
<ul>
<li>If <code>set_value(r, ts...)</code> is called, passes <code>ts...</code> to <code>set_valus(output_receiver, ts...)</code>.</li>
<li>If <code>set_error(r, e)</code> is called:
<ul>
<li>copies <code>e</code> into <code>os2</code> as <code>e2</code>, calls <code>std::invoke(f, e2)</code> to return some <code>invoke_result</code></li>
<li>calls <code>execution::connect(invoke_result, output_receiver)</code> resulting in some <code>operation_state</code> <code>os3</code>, stores <code>os3</code> as a subobject of <code>os2</code> and calls <code>execution::start(os3)</code>.</li>
<li>the destructor of <code>os2</code> must be sequenced after the completion of the operation represented by <code>invoke_result</code>.</li>
<li>If <code>f</code> or <code>connect()</code> throws, catches the exception as <code>e3</code> and passes it to <code>set_error(output_receiver, 3)</code>.</li>
</ul></li>
<li>If <code>set_done(r)</code> is called, calls <code>set_done(output_receiver)</code>.</li>
</ul>
<p>When <code>start</code> is called on <code>os2</code>, call <code>execution::start(os)</code>.</p></li>
<li><p>Otherwise the expression <code>execution::let_error(s, f)</code> is ill-formed.</p></li>
</ul>
<h2 id="executionensure_started"><span class="header-section-number">3.10</span> execution::ensure_started<a href="#executionensure_started" class="self-link"></a></h2>
<h3 id="overview-8"><span class="header-section-number">3.10.1</span> Overview<a href="#overview-8" class="self-link"></a></h3>
<p><code>ensure_started</code> is a sender adapter that takes a <code>sender</code>, eagerly submits it and returns a <code>sender</code> that propagates the value by reference and can be used as an l-value.</p>
<p>Signature:</p>
<div class="sourceCode" id="cb32"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb32-1"><a href="#cb32-1"></a><span class="kw">template</span> <span class="op">&lt;</span>execution<span class="op">::</span>sender S<span class="op">&gt;</span></span>
<span id="cb32-2"><a href="#cb32-2"></a>see<span class="op">-</span>below ensure_started<span class="op">(</span>S s<span class="op">)</span>;</span></code></pre></div>
<p><em>[ Example:</em></p>
<div class="sourceCode" id="cb33"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb33-1"><a href="#cb33-1"></a><span class="kw">auto</span> s1 <span class="op">=</span> just<span class="op">(</span><span class="dv">3</span><span class="op">)</span> <span class="op">|</span> ensure_started<span class="op">()</span>;</span>
<span id="cb33-2"><a href="#cb33-2"></a><span class="kw">auto</span> s2 <span class="op">=</span> s1 <span class="op">|</span> transform<span class="op">([](</span><span class="kw">const</span> <span class="dt">int</span><span class="op">&amp;</span> a<span class="op">){</span><span class="cf">return</span> a<span class="op">+</span><span class="dv">1</span>;<span class="op">}))</span></span>
<span id="cb33-3"><a href="#cb33-3"></a><span class="dt">int</span> r <span class="op">=</span> sync_wait<span class="op">(</span></span>
<span id="cb33-4"><a href="#cb33-4"></a>  transform<span class="op">(</span></span>
<span id="cb33-5"><a href="#cb33-5"></a>    s2,</span>
<span id="cb33-6"><a href="#cb33-6"></a>    <span class="op">[](</span><span class="dt">int</span> b<span class="op">){</span><span class="cf">return</span> b<span class="op">*</span><span class="dv">2</span>;<span class="op">}))</span>;</span>
<span id="cb33-7"><a href="#cb33-7"></a><span class="co">// r==8</span></span></code></pre></div>
<h3 id="wording-8"><span class="header-section-number">3.10.2</span> Wording<a href="#wording-8" class="self-link"></a></h3>
<p>The name <code>execution::ensure_started</code> denotes a customization point object. For some subexpressions <code>s</code>, let <code>S</code> be a type such that <code>decltype((s))</code> is <code>S</code>. The expression <code>execution::ensure_started(s, f)</code> is expression-equivalent to:</p>
<ul>
<li><code>s.ensure_started()</code> if that expression is valid and if <code>s</code> satisfies <code>sender</code>.</li>
<li>Otherwise, <code>ensure_started(s)</code>, if that expression is valid, if <code>s</code> satisfies <code>sender</code> with overload resolution performed in a context that includes the declaration</li>
</ul>
<div class="sourceCode" id="cb34"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb34-1"><a href="#cb34-1"></a>   void ensure_started() = delete;</span></code></pre></div>
<p>and that does not include a declaration of <code>execution::ensure_started</code>.</p>
<ul>
<li><p>Otherwise, returns a <code>sender</code>, <code>s2</code>, that, constructs a shared state <code>shr</code>, constructs a receiver, <code>r</code> and passes that receiver to <code>execution::connect(s, r)</code> resulting in an <code>operation_state</code> <code>os</code> that is stored as a subobject of <code>shr</code>.</p>
<ul>
<li>If <code>set_value(r, ts)</code> is called stores <code>ts</code> as subobjects of <code>os</code>.</li>
<li>If <code>set_error(r, e)</code> is called, stores <code>e</code> as a subobject of <code>os</code>.</li>
<li>If <code>set_done(r)</code> is called stores the done result as a subobject of <code>os</code>.</li>
</ul>
<p>When some <code>output_receiver</code> has been passed to <code>connect</code> on <code>s2</code>, resulting in an <code>operation_state</code> <code>os2</code> and one of the above has been called on <code>r</code>:</p>
<ul>
<li>If <code>r</code> was satisfied with a call to <code>set_value</code>, call <code>set_value(output_receiver, ts...)</code></li>
<li>If <code>r</code> was satisfied with a call to <code>set_error</code>, call <code>set_error(output_receiver, e)</code>.</li>
<li>If <code>r</code> was satisfied with a call to <code>set_done</code>, call <code>execution::set_done(output_receiver)</code>.</li>
</ul></li>
<li><p>When <code>start</code> is called on <code>os2</code>, call <code>execution::start(os)</code>.</p></li>
<li><p>If <code>s2</code> is destroyed before <code>start</code> is called on <code>os2</code>, calls <code>std::terminate()</code>.</p></li>
</ul>
<p><strong>Note:</strong> See <a href="#planned-developments">Planned Developments</a>.</p>
<h1 id="customization-and-example"><span class="header-section-number">4</span> Customization and example<a href="#customization-and-example" class="self-link"></a></h1>
<p>Each of these algorithms, apart from <code>just</code>, is customizable on one or more <code>sender</code> implementations. This allows full optimization. For example, in the following simple work chain:</p>
<div class="sourceCode" id="cb35"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb35-1"><a href="#cb35-1"></a>auto s = just(3) |                                        // s1</span>
<span id="cb35-2"><a href="#cb35-2"></a>         on(scheduler1) |                                 // s2</span>
<span id="cb35-3"><a href="#cb35-3"></a>         transform([](int a){return a+1;}) |              // s3</span>
<span id="cb35-4"><a href="#cb35-4"></a>         transform([](int a){return a*2;}) |              // s4</span>
<span id="cb35-5"><a href="#cb35-5"></a>         on(scheduler2) |                                 // s5</span>
<span id="cb35-6"><a href="#cb35-6"></a>         let_error([](auto e){return just(3);});          // s6</span>
<span id="cb35-7"><a href="#cb35-7"></a>int r = sync_wait(s);</span></code></pre></div>
<p>The result of <code>s1</code> might be a <code>just_sender&lt;int&gt;</code> implemented by the standard library vendor.</p>
<p><code>on(just_sender&lt;int&gt;, scheduler1)</code> has no customization defined, and this expression returns an <code>scheduler1_on_sender&lt;int&gt;</code> that is a custom type from the author of <code>scheduler1</code>, it will call <code>submit</code> on the result of <code>s1</code>.</p>
<p><code>s3</code> calls <code>transform(scheduler1_on_sender&lt;int&gt;, [](int a){return a+1;})</code> for which the author of <code>scheduler1</code> may have written a customization. The <code>scheduler1_on_sender</code> has stashed the value somewhere and build some work queue in the background. We do not see <code>submit</code> called at this point, it uses a behind-the-scenes implementation to schedule the work on the work queue. An <code>scheduler1_transform_sender&lt;int&gt;</code> is returned.</p>
<p><code>s4</code> implements a very similar customization, and again does not call <code>submit</code>. There need be no synchronization in this chain.</p>
<p>At <code>s5</code>, however, the implementor of <code>scheduler2</code> does not know about the implementation of <code>scheduler1</code>. At this point it will call <code>submit</code> on the incoming <code>scheduler1_transform_sender</code>, forcing <code>scheduler1</code>’s sender to implement the necessary synchronization to map back from the behind-the-scenes optimal queue to something interoperable with another vendor’s implementation.</p>
<p><code>let_error</code> at <code>s6</code> will be generic in terms of <code>submit</code> and not do anything special, this uses the default implementation in terms of <code>submit</code>. <code>sync_wait</code> similarly constructs a <code>condition_variable</code> and a temporary <code>int</code>, submits a <code>receiver</code> to <code>s</code> and waits on the <code>condition_variable</code>, blocking the calling thread.</p>
<p><code>r</code> is of course the value 8 at this point assuming that neither scheduler triggered an error. If there were to be a scheduling error, then that error would propagate to <code>let_error</code> and <code>r</code> would subsequently have the value <code>3</code>.</p>
<h1 id="planned-developments"><span class="header-section-number">5</span> Planned developments<a href="#planned-developments" class="self-link"></a></h1>
<p>Future changes and discussion points based on R3 of this paper.</p>
<h2 id="when_all-and-ensure_starteds-contexts"><span class="header-section-number">5.1</span> when_all and ensure_started’s contexts<a href="#when_all-and-ensure_starteds-contexts" class="self-link"></a></h2>
<p>Based on experience in Facebook’s codebase, we believe that <code>when_all</code> and <code>ensure_started</code> should return senders that require a <code>scheduler_provider</code>s and use forward progress delegation as discussed in <span class="citation" data-cites="P1898R1">[<a href="#ref-P1898R1" role="doc-biblioref">P1898R1</a>]</span>.</p>
<p>In the case of <code>when_all</code>, the context the returned <code>sender</code> completes on will depend on which incoming <code>sender</code> completes last. It is thus non-deterministic across that set.</p>
<p><code>ensure_started</code> is similarly adding non-determinism by removing laziness. If the <code>sender</code> returned by <code>ensure_started</code> is complete by the time a <code>receiver</code> is connected to it, the <code>start</code> call would complete inline with the caller.</p>
<p>In both cases, requiring a <code>scheduler_provider</code>, as discussed in <span class="citation" data-cites="P1898R1">[<a href="#ref-P1898R1" role="doc-biblioref">P1898R1</a>]</span> would offer determinism by guaranteeing a transition onto some downstream scheduler and adding wording to require submission onto that provided <code>scheduler</code> if it does not match the completing context.</p>
<h2 id="when_all-for-void-types-and-mixed-success"><span class="header-section-number">5.2</span> when_all for void types and mixed success<a href="#when_all-for-void-types-and-mixed-success" class="self-link"></a></h2>
<p>We should add a when_all variant that returns tuples and variants in its result, or some similar mechanism for to allow parameter packs, including empty packs in the form of void-senders, and mixed success/error to propagate.</p>
<h2 id="when_all-and-ensure_started-both-require-cancellation-and-async-cleanup-to-be-fully-flexible"><span class="header-section-number">5.3</span> when_all and ensure_started both require cancellation and async cleanup to be fully flexible<a href="#when_all-and-ensure_started-both-require-cancellation-and-async-cleanup-to-be-fully-flexible" class="self-link"></a></h2>
<p>Under error circumstances, <code>when_all</code> should cancel the other incoming work. This will be described separately.</p>
<p><code>ensure_started</code> similarly needs to be updated to describe how it behaves in the presence of one downstream task being cancelled, and precisely when and where the shared state is destroyed. This would be a preferable solution to termination, as described above, particularly in cases where <code>ensure_started</code> is used as part of a set of operations where something else might throw and cause the <code>sender</code> to be destroyed.</p>
<h1 id="proposed-question-for-the-prague-2020-meeting"><span class="header-section-number">6</span> Proposed question for the Prague 2020 meeting<a href="#proposed-question-for-the-prague-2020-meeting" class="self-link"></a></h1>
<h2 id="replace-bulk_execute-in-p0443-with-indexed_for-as-described-above."><span class="header-section-number">6.1</span> Replace <code>bulk_execute</code> in P0443 with <code>indexed_for</code> as described above.<a href="#replace-bulk_execute-in-p0443-with-indexed_for-as-described-above." class="self-link"></a></h2>
<p><code>indexed_for</code> as described above should replace bulk_execute during the merge of <span class="citation" data-cites="P0443R11">[<a href="#ref-P0443R11" role="doc-biblioref">P0443R11</a>]</span> into C++23. Suggest fine-tuning this wording and forwarding to LEWG.</p>
<p>The changes this leads to:</p>
<ul>
<li>Renames the algorithm to fit in with a set of user-level algorithms rather than making it distinct and internal-only. We found it hard to define a difference between <code>bulk_execute</code> and <code>indexed_for</code> and so suggest we not try, instead we rename it.</li>
<li>Propagating the data from the incoming sender into the invocable by reference and out the other end. This allows the algorithm to be a side-effecting view on data, but because that data is in-band in the data stream it is safe from a lifetime point of view. More so that it would be if the data had to be captured by reference.</li>
<li>Replaces the max value with a range for the index space. This allows for more flexibility.</li>
<li>Adds the execution policy back in, defining the forward progress guarantee both the invocable and range accessor make. This is preferred because the policy is a statement the programmer makes about the capabilities of the invocable. An indexed_for that requires seq, and an executor that cannot execute seq can fail at this point. An invocable that requires seq run on an executor that cannot run seq algorithms would be invisible at the point of chaining the algorithm.</li>
<li>Does not add any sort of factory as <span class="citation" data-cites="P1993R0">[<a href="#ref-P1993R0" role="doc-biblioref">P1993R0</a>]</span>. These are not necessary if we carry the data in the stream. Data can be allocated to a device using, for example, a <code>device_vector</code>. This maintains full flexibility - we can add custom data management algorithms independently and keep <code>indexed_for</code> focused on its primary use cause: the asynchronous for loop itself.</li>
<li>Relaxes the CopyConstructible restriction that <span class="citation" data-cites="P0443R11">[<a href="#ref-P0443R11" role="doc-biblioref">P0443R11</a>]</span>, but also the standard algorithms in C++20 place. Wide discussion suggests that this restriction may not be necessary, and it could certainly be harmful. In an asynchronous world we cannot rely on scoped <code>reference_wrapper</code> semantics, and the cost of injecting <code>shared_ptr</code> would be high. If an implementation needs to copy, then that implementation should implement a wrapper that is custom for the algorthmic structure it is using. For example, a forking tree of threads may allocate once on the first thread by move and reference back to it, knowing the lifetime is safe.</li>
</ul>
<h2 id="result-of-discussion-and-prague-sg1-vote-on-p1897r2"><span class="header-section-number">6.2</span> Result of discussion and Prague SG1 vote on P1897R2<a href="#result-of-discussion-and-prague-sg1-vote-on-p1897r2" class="self-link"></a></h2>
<p>Poll: We should add a sender argument and sender result to bulk execution functions (providing an opportunity to build shared state, established dependencies in/out)</p>
<p>SA: 17; F: 7; N: 0; A: 0; SA: 0</p>
<p>Consensus.</p>
<p>Poll: We should replace bulk_execute with indexed_for</p>
<p>SA: 4; F: 11; N: 3; A: 7; SA: 1</p>
<p>No consensus for change. Discussed in the room that indexed_for (and other algorithms by inference) should be build on top of bulk_execute.</p>
<p>The bulk_execute primitive should take an execution policy to constrain the invocable.</p>
<p>SA: 5; F: 7; N: 8; A: 3; SA: 1</p>
<p>R3 of this paper removes <code>indexed_for</code>. If <code>bulk_execute</code> is to remain, there is less urgent need to add <code>indexed_for</code>. Instead R3 focuses on the core set of algorithms. Something like <code>indexed_for</code>, or <code>for_each</code> will be in the async update of the parallel algorithms.</p>
<h1 id="references"><span class="header-section-number">7</span> References<a href="#references" class="self-link"></a></h1>

<div id="refs" role="doc-bibliography">
<div id="ref-P0443R11">
<p>[P0443R11] 2019. A Unified Executors Proposal for C++. <br />
<a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p0443r11.html">http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p0443r11.html</a></p>
</div>
<div id="ref-P0443R13">
<p>[P0443R13] 2020. A Unified Executors Proposal for C++. <br />
<a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2020/p0443r13.html">http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2020/p0443r13.html</a></p>
</div>
<div id="ref-P1660R0">
<p>[P1660R0] 2019. A Compromise Executor Design Sketch. <br />
<a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p1660r0.pdf">http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p1660r0.pdf</a></p>
</div>
<div id="ref-P1898R1">
<p>[P1898R1] 2020. Forward progress delegation for executors. <br />
<a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2020/p1898r1.html">http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2020/p1898r1.html</a></p>
</div>
<div id="ref-P1993R0">
<p>[P1993R0] 2019. Restore factories to bulk_execute. <br />
<a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p1993r0.pdf">http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p1993r0.pdf</a></p>
</div>
</div>
</div>
</div>
</body>
</html>
