<!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="2019-10-06" />
  <title>Forward progress delegation for executors</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">Forward progress delegation for executors</h1>

<table style="border:none;float:right">
  <tr>
    <td>Document #: </td>
    <td>P1898R0</td>
  </tr>
  <tr>
    <td>Date: </td>
    <td>2019-10-06</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="introduction"><span class="header-section-number">1</span> Introduction<a href="#introduction" class="self-link"></a></h1>
<p>This is an early proposal to sound out one approach to forward progress delegation for executors, based in part on a discussion of the <code>DeferredExecutor</code> that works with <a href="https://github.com/facebook/folly/">folly’s</a> <a href="https://github.com/facebook/folly/blob/master/folly/futures/Future.h">SemiFuture</a>.</p>
<p>In this paper <code>executor</code> refers to the general concept as expressed in the title of <span class="citation" data-cites="P0443R11">[<a href="#ref-P0443R11" role="doc-biblioref">P0443R11</a>]</span> and the long standing executors effort. The concrete concepts used may be <code>executor</code>, <code>scheduler</code> or <code>sender</code> depending on the context.</p>
<p>Building on the definitions in <span class="citation" data-cites="P0443R11">[<a href="#ref-P0443R11" role="doc-biblioref">P0443R11</a>]</span> and <span class="citation" data-cites="P1660R0">[<a href="#ref-P1660R0" role="doc-biblioref">P1660R0</a>]</span>, we start with the definition of a <code>receiver</code> we propose adding a concept <code>scheduler_provider</code> that allows a <code>sender</code> to require that a <code>receiver</code> passed to it is able to provide an executor, or more accurately a <code>scheduler</code> in the language of <span class="citation" data-cites="P0443R11">[<a href="#ref-P0443R11" role="doc-biblioref">P0443R11</a>]</span>. This concept offers a <code>get_scheduler</code> customization point that allows a <code>scheduler</code> to be propagated from downstream to upstream asynchronous tasks. A given <code>scheduler</code>, and therefore any <code>sender</code> created off that scheduler may require the availability of such a customization on <code>recivers</code>s passed to the <code>submit</code> operation.</p>
<p>A given <code>sender</code> may delegate any tasks it is unable to perform to the scheduler it acquires from the downstream <code>Callback</code>.</p>
<p>A <code>sync_wait</code> operation, as defined in <span class="citation" data-cites="P1897R0">[<a href="#ref-P1897R0" role="doc-biblioref">P1897</a>]</span>, encapsulates a <code>scheduler</code> that wraps a context owned by and executed on the calling thread. This will make progress and as such can act as the executor-of-last-resort when the C++20 guarantee is required in blocking code.</p>
<p>We also propose that a set of definitions for forward progress of asynchronous graph nodes be provided.</p>
<h1 id="motivation-and-scope"><span class="header-section-number">2</span> Motivation and Scope<a href="#motivation-and-scope" class="self-link"></a></h1>
<h2 id="forward-progress-delegation-in-c20."><span class="header-section-number">2.1</span> Forward progress delegation in C++20.<a href="#forward-progress-delegation-in-c20." class="self-link"></a></h2>
<p>The standard parallel algorithms support weak forward progress guarantees. For example, with the code:</p>
<div class="sourceCode" id="cb1"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb1-1"><a href="#cb1-1"></a>vector<span class="op">&lt;</span><span class="dt">int</span><span class="op">&gt;</span> vec<span class="op">{</span><span class="dv">1</span>, <span class="dv">2</span>, <span class="dv">3</span><span class="op">}</span>;</span>
<span id="cb1-2"><a href="#cb1-2"></a>vector<span class="op">&lt;</span><span class="dt">int</span><span class="op">&gt;</span> out<span class="op">(</span>vec<span class="op">.</span>size<span class="op">()</span>, <span class="dv">0</span><span class="op">)</span>;</span>
<span id="cb1-3"><a href="#cb1-3"></a>transform<span class="op">(</span></span>
<span id="cb1-4"><a href="#cb1-4"></a>  std<span class="op">::</span>execution<span class="op">::</span>par,</span>
<span id="cb1-5"><a href="#cb1-5"></a>  begin<span class="op">(</span>vec<span class="op">)</span>,</span>
<span id="cb1-6"><a href="#cb1-6"></a>  end<span class="op">(</span>vec<span class="op">)</span>,</span>
<span id="cb1-7"><a href="#cb1-7"></a>  begin<span class="op">(</span>out<span class="op">)</span>,</span>
<span id="cb1-8"><a href="#cb1-8"></a>  <span class="op">[](</span><span class="dt">int</span> i<span class="op">){</span><span class="cf">return</span> i<span class="op">+</span><span class="dv">1</span>;<span class="op">}</span> <span class="co">// s0</span></span>
<span id="cb1-9"><a href="#cb1-9"></a>  <span class="op">)</span>; <span class="co">// s1</span></span></code></pre></div>
<p>Allows the runtime to assume that the statement <code>s0</code> is safe to execute concurrently for some set of agents, and makes the guarantee that once started each instance of <code>s0</code> will run to completion, independent of whatever else is happening in the system.</p>
<p>However, the same is true of statement <code>s1</code>. Once started, <code>s1</code> must also run to completion. As a result, while the runtime makes no guarantee about instances of <code>s0</code> the collection of such instances must complete - and therefore every instance must run eventually.</p>
<p>In practice, on shared resources, other work in the system might occupy every available thread.<br />
To guarantee that all <code>s0</code>s are able to run eventually, we might need to create three threads to achieve this - and in the extreme this might not be possible.</p>
<p>We therefore fall back to a design where the calling thread may run the remaining work. We know the caller is making progress. If no thread ever becomes available to run the work the caller might iterate through all instances of <code>s0</code> serially.</p>
<h2 id="forward-progress-delegation-in-an-asynchronous-world"><span class="header-section-number">2.2</span> Forward progress delegation in an asynchronous world<a href="#forward-progress-delegation-in-an-asynchronous-world" class="self-link"></a></h2>
<p>Unfortunately, if <code>transform</code> is made asynchronous, then we have a challenge. Take a result-by-return formulation of async transform as an example.</p>
<p>In either an imagined coroutine formulation:</p>
<div class="sourceCode" id="cb2"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb2-1"><a href="#cb2-1"></a>awaitable<span class="op">&lt;</span>vector<span class="op">&lt;</span><span class="dt">int</span><span class="op">&gt;&gt;</span> doWork<span class="op">(</span>vector<span class="op">&lt;</span><span class="dt">int</span><span class="op">&gt;</span> vec<span class="op">)</span> <span class="op">{</span></span>
<span id="cb2-2"><a href="#cb2-2"></a>  <span class="kw">co_return</span> <span class="kw">co_await</span> bulk_coroutine_transform<span class="op">(</span></span>
<span id="cb2-3"><a href="#cb2-3"></a>    vec, <span class="op">[](</span><span class="dt">int</span> i<span class="op">)</span> <span class="op">{</span><span class="cf">return</span> i<span class="op">+</span><span class="dv">1</span>;<span class="op">})</span>;</span>
<span id="cb2-4"><a href="#cb2-4"></a><span class="op">}</span></span></code></pre></div>
<p>Or a declarative formulation as in <span class="citation" data-cites="P1897R0">[<a href="#ref-P1897R0" role="doc-biblioref">P1897</a>]</span>:</p>
<div class="sourceCode" id="cb3"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb3-1"><a href="#cb3-1"></a>sender_to<span class="op">&lt;</span>vector<span class="op">&lt;</span><span class="dt">int</span><span class="op">&gt;&gt;</span> doWork<span class="op">(</span>sender_to<span class="op">&lt;</span>vector<span class="op">&lt;</span><span class="dt">int</span><span class="op">&gt;&gt;</span> vec<span class="op">)</span> <span class="op">{</span></span>
<span id="cb3-2"><a href="#cb3-2"></a>  <span class="cf">return</span> std<span class="op">::</span>move<span class="op">(</span>vec<span class="op">)</span> <span class="op">|</span> bulk_transform<span class="op">([](</span><span class="dt">int</span> i<span class="op">)</span> <span class="op">{</span><span class="cf">return</span> i<span class="op">+</span><span class="dv">1</span>;<span class="op">})</span>;</span>
<span id="cb3-3"><a href="#cb3-3"></a><span class="op">}</span></span></code></pre></div>
<p>There is no guaranteed context to delegate work too. We cannot block the caller in either case - the body itself is asynchronous code. We need a more general approach.</p>
<h1 id="examples-of-use"><span class="header-section-number">3</span> Examples of use<a href="#examples-of-use" class="self-link"></a></h1>
<h2 id="delegation-in-the-folly-futures-library"><span class="header-section-number">3.1</span> Delegation in the folly futures library<a href="#delegation-in-the-folly-futures-library" class="self-link"></a></h2>
<p>In Facebook’s folly library we have a limited, but extreme, form of forward progress delegation in the <code>DeferredExecutor</code>. As folly has evolved from an eager Futures library and added laziness, and because folly’s executors are passed by reference, this is special-cased in folly and is a hidden implementation detail of the <code>SemiFuture</code> class. The principle would apply the same if it were made public, with appropriate lifetime improvements. The <code>DeferredExecutor</code> functions as a mechanism to allow APIs to force the caller to specify work can run, but to also allow them to push some work on to the caller. For example, an API that retrieves network data and then deserializes it need not provide any execution context of its own, it can defer all deserialization work into a context of the caller’s choice.</p>
<p><code>DeferredExecutor</code> is a single-slot holder of a callback. It will never run tasks itself, and represents no execution context of its own. The <code>DeferredExecutor</code> instance will acquire an execution context when it is passed a downstream executor.</p>
<p>So, for example take this simple chain of work:</p>
<div class="sourceCode" id="cb4"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb4-1"><a href="#cb4-1"></a>SemiFuture<span class="op">&lt;</span><span class="dt">int</span><span class="op">&gt;</span> f1 <span class="op">=</span> doSomethingInterestingEagerly<span class="op">()</span>;</span>
<span id="cb4-2"><a href="#cb4-2"></a>SemiFuture<span class="op">&lt;</span><span class="dt">int</span><span class="op">&gt;</span> f2 <span class="op">=</span> std<span class="op">::</span>move<span class="op">(</span>f1<span class="op">)</span></span>
<span id="cb4-3"><a href="#cb4-3"></a>  <span class="op">.</span>deferValue<span class="op">([](</span><span class="dt">int</span> i<span class="op">){</span><span class="cf">return</span> i<span class="op">+</span><span class="dv">1</span>;<span class="op">})</span>; <span class="co">// s3</span></span>
<span id="cb4-4"><a href="#cb4-4"></a>Future<span class="op">&lt;</span><span class="dt">int</span><span class="op">&gt;</span> f3 <span class="op">=</span> f2<span class="op">.</span>via<span class="op">(&amp;</span>aRealExecutor<span class="op">)</span>;</span></code></pre></div>
<p>Even if <code>f1</code> is complete at the point that <code>s3</code> is enqueued onto the SemiFuture, <code>f2</code> will never become ready. The <code>deferValue</code> call enqueues work and ensures that there is a <code>DeferredExecutor</code> on the future, replacing the executor was that completed the work resulting in <code>f1</code>.</p>
<p>When <code>f1</code> completes it will pass callback <code>s3</code> to the executor’s <code>execute</code> method, and that callback will be held there, with no context to run it on.</p>
<p>With the creation of <code>f3</code>, an executor is attached and is passed to the <code>DeferredExecutor</code>’s <code>set_executor</code> method. At that point, if <code>f1</code> was complete, <code>s3</code> would be passed through to <code>aRealExecutor</code>. All forward progress here has been delegated from the <code>DeferredExecutor</code> to <code>aRealExecutor</code>.</p>
<p>Note that a blocking operation:</p>
<div class="sourceCode" id="cb5"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb5-1"><a href="#cb5-1"></a>SemiFuture<span class="op">&lt;</span><span class="dt">int</span><span class="op">&gt;</span> f1 <span class="op">=</span> doSomethingInterestingEagerly<span class="op">()</span>;</span>
<span id="cb5-2"><a href="#cb5-2"></a>SemiFuture<span class="op">&lt;</span><span class="dt">int</span><span class="op">&gt;</span> f2 <span class="op">=</span> std<span class="op">::</span>move<span class="op">(</span>f1<span class="op">)</span></span>
<span id="cb5-3"><a href="#cb5-3"></a>  <span class="op">.</span>deferValue<span class="op">([](</span><span class="dt">int</span> i<span class="op">){</span><span class="cf">return</span> i<span class="op">+</span><span class="dv">1</span>;<span class="op">})</span>; <span class="co">// s3</span></span>
<span id="cb5-4"><a href="#cb5-4"></a><span class="dt">int</span> result <span class="op">=</span> std<span class="op">::</span>move<span class="op">(</span>f2<span class="op">).</span>get<span class="op">()</span>;</span></code></pre></div>
<p>implicitly carries an executor driven by the caller, which is propagated similarly</p>
<p>A coroutine:</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="kw">co_await</span> std<span class="op">::</span>move<span class="op">(</span>f2<span class="op">)</span>;</span></code></pre></div>
<p>similarly propagates an executor that the coroutine carries with it in the implementation of folly’s <a href="https://github.com/facebook/folly/blob/master/folly/experimental/coro/Task.h">Task</a>. Unlike in the synchronous example, the coroutine is not blocking the caller, it is simply transferring work from an executor that is unable to perform it to the one the caller is aware of.</p>
<h2 id="submitting-work-to-an-accelerator"><span class="header-section-number">3.2</span> Submitting work to an accelerator<a href="#submitting-work-to-an-accelerator" class="self-link"></a></h2>
<p>We may have a chain of tasks that we want to run on an accelerator, but that are dependent on some input trigger. For example, in OpenCL we might submit a sequence of tasks to a queue, such that the first one is dependent on an event triggered by the host.</p>
<div class="sourceCode" id="cb7"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb7-1"><a href="#cb7-1"></a>host_event <span class="op">|</span> gpu_task <span class="op">|</span> gpu_task <span class="op">|</span> gpu_task</span></code></pre></div>
<p>We do not know when the host event completes. It may be ready immediately. When do we submit the gpu work? There are heuristics for doing this:</p>
<ul>
<li>The queue might be being consumed constantly by a hardware thread.</li>
<li>There may be a background driver thread pulling from the queue.</li>
<li>Some submit operations may cause work to happen.</li>
</ul>
<p>The last one is unstable if we do not know for sure that more work will arrive. Some hardware queuing models, such as HSA, require an explicit action to mark a set of tasks as valid. If we are building that queue from a weakly parallel agent, or if we want to delay passing the queue to the device until <code>host_event</code> is ready but we do not know whether the task completing <code>host_event</code> has a strong enough forward progress guarantee to safely call into a driver to trigger that queue copy we can instead structure this so that we require an executor to be specified. In that case the chain of tasks may require an executor to be attached at the end of the chain: a task may be enqueued onto that executor, that flushes the queue. This may remove the need for background threads.</p>
<h2 id="supporting-overflow-from-a-fixed-size-thread-pool"><span class="header-section-number">3.3</span> Supporting overflow from a fixed size thread pool<a href="#supporting-overflow-from-a-fixed-size-thread-pool" class="self-link"></a></h2>
<p>This is the traditional case for the parallel algorithms as specified in C++20. If for whatever reason a thread pool backing an executor is unable to take more work, for example it has stopped processing and not freed its input queue, then it may delegate work to the downstream executor.</p>
<p>Clearly, there are challenges with implementing this and making a true guarantee in all cases. What I believe is clear is that this has to be implemented in the executor - a queue of work passed to the executor is no longer owned by the algorithm. The algorithm is in no position to change where this work is scheduled once the executor owns it. Therefore the most appropriate solution is to make available a valid downstream executor to the upstream executor and allow one to delegate to the other.</p>
<h1 id="generalising"><span class="header-section-number">4</span> Generalising<a href="#generalising" class="self-link"></a></h1>
<p>Let us say that a given asynchronous algorithm promises some forward progress guarantee that depends on its executor/scheduler<a href="#fn1" class="footnote-ref" id="fnref1" role="doc-noteref"><sup>1</sup></a>.</p>
<p>So in a work chain (using <code>|</code> to represent a chaining of work as in C++20’s Ranges):</p>
<div class="sourceCode" id="cb8"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb8-1"><a href="#cb8-1"></a>sender_to<span class="op">&lt;</span>vector<span class="op">&lt;</span><span class="dt">int</span><span class="op">&gt;&gt;</span> doWork<span class="op">(</span>sender_to<span class="op">&lt;</span>vector<span class="op">&lt;</span><span class="dt">int</span><span class="op">&gt;&gt;</span> vec<span class="op">)</span> <span class="op">{</span></span>
<span id="cb8-2"><a href="#cb8-2"></a>  <span class="cf">return</span> std<span class="op">::</span>move<span class="op">(</span>vec<span class="op">)</span> <span class="op">|</span> on<span class="op">(</span>DeferredExecutor<span class="op">{})</span> <span class="op">|</span> bulk_transform<span class="op">([](</span><span class="dt">int</span> i<span class="op">)</span> <span class="op">{</span><span class="cf">return</span> i<span class="op">+</span><span class="dv">1</span>;<span class="op">})</span>;</span>
<span id="cb8-3"><a href="#cb8-3"></a><span class="op">}</span></span></code></pre></div>
<p><code>DeferredExecutor</code> is a executor known to make no forward progress guarantee for its algorithms. Indeed, if implemented as in folly, quite the opposite is true. It is guaranteed to make no progress. Note that this is laziness of runtime, but is independent of laziness of submission of a work chain.</p>
<p>As a result, the returned <code>sender</code> also makes no progress. We may want to be able to query that the resulting <code>sender</code> makes no progress, but we leave that to later. What it certainly means is that if we add a node to this chain, it must be able to provide an executor to this <code>sender</code>. The requirement to be provided with a executor can be enforced at compile time.</p>
<p>The mechanism to achieve this is that the submit method on the returned <code>sender</code> requires that the receiver passed to it offers an executor. At worst, this executor might also be deferred, but it is a requirement that at the point this work is made concrete, the <code>DeferredExecutor</code> has an executor available to delegate to.</p>
<p>Now let’s say we explicitly provide an executor with a strong guarantee. A concurrent executor like <code>NewThreadExecutor</code>:</p>
<div class="sourceCode" id="cb9"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb9-1"><a href="#cb9-1"></a>sender_to<span class="op">&lt;</span>vector<span class="op">&lt;</span><span class="dt">int</span><span class="op">&gt;&gt;</span> doWork<span class="op">(</span>sender_to<span class="op">&lt;</span>vector<span class="op">&lt;</span><span class="dt">int</span><span class="op">&gt;&gt;</span> vec<span class="op">)</span> <span class="op">{</span></span>
<span id="cb9-2"><a href="#cb9-2"></a>  <span class="cf">return</span> std<span class="op">::</span>move<span class="op">(</span>vec<span class="op">)</span> <span class="op">|</span></span>
<span id="cb9-3"><a href="#cb9-3"></a>    on<span class="op">(</span>DeferredExecutor<span class="op">{})</span> <span class="op">|</span></span>
<span id="cb9-4"><a href="#cb9-4"></a>    bulk_transform<span class="op">([](</span><span class="dt">int</span> i<span class="op">)</span> <span class="op">{</span><span class="cf">return</span> i<span class="op">+</span><span class="dv">1</span>;<span class="op">})</span> <span class="op">|</span> <span class="co">// c2</span></span>
<span id="cb9-5"><a href="#cb9-5"></a>    on<span class="op">(</span>NewThreadExecutor<span class="op">{})</span>;</span>
<span id="cb9-6"><a href="#cb9-6"></a><span class="op">}</span></span></code></pre></div>
<p>Now the algorithm <code>c2</code> is guaranteed to run on the <code>NewThreadExecutor</code>. It is guaranteed to make concurrent progress, which is enough to ensure that the <code>bulk_transform</code> starts and completes in reasonable time.</p>
<p>Note that this also provides an opportunity for an intermediate state. If instead of a <code>DeferredExecutor</code> we had a <code>BoundedQueueExecutor</code> with the obvious meaning and a queue size of 1:</p>
<div class="sourceCode" id="cb10"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb10-1"><a href="#cb10-1"></a>sender_to<span class="op">&lt;</span>vector<span class="op">&lt;</span><span class="dt">int</span><span class="op">&gt;&gt;</span> doWork<span class="op">(</span>sender_to<span class="op">&lt;</span>vector<span class="op">&lt;</span><span class="dt">int</span><span class="op">&gt;&gt;</span> vec<span class="op">)</span> <span class="op">{</span></span>
<span id="cb10-2"><a href="#cb10-2"></a>  <span class="cf">return</span> std<span class="op">::</span>move<span class="op">(</span>vec<span class="op">)</span> <span class="op">|</span></span>
<span id="cb10-3"><a href="#cb10-3"></a>    on<span class="op">(</span>BoundedQueueExecutor<span class="op">{</span><span class="dv">1</span><span class="op">})</span> <span class="op">|</span></span>
<span id="cb10-4"><a href="#cb10-4"></a>    bulk_transform<span class="op">([](</span><span class="dt">int</span> i<span class="op">)</span> <span class="op">{</span><span class="cf">return</span> i<span class="op">+</span><span class="dv">1</span>;<span class="op">})</span> <span class="op">|</span> <span class="co">// c2</span></span>
<span id="cb10-5"><a href="#cb10-5"></a>    on<span class="op">(</span>NewThreadExecutor<span class="op">{})</span>;</span>
<span id="cb10-6"><a href="#cb10-6"></a><span class="op">}</span></span></code></pre></div>
<p><code>bulk_transform</code> will launch a task per element of <code>vec</code> and <code>vec</code> is larger than 1. As a result, the second task may be rejected because of the queue bound. One option is to propagate a failure error downstream, such that the <code>transform</code> would fail with an error. However, if <code>BoundedQueueExecutor</code> can make use of the scheduler provided by the downstream <code>receiver</code>, it can instead delegate the work, ensuring that even though it hit a limit internally, the second scheduler will complete the work.</p>
<p>Any given executor should declare in its specification if it will delegate or not. If no delegate executor is provided, for example we submit all of this work with a null <code>receiver</code> making it fire-and-forget, then no delegation happens but we could instead still make the error available:</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="dt">void</span> doWork<span class="op">(</span>sender_to<span class="op">&lt;</span>vector<span class="op">&lt;</span><span class="dt">int</span><span class="op">&gt;&gt;</span> vec<span class="op">)</span> <span class="op">{</span></span>
<span id="cb11-2"><a href="#cb11-2"></a>  std<span class="op">::</span>move<span class="op">(</span>vec<span class="op">)</span> <span class="op">|</span></span>
<span id="cb11-3"><a href="#cb11-3"></a>    on<span class="op">(</span>BoundedQueueExecutor<span class="op">{</span><span class="dv">1</span><span class="op">})</span> <span class="op">|</span></span>
<span id="cb11-4"><a href="#cb11-4"></a>    bulk_transform<span class="op">([](</span><span class="dt">int</span> i<span class="op">)</span> <span class="op">{</span><span class="cf">return</span> i<span class="op">+</span><span class="dv">1</span>;<span class="op">})</span> <span class="op">|</span></span>
<span id="cb11-5"><a href="#cb11-5"></a>    on_error<span class="op">([](</span>Ex<span class="op">&amp;&amp;</span> ex<span class="op">)</span> <span class="op">{</span>Handle the error<span class="op">})</span> <span class="op">|</span></span>
<span id="cb11-6"><a href="#cb11-6"></a>    eagerify<span class="op">()</span>;</span>
<span id="cb11-7"><a href="#cb11-7"></a><span class="op">}</span></span></code></pre></div>
<p>Finally, note that this is objectively more flexible than throwing an exception on enqueue - the <code>submit</code> operation on the <code>sender_to</code> provided by both of the above instances of <code>BoundedQueueExecutor</code> can always succeed, removing the risk of trying to handle an enqueue failure from the caller. One will propagate an error inline, the other gives the executor the decision of whether to propagate work or not. That allows it to make the decision to delegate after a <em>successful</em> enqueue - for example when the queue has accepted work but the underlying execution context realizes some property cannot be satisfied.</p>
<h2 id="synchronous-operations"><span class="header-section-number">4.1</span> Synchronous operations<a href="#synchronous-operations" class="self-link"></a></h2>
<p>Providing the same guarantee as the synchronous transform:</p>
<div class="sourceCode" id="cb12"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb12-1"><a href="#cb12-1"></a>vector<span class="op">&lt;</span><span class="dt">int</span><span class="op">&gt;</span> vec<span class="op">{</span><span class="dv">1</span>, <span class="dv">2</span>, <span class="dv">3</span><span class="op">}</span>;</span>
<span id="cb12-2"><a href="#cb12-2"></a>vector<span class="op">&lt;</span><span class="dt">int</span><span class="op">&gt;</span> out<span class="op">(</span>vec<span class="op">.</span>size<span class="op">()</span>, <span class="dv">0</span><span class="op">)</span>;</span>
<span id="cb12-3"><a href="#cb12-3"></a>std<span class="op">::</span>transform<span class="op">(</span></span>
<span id="cb12-4"><a href="#cb12-4"></a>  std<span class="op">::</span>execution<span class="op">::</span>par,</span>
<span id="cb12-5"><a href="#cb12-5"></a>  begin<span class="op">(</span>vec<span class="op">)</span>,</span>
<span id="cb12-6"><a href="#cb12-6"></a>  end<span class="op">(</span>vec<span class="op">)</span>,</span>
<span id="cb12-7"><a href="#cb12-7"></a>  begin<span class="op">(</span>out<span class="op">)</span>,</span>
<span id="cb12-8"><a href="#cb12-8"></a>  <span class="op">[](</span><span class="dt">int</span> i<span class="op">){</span><span class="cf">return</span> i<span class="op">+</span><span class="dv">1</span>;<span class="op">}</span> <span class="co">// s0</span></span>
<span id="cb12-9"><a href="#cb12-9"></a>  <span class="op">)</span>; <span class="co">// s1</span></span></code></pre></div>
<p>May be achieved by something like:</p>
<div class="sourceCode" id="cb13"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb13-1"><a href="#cb13-1"></a>vector<span class="op">&lt;</span><span class="dt">int</span><span class="op">&gt;</span> doWork<span class="op">(</span>Sender<span class="op">&lt;</span>vector<span class="op">&lt;</span><span class="dt">int</span><span class="op">&gt;&gt;</span> vec<span class="op">)</span> <span class="op">{</span></span>
<span id="cb13-2"><a href="#cb13-2"></a>  vector<span class="op">&lt;</span><span class="dt">int</span><span class="op">&gt;</span> output;</span>
<span id="cb13-3"><a href="#cb13-3"></a>  ManualExecutor m;</span>
<span id="cb13-4"><a href="#cb13-4"></a>  std<span class="op">::</span>move<span class="op">(</span>vec<span class="op">)</span> <span class="op">|</span></span>
<span id="cb13-5"><a href="#cb13-5"></a>    bulk_transform<span class="op">(</span>std<span class="op">::</span>execution<span class="op">::</span>par, <span class="op">[](</span><span class="dt">int</span> i<span class="op">)</span> <span class="op">{</span><span class="cf">return</span> i<span class="op">+</span><span class="dv">1</span>;<span class="op">})</span> <span class="op">|</span></span>
<span id="cb13-6"><a href="#cb13-6"></a>    transform<span class="op">([&amp;</span>output<span class="op">](</span>vector<span class="op">&lt;</span><span class="dt">int</span><span class="op">&gt;</span> v<span class="op">){</span>output <span class="op">=</span> std<span class="op">::</span>move<span class="op">(</span>v<span class="op">)</span>;<span class="op">})</span> <span class="op">|</span></span>
<span id="cb13-7"><a href="#cb13-7"></a>    on<span class="op">(</span>m<span class="op">)</span>;</span>
<span id="cb13-8"><a href="#cb13-8"></a>  m<span class="op">.</span>drain<span class="op">()</span>;</span>
<span id="cb13-9"><a href="#cb13-9"></a>  <span class="cf">return</span> output;</span>
<span id="cb13-10"><a href="#cb13-10"></a><span class="op">}</span></span></code></pre></div>
<p>That is that the forward progress of the transform, if it is unable to satisfy the requirement, may delegate to the <code>ManualExecutor</code>. This is an executor that may be driven by the calling thread, in this case by the <code>drain</code> method. So we have attracted the delegation of work by providing an executor to delegate to, but donating the current thread’s forward progress to that executor until all the work is complete.</p>
<p>This would of course be wrapped in a wait algorithm in practice, as proposed in <span class="citation" data-cites="P1897R0">[<a href="#ref-P1897R0" role="doc-biblioref">P1897</a>]</span>:</p>
<div class="sourceCode" id="cb14"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb14-1"><a href="#cb14-1"></a>vector<span class="op">&lt;</span><span class="dt">int</span><span class="op">&gt;</span> doWork<span class="op">(</span>Sender<span class="op">&lt;</span>vector<span class="op">&lt;</span><span class="dt">int</span><span class="op">&gt;&gt;</span> vec<span class="op">)</span> <span class="op">{</span></span>
<span id="cb14-2"><a href="#cb14-2"></a>  <span class="cf">return</span> sync_wait<span class="op">(</span>std<span class="op">::</span>move<span class="op">(</span>vec<span class="op">)</span> <span class="op">|</span></span>
<span id="cb14-3"><a href="#cb14-3"></a>    bulk_transform<span class="op">(</span>std<span class="op">::</span>execution<span class="op">::</span>par, <span class="op">[](</span><span class="dt">int</span> i<span class="op">)</span> <span class="op">{</span><span class="cf">return</span> i<span class="op">+</span><span class="dv">1</span>;<span class="op">}))</span>;</span>
<span id="cb14-4"><a href="#cb14-4"></a><span class="op">}</span></span></code></pre></div>
<p><code>sync_wait</code> applied to an asynchronous bulk operation maintains the forward progress delegating behaviour we offer in the blocking version of the algorithm in C++20.</p>
<h1 id="impact-on-the-standard"><span class="header-section-number">5</span> Impact on the Standard<a href="#impact-on-the-standard" class="self-link"></a></h1>
<ul>
<li>Add <code>scheduler_provider</code> concept.</li>
<li>Add <code>get_scheduler</code> CPO.</li>
<li>Additional wording to the <code>sync_wait</code> CPO specifying that it will pass an <code>executor_provider</code> to the <code>submit</code> method of the passed <code>sender</code>.</li>
<li>Add <code>on</code> CPO to apply the <code>scheduler</code> both upstream and downstream.</li>
<li>Ensure that all asynchronous algorithms propagate the scheduler from the output to the input when this makes sense.</li>
</ul>
<h2 id="concept-scheduler_provider"><span class="header-section-number">5.1</span> Concept scheduler_provider<a href="#concept-scheduler_provider" class="self-link"></a></h2>
<h3 id="summary"><span class="header-section-number">5.1.1</span> Summary<a href="#summary" class="self-link"></a></h3>
<p>A concept for receivers that may provide a scheduler upstream. May be used to overload submit methods to allow delegation in the presence of a downstream scheduler. May be used to restrict the submit method on a <code>sender</code> to require an <code>executor_provider</code>. This restriction would be important to implement folly’s <code>SemiFuture</code> or <code>SemiAwaitable</code> concepts where all work is delegated.</p>
<h3 id="wording"><span class="header-section-number">5.1.2</span> Wording<a href="#wording" class="self-link"></a></h3>
<div class="sourceCode" id="cb15"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb15-1"><a href="#cb15-1"></a>template&lt;class R&gt;</span>
<span id="cb15-2"><a href="#cb15-2"></a>concept scheduler_provider =</span>
<span id="cb15-3"><a href="#cb15-3"></a>  receiver&lt;R&gt; &amp;&amp;</span>
<span id="cb15-4"><a href="#cb15-4"></a>  requires(R&amp;&amp; r) {  </span>
<span id="cb15-5"><a href="#cb15-5"></a>    { execution::get_scheduler((R&amp;&amp;) r) } noexcept;</span>
<span id="cb15-6"><a href="#cb15-6"></a>  };</span></code></pre></div>
<h2 id="executionget_scheduler"><span class="header-section-number">5.2</span> execution::get_scheduler<a href="#executionget_scheduler" class="self-link"></a></h2>
<h3 id="summary-1"><span class="header-section-number">5.2.1</span> Summary<a href="#summary-1" class="self-link"></a></h3>
<p>When applied to a <code>receiver</code>, if supported, will return a <code>scheduler</code> that the <code>receiver</code> expects to be able to run delegated work safely, and that callers may assume they are able to delegate work to.</p>
<h3 id="wording-1"><span class="header-section-number">5.2.2</span> Wording<a href="#wording-1" class="self-link"></a></h3>
<p>The name <code>execution::get_scheduler</code> denotes a customization point object. The expression <code>execution::get_scheduler(R)</code> for some subexpression <code>R</code> is expression-equivalent to:</p>
<ul>
<li><code>R.get_scheduler()</code> if that expression is valid.</li>
<li>Otherwise, <code>get_scheduler(R)</code> if that expression is valid 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>        template&lt;class R&gt;</span>
<span id="cb16-2"><a href="#cb16-2"></a>          void get_scheduler(R) = delete;</span></code></pre></div>
<h2 id="executionsync_wait-modifications"><span class="header-section-number">5.3</span> execution::sync_wait modifications<a href="#executionsync_wait-modifications" class="self-link"></a></h2>
<h3 id="summary-2"><span class="header-section-number">5.3.1</span> Summary<a href="#summary-2" class="self-link"></a></h3>
<p>Modifications to <code>sync_wait</code> as specified in <span class="citation" data-cites="P1897R0">[<a href="#ref-P1897R0" role="doc-biblioref">P1897</a>]</span>. Rather than simply passing a <code>receiver</code> to the passed <code>sender</code>, <code>sync_wait</code> constructs an execution context on the blocked thread, such that the blocked thread drives that executor and executed enqueued work. A <code>scheduler</code> representing that execution context will be made available from the <code>receiver</code> passed to <code>submit</code> on the <code>sender</code>, thus making that <code>receiver</code> a <code>scheduler_provider</code>.</p>
<h3 id="wording-2"><span class="header-section-number">5.3.2</span> Wording<a href="#wording-2" class="self-link"></a></h3>
<ul>
<li>Otherwise:
<ul>
<li>Constructs an execution context owned by the calling thread such that tasks enqueued into that context will be processed by the calling thread.</li>
<li>Constructs a <code>scheduler</code>, <code>s</code> representing that execution context.</li>
<li>Constructs a <code>receiver</code>, <code>r</code> over an implementation-defined synchronization primitive and passes that callback to <code>execution::submit(S, r)</code>.</li>
<li><code>execution::get_scheduler(r)</code> will return <code>s</code>.<br />
</li>
<li>Waits on the synchronization primitive to block on completion of <code>S</code>.</li>
</ul></li>
</ul>
<p>The thread that invokes <code>sync_wait</code> will block with forward progress delegation on completion of the work where supported by the implementation of <code>s</code>.</p>
<h2 id="executionon"><span class="header-section-number">5.4</span> execution::on<a href="#executionon" class="self-link"></a></h2>
<h3 id="summary-3"><span class="header-section-number">5.4.1</span> Summary<a href="#summary-3" class="self-link"></a></h3>
<p>Transitions execution from one executor to the context of a scheduler. Passes a <code>scheduler_provider</code> to the passed <code>sender</code>, such that it is valid to apply <code>on</code> to a sender that restricts its <code>submit</code> operation to <code>scheduler_provider</code>s. That is that:</p>
<div class="sourceCode" id="cb17"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb17-1"><a href="#cb17-1"></a>sender1 | on(scheduler1) | bulk_execute(f)...</span></code></pre></div>
<p>will return a sender that runs in the context of <code>scheduler1</code> such that <code>f</code> will run on the context of <code>scheduler1</code>, potentially customized, but that is not triggered until the completion of <code>sender1</code>. If <code>sender1</code> calls <code>get_scheduler</code> on the <code>receiver</code> passed by <code>on</code> to <code>sender1</code>’s <code>submit</code> operation, then that shall return <code>scheduler1</code>.</p>
<p><code>on(S1, S2)</code> may be customized on either or both of <code>S1</code> and <code>S2</code>. <code>on</code> differs from <code>via</code> in that via does not provide the executor to the passed <code>sender</code>, thus not allowing the upstream work to be delegated downstream.</p>
<h3 id="wording-3"><span class="header-section-number">5.4.2</span> Wording<a href="#wording-3" class="self-link"></a></h3>
<p>The name <code>execution::on</code> denotes a customization point object. The expression <code>execution::on(S1, S2)</code> for some subexpressions <code>S1</code>, <code>S2</code> is expression-equivalent to:</p>
<ul>
<li><code>S1.on(S2)</code> if that expression is valid.</li>
<li>Otherwise, <code>on(S1, S2)</code> if that expression is valid with overload resolution performed in a context that includes the declaration</li>
</ul>
<div class="sourceCode" id="cb18"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb18-1"><a href="#cb18-1"></a>        template&lt;class S1, class S2&gt;</span>
<span id="cb18-2"><a href="#cb18-2"></a>          void on(S1, S2) = delete;</span></code></pre></div>
<ul>
<li>Otherwise constructs a <code>receiver</code> <code>r</code> such that when <code>on_value</code>, <code>on_error</code> or <code>on_done</code> is called on <code>r</code> the value(s) or error(s) are packaged, and a callback <code>c2</code> constructed such that when <code>execution::value(c2)</code> is called, the stored value or error is transmitted and <code>c2</code> is submitted to a <code>sender</code> obtained from <code>S2</code>.</li>
<li><code>get_scheduler(r)</code> shall return <code>S2</code>.</li>
<li>The returned sender_to’s value types match those of <code>S1</code>.</li>
<li>The returned sender_to’s execution context is that of <code>S2</code>.</li>
</ul>
<p>If <code>execution::is_noexcept_sender(S1)</code> returns true at compile-time, and <code>execution::is_noexcept_sender(S2)</code> returns true at compile-time and all entries in <code>S1::value_types</code> are nothrow movable, <code>execution::is_noexcept_sender(on(S1, S2))</code> should return <code>true</code> at compile time.</p>
<h2 id="delegation-through-asynchronous-algorithms"><span class="header-section-number">5.5</span> Delegation through asynchronous algorithms<a href="#delegation-through-asynchronous-algorithms" class="self-link"></a></h2>
<h3 id="summary-4"><span class="header-section-number">5.5.1</span> Summary<a href="#summary-4" class="self-link"></a></h3>
<p>Most algorithms do not provide executors. These algorithms should avoid breaking the forward progress delegation chain and should allow executors to propagate from <code>receiver</code> to <code>sender</code> in a chain.</p>
<h3 id="wording-4"><span class="header-section-number">5.5.2</span> Wording<a href="#wording-4" class="self-link"></a></h3>
<p>To <code>execution::transform</code>, <code>execution::bulk_transform</code>, <code>execution::bulk_execute</code> and <code>execution::handle_error</code> add the text:</p>
<p>If <code>r2</code> is the <code>receiver</code> passed to <code>submit(s, r2)</code> and <code>s</code> is the <code>sender</code> returned by ALGORITHM and <code>r</code> is the <code>receiver</code> provided to <code>submit(S, r)</code>. if <code>get_scheduler(r2)</code> is defined then <code>get_scheduler(r)</code> is equal to <code>get_scheduler(r2)</code>.</p>
<h1 id="future-work-adding-algorithm-level-forward-progress-definitions"><span class="header-section-number">6</span> Future Work: adding algorithm-level forward progress definitions<a href="#future-work-adding-algorithm-level-forward-progress-definitions" class="self-link"></a></h1>
<p>We have definitions in the standard for forward progress guarantees for threads of execution. These apply to the workers in the parallel algorithms fairly clearly.</p>
<p>It is not clear:</p>
<ul>
<li>Whether it is safe to call executor, scheduler or sender operations under weak forward progress.
<ul>
<li>This will be important for launching nested work.</li>
<li>It may be important simply for having one worker trigger the next.</li>
</ul></li>
<li>Whether it is safe to build a chain of work in a weak agent.</li>
<li>What the forward progress guarantee may be of the completion of a parallel algorithm.</li>
</ul>
<p>All of these are relatively well-defined for synchronous algorithms as they stand. These are blocking algorithms.</p>
<p>Therefore</p>
<div class="sourceCode" id="cb19"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb19-1"><a href="#cb19-1"></a>vector<span class="op">&lt;</span><span class="dt">int</span><span class="op">&gt;</span> vec<span class="op">{</span><span class="dv">1</span>, <span class="dv">2</span>, <span class="dv">3</span><span class="op">}</span>;</span>
<span id="cb19-2"><a href="#cb19-2"></a>vector<span class="op">&lt;</span><span class="dt">int</span><span class="op">&gt;</span> out<span class="op">(</span>vec<span class="op">.</span>size<span class="op">()</span>, <span class="dv">0</span><span class="op">)</span>;</span>
<span id="cb19-3"><a href="#cb19-3"></a>transform<span class="op">(</span></span>
<span id="cb19-4"><a href="#cb19-4"></a>  std<span class="op">::</span>execution<span class="op">::</span>par_unseq,</span>
<span id="cb19-5"><a href="#cb19-5"></a>  begin<span class="op">(</span>vec<span class="op">)</span>,</span>
<span id="cb19-6"><a href="#cb19-6"></a>  end<span class="op">(</span>vec<span class="op">)</span>,</span>
<span id="cb19-7"><a href="#cb19-7"></a>  begin<span class="op">(</span>out<span class="op">)</span>,</span>
<span id="cb19-8"><a href="#cb19-8"></a>  <span class="op">[](</span><span class="dt">int</span> i<span class="op">){</span></span>
<span id="cb19-9"><a href="#cb19-9"></a>    vector<span class="op">&lt;</span><span class="dt">int</span><span class="op">&gt;</span> innervec<span class="op">{</span><span class="dv">1</span>, <span class="dv">2</span>, <span class="dv">3</span><span class="op">}</span>;</span>
<span id="cb19-10"><a href="#cb19-10"></a>    vector<span class="op">&lt;</span><span class="dt">int</span><span class="op">&gt;</span> innerout<span class="op">(</span>vec<span class="op">.</span>size<span class="op">()</span>, <span class="dv">0</span><span class="op">)</span>;</span>
<span id="cb19-11"><a href="#cb19-11"></a>    transform<span class="op">(</span></span>
<span id="cb19-12"><a href="#cb19-12"></a>      std<span class="op">::</span>execution<span class="op">::</span>par_unseq,</span>
<span id="cb19-13"><a href="#cb19-13"></a>      begin<span class="op">(</span>innervec<span class="op">)</span>,</span>
<span id="cb19-14"><a href="#cb19-14"></a>      end<span class="op">(</span>innervec<span class="op">)</span>,</span>
<span id="cb19-15"><a href="#cb19-15"></a>      begin<span class="op">(</span>innerout<span class="op">)</span>,</span>
<span id="cb19-16"><a href="#cb19-16"></a>      <span class="op">[](</span><span class="dt">int</span> i<span class="op">){</span></span>
<span id="cb19-17"><a href="#cb19-17"></a>        <span class="cf">return</span> i<span class="op">+</span><span class="dv">1</span>;</span>
<span id="cb19-18"><a href="#cb19-18"></a>      <span class="op">})</span>;</span>
<span id="cb19-19"><a href="#cb19-19"></a>    <span class="cf">return</span> innerout<span class="op">[</span><span class="dv">0</span><span class="op">]</span>;</span>
<span id="cb19-20"><a href="#cb19-20"></a>  <span class="op">})</span>;</span></code></pre></div>
<p>is not valid.</p>
<p>However, it is not clear that something like</p>
<div class="sourceCode" id="cb20"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb20-1"><a href="#cb20-1"></a>vector<span class="op">&lt;</span><span class="dt">int</span><span class="op">&gt;</span> vec<span class="op">{</span><span class="dv">1</span>, <span class="dv">2</span>, <span class="dv">3</span><span class="op">}</span>;</span>
<span id="cb20-2"><a href="#cb20-2"></a>vector<span class="op">&lt;</span><span class="dt">int</span><span class="op">&gt;</span> out<span class="op">(</span>vec<span class="op">.</span>size<span class="op">()</span>, <span class="dv">0</span><span class="op">)</span>;</span>
<span id="cb20-3"><a href="#cb20-3"></a>transform<span class="op">(</span></span>
<span id="cb20-4"><a href="#cb20-4"></a>  std<span class="op">::</span>execution<span class="op">::</span>par_unseq,</span>
<span id="cb20-5"><a href="#cb20-5"></a>  begin<span class="op">(</span>vec<span class="op">)</span>,</span>
<span id="cb20-6"><a href="#cb20-6"></a>  end<span class="op">(</span>vec<span class="op">)</span>,</span>
<span id="cb20-7"><a href="#cb20-7"></a>  begin<span class="op">(</span>out<span class="op">)</span>,</span>
<span id="cb20-8"><a href="#cb20-8"></a>  <span class="op">[](</span><span class="dt">int</span> i<span class="op">){</span></span>
<span id="cb20-9"><a href="#cb20-9"></a>    <span class="kw">auto</span> s <span class="op">=</span> makeReadySender<span class="op">(</span><span class="dv">0</span><span class="op">)</span>;</span>
<span id="cb20-10"><a href="#cb20-10"></a>    <span class="kw">auto</span> resultSender <span class="op">=</span> std<span class="op">::</span>execution<span class="op">::</span>transform<span class="op">(</span></span>
<span id="cb20-11"><a href="#cb20-11"></a>      std<span class="op">::</span>move<span class="op">(</span>s<span class="op">)</span></span>
<span id="cb20-12"><a href="#cb20-12"></a>      <span class="op">[](</span><span class="dt">int</span> i<span class="op">){</span></span>
<span id="cb20-13"><a href="#cb20-13"></a>        <span class="cf">return</span> i<span class="op">+</span><span class="dv">1</span>;</span>
<span id="cb20-14"><a href="#cb20-14"></a>      <span class="op">})</span>;</span>
<span id="cb20-15"><a href="#cb20-15"></a>    std<span class="op">::</span>execution<span class="op">::</span>submit<span class="op">(</span></span>
<span id="cb20-16"><a href="#cb20-16"></a>      std<span class="op">::</span>move<span class="op">(</span>resultSender<span class="op">)</span>,</span>
<span id="cb20-17"><a href="#cb20-17"></a>      NullCallback<span class="op">{})</span>;</span>
<span id="cb20-18"><a href="#cb20-18"></a>    <span class="cf">return</span> <span class="dv">0</span>;</span>
<span id="cb20-19"><a href="#cb20-19"></a>  <span class="op">})</span>;</span></code></pre></div>
<p>is not valid.</p>
<p>This code is entirely inline. It is blocking, but trivially so because no executor or scheduler is involved. Can we make a claim that building and running this operation satisfies <code>par_unseq</code>’s guarantee? Could we if it had a scheduler involved - would <code>schedule</code>, <code>submit</code> or <code>execute</code> satisfy the guarantee?</p>
<p>We believe that we need to start to think through these problems. At a minimum, <code>submit</code> on a <code>sender</code> may have to make a statement about the guarantee it makes. <code>sender</code>s may then propagate that guarantee, and that of the upstream sender, down a chain.</p>
<p>This is food for thought. We don’t have a concrete design in mind, but would like us as a group to be thinking about how asynchronous algorithms and the forward progress definitions interact in general.</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-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-P1897R0">
<p>[P1897] 2019. Towards C++23 executors: A proposal for an initial set of algorithms. <br />
<a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p1897r0.pdf">http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p1897r0.pdf</a></p>
</div>
</div>
<section class="footnotes" role="doc-endnotes">
<hr />
<ol>
<li id="fn1" role="doc-endnote"><p>Potentially also on the execution property passed but we leave that out here for simplicity.<a href="#fnref1" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
</ol>
</section>
</div>
</div>
</body>
</html>
