<!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="2025-07-15" />
  <title>Concerns about the design of std::execution::task</title>
  <style>
code{white-space: pre-wrap;}
span.smallcaps{font-variant: small-caps;}
span.underline{text-decoration: underline;}
div.column{display: inline-block; vertical-align: top; width: 50%;}
div.csl-block{margin-left: 1.5em;}
ul.task-list{list-style: none;}
html { -webkit-text-size-adjust: 100%; }
pre > code.sourceCode { white-space: pre; position: relative; }
pre > code.sourceCode > span { display: inline-block; line-height: 1.25; }
pre > code.sourceCode > span:empty { height: 1.2em; }
.sourceCode { overflow: visible; }
code.sourceCode > span { color: inherit; text-decoration: inherit; }
div.sourceCode { margin: 1em 0; }
pre.sourceCode { margin: 0; }
@media screen {
div.sourceCode { overflow: auto; }
}
@media print {
pre > code.sourceCode { white-space: pre-wrap; }
pre > code.sourceCode > span { text-indent: -5em; padding-left: 5em; }
}
pre.numberSource code
{ counter-reset: source-line 0; }
pre.numberSource code > span
{ position: relative; left: -4em; counter-increment: source-line; }
pre.numberSource code > span > a:first-child::before
{ content: counter(source-line);
position: relative; left: -1em; text-align: right; vertical-align: baseline;
border: none; display: inline-block;
-webkit-touch-callout: none; -webkit-user-select: none;
-khtml-user-select: none; -moz-user-select: none;
-ms-user-select: none; user-select: none;
padding: 0 4px; width: 4em;
color: #aaaaaa;
}
pre.numberSource { margin-left: 3em; border-left: 1px solid #aaaaaa; padding-left: 4px; }
div.sourceCode
{ background-color: #f6f8fa; }
@media screen {
pre > code.sourceCode > span > a:first-child::before { text-decoration: underline; }
}
code span { } 
code span.al { color: #ff0000; } 
code span.an { } 
code span.at { } 
code span.bn { color: #9f6807; } 
code span.bu { color: #9f6807; } 
code span.cf { color: #00607c; } 
code span.ch { color: #9f6807; } 
code span.cn { } 
code span.co { color: #008000; font-style: italic; } 
code span.cv { color: #008000; font-style: italic; } 
code span.do { color: #008000; } 
code span.dt { color: #00607c; } 
code span.dv { color: #9f6807; } 
code span.er { color: #ff0000; font-weight: bold; } 
code span.ex { } 
code span.fl { color: #9f6807; } 
code span.fu { } 
code span.im { } 
code span.in { color: #008000; } 
code span.kw { color: #00607c; } 
code span.op { color: #af1915; } 
code span.ot { } 
code span.pp { color: #6f4e37; } 
code span.re { } 
code span.sc { color: #9f6807; } 
code span.ss { color: #9f6807; } 
code span.st { color: #9f6807; } 
code span.va { } 
code span.vs { color: #9f6807; } 
code span.wa { color: #008000; font-weight: bold; } 
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;
text-align: justify;
}
@media screen and (max-width: 30em) {
body {
margin: 1.5em;
}
}
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; }
</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">Concerns about the design of
std::execution::task</h1>
<table style="border:none;float:right">
  <tr>
    <td>Document #:</td>
    <td>P3801R0</td>
  </tr>
  <tr>
    <td>Date:</td>
    <td>2025-07-15</td>
  </tr>
  <tr>
    <td style="vertical-align:top">Project:</td>
    <td>Programming Language C++</td>
  </tr>
  <tr>
    <td style="vertical-align:top">Audience:</td>
    <td>
      LEWG<br>
    </td>
  </tr>
  <tr>
    <td style="vertical-align:top">Reply-to:</td>
    <td>
      Jonathan Müller<br>&lt;<a href="mailto:foonathan@jonathanmueller.dev" class="email">foonathan@jonathanmueller.dev</a>&gt;<br>
    </td>
  </tr>
</table>
</header>
<div style="clear:both">
<div id="TOC" role="doc-toc">
<h1 id="toctitle">Contents</h1>
<ul>
<li><a href="#abstract" id="toc-abstract"><span class="toc-section-number">1</span> Abstract</a></li>
<li><a href="#background" id="toc-background"><span class="toc-section-number">2</span> Background</a></li>
<li><a href="#major-concerns" id="toc-major-concerns"><span class="toc-section-number">3</span> Major concerns</a>
<ul>
<li><a href="#iterative-code-can-stack-overflow" id="toc-iterative-code-can-stack-overflow"><span class="toc-section-number">3.1</span> Iterative code can stack
overflow</a></li>
<li><a href="#parameter-lifetime-is-surprising" id="toc-parameter-lifetime-is-surprising"><span class="toc-section-number">3.2</span> Parameter lifetime is
surprising</a></li>
<li><a href="#no-protection-against-dangling-references" id="toc-no-protection-against-dangling-references"><span class="toc-section-number">3.3</span> No protection against dangling
references</a></li>
</ul></li>
<li><a href="#minor-concerns" id="toc-minor-concerns"><span class="toc-section-number">4</span> Minor concerns</a>
<ul>
<li><a href="#co_yield-with_errorx-is-clunky" id="toc-co_yield-with_errorx-is-clunky"><span class="toc-section-number">4.1</span>
<code class="sourceCode default">co_yield with_error(x)</code> is
clunky</a></li>
<li><a href="#co_await-exschedulesch-is-an-expensive-no-op" id="toc-co_await-exschedulesch-is-an-expensive-no-op"><span class="toc-section-number">4.2</span>
<code class="sourceCode default">co_await ex::schedule(sch)</code> is an
expensive no-op</a></li>
<li><a href="#coroutine-cancellation-is-ad-hoc" id="toc-coroutine-cancellation-is-ad-hoc"><span class="toc-section-number">4.3</span> Coroutine cancellation is
ad-hoc</a></li>
</ul></li>
<li><a href="#next-steps" id="toc-next-steps"><span class="toc-section-number">5</span> Next steps</a></li>
<li><a href="#bibliography" id="toc-bibliography"><span class="toc-section-number">6</span> References</a></li>
</ul>
</div>
<h1 data-number="1" id="abstract"><span class="header-section-number">1</span> Abstract<a href="#abstract" class="self-link"></a></h1>
<p><span class="citation" data-cites="P3552R3">[<a href="#ref-P3552R3" role="doc-biblioref">P3552R3</a>]</span>, proposing the coroutine task
type <code class="sourceCode default">std::execution::task</code>, was
approved in Sofia for inclusion in C++26. I have strong concerns about
its design and urge the committee to reconsider.</p>
<h1 data-number="2" id="background"><span class="header-section-number">2</span> Background<a href="#background" class="self-link"></a></h1>
<p><span class="citation" data-cites="P3552R3">[<a href="#ref-P3552R3" role="doc-biblioref">P3552R3</a>]</span> (finally) proposes a coroutine
task type, <code class="sourceCode default">std::execution::task</code>
(<code class="sourceCode default">ex::task</code> from now on), to the
C++ standard library. The paper first appeared early this year and was
initially discussed in Hagenberg, where it was forwarded by SG1, and
received feedback in LEWG. The feature freeze deadline for C++26 then
passed without a vote to forward it to LWG.</p>
<p>However, the paper was then further discussed in two LEWG telecons in
April and May, where it was then forwarded to LWG for inclusion in
C++26. This was done without following the procedure for making a
schedule exception outlined in <span class="citation" data-cites="P1000R6">[<a href="#ref-P1000R6" role="doc-biblioref">P1000R6</a>]</span>, and without taking an
electronic vote to confirm the telecon decision.</p>
<p>In Sofia, LWG surprisingly found time to approve the wording, so it
appeared in the Sofia plenary. After objections, the vote passed 77 to
11, with 29 abstentions, thus adding it to the C++26 standard.</p>
<p>I championed the plenary objections: First off, the proper procedure
was not followed: The paper was forwarded after the feature freeze
deadline in a telecon. Second,
<code class="sourceCode default">ex::task</code> is an important feature
that came late in the C++26 cycle to begin with, and I’m worried about
technical issues in the design we might not be aware of yet. By the time
they surface, C++26 is frozen, and any fixes are constrained by
backwards compatibility. Third, I have concrete technical concerns with
the design. As the committee plenary is not the appropriate venue for
technical discussions, I am voicing them in this paper. I apologize for
them coming late in the cycle; I did not realize until shortly before
Sofia that <code class="sourceCode default">ex::task</code> was on track
for C++26 and though I had the entire C++29 cycle to write a paper.</p>
<p>The author of <span class="citation" data-cites="P3552R3">[<a href="#ref-P3552R3" role="doc-biblioref">P3552R3</a>]</span> is also
working on a paper collecting some issues and suggestions for fixes in
<span class="citation" data-cites="P3796R0">[<a href="#ref-P3796R0" role="doc-biblioref">P3796R0</a>]</span>.</p>
<h1 data-number="3" id="major-concerns"><span class="header-section-number">3</span> Major concerns<a href="#major-concerns" class="self-link"></a></h1>
<p>In my opinion, these issues need to be addressed before adopting
<code class="sourceCode default">ex::task</code>. Unfortunately, I do
not yet see a clear path to fix them in the limited time frame we have
left for C++26.</p>
<h2 data-number="3.1" id="iterative-code-can-stack-overflow"><span class="header-section-number">3.1</span> Iterative code can stack
overflow<a href="#iterative-code-can-stack-overflow" class="self-link"></a></h2>
<p>Consider:</p>
<div class="sourceCode" id="cb1"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a>ex<span class="op">::</span>task<span class="op">&lt;</span><span class="dt">void</span><span class="op">&gt;</span> f<span class="op">(</span><span class="dt">int</span> i<span class="op">)</span>;</span>
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-3"><a href="#cb1-3" aria-hidden="true" tabindex="-1"></a>ex<span class="op">::</span>task<span class="op">&lt;</span><span class="dt">void</span><span class="op">&gt;</span> g<span class="op">(</span><span class="dt">int</span> total<span class="op">)</span> <span class="op">{</span></span>
<span id="cb1-4"><a href="#cb1-4" aria-hidden="true" tabindex="-1"></a>    <span class="cf">for</span> <span class="op">(</span><span class="kw">auto</span> i <span class="op">=</span> <span class="dv">0</span>; i <span class="op">&lt;</span> total; <span class="op">++</span>i<span class="op">)</span> <span class="op">{</span></span>
<span id="cb1-5"><a href="#cb1-5" aria-hidden="true" tabindex="-1"></a>        <span class="kw">co_await</span> f<span class="op">(</span>i<span class="op">)</span>;</span>
<span id="cb1-6"><a href="#cb1-6" aria-hidden="true" tabindex="-1"></a>    <span class="op">}</span></span>
<span id="cb1-7"><a href="#cb1-7" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
<p><a href="https://godbolt.org/z/38ToEvGaa">Full example</a></p>
<p>Depending on the value of
<code class="sourceCode default">total</code> and the scheduler used to
execute <code class="sourceCode default">g</code> on, this can lead to a
stack overflow. Concretely, if the
<code class="sourceCode default">ex::inline_scheduler</code> is used,
each call to <code class="sourceCode default">f</code> will execute
eagerly, but, because <code class="sourceCode default">ex::task</code>
does not support symmetric transfer, each schedule back-and-forth will
add additional stack frames.</p>
<p>Having iterative code that is actually recursive is a potential
security vulnerability.</p>
<p>A potential fix is adding symmetric transfer to
<code class="sourceCode default">ex::task</code> with an
<code class="sourceCode default">operator co_await</code> overload.
However, while this would solve the example above, it would not solve
the general problem of stack overflow when awaiting other senders. A
thorough fix is non-trivial and requires support for guaranteed tail
calls.</p>
<h2 data-number="3.2" id="parameter-lifetime-is-surprising"><span class="header-section-number">3.2</span> Parameter lifetime is
surprising<a href="#parameter-lifetime-is-surprising" class="self-link"></a></h2>
<p>Consider:</p>
<div class="sourceCode" id="cb2"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a><span class="kw">struct</span> Tracker <span class="op">{</span></span>
<span id="cb2-2"><a href="#cb2-2" aria-hidden="true" tabindex="-1"></a>    <span class="kw">const</span> <span class="dt">char</span><span class="op">*</span> label;</span>
<span id="cb2-3"><a href="#cb2-3" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb2-4"><a href="#cb2-4" aria-hidden="true" tabindex="-1"></a>    Tracker<span class="op">(</span><span class="kw">const</span> <span class="dt">char</span><span class="op">*</span> label<span class="op">)</span> <span class="op">:</span> label<span class="op">(</span>label<span class="op">)</span> <span class="op">{</span> std<span class="op">::</span>printf<span class="op">(</span><span class="st">&quot;Tracker &#39;</span><span class="sc">%s</span><span class="st">&#39; created</span><span class="sc">\n</span><span class="st">&quot;</span>, label<span class="op">)</span>; <span class="op">}</span></span>
<span id="cb2-5"><a href="#cb2-5" aria-hidden="true" tabindex="-1"></a>    Tracker<span class="op">(</span>Tracker<span class="op">&amp;&amp;</span> other<span class="op">)</span> <span class="kw">noexcept</span> <span class="op">:</span> label<span class="op">(</span>other<span class="op">.</span>label<span class="op">)</span> <span class="op">{</span> other<span class="op">.</span>label <span class="op">=</span> <span class="kw">nullptr</span>; <span class="op">}</span></span>
<span id="cb2-6"><a href="#cb2-6" aria-hidden="true" tabindex="-1"></a>    <span class="op">~</span>Tracker<span class="op">()</span> <span class="op">{</span></span>
<span id="cb2-7"><a href="#cb2-7" aria-hidden="true" tabindex="-1"></a>        <span class="cf">if</span> <span class="op">(</span>label <span class="op">!=</span> <span class="kw">nullptr</span><span class="op">)</span></span>
<span id="cb2-8"><a href="#cb2-8" aria-hidden="true" tabindex="-1"></a>            std<span class="op">::</span>printf<span class="op">(</span><span class="st">&quot;Tracker &#39;</span><span class="sc">%s</span><span class="st">&#39; destroyed</span><span class="sc">\n</span><span class="st">&quot;</span>, label<span class="op">)</span>;</span>
<span id="cb2-9"><a href="#cb2-9" aria-hidden="true" tabindex="-1"></a>    <span class="op">}</span></span>
<span id="cb2-10"><a href="#cb2-10" aria-hidden="true" tabindex="-1"></a><span class="op">}</span>;</span>
<span id="cb2-11"><a href="#cb2-11" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb2-12"><a href="#cb2-12" aria-hidden="true" tabindex="-1"></a>ex<span class="op">::</span>task<span class="op">&lt;</span><span class="dt">void</span><span class="op">&gt;</span> f<span class="op">(</span>Tracker param <span class="op">=</span> Tracker<span class="op">(</span><span class="st">&quot;f param&quot;</span><span class="op">))</span> <span class="op">{</span></span>
<span id="cb2-13"><a href="#cb2-13" aria-hidden="true" tabindex="-1"></a>    Tracker local<span class="op">(</span><span class="st">&quot;f local&quot;</span><span class="op">)</span>;</span>
<span id="cb2-14"><a href="#cb2-14" aria-hidden="true" tabindex="-1"></a>    <span class="kw">co_return</span>;</span>
<span id="cb2-15"><a href="#cb2-15" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span>
<span id="cb2-16"><a href="#cb2-16" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb2-17"><a href="#cb2-17" aria-hidden="true" tabindex="-1"></a><span class="dt">int</span> main<span class="op">()</span> <span class="op">{</span></span>
<span id="cb2-18"><a href="#cb2-18" aria-hidden="true" tabindex="-1"></a>    Tracker main<span class="op">(</span><span class="st">&quot;main&quot;</span><span class="op">)</span>;</span>
<span id="cb2-19"><a href="#cb2-19" aria-hidden="true" tabindex="-1"></a>    ex<span class="op">::</span>sync_wait<span class="op">(</span>f<span class="op">()</span> <span class="op">|</span> ex<span class="op">::</span>then<span class="op">([]</span> <span class="op">{</span> std<span class="op">::</span>printf<span class="op">(</span><span class="st">&quot;then</span><span class="sc">\n</span><span class="st">&quot;</span><span class="op">)</span>; <span class="op">}))</span>;</span>
<span id="cb2-20"><a href="#cb2-20" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
<p><a href="https://godbolt.org/z/PMYKrdq95">Full example</a></p>
<p>Unlike for a regular function call, where the return statement
destroys the local variables, then the parameters, then returns to the
caller, an <code class="sourceCode default">ex::task</code> function
destroys the local variables, completes the resumer, triggering
potentially arbitrary work, and only then destroys the parameters once
the entire pipeline of senders is completed.</p>
<p>This is surprising behavior that can lead to unnecessary memory
consumption and potentially hard to figure out bugs. It fundamentally
breaks the promises of RAII where destruction is strictly tight to the
end of a scope.</p>
<p>This is notably not an inherent flaw of coroutines, only of the
particular design of <code class="sourceCode default">ex::task</code>.
In particular, it is caused by the lifetime of
<code class="sourceCode default">ex::task</code>’s operation state, see
also <span class="citation" data-cites="P3373R1">[<a href="#ref-P3373R1" role="doc-biblioref">P3373R1</a>]</span>.</p>
<h2 data-number="3.3" id="no-protection-against-dangling-references"><span class="header-section-number">3.3</span> No protection against dangling
references<a href="#no-protection-against-dangling-references" class="self-link"></a></h2>
<p>(In-)Famously, reference parameters to coroutine functions are
problematic because they are shallowly copied into the coroutine state:
The caller has to make sure that the reference will live as long as the
coroutine is running.</p>
<div class="sourceCode" id="cb3"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a>ex<span class="op">::</span>task<span class="op">&lt;</span><span class="dt">int</span><span class="op">&gt;</span> f<span class="op">(</span><span class="kw">const</span> <span class="dt">int</span><span class="op">&amp;</span> x<span class="op">)</span>;</span>
<span id="cb3-2"><a href="#cb3-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-3"><a href="#cb3-3" aria-hidden="true" tabindex="-1"></a>ex<span class="op">::</span>task<span class="op">&lt;</span><span class="dt">void</span><span class="op">&gt;</span> g<span class="op">()</span> <span class="op">{</span></span>
<span id="cb3-4"><a href="#cb3-4" aria-hidden="true" tabindex="-1"></a>    <span class="kw">auto</span> task <span class="op">=</span> f<span class="op">(</span><span class="dv">11</span><span class="op">)</span>; <span class="co">// coroutine contains dangling reference</span></span>
<span id="cb3-5"><a href="#cb3-5" aria-hidden="true" tabindex="-1"></a>    <span class="kw">co_await</span> co; <span class="co">// error: dangling reference is observed</span></span>
<span id="cb3-6"><a href="#cb3-6" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
<p>The core guidelines therefore recommends against passing parameters
by reference (and they should also extend that to reference types such
as <code class="sourceCode default">std::string_view</code>), and to
avoid capturing lambdas as coroutines. The natural sender/receiver
solution is structured concurrency, which ensures references live long
enough. However, these are just guidelines, no enforced rules. Given the
current climate, we should strive to have rules that are enforced by the
compiler.</p>
<p>While the general solution requires extensive language changes, which
is a bit unfair to expect from a library extension, Google’s coroutine
library <span class="citation" data-cites="google-coro">[<a href="#ref-google-coro" role="doc-biblioref">google-coro</a>]</span> has
a pure library solution: Their default coroutine type
<code class="sourceCode default">Co</code> is immovable and
<code class="sourceCode default">co_await</code> takes it by-value. That
way you can only <code class="sourceCode default">co_await</code>
prvalue coroutines, which makes it impossible to have dangling
references: All temporaries are guaranteed to live for the entire
statement, which includes the
<code class="sourceCode default">co_await</code> expression. They then
have a separate movable <code class="sourceCode default">Future</code>
type that packages all reference arguments together with the coroutine
state.</p>
<div class="sourceCode" id="cb4"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true" tabindex="-1"></a>Co<span class="op">&lt;</span><span class="dt">int</span><span class="op">&gt;</span> f<span class="op">(</span><span class="kw">const</span> <span class="dt">int</span><span class="op">&amp;</span> x<span class="op">)</span>;</span>
<span id="cb4-2"><a href="#cb4-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-3"><a href="#cb4-3" aria-hidden="true" tabindex="-1"></a>Co<span class="op">&lt;</span><span class="dt">void</span><span class="op">&gt;</span> g<span class="op">()</span> <span class="op">{</span></span>
<span id="cb4-4"><a href="#cb4-4" aria-hidden="true" tabindex="-1"></a>    <span class="kw">auto</span> a <span class="op">=</span> <span class="kw">co_await</span> f<span class="op">(</span><span class="dv">42</span><span class="op">)</span>; <span class="co">// okay, temporary destroyed after the `co_await`</span></span>
<span id="cb4-5"><a href="#cb4-5" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-6"><a href="#cb4-6" aria-hidden="true" tabindex="-1"></a>    <span class="kw">auto</span> co <span class="op">=</span> f<span class="op">(</span><span class="dv">11</span><span class="op">)</span>; <span class="co">// coroutine contains dangling reference</span></span>
<span id="cb4-7"><a href="#cb4-7" aria-hidden="true" tabindex="-1"></a>    <span class="kw">co_await</span> co; <span class="co">// error: cannot co_await, so `f` cannot observe dangling reference</span></span>
<span id="cb4-8"><a href="#cb4-8" aria-hidden="true" tabindex="-1"></a>    <span class="kw">co_await</span> std<span class="op">::</span>move<span class="op">(</span>co<span class="op">)</span>; <span class="co">// likewise an error</span></span>
<span id="cb4-9"><a href="#cb4-9" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
<p>This design is strictly safer than the current design of
<code class="sourceCode default">ex::task</code>, but it would require
some work to make it compatible with senders/receivers.</p>
<h1 data-number="4" id="minor-concerns"><span class="header-section-number">4</span> Minor concerns<a href="#minor-concerns" class="self-link"></a></h1>
<p>These are not issues that necessarily need to be fixed before
standardization (nor do they ever need to be fixed), but if we didn’t
have the time pressure for C++26, I would have liked to see them
addressed. Some of them could also be addressed in a future standard
with a backwards-compatible evolution of the design.</p>
<h2 data-number="4.1" id="co_yield-with_errorx-is-clunky"><span class="header-section-number">4.1</span>
<code class="sourceCode default">co_yield with_error(x)</code> is
clunky<a href="#co_yield-with_errorx-is-clunky" class="self-link"></a></h2>
<p>The syntax to call <code class="sourceCode default">set_error</code>
on the receiver without having to throw an exception is clunky:</p>
<div class="sourceCode" id="cb5"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb5-1"><a href="#cb5-1" aria-hidden="true" tabindex="-1"></a>ex<span class="op">::</span>task<span class="op">&lt;</span><span class="dt">void</span><span class="op">&gt;</span> f<span class="op">()</span> <span class="op">{</span></span>
<span id="cb5-2"><a href="#cb5-2" aria-hidden="true" tabindex="-1"></a>    <span class="kw">co_yield</span> with_error<span class="op">(</span>error_code<span class="op">)</span>;</span>
<span id="cb5-3"><a href="#cb5-3" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
<p>It would be much nicer to have
<code class="sourceCode default">co_return</code> instead of
<code class="sourceCode default">co_yield</code>:
<code class="sourceCode default">co_return</code> has the usual
semantics of returning a value and exiting a function;
<code class="sourceCode default">co_yield</code> has the usual semantics
of yielding a value and having the function continue later.</p>
<p>The reason <code class="sourceCode default">co_yield</code> is used,
is that a coroutine promise can only specify
<code class="sourceCode default">return_void</code> or
<code class="sourceCode default">return_value</code>, but not both. If
we want to allow <code class="sourceCode default">co_return;</code>, we
cannot have <code class="sourceCode default">co_return with_error(error_code);</code>.
This is unfortunate, but could be fixed by changing the language to drop
that restriction.</p>
<h2 data-number="4.2" id="co_await-exschedulesch-is-an-expensive-no-op"><span class="header-section-number">4.2</span>
<code class="sourceCode default">co_await ex::schedule(sch)</code> is an
expensive no-op<a href="#co_await-exschedulesch-is-an-expensive-no-op" class="self-link"></a></h2>
<p><code class="sourceCode default">ex::task</code> is (correctly)
scheduler affine; it will always be resumed on the same scheduler it was
started on (unless the scheduler was explicitly changed). This leads to
a potential footgun, however.</p>
<p>When using senders injecting
<code class="sourceCode default">ex::schedule(sch)</code> means that
future work is scheduled on
<code class="sourceCode default">sch</code>:</p>
<div class="sourceCode" id="cb6"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb6-1"><a href="#cb6-1" aria-hidden="true" tabindex="-1"></a>ex<span class="op">::</span>schedule<span class="op">(</span>sch<span class="op">)</span></span>
<span id="cb6-2"><a href="#cb6-2" aria-hidden="true" tabindex="-1"></a><span class="op">|</span> ex<span class="op">::</span>then<span class="op">([]{</span> std<span class="op">::</span>printf<span class="op">(</span><span class="st">&quot;on new scheduler</span><span class="sc">\n</span><span class="st">&quot;</span><span class="op">)</span>; <span class="op">})</span></span></code></pre></div>
<p>Translating this naively to
<code class="sourceCode default">ex::task</code>,
<code class="sourceCode default">co_await ex::schedule(sch)</code> does
nothing due to scheduler affinity of task:</p>
<div class="sourceCode" id="cb7"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb7-1"><a href="#cb7-1" aria-hidden="true" tabindex="-1"></a>ex<span class="op">::</span>task<span class="op">&lt;</span><span class="dt">void</span><span class="op">&gt;</span> f<span class="op">()</span> <span class="op">{</span></span>
<span id="cb7-2"><a href="#cb7-2" aria-hidden="true" tabindex="-1"></a>    <span class="kw">co_await</span> ex<span class="op">::</span>schedule<span class="op">(</span>sch<span class="op">)</span>;</span>
<span id="cb7-3"><a href="#cb7-3" aria-hidden="true" tabindex="-1"></a>    std<span class="op">::</span>printf<span class="op">(</span><span class="st">&quot;still on old scheduler</span><span class="sc">\n</span><span class="st">&quot;</span><span class="op">)</span>;</span>
<span id="cb7-4"><a href="#cb7-4" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
<p>The
<code class="sourceCode default">co_await ex::schedule(sch)</code>
schedules work on <code class="sourceCode default">sch</code>, but all
the work does is reschedule the task back to the original scheduler: It
is an expensive no-op.</p>
<p>The correct way to change the scheduler is to use <code class="sourceCode default">co_await ex::change_coroutine_scheduler(sch)</code>:</p>
<div class="sourceCode" id="cb8"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb8-1"><a href="#cb8-1" aria-hidden="true" tabindex="-1"></a>ex<span class="op">::</span>task<span class="op">&lt;</span><span class="dt">void</span><span class="op">&gt;</span> f<span class="op">()</span> <span class="op">{</span></span>
<span id="cb8-2"><a href="#cb8-2" aria-hidden="true" tabindex="-1"></a>    <span class="kw">co_await</span> ex<span class="op">::</span>change_coroutine_scheduler<span class="op">(</span>sch<span class="op">)</span>;</span>
<span id="cb8-3"><a href="#cb8-3" aria-hidden="true" tabindex="-1"></a>    std<span class="op">::</span>printf<span class="op">(</span><span class="st">&quot;now on new scheduler</span><span class="sc">\n</span><span class="st">&quot;</span><span class="op">)</span>;</span>
<span id="cb8-4"><a href="#cb8-4" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
<p>It is unfortunate that the nicer name
(<code class="sourceCode default">ex::schedule</code>) is not what you
want, and instead you have to use the more ugly
<code class="sourceCode default">ex::change_coroutine_scheduler</code>.
However, making <code class="sourceCode default">ex::schedule</code> do
something special from other senders is also not a good idea, because
then <code class="sourceCode default">co_await ex::schedule(sch)</code>
and (potentially) <code class="sourceCode default">co_await (ex::schedule(sch) | ex::then([]{}))</code>
would have different semantics, which is very confusing.</p>
<p>If we had the language change to make
<code class="sourceCode default">co_return with_error(error_code)</code>
work, <code class="sourceCode default">co_yield</code> is freed up and
could to be used for scheduler changes. This is consistent with the use
of <code class="sourceCode default">std::this_thread::yield</code> to
yield the current thread back to the OS scheduler. Concretely, we could
do a scheduler change with
<code class="sourceCode default">co_yield sch;</code> (“yield to the
scheduler <code class="sourceCode default">sch</code>”) and also
introduce support for <code class="sourceCode default">co_yield;</code>
with the semantics of yielding to the current scheduler. That way, at
least the correct alternative to the wrong
<code class="sourceCode default">co_await ex::schedule(sch)</code> is
the shorter <code class="sourceCode default">co_yield sch;</code>.</p>
<h2 data-number="4.3" id="coroutine-cancellation-is-ad-hoc"><span class="header-section-number">4.3</span> Coroutine cancellation is
ad-hoc<a href="#coroutine-cancellation-is-ad-hoc" class="self-link"></a></h2>
<p>Senders have first-class support for cancellation with
<code class="sourceCode default">set_stopped</code>, but coroutines do
not. Therefore, <code class="sourceCode default">std::execution</code>
has invented a custom customization point on the promise type,
<code class="sourceCode default">unhandled_stopped</code> to allow
propagation and recovery from cancellation.</p>
<p>A cleaner solution would extend language coroutines to support
cancellation natively. It can also make it possible to have symmetric
transfer when recovering from cancellation, instead of adding additional
stack frames.</p>
<h1 data-number="5" id="next-steps"><span class="header-section-number">5</span> Next steps<a href="#next-steps" class="self-link"></a></h1>
<p>The design of <span class="citation" data-cites="P3552R3">[<a href="#ref-P3552R3" role="doc-biblioref">P3552R3</a>]</span> is not
perfect; the author acknowledges that. However, they and a majority of
WG21 believe that it is better to have something now, than nothing at
all and that any obvious issues can be fixed using the NB comments
process.</p>
<p>I fundamentally disagree.</p>
<p>As a standardization committee, we are drafting a standard that
should outlive us. We are not working on some open-source library, we
are designing the foundation for an entire ecosystem. If something has
issues, and we know that it has issues, we should not have allowed a
vote to approve it.</p>
<p>The authors did a great job in the limited time frame they had, and I
thank them for it. However, if they feel its essential for
<code class="sourceCode default">ex::task</code> to be in C++26, they
should have written a paper earlier.</p>
<p>Trying to fix a design under time pressure before the standard is
shipped out is a bad idea. I strongly urge the committee to reconsider
the adoption of <span class="citation" data-cites="P3552R3">[<a href="#ref-P3552R3" role="doc-biblioref">P3552R3</a>]</span> for
C++26.</p>
<h1 data-number="6" id="bibliography"><span class="header-section-number">6</span> References<a href="#bibliography" class="self-link"></a></h1>
<div id="refs" class="references csl-bib-body hanging-indent" data-entry-spacing="0" role="list">
<div id="ref-google-coro" class="csl-entry" role="listitem">
[google-coro] C++ Coroutines at Scale - Implementation Choices at Google
- Aaron Jacobs - C++Now 2024. <a href="https://www.youtube.com/watch?v=k-A12dpMYHo"><div class="csl-block">https://www.youtube.com/watch?v=k-A12dpMYHo</div></a>
</div>
<div id="ref-P1000R6" class="csl-entry" role="listitem">
[P1000R6] Herb Sutter. 2024-05-19. C++ IS schedule. <a href="https://wg21.link/p1000r6"><div class="csl-block">https://wg21.link/p1000r6</div></a>
</div>
<div id="ref-P3373R1" class="csl-entry" role="listitem">
[P3373R1] Robert Leahy. 2025-01-12. Of Operation States and Their
Lifetimes. <a href="https://wg21.link/p3373r1"><div class="csl-block">https://wg21.link/p3373r1</div></a>
</div>
<div id="ref-P3552R3" class="csl-entry" role="listitem">
[P3552R3] Add a Coroutine Task Type. <a href="https://wg21.link/P3552R3"><div class="csl-block">https://wg21.link/P3552R3</div></a>
</div>
<div id="ref-P3796R0" class="csl-entry" role="listitem">
[P3796R0] Coroutine Task Issues. <a href="https://wg21.link/P3796R0"><div class="csl-block">https://wg21.link/P3796R0</div></a>
</div>
</div>
</div>
</div>
</body>
</html>
