<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
<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-06-20" />
  <title>Add a Coroutine Task Type</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;}
      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 { } /* 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;
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/x-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">Add a Coroutine Task
Type</h1>
<table style="border:none;float:right">
  <tr>
    <td>Document #:</td>
    <td>
      P3552R3
      [<a href="https://wg21.link/P3552">Latest</a>]
      [<a href="https://wg21.link/P3552/status">Status</a>]
    </td>
  </tr>
  <tr>
    <td>Date:</td>
    <td>2025-06-20</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>
      Concurrency Working Group (SG1)<br>
      Library Evolution Working Group (LEWG)<br>
      Library Working Group (LWG)<br>
    </td>
  </tr>
  <tr>
    <td style="vertical-align:top">Reply-to:</td>
    <td>
      Dietmar Kühl (Bloomberg)<br>&lt;<a href="mailto:dkuhl@bloomberg.net" class="email">dkuhl@bloomberg.net</a>&gt;<br>
      Maikel Nadolski<br>&lt;<a href="mailto:maikel.nadolski@gmail.com" class="email">maikel.nadolski@gmail.com</a>&gt;<br>
    </td>
  </tr>
</table>
</header>
<div style="clear:both">
<p>C++20 added support for coroutines that can improve the experience
writing asynchronous code. C++26 added the sender/receiver model for a
general interface to asynchronous operations. The expectation is that
users would use the framework using some coroutine type. To support
that, a suitable class needs to be defined and this proposal is
providing such a definition.</p>
<p>Just to get an idea what this proposal is about: here is a simple
<code class="sourceCode cpp">Hello, world</code> written using the
proposed coroutine type:</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><span class="pp">#include </span><span class="im">&lt;execution&gt;</span></span>
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a><span class="pp">#include </span><span class="im">&lt;iostream&gt;</span></span>
<span id="cb1-3"><a href="#cb1-3" aria-hidden="true" tabindex="-1"></a><span class="pp">#include </span><span class="im">&lt;task&gt;</span></span>
<span id="cb1-4"><a href="#cb1-4" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-5"><a href="#cb1-5" aria-hidden="true" tabindex="-1"></a><span class="kw">namespace</span> ex <span class="op">=</span> std<span class="op">::</span>execution;</span>
<span id="cb1-6"><a href="#cb1-6" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-7"><a href="#cb1-7" aria-hidden="true" tabindex="-1"></a><span class="dt">int</span> main<span class="op">()</span> <span class="op">{</span></span>
<span id="cb1-8"><a href="#cb1-8" aria-hidden="true" tabindex="-1"></a>    <span class="cf">return</span> std<span class="op">::</span>get<span class="op">&lt;</span><span class="dv">0</span><span class="op">&gt;(*</span>ex<span class="op">::</span>sync_wait<span class="op">([]-&gt;</span>ex<span class="op">::</span>task<span class="op">&lt;</span><span class="dt">int</span><span class="op">&gt;</span> <span class="op">{</span></span>
<span id="cb1-9"><a href="#cb1-9" aria-hidden="true" tabindex="-1"></a>        std<span class="op">::</span>cout <span class="op">&lt;&lt;</span> <span class="st">&quot;Hello, world!</span><span class="sc">\n</span><span class="st">&quot;</span>;</span>
<span id="cb1-10"><a href="#cb1-10" aria-hidden="true" tabindex="-1"></a>        <span class="kw">co_return</span> <span class="kw">co_await</span> ex<span class="op">::</span>just<span class="op">(</span><span class="dv">0</span><span class="op">)</span>;</span>
<span id="cb1-11"><a href="#cb1-11" aria-hidden="true" tabindex="-1"></a>    <span class="op">}()))</span>;</span>
<span id="cb1-12"><a href="#cb1-12" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
<h1 data-number="1" id="change-history"><span class="header-section-number">1</span> Change History<a href="#change-history" class="self-link"></a></h1>
<h2 data-number="1.1" id="r0-initial-revision"><span class="header-section-number">1.1</span> R0 Initial Revision<a href="#r0-initial-revision" class="self-link"></a></h2>
<h2 data-number="1.2" id="r1-hagenberg-feedback"><span class="header-section-number">1.2</span> R1 Hagenberg Feedback<a href="#r1-hagenberg-feedback" class="self-link"></a></h2>
<ul>
<li>Changed the name from <code class="sourceCode cpp">lazy</code> to
<code class="sourceCode cpp">task</code> based on SG1 feedback and
dropped the section on why <code class="sourceCode cpp">lazy</code> was
chosen.</li>
<li>Changed the name of
<code class="sourceCode cpp">any_scheduler</code> to
<code class="sourceCode cpp">task_scheduler</code>.</li>
<li>Added wording for the <code class="sourceCode cpp">task</code>
specification.</li>
</ul>
<h2 data-number="1.3" id="r2-lewg-feedback"><span class="header-section-number">1.3</span> R2 LEWG Feedback<a href="#r2-lewg-feedback" class="self-link"></a></h2>
<ul>
<li>Removed the use <code class="sourceCode cpp">decay_t</code> from the
specification.</li>
<li>Changed the wording to avoid
<code class="sourceCode cpp">exception_ptr</code> when unavailable.</li>
<li>Renamed <code class="sourceCode cpp">Context</code> to
<code class="sourceCode cpp">Environment</code> to better reflect the
argument’s use.</li>
<li>Made exposition-only “macros” use italics.</li>
<li>Added a feature test macro.</li>
<li>Fixed some typos.</li>
</ul>
<h2 data-number="1.4" id="r3-lwg-feedback"><span class="header-section-number">1.4</span> R3 LWG Feedback<a href="#r3-lwg-feedback" class="self-link"></a></h2>
<h1 data-number="2" id="prior-work"><span class="header-section-number">2</span> Prior Work<a href="#prior-work" class="self-link"></a></h1>
<p>This proposal isn’t the first to propose a coroutine type. Prior
proposals didn’t see any recent (post introduction of sender/receiver)
update, although corresponding proposals were discussed informally on
multiple occasions. There are also implementations of coroutine types
based on a sender/receiver model in active use. This section provides an
overview of this prior work, and where relevant, of corresponding
discussions. This section is primarily for motivating requirements and
describing some points in the design space.</p>
<h2 data-number="2.1" id="p1056-add-lazy-coroutine-coroutine-task-type"><span class="header-section-number">2.1</span> <a href="https://wg21.link/P1056">P1056</a>: Add lazy coroutine (coroutine
task) type<a href="#p1056-add-lazy-coroutine-coroutine-task-type" class="self-link"></a></h2>
<p>The paper describes a
<code class="sourceCode cpp">task</code>/<code class="sourceCode cpp">lazy</code>
type (in <a href="https://wg21.link/P1056r0">P1056r0</a> the name was
<code class="sourceCode cpp">task</code>; the primary change for <a href="https://wg21.link/P1056r1">P1056r1</a> is changing the name to
<code class="sourceCode cpp">lazy</code>). The fundamental idea is to
have a coroutine type which can be
<code class="sourceCode cpp"><span class="kw">co_await</span></code>ed:
the interface of <code class="sourceCode cpp">lazy</code> consists of
move constructor, deliberately no move assignment, a destructor, and
<code class="sourceCode cpp"><span class="kw">operator</span> <span class="kw">co_await</span><span class="op">()</span></code>.
The proposals don’t go into much detail on how to eventually use a
coroutine, but it mentions that there could be functions like <code class="sourceCode cpp">sync_await<span class="op">(</span>task<span class="op">&lt;</span>To<span class="op">&gt;)</span></code>
to await completion of a task (similar to <a href="https://eel.is/c++draft/exec.sync.wait"><code class="sourceCode cpp">execution<span class="op">::</span>sync_wait<span class="op">(</span>sender<span class="op">)</span></code></a>)
or a few variations of that.</p>
<p>A fair part of the paper argues why <code class="sourceCode cpp">future<span class="op">.</span>then<span class="op">()</span></code>
is <em>not</em> a good approach to model coroutines and their results.
Using <code class="sourceCode cpp">future</code> requires allocation,
synchronisation, reference counting, and scheduling which can all be
avoided when using coroutines in a structured way.</p>
<p>The paper also mentions support for <a href="https://wg21.link/P0913">symmetric transfer</a> and allocator
support. Both of these are details on how the coroutine is
implemented.</p>
<p><a href="https://wiki.edg.com/bin/view/Wg21rapperswil2018/P1056R0">Discussion
for P1056r0 in SG1</a></p>
<ul>
<li>The task doesn’t really have anything to do with concurrency.</li>
<li>Decomposing a task cheaply is fundamental. The <a href="https://wg21.link/P0981R0">HALO Optimisations</a> help.</li>
<li>The <code class="sourceCode cpp">task</code> isn’t move assignable
because there are better approaches than using containers to hold them.
It is move constructible as there are no issues with overwriting a
potentially live task.</li>
<li>Resuming where things complete is unsafe but the task didn’t want to
impose any overhead on everybody.</li>
<li>There can be more than one <code class="sourceCode cpp">task</code>
type for different needs.</li>
<li>Holding a mutex lock while
<code class="sourceCode cpp"><span class="kw">co_await</span></code>ing
which may resume on a different thread is hazardous. Static analysers
should be able to detect these cases.</li>
<li>Votes confirmed the no move assignment and forwarding to LEWG
assuming the name is not <code class="sourceCode cpp">task</code>.</li>
<li>Votes against deal with associated executors and a request to have
strong language about transfer between threads.</li>
</ul>
<h2 data-number="2.2" id="p2506-stdlazy-a-coroutine-for-deferred-execution"><span class="header-section-number">2.2</span> <a href="https://wg21.link/P2506">P2506</a>: std::lazy: a coroutine for
deferred execution<a href="#p2506-stdlazy-a-coroutine-for-deferred-execution" class="self-link"></a></h2>
<p>This paper is effectively restating what <a href="https://wg21.link/P1056">P1056</a> said with the primary change
being more complete proposed wording. Although sender/receiver were
discussed when the paper was written but <a href="https://wg21.link/P2300"><code class="sourceCode cpp">std<span class="op">::</span>execution</code></a>
hadn’t made it into the working paper, the proposal did <em>not</em>
take a sender/receiver interface into account.</p>
<p>Although there were mails seemingly scheduling a discussion in LEWG,
we didn’t manage to actually locate any discussion notes.</p>
<h2 data-number="2.3" id="cppcoro"><span class="header-section-number">2.3</span> <a href="https://github.com/lewissbaker/cppcoro">cppcoro</a><a href="#cppcoro" class="self-link"></a></h2>
<p>This library contains multiple coroutine types, algorithms, and some
facilities for asynchronous work. For the purpose of this discussion
only the task types are of interest. There are two task types <a href="https://github.com/lewissbaker/cppcoro?tab=readme-ov-file#taskt"><code class="sourceCode cpp">cppcoro<span class="op">::</span>task</code></a>
and <a href="https://github.com/lewissbaker/cppcoro?tab=readme-ov-file#shared_taskt"><code class="sourceCode cpp">cppcoro<span class="op">::</span>shared_task</code></a>.
The key difference between <code class="sourceCode cpp">task</code> and
<code class="sourceCode cpp">shared_task</code> is that the latter can
be copied and awaited by multiple other coroutines. As a result
<code class="sourceCode cpp">shared_task</code> always produces an
lvalue and may have slightly higher costs due to the need to maintain a
reference count.</p>
<p>The types and algorithms are pre-sender/receiver and operate entirely
in terms for awaiters/awaitables. The interface of both task types is a
bit richer than that from <a href="https://wg21.link/P1056">P1056</a>/<a href="https://wg21.link/P2506">P2506</a>. Below
<code class="sourceCode cpp">t</code> is either a <code class="sourceCode cpp">cppcoro<span class="op">::</span>task<span class="op">&lt;</span>T<span class="op">&gt;</span></code>
or a <code class="sourceCode cpp">cppcoro<span class="op">::</span>shared_task<span class="op">&lt;</span>T<span class="op">&gt;</span></code>:</p>
<ul>
<li>The task objects can be move constructed and move assigned; <code class="sourceCode cpp">shared_task<span class="op">&lt;</span>T<span class="op">&gt;</span></code>
object can also be copy constructed and copy assigned.</li>
<li>Using <code class="sourceCode cpp">t<span class="op">.</span>is_ready<span class="op">()</span></code>
it can be queried if <code class="sourceCode cpp">t</code> has
completed.</li>
<li>Using
<code class="sourceCode cpp"><span class="kw">co_await</span> t</code>
awaits completion of <code class="sourceCode cpp">t</code>, yielding the
result. The result may be throwing an exception if the coroutine
completed by throwing.</li>
<li>Using <code class="sourceCode cpp"><span class="kw">co_await</span> t<span class="op">.</span>when_ready<span class="op">()</span></code>
allows synchronising with the completion of
<code class="sourceCode cpp">t</code> without actually getting the
result. This form of synchronisation won’t throw any exception.</li>
<li><code class="sourceCode cpp">cpproro<span class="op">::</span>shared_task<span class="op">&lt;</span>T<span class="op">&gt;</span></code>
also supports equality comparisons.</li>
</ul>
<p>In both cases, the task starts suspended and is resumed when it is
<code class="sourceCode cpp"><span class="kw">co_await</span></code>ed. This
way a continuation is known when the task is resumed, which is similar
to <code class="sourceCode cpp">start<span class="op">(</span>op<span class="op">)</span></code>ing
an operation state <code class="sourceCode cpp">op</code>. The coroutine
body needs to use
<code class="sourceCode cpp"><span class="kw">co_await</span></code> or
<code class="sourceCode cpp"><span class="kw">co_return</span></code>.
<code class="sourceCode cpp"><span class="kw">co_await</span></code>
expects an awaitable or an awaiter as argument. Using
<code class="sourceCode cpp"><span class="kw">co_yield</span></code> is
not supported. The implementation supports symmetric transfer but
doesn’t mention allocators.</p>
<p>The <code class="sourceCode cpp">shared_task<span class="op">&lt;</span>T<span class="op">&gt;</span></code>
is similar to <a href="https://eel.is/c++draft/exec.split"><code class="sourceCode cpp">split<span class="op">(</span>sender<span class="op">)</span></code></a>:
in both cases, the same result is produced for multiple consumers.
Correspondingly, there isn’t a need to support a separate <code class="sourceCode cpp">shared_task<span class="op">&lt;</span>T<span class="op">&gt;</span></code>
in a sender/receiver world. Likewise, throwing of results can be avoid
by suitably rewriting the result of the
<code class="sourceCode cpp">set_error</code> channel avoiding the need
for an operation akin to
<code class="sourceCode cpp">when_ready<span class="op">()</span></code>.</p>
<h2 data-number="2.4" id="libunifex"><span class="header-section-number">2.4</span> <a href="https://github.com/facebookexperimental/libunifex">libunifex</a><a href="#libunifex" class="self-link"></a></h2>
<p><code class="sourceCode cpp">unifex</code> is an earlier
implementation of the sender/receiver ideas. Compared to <code class="sourceCode cpp">std<span class="op">::</span>execution</code> it
is lacking some of the flexibilities. For example, it doesn’t have a
concept of environments or domains. However, the fundamental idea of
three completion channels for success, failure, and cancellation and the
general shape of how these are used is present (even using the same
names for <code class="sourceCode cpp">set_value</code> and
<code class="sourceCode cpp">set_error</code>; the equivalent of
<code class="sourceCode cpp">set_stopped</code> is called
<code class="sourceCode cpp">set_done</code>).
<code class="sourceCode cpp">unifex</code> is in production use in
multiple places. The implementation includes a <a href="https://github.com/facebookexperimental/libunifex/blob/main/include/unifex/task.hpp"><code class="sourceCode cpp">unifex<span class="op">::</span>task<span class="op">&lt;</span>T<span class="op">&gt;</span></code></a>.</p>
<p>As <code class="sourceCode cpp">unifex</code> is
sender/receiver-based, its <code class="sourceCode cpp">unifex<span class="op">::</span>task<span class="op">&lt;</span>T<span class="op">&gt;</span></code>
is implemented such that
<code class="sourceCode cpp"><span class="kw">co_await</span></code> can
deal with senders in addition to awaitables or awaiters. Also, <code class="sourceCode cpp">unifex<span class="op">::</span>task<span class="op">&lt;</span>T<span class="op">&gt;</span></code>
is <em>scheduler affine</em>: the coroutine code resumes on the same
scheduler even if a sender completed on a different scheduler. The
task’s scheduler is taken from the receiver it is
<code class="sourceCode cpp"><span class="fu">connect</span></code>ed
to. The exception for rescheduling on the task’s scheduler is explicitly
awaiting the result of <code class="sourceCode cpp">schedule<span class="op">(</span>sched<span class="op">)</span></code>
for some scheduler <code class="sourceCode cpp">sched</code>: the
operation changes the task’s scheduler to be
<code class="sourceCode cpp">sched</code>. The relevant treatment is in
the promise type’s <code class="sourceCode cpp">await_transform<span class="op">()</span></code>:</p>
<ul>
<li>If a sender <code class="sourceCode cpp">sndr</code> which is the
result of <code class="sourceCode cpp">schedule<span class="op">(</span>sched<span class="op">)</span></code>
is
<code class="sourceCode cpp"><span class="kw">co_await</span></code>ed,
the corresponding <code class="sourceCode cpp">sched</code> is installed
as the task’s scheduler and the task resumes on the context completing
<code class="sourceCode cpp">sndr</code>. Feedback from people working
with unifex suggests that this choice for changing the scheduler is too
subtle. While it is considered important to be able to explicitly change
the scheduler a task executes on, doing so should be more explicit.</li>
<li>For both senders and awaiters being awaited, the coroutine will be
resumed on the task’s current scheduler when the task is scheduler
affine. In general that is done by continuing with the senders result on
the task’s scheduler, similar to <code class="sourceCode cpp">continues_on<span class="op">(</span>sender,   scheduler<span class="op">)</span></code>.
The rescheduling is avoided when the sender is tagged as not changing
scheduler (using a <code class="sourceCode cpp"><span class="kw">static</span> <span class="kw">constexpr</span></code>
member named <code class="sourceCode cpp">blocking</code> which is
initialized to <code class="sourceCode cpp">blocking_kind<span class="op">::</span>always_inline</code>).</li>
<li>If a sender is
<code class="sourceCode cpp"><span class="kw">co_await</span></code>ed
it gets
<code class="sourceCode cpp"><span class="fu">connect</span></code>ed to
a receiver provided by the task to form an awaiter holding an operation
state. The operation state gets
<code class="sourceCode cpp">start</code>ed by the awaiter’s
<code class="sourceCode cpp">await_suspend</code>. The receiver arranges
for a <code class="sourceCode cpp">set_value</code> completion to become
a value returned from <code class="sourceCode cpp">await_resume</code>,
a <code class="sourceCode cpp">set_error</code> completion to become an
exception, and a <code class="sourceCode cpp">set_done</code> completion
to resume a special “on done” coroutine handle rather than resuming the
task itself effectively behaving like an uncatchable exception (all
relevant state is properly destroyed and the coroutine is never
resumed).</li>
</ul>
<p>When
<code class="sourceCode cpp"><span class="kw">co_await</span></code>ing
a sender <code class="sourceCode cpp">sndr</code> there can be at most
one <code class="sourceCode cpp">set_value</code> completion: if there
are more than one <code class="sourceCode cpp">set_value</code>
completions the promise type’s
<code class="sourceCode cpp">await_transform</code> will just return
<code class="sourceCode cpp">sndr</code> and the result cannot be
<code class="sourceCode cpp"><span class="kw">co_await</span></code>ed
(unless it is also given an awaitable interface). The result type of
<code class="sourceCode cpp"><span class="kw">co_await</span> sndr</code>
depends on the number of arguments to
<code class="sourceCode cpp">set_value</code>:</p>
<ul>
<li>If there are no arguments for
<code class="sourceCode cpp">set_value</code> then the type of <code class="sourceCode cpp"><span class="kw">co_await</span> sndr</code> will
be
<code class="sourceCode cpp"><span class="dt">void</span></code>.</li>
<li>If there is exactly one argument of type
<code class="sourceCode cpp">T</code> for
<code class="sourceCode cpp">set_value</code> then the type of <code class="sourceCode cpp"><span class="kw">co_await</span> sndr</code> will
be <code class="sourceCode cpp">T</code>.</li>
<li>If there are more than one arguments for
<code class="sourceCode cpp">set_value</code> then the type of <code class="sourceCode cpp"><span class="kw">co_await</span> sndr</code> will
be <code class="sourceCode cpp">std<span class="op">::</span>tuple<span class="op">&lt;</span>T1, T2, <span class="op">...&gt;</span></code>
with the corresponding argument types.</li>
</ul>
<p>If a receiver doesn’t have a scheduler, it can’t be <code class="sourceCode cpp"><span class="fu">connect</span><span class="op">()</span></code>ed
to a <code class="sourceCode cpp">unifex<span class="op">::</span>task<span class="op">&lt;</span>T<span class="op">&gt;</span></code>.
In particular, when using a <code class="sourceCode cpp">unifex<span class="op">::</span>async_scope scope</code>
it isn’t possible to directly call <code class="sourceCode cpp">scope<span class="op">.</span>spawn<span class="op">(</span>task<span class="op">)</span></code>
with a <code class="sourceCode cpp">unifex<span class="op">::</span>task<span class="op">&lt;</span>T<span class="op">&gt;</span> task</code>
as the <code class="sourceCode cpp">unifex<span class="op">::</span>async_scope</code>
doesn’t provide a scheduler. The <code class="sourceCode cpp">unifex<span class="op">::</span>async_scope</code>
provides a few variations of
<code class="sourceCode cpp">spawn<span class="op">()</span></code>
which take a scheduler as argument.</p>
<p><code class="sourceCode cpp">unifex</code> provides some sender
algorithms to transform the sender result into something which may be
more suitable to be
<code class="sourceCode cpp"><span class="kw">co_await</span></code>ed.
For example, <code class="sourceCode cpp">unifex<span class="op">::</span>done_as_optional<span class="op">(</span>sender<span class="op">)</span></code>
turns a successful completion for a type
<code class="sourceCode cpp">T</code> into an <code class="sourceCode cpp">std<span class="op">::</span>optional<span class="op">&lt;</span>T<span class="op">&gt;</span></code>
and the cancellation completion
<code class="sourceCode cpp">set_done</code> into a
<code class="sourceCode cpp">set_value</code> completion with a
disengaged <code class="sourceCode cpp">std<span class="op">::</span>optional<span class="op">&lt;</span>T<span class="op">&gt;</span></code>.</p>
<p>The <code class="sourceCode cpp">unifex<span class="op">::</span>task<span class="op">&lt;</span>T<span class="op">&gt;</span></code>
is itself a sender and can be used correspondingly. To deal with
scheduler affinity a type erased scheduler <code class="sourceCode cpp">unifex<span class="op">::</span>any_scheduler</code>
is used.</p>
<p>The <code class="sourceCode cpp">unifex<span class="op">::</span>task<span class="op">&lt;</span>T<span class="op">&gt;</span></code>
doesn’t have allocator support. When creating a task multiple objects
are allocated on the heap: it seems there is a total of 6 allocations
for each <code class="sourceCode cpp">unifex<span class="op">::</span>task<span class="op">&lt;</span>T<span class="op">&gt;</span></code>
being created. After that, it seems the different
<code class="sourceCode cpp"><span class="kw">co_await</span></code>s
don’t use a separate allocation.</p>
<p>The <code class="sourceCode cpp">unifex<span class="op">::</span>task<span class="op">&lt;</span>T<span class="op">&gt;</span></code>
doesn’t directly guard against stack overflow. Due to rescheduling
continuations on a scheduler when the completion isn’t always inline,
the issue only arises when
<code class="sourceCode cpp"><span class="kw">co_await</span></code>ing
many senders with <code class="sourceCode cpp">blocking_kind<span class="op">::</span>always_inline</code>
or when the scheduler resumes inline.</p>
<h2 data-number="2.5" id="stdexec"><span class="header-section-number">2.5</span> <a href="https://github.com/NVIDIA/stdexec">stdexec</a><a href="#stdexec" class="self-link"></a></h2>
<p>The <a href="https://github.com/NVIDIA/stdexec/blob/main/include/exec/task.hpp"><code class="sourceCode cpp">exec<span class="op">::</span>task</code></a>
in stdexec is somewhat similar to the unifex task with some choices
being different, though:</p>
<ul>
<li>The <code class="sourceCode cpp">exec<span class="op">::</span>task<span class="op">&lt;</span>T, C<span class="op">&gt;</span></code>
is also scheduler affine. The chosen scheduler is unconditionally used
for every
<code class="sourceCode cpp"><span class="kw">co_await</span></code>,
i.e., there is no attempt made to avoid scheduling, e.g., when the
<code class="sourceCode cpp"><span class="kw">co_await</span></code>ed
sender completes inline.</li>
<li>Unlike unifex, it is OK if the receiver’s environment doesn’t
provide a scheduler. In that case an inline scheduler is used. If an
inline scheduler is used there is the possibility of stack
overflow.</li>
<li>It is possible to <code class="sourceCode cpp"><span class="kw">co_await</span> just_error<span class="op">(</span>e<span class="op">)</span></code>
and <code class="sourceCode cpp"><span class="kw">co_await</span> just_stopped<span class="op">()</span></code>,
i.e., the sender isn’t required to have a
<code class="sourceCode cpp">set_value_t</code> completion.</li>
</ul>
<p>The <code class="sourceCode cpp">exec<span class="op">::</span>task<span class="op">&lt;</span>T, C<span class="op">&gt;</span></code>
also provides a <em>context</em> <code class="sourceCode cpp">C</code>.
An object of this type becomes the environment for receivers <code class="sourceCode cpp"><span class="fu">connect</span><span class="op">()</span></code>ed
to
<code class="sourceCode cpp"><span class="kw">co_await</span></code>ed
senders. The default context provides access to the task’s scheduler. In
addition an <code class="sourceCode cpp">in_place_stop_token</code> is
provides which forwards the stop requests from the environment of the
receiver which is connected to the task.</p>
<p>Like the unifex task <code class="sourceCode cpp">exec<span class="op">::</span>task<span class="op">&lt;</span>T, C<span class="op">&gt;</span></code>
doesn’t provide any allocator support. When creating a task there are
two allocations.</p>
<h1 data-number="3" id="objectives"><span class="header-section-number">3</span> Objectives<a href="#objectives" class="self-link"></a></h1>
<p>Also see <a href="https://github.com/cplusplus/sender-receiver/issues/241">sender/receiver
issue 241</a>.</p>
<p>Based on the prior work and discussions around corresponding
coroutine support there is a number of required or desired features
(listed in no particular order):</p>
<ol type="1">
<li><p>A coroutine task needs to be awaiter/awaitable friendly, i.e., it
should be possibly to
<code class="sourceCode cpp"><span class="kw">co_await</span></code>
awaitables which includes both library provided and user provided ones.
While that seems obvious, it is possible to create an
<code class="sourceCode cpp">await_transform</code> which is deleted for
awaiters and that should be prohibited.</p></li>
<li><p>When composing sender algorithms without using a coroutine it is
common to adapt the results using suitable algorithms and the
completions for sender algorithms are designed accordingly. On the other
hand, when awaiting senders in a coroutine it may be considered annoying
having to transform the result into a shape which is friendly to a
coroutine use. Thus, it may be reasonable to support rewriting certain
shapes of completion signatures into something different to make the use
of senders easier in a coroutine task. See the section on the <a href="#result-type-for-co_await">result type for
<code class="sourceCode cpp"><span class="kw">co_await</span></code></a>
for a discussion.</p></li>
<li><p>A coroutine task needs to be sender friendly: it is expected that
asynchronous code is often written using coroutines awaiting senders.
However, depending on how senders are treated by a coroutine some
senders may not be awaitable. For example neither unifex nor stdexec
support
<code class="sourceCode cpp"><span class="kw">co_await</span></code>ing
senders with more than one <code class="sourceCode cpp">set_value</code>
completion.</p></li>
<li><p>It is possibly confusing and problematic if coroutines resume on
a different execution context than the one they were suspended on: the
textual similarity to normal functions makes it look as if things are
executed sequentially. Experience also indicates that continuing a
coroutine on whatever context a
<code class="sourceCode cpp"><span class="kw">co_await</span></code>ed
operation completes frequently leads to issues. Senders could, however,
complete on an entirely different scheduler than where they started.
When composing senders (not using coroutines) changing contexts is
probably OK because it is done deliberately, e.g., using
<code class="sourceCode cpp">continues_on</code>, and the way to express
things is new with fewer attached expectations.</p>
<p>To bring these two views together a coroutine task should be
scheduler affine by default, i.e., it should normally resume on the same
scheduler. There should probably also be an explicit way to opt out of
scheduler affinity when the implications are well understood.</p>
<p>Note that scheduler affinity does <em>not</em> mean that a task is
always continuing on the same thread: a scheduler may refer to a thread
pool and the task will continue on one of the threads (which also means
that thread local storage cannot be used to propagate contexts
implicitly; see the <a href="#environment-support">discussion on
environments</a> below).</p></li>
<li><p>When using coroutines there will probably be an allocation at
least for the coroutine frame (the <a href="https://wg21.link/P0981R0">HALO optimisations</a> can’t always
work). To support the use in environments where memory allocations using
<code class="sourceCode cpp"><span class="kw">new</span></code>/<code class="sourceCode cpp"><span class="kw">delete</span></code>
aren’t supported the coroutine task should support allocations using
allocators.</p></li>
<li><p>Receivers have associated environments which can support an open
set of queries. Normally, queries on an environment can be forwarded to
the environment of a <code class="sourceCode cpp"><span class="fu">connect</span><span class="op">()</span></code>ed
receiver. Since the coroutine types are determined before the
coroutine’s receiver is known and the queries themselves don’t specify a
result type that isn’t possible when a coroutine provides a receiver to
a sender in a
<code class="sourceCode cpp"><span class="kw">co_await</span></code>
expression. It should still be possible to provide a user-customisable
environment from the receiver used by
<code class="sourceCode cpp"><span class="kw">co_await</span></code>
expressions. One aspect of this environment is to forward stop requests
to
<code class="sourceCode cpp"><span class="kw">co_await</span></code>ed
child operations. Another is possibly changing the scheduler to be used
when a child operation queries
<code class="sourceCode cpp">get_scheduler</code> from the receiver’s
environment. Also, in non-asynchronous code it is quite common to pass
some form of context implicitly using thread local storage. In an
asynchronous world such contexts could be forwarded using the
environment.</p></li>
<li><p>The coroutine should be able to indicate that it was canceled,
i.e., to get <code class="sourceCode cpp">set_stopped<span class="op">()</span></code>
called on the task’s receiver. <code class="sourceCode cpp">std<span class="op">::</span>execution<span class="op">::</span>with_awaitable_senders</code>
already provided this ability senders being
<code class="sourceCode cpp"><span class="kw">co_await</span></code>ed
but that doesn’t necessarily extend to the coroutine
implementation.</p></li>
<li><p>Similar to indicating that a task got canceled it would be good
if a task could indicate that an error occurred without throwing an
exception which escapes from the coroutine.</p></li>
<li><p>In general a task has to assume that an exception escapes the
coroutine implementation. As a result, the task’s completion signatures
need to include <code class="sourceCode cpp">set_error_t<span class="op">(</span>std<span class="op">::</span>exception_ptr<span class="op">)</span></code>.
If it can be indicated to the task that no exception will escape the
coroutine, this completion signature can be avoided.</p></li>
<li><p>When many
<code class="sourceCode cpp"><span class="kw">co_await</span></code>ed
operations complete synchronously, there is a chance for stack overflow.
It may be reasonable to have the implementation prevent stack overflow
by using a suitable scheduler sometimes.</p></li>
<li><p>In some situations it can be useful to somehow schedule an
asynchronous clean-up operation which is triggered upon coroutine exit.
See the section on <a href="#asynchronous-clean-up">asynchronous
clean-up</a> below for more discussing</p></li>
<li><p>The <code class="sourceCode cpp">task</code> coroutine provided
by the standard library may not always fit user’s needs although they
may need/want various of the facilities. To avoid having users implement
all functionality from scratch <code class="sourceCode cpp">task</code>
should use specified components which can be used by users when building
their own coroutine. The components
<code class="sourceCode cpp">as_awaitable</code> and
<code class="sourceCode cpp">with_awaitable_sender</code> are two parts
of achieving this objective but there are likely others.</p>
<p>The algorithm <code class="sourceCode cpp">std<span class="op">::</span>execution<span class="op">::</span>as_awaitable</code>
does turn a sender into an awaitable and is expected to be used by
custom written coroutines. Likewise, it is intended that custom
coroutines use the CRTP class template <code class="sourceCode cpp">std<span class="op">::</span>execution<span class="op">::</span>with_awaitable_senders</code>.
It may be reasonable to adjust the functionality of these components
instead of defining the functionality specific to a <code class="sourceCode cpp">task<span class="op">&lt;...&gt;</span></code>
coroutine task.</p></li>
</ol>
<p>It is important to note that different coroutine task implementations
can live side by side: not all functionality has to be implemented by
the same coroutine task. The objective for this proposal is to select a
set of features which provides a coroutine task suitable for most uses.
It may also be reasonable to provide some variations as different names.
A future revision of the standard or third party libraries can also
provide additional variations.</p>
<h1 data-number="4" id="design"><span class="header-section-number">4</span> Design<a href="#design" class="self-link"></a></h1>
<p>This section discusses various design options for achieving the
listed objectives. Most of the designs are independent of each other and
can be left out if the consensus is that it shouldn’t be used for
whatever reason.</p>
<h2 data-number="4.1" id="template-declaration-for-task"><span class="header-section-number">4.1</span> Template Declaration for
<code class="sourceCode cpp">task</code><a href="#template-declaration-for-task" class="self-link"></a></h2>
<p>Coroutines can use
<code class="sourceCode cpp"><span class="kw">co_return</span></code> to
produce a value. The value returned can reasonably provide the argument
for the <code class="sourceCode cpp">set_value_t</code> completion of
the coroutines. As the type of a coroutine is defined even before the
coroutine body is given, there is no way to deduce the result type. The
result type is probably the primary customisation and should be the
first template parameter which gets defaulted to
<code class="sourceCode cpp"><span class="dt">void</span></code> for
coroutines not producing any value. For example:</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="dt">int</span> main<span class="op">()</span> <span class="op">{</span></span>
<span id="cb2-2"><a href="#cb2-2" aria-hidden="true" tabindex="-1"></a>    ex<span class="op">::</span>sync_wait<span class="op">([]-&gt;</span>ex<span class="op">::</span>task<span class="op">&lt;&gt;{</span></span>
<span id="cb2-3"><a href="#cb2-3" aria-hidden="true" tabindex="-1"></a>        <span class="dt">int</span> result <span class="op">=</span> <span class="kw">co_await</span> <span class="op">[]-&gt;</span>ex<span class="op">::</span>task<span class="op">&lt;</span><span class="dt">int</span><span class="op">&gt;</span> <span class="op">{</span> <span class="kw">co_return</span> <span class="dv">42</span>; <span class="op">}()</span>;</span>
<span id="cb2-4"><a href="#cb2-4" aria-hidden="true" tabindex="-1"></a>        <span class="ot">assert</span><span class="op">(</span>result <span class="op">==</span> <span class="dv">42</span><span class="op">)</span>;</span>
<span id="cb2-5"><a href="#cb2-5" aria-hidden="true" tabindex="-1"></a>    <span class="op">}())</span>;</span>
<span id="cb2-6"><a href="#cb2-6" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
<p>The inner coroutines completes with <code class="sourceCode cpp">set_value_t<span class="op">(</span><span class="dt">int</span><span class="op">)</span></code>
which gets translated to the value returned from
<code class="sourceCode cpp"><span class="kw">co_await</span></code>
(see <a href="#result-type-for-co_await"><code class="sourceCode cpp"><span class="kw">co_await</span></code>
result type below</a> for more details). The outer coroutine completes
with <code class="sourceCode cpp">set_value_t<span class="op">()</span></code>.</p>
<p>Beyond the result type there are a number of features for a coroutine
task which benefit from customisation or for which it may be desirable
to disable them because they introduce a cost. As many template
parameters become unwieldy, it makes sense to combine these into a
[defaulted] context parameter. The aspects which benefit from
customisation are at least:</p>
<ul>
<li><a href="#environment-support">Customising the environment</a> for
child operations. The context itself can actually become part of the
environment.</li>
<li>Disable <a href="#scheduler-affinity">scheduler affinity</a> and/or
configure the strategy for obtaining the coroutine’s scheduler.</li>
<li>Configure <a href="#allocator-support">allocator awareness</a>.</li>
<li>Indicate that the coroutine should be <a href="#error-reporting">noexcept</a>.</li>
<li>Define <a href="#error-reporting">additional error types</a>.</li>
</ul>
<p>The default context should be used such that any empty type provides
the default behaviour instead of requiring a lot of boilerplate just to
configure a particular aspect. For example, it should be possible to
selectively enable <a href="#allocator-support">allocator support</a>
using something like this:</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><span class="kw">struct</span> allocator_aware_context <span class="op">{</span></span>
<span id="cb3-2"><a href="#cb3-2" aria-hidden="true" tabindex="-1"></a>    <span class="kw">using</span> allocator_type <span class="op">=</span> std<span class="op">::</span>pmr<span class="op">::</span>polymorphic_allocator<span class="op">&lt;</span>std<span class="op">::</span>byte<span class="op">&gt;</span>;</span>
<span id="cb3-3"><a href="#cb3-3" aria-hidden="true" tabindex="-1"></a><span class="op">}</span>;</span>
<span id="cb3-4"><a href="#cb3-4" aria-hidden="true" tabindex="-1"></a><span class="kw">template</span> <span class="op">&lt;</span><span class="kw">class</span> T<span class="op">&gt;</span></span>
<span id="cb3-5"><a href="#cb3-5" aria-hidden="true" tabindex="-1"></a><span class="kw">using</span> my_task <span class="op">=</span> ex<span class="op">::</span>task<span class="op">&lt;</span>T, allocator_aware_context<span class="op">&gt;</span>;</span></code></pre></div>
<p>Using various different types for task coroutines isn’t a problem as
the corresponding objects normally don’t show up in containers. Tasks
are mostly
<code class="sourceCode cpp"><span class="kw">co_await</span></code>ed
by other tasks, used as child senders when composing work graphs, or
maintained until completed using something like a <a href="https://wg21.link/p3149"><code class="sourceCode cpp">counting_scope</code></a>.
When they are used in a container, e.g., to process data using a range
of coroutines, they are likely to use the same result type and context
types for configurations.</p>
<h2 data-number="4.2" id="task-completion-signatures"><span class="header-section-number">4.2</span>
<code class="sourceCode cpp">task</code> Completion Signatures<a href="#task-completion-signatures" class="self-link"></a></h2>
<p>The discussion above established that <code class="sourceCode cpp">task<span class="op">&lt;</span>T, C<span class="op">&gt;</span></code>
can have a successful completion using <code class="sourceCode cpp">set_value_t<span class="op">(</span>T<span class="op">)</span></code>.
The coroutine completes accordingly when it is exited using a matching
<code class="sourceCode cpp"><span class="kw">co_return</span></code>.
When <code class="sourceCode cpp">T</code> is
<code class="sourceCode cpp"><span class="dt">void</span></code> the
coroutine also completes successfully using
<code class="sourceCode cpp">set_value<span class="op">()</span></code>
when floating off the end of the coroutine or when using a
<code class="sourceCode cpp"><span class="kw">co_return</span></code>
without an expression.</p>
<p>If a coroutine exits with an exception completing the corresponding
operation with <code class="sourceCode cpp">set_error<span class="op">(</span>std<span class="op">::</span>exception_ptr<span class="op">)</span></code>
is an obvious choice. Note that a
<code class="sourceCode cpp"><span class="kw">co_await</span></code>
expression results in throwing an exception when the awaited operation
completes with <code class="sourceCode cpp">set_error<span class="op">(</span>E<span class="op">)</span></code>
(<a href="#result-type-for-co_await">see below</a>), i.e., the coroutine
itself doesn’t necessarily need to
<code class="sourceCode cpp"><span class="cf">throw</span></code> an
exception itself.</p>
<p>Finally, a
<code class="sourceCode cpp"><span class="kw">co_await</span></code>
expression completing with <code class="sourceCode cpp">set_stoppped<span class="op">()</span></code>
results in aborting the coroutine immediately (<a href="#result-type-for-co_await">see below</a>) and causing the
coroutine itself to also complete with <code class="sourceCode cpp">set_stopped<span class="op">()</span></code>.</p>
<p>The coroutine implementation cannot inspect the coroutine body to
determine how the different asynchronous operations may complete. As a
result, the default completion signatures for <code class="sourceCode cpp">task<span class="op">&lt;</span>T<span class="op">&gt;</span></code>
are</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>ex<span class="op">::</span>completion_signatures<span class="op">&lt;</span></span>
<span id="cb4-2"><a href="#cb4-2" aria-hidden="true" tabindex="-1"></a>    ex<span class="op">::</span>set_value_t<span class="op">(</span>T<span class="op">)</span>,  <span class="co">// or ex::set_value_t() if T == void</span></span>
<span id="cb4-3"><a href="#cb4-3" aria-hidden="true" tabindex="-1"></a>    ex<span class="op">::</span>set_error_t<span class="op">(</span>std<span class="op">::</span>exception_ptr<span class="op">)</span>,</span>
<span id="cb4-4"><a href="#cb4-4" aria-hidden="true" tabindex="-1"></a>    ex<span class="op">:</span>set_stopped_t<span class="op">()</span></span>
<span id="cb4-5"><a href="#cb4-5" aria-hidden="true" tabindex="-1"></a><span class="op">&gt;</span>;</span></code></pre></div>
<p>Support for <a href="#error-reporting">reporting an error without
exception</a> may modify the completion signatures.</p>
<h2 data-number="4.3" id="task-constructors-and-assignments"><span class="header-section-number">4.3</span>
<code class="sourceCode cpp">task</code> constructors and assignments<a href="#task-constructors-and-assignments" class="self-link"></a></h2>
<p>Coroutines are created via a factory function which returns the
coroutine type and whose body uses one of the
<code class="sourceCode cpp">co_<span class="op">*</span></code>
function, e.g.</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>task<span class="op">&lt;&gt;</span> nothing<span class="op">(){</span> <span class="kw">co_return</span>; <span class="op">}</span></span></code></pre></div>
<p>The actual object is created via the promise type’s
<code class="sourceCode cpp">get_return_object</code> function and it is
between the promise and coroutine types how that actually works: this
constructor is an implementation detail. To be valid senders the
coroutine type needs to be destructible and it needs to have a move
constructor. Other than that, constructors and assignments either don’t
make sense or enable dangerous practices:</p>
<ol type="1">
<li><p>Copy constructor and copy assignment don’t make sense because
there is no way to copy the actual coroutine state.</p></li>
<li><p>Move assignment is rather questionable because it makes it easy
to transport the coroutine away from referenced entities.</p>
<p>Previous papers <a href="https://wg21.link/p1056">P1056</a> and <a href="https://wg21.link/p2506">P2506</a> also argued against a move
assignment. However, one of the arguments doesn’t apply to the
<code class="sourceCode cpp">task</code> proposed here: There is no need
to deal with cancellation when assigning or destroying a
<code class="sourceCode cpp">task</code> object. Upon
<code class="sourceCode cpp">start<span class="op">()</span></code> of
<code class="sourceCode cpp">task</code> the coroutine handle is
transferred to an operation state and the original coroutine object
doesn’t have any reference to the object anymore.</p></li>
<li><p>If there is no assignment, a default constructed object doesn’t
make much sense, i.e., <code class="sourceCode cpp">task</code> also
doesn’t have a default constructor.</p></li>
</ol>
<p>Based on experience with <a href="https://github.com/facebook/folly">Folly</a> the suggestion was
even stronger: <code class="sourceCode cpp">task</code> shouldn’t even
have move construction! That would mean that
<code class="sourceCode cpp">task</code> can’t be a sender or that there
would need to be some internal interface enabling the necessary
transfer. That direction isn’t pursued by this proposal.</p>
<p>The lack of move assignment doesn’t mean that
<code class="sourceCode cpp">task</code> can’t be held in a container:
it is perfectly fine to <code class="sourceCode cpp">push_back</code>
objects of this type into a container, e.g.:</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>std<span class="op">::</span>vector<span class="op">&lt;</span>ex<span class="op">::</span>task<span class="op">&lt;&gt;&gt;</span> cont;</span>
<span id="cb6-2"><a href="#cb6-2" aria-hidden="true" tabindex="-1"></a>cont<span class="op">.</span>emplace_back<span class="op">([]-&gt;</span>ex<span class="op">::</span>task<span class="op">&lt;&gt;</span> <span class="op">{</span> <span class="kw">co_return</span>; <span class="op">}())</span>;</span>
<span id="cb6-3"><a href="#cb6-3" aria-hidden="true" tabindex="-1"></a>cont<span class="op">.</span>push_back<span class="op">([]-&gt;</span>ex<span class="op">::</span>task<span class="op">&lt;&gt;</span> <span class="op">{</span> <span class="kw">co_return</span>; <span class="op">}())</span>;</span></code></pre></div>
<p>The expectation is that most of the time coroutines don’t end up in
normal containers. Instead, they’d be managed by a <a href="https://wg21.link/p3149"><code class="sourceCode cpp">counting_scope</code></a>
or hold on to by objects in a work graph composed of senders.</p>
<p>Technically there isn’t a problem adding a default constructor, move
assignment, and a
<code class="sourceCode cpp">swap<span class="op">()</span></code>
function. Based on experience with similar components it seems
<code class="sourceCode cpp">task</code> is better off not having
them.</p>
<h2 data-number="4.4" id="result-type-for-co_await"><span class="header-section-number">4.4</span> Result Type For
<code class="sourceCode cpp"><span class="kw">co_await</span></code><a href="#result-type-for-co_await" class="self-link"></a></h2>
<p>When
<code class="sourceCode cpp"><span class="kw">co_await</span></code>ing
a sender <code class="sourceCode cpp">sndr</code> in a coroutine,
<code class="sourceCode cpp">sndr</code> needs to be transformed to an
awaitable. The existing approach is to use <code class="sourceCode cpp">execution<span class="op">::</span>as_waitable<span class="op">(</span>sndr<span class="op">)</span></code>
<a href="https://eel.is/c++draft/exec.as.awaitable">[exex.as.awaitable]</a>
in the promise type’s
<code class="sourceCode cpp">await_transform</code> and
<code class="sourceCode cpp">task</code> uses that approach. The
awaitable returned from <code class="sourceCode cpp">as_awaitable<span class="op">(</span>sndr<span class="op">)</span></code>
has the following behaviour (<code class="sourceCode cpp">rcvr</code> is
the receiver the sender <code class="sourceCode cpp">sndr</code> is
connected to):</p>
<ol type="1">
<li><p>When <code class="sourceCode cpp">sndr</code> completes with
<code class="sourceCode cpp">set_stopped<span class="op">(</span>std<span class="op">::</span>move<span class="op">(</span>rcvr<span class="op">))</span></code>
the function <code class="sourceCode cpp">unhandled_stopped<span class="op">()</span></code>
on the promise type is called and the awaiting coroutine is never
resumed. The <code class="sourceCode cpp">unhandled_stopped<span class="op">()</span></code>
results in <code class="sourceCode cpp">task</code> itself also
completing with <code class="sourceCode cpp">set_stopped_t<span class="op">()</span></code>.</p></li>
<li><p>When <code class="sourceCode cpp">sndr</code> completes with
<code class="sourceCode cpp">set_error<span class="op">(</span>std<span class="op">::</span>move<span class="op">(</span>rcvr<span class="op">)</span>, error<span class="op">)</span></code>
the coroutine is resumed and the <code class="sourceCode cpp"><span class="kw">co_await</span> sndr</code>
expression results in <code class="sourceCode cpp">error</code> being
thrown as an exceptions (with special treatment for <code class="sourceCode cpp">std<span class="op">::</span>error_code</code>).</p></li>
<li><p>When <code class="sourceCode cpp">sndr</code> completes with
<code class="sourceCode cpp">set_value<span class="op">(</span>std<span class="op">::</span>move<span class="op">(</span>rcvr<span class="op">)</span>, a<span class="op">...)</span></code>
the expression <code class="sourceCode cpp"><span class="kw">co_await</span> sndr</code>
produces a result corresponding the arguments to
<code class="sourceCode cpp">set_value</code>:</p>
<ol type="1">
<li>If the argument list is empty, the result of <code class="sourceCode cpp"><span class="kw">co_await</span> sndr</code> is
<code class="sourceCode cpp"><span class="dt">void</span></code>.</li>
<li>Otherwise, if the argument list contains exactly one element the
result of <code class="sourceCode cpp"><span class="kw">co_await</span> sndr</code> is
<code class="sourceCode cpp">a<span class="op">...</span></code>.</li>
<li>Otherwise, the result of <code class="sourceCode cpp"><span class="kw">co_await</span> sndr</code> is
<code class="sourceCode cpp">std<span class="op">::</span>tuple<span class="op">(</span>a<span class="op">...)</span></code>.</li>
</ol></li>
</ol>
<p>Note that the sender <code class="sourceCode cpp">sndr</code> is
allowed to have no <code class="sourceCode cpp">set_value_t</code>
completion signatures. In this case the result type of the awaitable
returned from <code class="sourceCode cpp">as_awaitable<span class="op">(</span>sndr<span class="op">)</span></code>
is declared to be
<code class="sourceCode cpp"><span class="dt">void</span></code> but
<code class="sourceCode cpp"><span class="kw">co_await</span> sndr</code>
would never return normally: the only ways to complete without a
<code class="sourceCode cpp">set_value_t</code> completion is to
complete with <code class="sourceCode cpp">set_stopped<span class="op">(</span>std<span class="op">::</span>move<span class="op">(</span>rcvr<span class="op">)</span></code>
or with <code class="sourceCode cpp">set_error<span class="op">(</span>std<span class="op">::</span>move<span class="op">(</span>rcvr<span class="op">)</span>, error<span class="op">)</span></code>,
i.e., the expression either results in the coroutine to be never resumed
or an exception being thrown.</p>
<p>Here is an example which summarises the different supported result
types:</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>task<span class="op">&lt;&gt;</span> fun<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>just<span class="op">()</span>;                               <span class="co">// void</span></span>
<span id="cb7-3"><a href="#cb7-3" aria-hidden="true" tabindex="-1"></a>    <span class="kw">auto</span> v <span class="op">=</span> <span class="kw">co_await</span> ex<span class="op">::</span>just<span class="op">(</span><span class="dv">0</span><span class="op">)</span>;                     <span class="co">// int</span></span>
<span id="cb7-4"><a href="#cb7-4" aria-hidden="true" tabindex="-1"></a>    <span class="kw">auto</span><span class="op">[</span>i, b, c<span class="op">]</span> <span class="op">=</span> <span class="kw">co_await</span> ex<span class="op">::</span>just<span class="op">(</span><span class="dv">0</span>, <span class="kw">true</span>, <span class="ch">&#39;c&#39;</span><span class="op">)</span>;   <span class="co">// tuple&lt;int, bool, char&gt;</span></span>
<span id="cb7-5"><a href="#cb7-5" aria-hidden="true" tabindex="-1"></a>    <span class="cf">try</span> <span class="op">{</span> <span class="kw">co_await</span> ex<span class="op">::</span>just_error<span class="op">(</span><span class="dv">0</span><span class="op">)</span>; <span class="op">}</span> <span class="cf">catch</span> <span class="op">(</span><span class="dt">int</span><span class="op">)</span> <span class="op">{}</span> <span class="co">// exception</span></span>
<span id="cb7-6"><a href="#cb7-6" aria-hidden="true" tabindex="-1"></a>    <span class="kw">co_await</span> ex<span class="op">::</span>just_stopped<span class="op">()</span>;                       <span class="co">// cancel: never resumed</span></span>
<span id="cb7-7"><a href="#cb7-7" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
<p>The sender <code class="sourceCode cpp">sndr</code> can have at most
one <code class="sourceCode cpp">set_value_t</code> completion
signature: if there are more than one
<code class="sourceCode cpp">set_value_t</code> completion signatures
<code class="sourceCode cpp">as_awaitable<span class="op">(</span>sndr<span class="op">)</span></code>
is invalid and fails to compile: users who want to
<code class="sourceCode cpp"><span class="kw">co_await</span></code> a
sender with more than one
<code class="sourceCode cpp">set_value_t</code> completions need to use
<code class="sourceCode cpp"><span class="kw">co_await</span> into_variant<span class="op">(</span>s<span class="op">)</span></code>
(or similar) to transform the completion signatures appropriately. It
would be possible to move this transformation into <code class="sourceCode cpp">as_awaitable<span class="op">(</span>sndr<span class="op">)</span></code>.</p>
<p>Using effectively <code class="sourceCode cpp">into_variant<span class="op">(</span>s<span class="op">)</span></code>
isn’t the only possible transformation if there are multiple
<code class="sourceCode cpp">set_value_t</code> transformations. To
avoid creating a fairly hard to use result object, <code class="sourceCode cpp">as_awaitable<span class="op">(</span>sndr<span class="op">)</span></code>
could detect certain usage patterns and rather create a result which is
easier to use when being
<code class="sourceCode cpp"><span class="kw">co_await</span></code>ed. An
example for this situation is the <code class="sourceCode cpp">queue<span class="op">.</span>async_pop<span class="op">()</span></code>
operation for <a href="https://wg21.link/P0260">concurrent queues</a>:
this operation can complete successfully in two ways:</p>
<ol type="1">
<li>When an object was extracted the operation completes with <code class="sourceCode cpp">set_value<span class="op">(</span>std<span class="op">::</span>move<span class="op">(</span>rcvr<span class="op">)</span>, value<span class="op">)</span></code>.</li>
<li>When the queue was closed the operation completes with <code class="sourceCode cpp">set_value<span class="op">(</span>std<span class="op">::</span>move<span class="op">(</span>rcvr<span class="op">))</span></code>.</li>
</ol>
<p>Turning the result of <code class="sourceCode cpp">queue<span class="op">.</span>async_pop<span class="op">()</span></code>
into an awaitable using the current <code class="sourceCode cpp">as_awaitable<span class="op">(</span>queue<span class="op">.</span>async_pop<span class="op">())</span></code>
([<a href="https://eel.is/c++draft/exec#as.awaitable">exec.as.awaitable</a>])
fails because the function accepts only senders with at most one
<code class="sourceCode cpp">set_value_t</code> completion. Thus, it is
necessary to use something like the below:</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>task<span class="op">&lt;&gt;</span> pop_demo<span class="op">(</span><span class="kw">auto</span><span class="op">&amp;</span> queue<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="co">// auto value = co_await queue.async_pop(); // doesn&#39;t work</span></span>
<span id="cb8-3"><a href="#cb8-3" aria-hidden="true" tabindex="-1"></a>    std<span class="op">::</span>optional v0 <span class="op">=</span> <span class="kw">co_await</span> <span class="op">(</span>queue<span class="op">.</span>async_pop<span class="op">()</span> <span class="op">|</span> into_optional<span class="op">)</span>;</span>
<span id="cb8-4"><a href="#cb8-4" aria-hidden="true" tabindex="-1"></a>    std<span class="op">::</span>optional v1 <span class="op">=</span> <span class="kw">co_await</span> into_optional<span class="op">(</span>queue<span class="op">.</span>async_pop<span class="op">())</span>;</span>
<span id="cb8-5"><a href="#cb8-5" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
<p>The algorithm <code class="sourceCode cpp">into_optional<span class="op">(</span>sndr<span class="op">)</span></code>
would determine that there is exactly one
<code class="sourceCode cpp">set_value_t</code> completion with
arguments and produce an <code class="sourceCode cpp">std<span class="op">::</span>optional<span class="op">&lt;</span>T<span class="op">&gt;</span></code>
if there is just one parameter of type
<code class="sourceCode cpp">T</code> and produce a <code class="sourceCode cpp">std<span class="op">::</span>optional<span class="op">&lt;</span>std<span class="op">::</span>tuple<span class="op">&lt;</span>T<span class="op">...&gt;&gt;</span></code>
if there are more than one parameter with types
<code class="sourceCode cpp">T<span class="op">...</span></code>. It
would be possible to apply this transformation when a corresponding set
of completions is detected. The proposal <a href="https://wg21.link/P3570">optional variants in sender/receiver</a>
goes into this direction.</p>
<p>This proposal currently doesn’t propose a change to
<code class="sourceCode cpp">as_awaitable</code> ([<a href="https://eel.is/c++draft/exec#as.awaitable">exec.as.awaitable</a>]).
The primary reason is that there are likely many different shapes of
completions each with a different desirable transformation. If these are
all absorbed into <code class="sourceCode cpp">as_awaitable</code> it is
likely fairly hard to reason what exact result is returned. Also, there
are likely different options of how a result could be transformed:
<code class="sourceCode cpp">into_optional</code> is just one example.
It could be preferable to turn the two results into an <code class="sourceCode cpp">std<span class="op">::</span>expected</code>
instead. However, there should probably be some transformation
algorithms like <code class="sourceCode cpp">into_optional</code>,
<code class="sourceCode cpp">into_expected</code>, etc. similar to
<code class="sourceCode cpp">into_variant</code>.</p>
<h2 data-number="4.5" id="scheduler-affinity"><span class="header-section-number">4.5</span> Scheduler Affinity<a href="#scheduler-affinity" class="self-link"></a></h2>
<p>Coroutines look very similar to synchronous code with a few
<code class="sourceCode cpp">co</code>-keywords sprinkled over the code.
When reading such code the expectation is typically that all code
executes on the same context despite some
<code class="sourceCode cpp"><span class="kw">co_await</span></code>
expressions using senders which may explicitly change the scheduler.
There are various issues when using
<code class="sourceCode cpp"><span class="kw">co_await</span></code>
naïvely:</p>
<ul>
<li>Users may expect that work continues on the same context where it
was started. If the coroutine simply resumes when the
<code class="sourceCode cpp"><span class="kw">co_await</span></code>ed
senders calls a completion function code may execute some lengthy
operation on a context which is expected to keep a UI responsive or
which is meant to deal with I/O.</li>
<li>Conversely, running a loop
<code class="sourceCode cpp"><span class="kw">co_await</span></code>ing
some work may be seen as unproblematic but may actually easily cause a
stack overflow if
<code class="sourceCode cpp"><span class="kw">co_await</span></code>ed
work immediately completes (also <a href="#avoiding-stack-overflow">see
below</a>).</li>
<li>When
<code class="sourceCode cpp"><span class="kw">co_await</span></code>ing
some work completes on a different context and later a blocking call is
made from the coroutine which also ends up
<code class="sourceCode cpp"><span class="kw">co_await</span></code>ing
some work from the same resource there can be a dead lock.</li>
</ul>
<p>Thus, the execution should normally be scheduled on the original
scheduler: doing so can avoid the problems mentioned above (assuming a
scheduler is used which doesn’t immediately complete without actually
scheduling anything). This transfer of the execution with a coroutine is
referred to as <em>scheduler affinity</em>. Note: a scheduler may
execute on multiple threads, e.g., for a pool scheduler: execution would
get to any of these threads, i.e., thread local storage is <em>not</em>
guaranteed to access the same data even with scheduler affinity. Also,
scheduling work has some cost even if this cost can often be fairly
small.</p>
<p>The basic idea for scheduler affinity consists of a few parts:</p>
<ol type="1">
<li><p>A scheduler is determined when
<code class="sourceCode cpp">start</code>ing an operation state which
resulted from
<code class="sourceCode cpp"><span class="fu">connect</span></code>ing a
coroutine to a receiver. This scheduler is used to resume execution of
the coroutine. The scheduler is determined based on the receiver
<code class="sourceCode cpp">rcvr</code>’s environment.</p>
<div class="sourceCode" id="cb9"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb9-1"><a href="#cb9-1" aria-hidden="true" tabindex="-1"></a><span class="kw">auto</span> scheduler <span class="op">=</span> get_scheduler<span class="op">(</span>get_env<span class="op">(</span>rcvr<span class="op">))</span>;</span></code></pre></div></li>
<li><p>The type of <code class="sourceCode cpp">scheduler</code> is
unknown when the coroutine is created. Thus, the coroutine
implementation needs to operate in terms of a scheduler with a known
type which can be constructed from
<code class="sourceCode cpp">scheduler</code>. The used scheduler type
is determined based on the context parameter
<code class="sourceCode cpp">C</code> of the coroutine type <code class="sourceCode cpp">task<span class="op">&lt;</span>T, C<span class="op">&gt;</span></code>
using <code class="sourceCode cpp"><span class="kw">typename</span> C<span class="op">::</span>scheduler_type</code>
and defaults to <code class="sourceCode cpp">task_scheduler</code> if
this type isn’t defined.
<code class="sourceCode cpp">task_scheduler</code> uses type-erasure to
deal with arbitrary schedulers (and small object optimisations to avoid
allocations). The used scheduler type can be parameterised to allow use
of <code class="sourceCode cpp">task</code> contexts where the scheduler
type is known, e.g., to avoid the costs of type erasure.</p>
<p>Originally <code class="sourceCode cpp">task_scheduler</code> was
called <code class="sourceCode cpp">any_scheduler</code> but there was
feedback from SG1 suggesting that a general
<code class="sourceCode cpp">any_scheduler</code> may need to cover
various additional properties. To avoid dealing with generalizing the
facility a different name is used. The name remains specified as it is
still a useful component, at least until an
<code class="sourceCode cpp">any_scheduler</code> is defined by the
standard library. If necessary, the type erased scheduler type used by
<code class="sourceCode cpp">task</code> can be unspecified.</p></li>
<li><p>When an operation which is
<code class="sourceCode cpp"><span class="kw">co_await</span></code>ed
completes the execution is transferred to the held scheduler using
<code class="sourceCode cpp">continues_on</code>. Injecting this
operation into the graph can be done in the promise type’s
<code class="sourceCode cpp">await_transform</code>:</p>
<div class="sourceCode" id="cb10"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb10-1"><a href="#cb10-1" aria-hidden="true" tabindex="-1"></a><span class="kw">template</span> <span class="op">&lt;</span>ex<span class="op">::</span>sender Sender<span class="op">&gt;</span></span>
<span id="cb10-2"><a href="#cb10-2" aria-hidden="true" tabindex="-1"></a><span class="kw">auto</span> await_transform<span class="op">(</span>Sender<span class="op">&amp;&amp;</span> sndr<span class="op">)</span> <span class="kw">noexcept</span> <span class="op">{</span></span>
<span id="cb10-3"><a href="#cb10-3" aria-hidden="true" tabindex="-1"></a>    <span class="cf">return</span> ex<span class="op">::</span>as_awaitable_sender<span class="op">(</span></span>
<span id="cb10-4"><a href="#cb10-4" aria-hidden="true" tabindex="-1"></a>        ex<span class="op">::</span>continues_on<span class="op">(</span>std<span class="op">::</span>forward<span class="op">&lt;</span>Sender<span class="op">&gt;(</span>sndr<span class="op">)</span>,</span>
<span id="cb10-5"><a href="#cb10-5" aria-hidden="true" tabindex="-1"></a>                         <span class="kw">this</span><span class="op">-&gt;</span>scheduler<span class="op">)</span>;</span>
<span id="cb10-6"><a href="#cb10-6" aria-hidden="true" tabindex="-1"></a>    <span class="op">)</span>;</span>
<span id="cb10-7"><a href="#cb10-7" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div></li>
</ol>
<p>There are a few immediate issues with the basic idea:</p>
<ol type="1">
<li>What should happen if there is no scheduler, i.e., <code class="sourceCode cpp">get_scheduler<span class="op">(</span>get_env<span class="op">(</span>rcvr<span class="op">))</span></code>
doesn’t exist?</li>
<li>What should happen if the obtained
<code class="sourceCode cpp">scheduler</code> is incompatible with the
coroutine’s scheduler?</li>
<li>Scheduling isn’t free and despite the potential problems it should
be possible to use <code class="sourceCode cpp">task</code> without
scheduler affinity.</li>
<li>When operations are known to complete inline the scheduler isn’t
actually changed and the scheduling operation should be avoided.</li>
<li>It should be possible to explicitly change the scheduler used by a
coroutine from within this coroutine.</li>
</ol>
<p>All of these issues can be addressed although there are different
choices in some of these cases.</p>
<p>In many cases the receiver can provide access to a scheduler via the
environment query. An example where no scheduler is available is when
starting a task on a <a href="https://wg21.link/p3149"><code class="sourceCode cpp">counting_scope</code></a>.
The scope doesn’t know about any schedulers and, thus, the receiver used
by <code class="sourceCode cpp">counting_scope</code> when
<code class="sourceCode cpp"><span class="fu">connect</span></code>ing
to a sender doesn’t support the
<code class="sourceCode cpp">get_scheduler</code> query, i.e., this
example doesn’t work:</p>
<div class="sourceCode" id="cb11"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb11-1"><a href="#cb11-1" aria-hidden="true" tabindex="-1"></a>ex<span class="op">::</span>spawn<span class="op">([]-&gt;</span>ex<span class="op">::</span>task<span class="op">&lt;</span><span class="dt">void</span><span class="op">&gt;</span> <span class="op">{</span> <span class="kw">co_await</span> ex<span class="op">::</span>just<span class="op">()</span>; <span class="op">}()</span>, token<span class="op">)</span>;</span></code></pre></div>
<p>Using
<code class="sourceCode cpp">spawn<span class="op">()</span></code> with
coroutines doing the actual work is expected to be quite common, i.e.,
it isn’t just a theoretical possibility that
<code class="sourceCode cpp">task</code> is used together with
<code class="sourceCode cpp">counting_scope</code>. The approach used by
<a href="https://github.com/facebookexperimental/libunifex"><code class="sourceCode cpp">unifex</code></a>
is to fail compilation when trying to
<code class="sourceCode cpp"><span class="fu">connect</span></code> a
<code class="sourceCode cpp">Task</code> to a receiver without a
scheduler. The approach taken by <a href="https://github.com/NVIDIA/stdexec"><code class="sourceCode cpp">stdexec</code></a>
is to keep executing inline in that case. Based on the experience that
silently changing contexts within a coroutine frequently causes bugs it
seems failing to compile is preferable.</p>
<p>Failing to construct the scheduler used by a coroutine with the
<code class="sourceCode cpp">scheduler</code> obtained from the receiver
is likely an error and should be addressed by the user appropriately.
Failing to compile is seems to be a reasonable approach in that case,
too.</p>
<p>It should be possible to avoid scheduler affinity explicitly to avoid
the cost of scheduling. Users should be very careful when pursuing this
direction but it can be a valid option. One way to achieve that is to
create an “inline scheduler” which immediately completes when it is
<code class="sourceCode cpp">start<span class="op">()</span></code>ed
and using this type for the coroutine. Explicitly providing a type
<code class="sourceCode cpp">inline_scheduler</code> implementing this
logic could allow creating suitable warnings. It would also allow
detecting that type in
<code class="sourceCode cpp">await_transform</code> and avoiding the use
of <code class="sourceCode cpp">continues_on</code> entirely.</p>
<p>When operations actually don’t change the scheduler there shouldn’t
be a need to schedule them again. In these cases it would be great if
the <code class="sourceCode cpp">continues_on</code> could be avoided.
At the moment there is no way to tell whether a sender will complete
inline. Using a sender query which determines whether a sender always
completes inline could avoid the rescheduling. Something like that is
implemented for <a href="https://github.com/facebookexperimental/libunifex"><code class="sourceCode cpp">unifex</code></a>:
senders define a property <code class="sourceCode cpp">blocking</code>
which can have the value <code class="sourceCode cpp">blocking_kind<span class="op">::</span>always_inline</code>.
The proposal <a href="https://wg21.link/P3206">A sender query for
completion behaviour</a> proposes a <code class="sourceCode cpp">get_completion_behaviour<span class="op">(</span>sndr, env<span class="op">)</span></code>
customisation point to address this need. The result can indicate that
the <code class="sourceCode cpp">sndr</code> returns synchronously
(using <code class="sourceCode cpp">completion_behaviour<span class="op">::</span>synchronous</code>
or <code class="sourceCode cpp">completion_behaviour<span class="op">::</span>inline_completion</code>).
If <code class="sourceCode cpp">sndr</code> returns synchronously there
isn’t a need to reschedule it.</p>
<p>In some situations it is desirable to explicitly switch to a
different scheduler from within the coroutine and from then on carry on
using this scheduler. <a href="https://github.com/facebookexperimental/libunifex"><code class="sourceCode cpp">unifex</code></a>
detects the use of <code class="sourceCode cpp"><span class="kw">co_await</span> schedule<span class="op">(</span>scheduler<span class="op">)</span>;</code>
for this purpose. That is, however, somewhat subtle. It may be
reasonable to use a dedicated awaiter for this purpose and use, e.g.</p>
<div class="sourceCode" id="cb12"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb12-1"><a href="#cb12-1" aria-hidden="true" tabindex="-1"></a><span class="kw">auto</span> previous <span class="op">=</span> <span class="kw">co_await</span> co_continue_on<span class="op">(</span>new_scheduler<span class="op">)</span>;</span></code></pre></div>
<p>Using this statement replaces the coroutine’s scheduler with the
<code class="sourceCode cpp">new_scheduler</code>. When the
<code class="sourceCode cpp"><span class="kw">co_await</span></code>
completes it is on <code class="sourceCode cpp">new_scheduler</code> and
further
<code class="sourceCode cpp"><span class="kw">co_await</span></code>
operations complete on
<code class="sourceCode cpp">new_scheduler</code>. The result of
<code class="sourceCode cpp"><span class="kw">co_await</span></code>ing
<code class="sourceCode cpp">co_continue_on</code> is the previously
used scheduler to allow transfer back to this scheduler. In <a href="https://github.com/NVIDIA/stdexec">stdexec</a> the corresponding
operation is called
<code class="sourceCode cpp">reschedule_coroutine</code>.</p>
<p>Another advantage of scheduling the operations on a scheduler instead
of immediately continuing on the context where the operation completed
is that it helps with stack overflows: when scheduling on a non-inline
scheduler the call stack is unwound. Without that it may be necessary to
inject scheduling just for the purpose of avoiding stack overflow when
too many operations complete inline.</p>
<h2 data-number="4.6" id="allocator-support"><span class="header-section-number">4.6</span> Allocator Support<a href="#allocator-support" class="self-link"></a></h2>
<p>When using coroutines at least the coroutine frame may end up being
allocated on the heap: the <a href="https://wg21.link/P0981">HALO</a>
optimisations aren’t always possible, e.g., when a coroutine becomes a
child of another sender. To control how this allocation is done and to
support environments where allocations aren’t possible
<code class="sourceCode cpp">task</code> should have allocator support.
The idea is to pick up on a pair of arguments of type <code class="sourceCode cpp">std<span class="op">::</span>allocator_arg_t</code>
and an allocator type being passed and use the corresponding allocator
if present. For example:</p>
<div class="sourceCode" id="cb13"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb13-1"><a href="#cb13-1" aria-hidden="true" tabindex="-1"></a><span class="kw">struct</span> allocator_aware_context <span class="op">{</span></span>
<span id="cb13-2"><a href="#cb13-2" aria-hidden="true" tabindex="-1"></a>    <span class="kw">using</span> allocator_type <span class="op">=</span> std<span class="op">::</span>pmr<span class="op">::</span>polymorphic_allocator<span class="op">&lt;</span>std<span class="op">::</span>byte<span class="op">&gt;</span>;</span>
<span id="cb13-3"><a href="#cb13-3" aria-hidden="true" tabindex="-1"></a><span class="op">}</span>;</span>
<span id="cb13-4"><a href="#cb13-4" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb13-5"><a href="#cb13-5" aria-hidden="true" tabindex="-1"></a><span class="kw">template</span> <span class="op">&lt;</span><span class="kw">class</span><span class="op">...</span>A<span class="op">&gt;</span></span>
<span id="cb13-6"><a href="#cb13-6" aria-hidden="true" tabindex="-1"></a>ex<span class="op">::</span>task<span class="op">&lt;</span><span class="dt">int</span>, allocator_aware_context<span class="op">&gt;</span> fun<span class="op">(</span><span class="dt">int</span> value, A<span class="op">&amp;&amp;...)</span> <span class="op">{</span></span>
<span id="cb13-7"><a href="#cb13-7" aria-hidden="true" tabindex="-1"></a>    <span class="kw">co_return</span> value;</span>
<span id="cb13-8"><a href="#cb13-8" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span>
<span id="cb13-9"><a href="#cb13-9" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb13-10"><a href="#cb13-10" aria-hidden="true" tabindex="-1"></a><span class="dt">int</span> main<span class="op">()</span> <span class="op">{</span></span>
<span id="cb13-11"><a href="#cb13-11" aria-hidden="true" tabindex="-1"></a>    <span class="co">// Use the coroutine without passing an allocator:</span></span>
<span id="cb13-12"><a href="#cb13-12" aria-hidden="true" tabindex="-1"></a>    ex<span class="op">::</span>sync_wait<span class="op">(</span>fun<span class="op">(</span><span class="dv">17</span><span class="op">))</span>;</span>
<span id="cb13-13"><a href="#cb13-13" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb13-14"><a href="#cb13-14" aria-hidden="true" tabindex="-1"></a>    <span class="co">// Use the coroutine with passing an allocator:</span></span>
<span id="cb13-15"><a href="#cb13-15" aria-hidden="true" tabindex="-1"></a>    <span class="kw">using</span> allocator_type <span class="op">=</span> std<span class="op">::</span>pmr<span class="op">::</span>polymorphic_alloctor<span class="op">&lt;</span>std<span class="op">::</span>byte<span class="op">&gt;</span>;</span>
<span id="cb13-16"><a href="#cb13-16" aria-hidden="true" tabindex="-1"></a>    ex<span class="op">::</span>sync_wait<span class="op">(</span>fun<span class="op">(</span><span class="dv">17</span>, std<span class="op">::</span>allocator_arg, allocator_type<span class="op">()))</span>;</span>
<span id="cb13-17"><a href="#cb13-17" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
<p>The arguments passed when creating the coroutine are made available
to an <code class="sourceCode cpp"><span class="kw">operator</span> <span class="kw">new</span></code>
of the promise type, i.e., this operator can extract the allocator, if
any, from the list of parameters and use that for the purpose of
allocation. The matching <code class="sourceCode cpp"><span class="kw">operator</span> <span class="kw">delete</span></code>
gets passed only the pointer to release and the originally requested
<code class="sourceCode cpp">size</code>. To have access to the correct
allocator in <code class="sourceCode cpp"><span class="kw">operator</span> <span class="kw">delete</span></code>
the allocator either needs to be stateless or a copy needs to be
accessible via the pointer passed to <code class="sourceCode cpp"><span class="kw">operator</span> <span class="kw">delete</span></code>,
e.g., stored at the offset <code class="sourceCode cpp">size</code>.</p>
<p>To avoid any cost introduced by type erasing an allocator type as
part of the <code class="sourceCode cpp">task</code> definition the
expected allocator type is obtained from the context argument
<code class="sourceCode cpp">C</code> of <code class="sourceCode cpp">task<span class="op">&lt;</span>T, C<span class="op">&gt;</span></code>:</p>
<div class="sourceCode" id="cb14"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb14-1"><a href="#cb14-1" aria-hidden="true" tabindex="-1"></a><span class="kw">using</span> allocator_type <span class="op">=</span> ex<span class="op">::</span>allocator_of_t<span class="op">&lt;</span>C<span class="op">&gt;</span>;</span></code></pre></div>
<p>This
<code class="sourceCode cpp"><span class="kw">using</span></code> alias
uses <code class="sourceCode cpp"><span class="kw">typename</span> C<span class="op">::</span>allocator_type</code>
if present or defaults to <code class="sourceCode cpp">std<span class="op">::</span>allocator<span class="op">&lt;</span>std<span class="op">::</span>byte<span class="op">&gt;</span></code>
otherwise. This <code class="sourceCode cpp">allocator_type</code> has
to be for the type
<code class="sourceCode cpp">std<span class="op">::</span>byte</code>
(if necessary it is possible to relax that constraint).</p>
<p>The allocator used for the coroutine frame should also be used for
any other allocators needed for the coroutine itself, e.g., when type
erasing something needed for its operation (although in most cases a
small object optimisation would be preferable and sufficient). Also, the
allocator should be made available to child operations via the
respective receiver’s environment using the
<code class="sourceCode cpp">get_allocator</code> query. The arguments
passed to the coroutine are also available to the constructor of the
promise type (if there is a matching on) and the allocator can be
obtained from there:</p>
<div class="sourceCode" id="cb15"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb15-1"><a href="#cb15-1" aria-hidden="true" tabindex="-1"></a><span class="kw">struct</span> allocator_aware_context <span class="op">{</span></span>
<span id="cb15-2"><a href="#cb15-2" aria-hidden="true" tabindex="-1"></a>    <span class="kw">using</span> allocator_type <span class="op">=</span> pmr<span class="op">::</span>polymorphic_allocator<span class="op">&lt;</span>std<span class="op">::</span>byte<span class="op">&gt;</span>;</span>
<span id="cb15-3"><a href="#cb15-3" aria-hidden="true" tabindex="-1"></a><span class="op">}</span>;</span>
<span id="cb15-4"><a href="#cb15-4" aria-hidden="true" tabindex="-1"></a>fixed_resource<span class="op">&lt;</span><span class="dv">2048</span><span class="op">&gt;</span> resource;</span>
<span id="cb15-5"><a href="#cb15-5" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb15-6"><a href="#cb15-6" aria-hidden="true" tabindex="-1"></a>ex<span class="op">::</span>sync_wait<span class="op">([](</span><span class="kw">auto</span><span class="op">&amp;&amp;</span>, <span class="kw">auto</span><span class="op">*</span> resource<span class="op">)</span></span>
<span id="cb15-7"><a href="#cb15-7" aria-hidden="true" tabindex="-1"></a>        <span class="op">-&gt;</span> ex<span class="op">::</span>task<span class="op">&lt;</span><span class="dt">void</span>, allocator_aware_context<span class="op">&gt;</span> <span class="op">{</span></span>
<span id="cb15-8"><a href="#cb15-8" aria-hidden="true" tabindex="-1"></a>    <span class="kw">auto</span> alloc <span class="op">=</span> <span class="kw">co_await</span> ex<span class="op">::</span>read_env<span class="op">(</span>ex<span class="op">::</span>get_allocator<span class="op">)</span>;</span>
<span id="cb15-9"><a href="#cb15-9" aria-hidden="true" tabindex="-1"></a>    use<span class="op">(</span>alloc<span class="op">)</span>;</span>
<span id="cb15-10"><a href="#cb15-10" aria-hidden="true" tabindex="-1"></a><span class="op">}(</span>allocator_arg, <span class="op">&amp;</span>resource<span class="op">))</span>;</span></code></pre></div>
<h2 data-number="4.7" id="environment-support"><span class="header-section-number">4.7</span> Environment Support<a href="#environment-support" class="self-link"></a></h2>
<p>When
<code class="sourceCode cpp"><span class="kw">co_await</span></code>ing
child operations these may want to access an environment. Ideally, the
coroutine would expose the environment from the receiver it gets
<code class="sourceCode cpp"><span class="fu">connect</span></code>ed
to. Doing so isn’t directly possible because the coroutine types doesn’t
know about the receiver type which in turn determines the environment
type. Also, the queries don’t know the type they are going to return.
Thus, some extra mechanisms are needed to provide an environment.</p>
<p>A basic environment can be provided by some entities already known to
the coroutine, though:</p>
<ul>
<li>The <code class="sourceCode cpp">get_scheduler</code> query should
provide the scheduler maintained for <a href="#scheduler-affinity">scheduler affinity</a> whose type is
determined based on the coroutine’s context using <code class="sourceCode cpp">ex<span class="op">::</span>scheduler_of_t<span class="op">&lt;</span>C<span class="op">&gt;</span></code>.</li>
<li>The <code class="sourceCode cpp">get_allocator</code> query should
provide the <a href="#allocator-support">coroutine’s allocator</a> whose
type is determined based on the coroutine’s context using <code class="sourceCode cpp">ex<span class="op">::</span>allocator_of_t<span class="op">&lt;</span>C<span class="op">&gt;</span></code>.
The allocator gets initialized when constructing the promise type.</li>
<li>The <code class="sourceCode cpp">get_stop_token</code> query should
provide a stop token from a stop source which is linked to the stop
token obtained from the receiver’s environment. The type of the stop
source is determined from the coroutine’s context using <code class="sourceCode cpp">ex<span class="op">::</span>stop_source_of_t<span class="op">&lt;</span>C<span class="op">&gt;</span></code>
and defaults to <code class="sourceCode cpp">ex<span class="op">::</span>inplace_stop_source</code>.
Linking the stop source can be delayed until the first stop token is
requested or omitted entirely if <code class="sourceCode cpp">stop_possible<span class="op">()</span></code>
returns
<code class="sourceCode cpp"><span class="kw">false</span></code> or if
the stop token type of the coroutine’s receiver matches that of <code class="sourceCode cpp">ex<span class="op">::</span>stop_source_of_t<span class="op">&lt;</span>C<span class="op">&gt;</span></code>.</li>
</ul>
<p>For any other environment query the context
<code class="sourceCode cpp">C</code> of <code class="sourceCode cpp">task<span class="op">&lt;</span>T, C<span class="op">&gt;</span></code>
can be used. The coroutine can maintain an instance of type
<code class="sourceCode cpp">C</code>. In many cases queries from the
environment of the coroutine’s
<code class="sourceCode cpp">receiver</code> need to be forwarded. Let
<code class="sourceCode cpp">env</code> be <code class="sourceCode cpp">get_env<span class="op">(</span>receiver<span class="op">)</span></code>
and <code class="sourceCode cpp">Env</code> be the type of
<code class="sourceCode cpp">env</code>.
<code class="sourceCode cpp">C</code> gets optionally constructed with
access to the environment:</p>
<ol type="1">
<li>If <code class="sourceCode cpp">C<span class="op">::</span>env_type<span class="op">&lt;</span>Env<span class="op">&gt;</span></code>
is a valid type the coroutine state will contain an object
<code class="sourceCode cpp">own_env</code> of this type which is
constructed with <code class="sourceCode cpp">env</code>. The object
<code class="sourceCode cpp">own_env</code> will live at least as long
as the <code class="sourceCode cpp">C</code> object maintained and
<code class="sourceCode cpp">C</code> is constructed with a reference to
<code class="sourceCode cpp">own_env</code>, allowing
<code class="sourceCode cpp">C</code> to reference type-erased
representations for query results it needs to forward.</li>
<li>Otherwise, if <code class="sourceCode cpp">C<span class="op">(</span>env<span class="op">)</span></code>
is valid the <code class="sourceCode cpp">C</code> object is constructed
with the result of <code class="sourceCode cpp">get_env<span class="op">(</span>receiver<span class="op">)</span></code>.
Constructing the context with the receiver’s environment provides the
opportunity to store whatever data is needed from the environment to
later respond to queries as well.</li>
<li>Otherwise, <code class="sourceCode cpp">C</code> is default
constructed. This option typically applies if
<code class="sourceCode cpp">C</code> doesn’t need to provide any
environment queries.</li>
</ol>
<p>Any query which isn’t provided by the coroutine but is available from
the context <code class="sourceCode cpp">C</code> is forwarded. Any
other query shouldn’t be part of the overload set.</p>
<p>For example:</p>
<div class="sourceCode" id="cb16"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb16-1"><a href="#cb16-1" aria-hidden="true" tabindex="-1"></a><span class="kw">struct</span> context <span class="op">{</span></span>
<span id="cb16-2"><a href="#cb16-2" aria-hidden="true" tabindex="-1"></a>    <span class="dt">int</span> value<span class="op">{}</span>;</span>
<span id="cb16-3"><a href="#cb16-3" aria-hidden="true" tabindex="-1"></a>    <span class="dt">int</span> query<span class="op">(</span>get_value_t <span class="kw">const</span><span class="op">&amp;)</span> <span class="kw">const</span> <span class="kw">noexcept</span> <span class="op">{</span> <span class="cf">return</span> <span class="kw">this</span><span class="op">-&gt;</span>value; <span class="op">}</span></span>
<span id="cb16-4"><a href="#cb16-4" aria-hidden="true" tabindex="-1"></a>    context<span class="op">(</span><span class="kw">auto</span> <span class="kw">const</span><span class="op">&amp;</span> env<span class="op">):</span> value<span class="op">(</span>get_value<span class="op">(</span>env<span class="op">))</span> <span class="op">{}</span></span>
<span id="cb16-5"><a href="#cb16-5" aria-hidden="true" tabindex="-1"></a><span class="op">}</span>;</span>
<span id="cb16-6"><a href="#cb16-6" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb16-7"><a href="#cb16-7" aria-hidden="true" tabindex="-1"></a><span class="dt">int</span> main<span class="op">()</span> <span class="op">{</span></span>
<span id="cb16-8"><a href="#cb16-8" aria-hidden="true" tabindex="-1"></a>    ex<span class="op">::</span>sync_wait<span class="op">(</span></span>
<span id="cb16-9"><a href="#cb16-9" aria-hidden="true" tabindex="-1"></a>        ex<span class="op">::</span>write_env<span class="op">(</span></span>
<span id="cb16-10"><a href="#cb16-10" aria-hidden="true" tabindex="-1"></a>            <span class="op">[]-&gt;</span>demo<span class="op">::</span>task<span class="op">&lt;</span><span class="dt">void</span>, context<span class="op">&gt;</span> <span class="op">{</span></span>
<span id="cb16-11"><a href="#cb16-11" aria-hidden="true" tabindex="-1"></a>                <span class="kw">auto</span> sched<span class="op">(</span><span class="kw">co_await</span> ex<span class="op">::</span>read_env<span class="op">(</span>get_scheduler<span class="op">))</span>;</span>
<span id="cb16-12"><a href="#cb16-12" aria-hidden="true" tabindex="-1"></a>                <span class="kw">auto</span> value<span class="op">(</span><span class="kw">co_await</span> ex<span class="op">::</span>read_env<span class="op">(</span>get_value<span class="op">))</span>;</span>
<span id="cb16-13"><a href="#cb16-13" aria-hidden="true" tabindex="-1"></a>                std<span class="op">::</span>cout <span class="op">&lt;&lt;</span> <span class="st">&quot;value=&quot;</span> <span class="op">&lt;&lt;</span> value <span class="op">&lt;&lt;</span> <span class="st">&quot;</span><span class="sc">\n</span><span class="st">&quot;</span>;</span>
<span id="cb16-14"><a href="#cb16-14" aria-hidden="true" tabindex="-1"></a>                <span class="co">// ...</span></span>
<span id="cb16-15"><a href="#cb16-15" aria-hidden="true" tabindex="-1"></a>            <span class="op">}()</span>,</span>
<span id="cb16-16"><a href="#cb16-16" aria-hidden="true" tabindex="-1"></a>            ex<span class="op">::</span>make_env<span class="op">(</span>get_value, <span class="dv">42</span><span class="op">)</span></span>
<span id="cb16-17"><a href="#cb16-17" aria-hidden="true" tabindex="-1"></a>        <span class="op">)</span></span>
<span id="cb16-18"><a href="#cb16-18" aria-hidden="true" tabindex="-1"></a>    <span class="op">)</span>;</span>
<span id="cb16-19"><a href="#cb16-19" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
<h2 data-number="4.8" id="support-for-requesting-cancellationstopped"><span class="header-section-number">4.8</span> Support For Requesting
Cancellation/Stopped<a href="#support-for-requesting-cancellationstopped" class="self-link"></a></h2>
<p>When a coroutine task executes the actual work it may listen to a
stop token to recognise that it got canceled. Once it recognises that
its work should be stopped it should also complete with <code class="sourceCode cpp">set_stopped<span class="op">(</span>rcvr<span class="op">)</span></code>.
There is no special syntax needed as that is the result of using <code class="sourceCode cpp">just_stopped<span class="op">()</span></code>:</p>
<div class="sourceCode" id="cb17"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb17-1"><a href="#cb17-1" aria-hidden="true" tabindex="-1"></a><span class="kw">co_await</span> ex<span class="op">::</span>just_stopped<span class="op">()</span>;</span></code></pre></div>
<p>The sender <code class="sourceCode cpp">just_stopped<span class="op">()</span></code>
completes with <code class="sourceCode cpp">set_stopped<span class="op">()</span></code>
causing the coroutine to be canceled. Any other sender completing with
<code class="sourceCode cpp">set_stopped<span class="op">()</span></code> can
also be used.</p>
<h2 data-number="4.9" id="error-reporting"><span class="header-section-number">4.9</span> Error Reporting<a href="#error-reporting" class="self-link"></a></h2>
<p>The sender/receiver approach to error reporting is for operations to
complete with a call to <code class="sourceCode cpp">set_error<span class="op">(</span>rcvr, err<span class="op">)</span></code>
for some receiver object <code class="sourceCode cpp">rcvr</code> and an
error value <code class="sourceCode cpp">err</code>. The details of the
completions are used by algorithms to decide how to proceed. For
example, if any of the senders of <code class="sourceCode cpp">when_all<span class="op">(</span>sndr<span class="op">...)</span></code>
fails with a <code class="sourceCode cpp">set_error_t</code> completion
the other senders are stopped and the overall operation fails itself
forwarding the first error. Thus, it should be possible for coroutines
to complete with a <code class="sourceCode cpp">set_error_t</code>
completion. Using a <code class="sourceCode cpp">set_value_t</code>
completion using an error value isn’t quite the same as these are not
detected as errors by algorithms.</p>
<p>The error reporting used for <a href="https://github.com/facebookexperimental/libunifex"><code class="sourceCode cpp">unifex</code></a>
and <a href="https://github.com/NVIDIA/stdexec">stdexec</a> is to turn
an exception escaping from the coroutine into a <code class="sourceCode cpp">set_error_t<span class="op">(</span>std<span class="op">::</span>exception_ptr<span class="op">)</span></code>
completion: when <code class="sourceCode cpp">unhandled_exception<span class="op">()</span></code>
is called on the promise type the coroutine is suspended and the
function can just call <code class="sourceCode cpp">set_value<span class="op">(</span>r, std<span class="op">::</span>get_current_exception<span class="op">())</span></code>.
There are a few limitations with this approach:</p>
<ol type="1">
<li>The only supported error completion is <code class="sourceCode cpp">set_error_t<span class="op">(</span>std<span class="op">::</span>exception_ptr<span class="op">)</span></code>.
While the thrown exception can represent any error type and
<code class="sourceCode cpp">set_error_t</code> completions from
<code class="sourceCode cpp"><span class="kw">co_await</span></code>ed
operations resulting in the corresponding error being thrown it is
better if the other error types can be reported, too.</li>
<li>To report an error an exception needs to be thrown. In some
environments it is preferred to not throw exception or exceptions may
even be entirely banned or disabled which means that there isn’t a way
to report errors from coroutines unless a different mechanism is
provided.</li>
<li>To extract the actual error information from <code class="sourceCode cpp">std<span class="op">::</span>exception_ptr</code>
the exception has to be rethrown.</li>
<li>The completion signatures for <code class="sourceCode cpp">task<span class="op">&lt;</span>T, C<span class="op">&gt;</span></code>
necessarily contain <code class="sourceCode cpp">set_error_t<span class="op">(</span>std<span class="op">::</span>exception_ptr<span class="op">)</span></code>
which is problematic when exceptions are unavailable: <code class="sourceCode cpp">std<span class="op">::</span>exception_ptr</code>
may also be unavailable. Also, without exception as it is impossible to
decode the error. It can be desirable to have coroutine which don’t
declare such a completion signature.</li>
</ol>
<p>Before going into details on how errors can be reported it is
necessary to provide a way for <code class="sourceCode cpp">task<span class="op">&lt;</span>T, C<span class="op">&gt;</span></code>
to control the error completion signatures. Similar to the return type
the error types cannot be deduced from the coroutine body. Instead, they
can be declared using the context type
<code class="sourceCode cpp">C</code>:</p>
<ul>
<li>If present, <code class="sourceCode cpp"><span class="kw">typename</span> C<span class="op">::</span>error_signatures</code>
is used to declare the error types. This type needs to be a
specialisation of
<code class="sourceCode cpp">completion_signatures</code> listing the
valid <code class="sourceCode cpp">set_error_t</code> completions.</li>
<li>If this nested type is not present, <code class="sourceCode cpp">completion_signatures<span class="op">&lt;</span>set_error_t<span class="op">(</span>std<span class="op">::</span>exception_ptr<span class="op">)&gt;</span></code>
is used as a default.</li>
</ul>
<p>The name can be adjusted and it would be possible to use a different
type list template and listing the error types. The basic idea would
remain the same, i.e., the possible error types are declared via the
context type.</p>
<p>Reporting an error by having an exception escape the coroutine is
still possible but it doesn’t necessarily result in a
<code class="sourceCode cpp">set_error_t</code>: If an exception escapes
the coroutine and <code class="sourceCode cpp">set_error_t<span class="op">(</span>std<span class="op">::</span>exception_ptr<span class="op">)</span></code>
isn’t one of the supported the
<code class="sourceCode cpp">set_error_t</code> completions, <code class="sourceCode cpp">std<span class="op">::</span>terminate<span class="op">()</span></code>
is called. If an error is explicitly reported somehow, e.g., using one
of the approaches described below, and the error type isn’t supported by
the context’s <code class="sourceCode cpp">error_signatures</code>, the
program is ill-formed.</p>
<p>The discussion below assumes the use of the class template <code class="sourceCode cpp">with_error<span class="op">&lt;</span>E<span class="op">&gt;</span></code>
to indicate that the coroutine completed with an error. It can be as
simple as</p>
<div class="sourceCode" id="cb18"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb18-1"><a href="#cb18-1" aria-hidden="true" tabindex="-1"></a><span class="kw">template</span> <span class="op">&lt;</span><span class="kw">class</span> E<span class="op">&gt;</span> <span class="kw">struct</span> with_error<span class="op">{</span> E error; <span class="op">}</span>;</span></code></pre></div>
<p>The name can be different although it shouldn’t collide with already
use names (like <code class="sourceCode cpp">error_code</code> or
<code class="sourceCode cpp">upon_error</code>). Also, in some cases
there isn’t really a need to wrap the error into a recognisable class
template. Using a marker type probably helps with readability and
avoiding ambiguities in other cases.</p>
<p>Besides exceptions there are three possible ways how a coroutine can
be exited:</p>
<ol type="1">
<li><p>The coroutine is exited when using
<code class="sourceCode cpp"><span class="kw">co_return</span></code>,
optionally with an argument. Flowing off the end of a coroutine is
equivalent to explicitly using
<code class="sourceCode cpp"><span class="kw">co_return</span>;</code>
instead of flowing off. It would be possible to turn the use of</p>
<div class="sourceCode" id="cb19"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb19-1"><a href="#cb19-1" aria-hidden="true" tabindex="-1"></a><span class="kw">co_return</span> with_error<span class="op">{</span>err<span class="op">}</span>;</span></code></pre></div>
<p>into a <code class="sourceCode cpp">set_error<span class="op">(</span>std<span class="op">::</span>move<span class="op">(</span>rcvr<span class="op">)</span>, err<span class="op">)</span></code>
completion.</p>
<p>One restriction with this approach is that for a <code class="sourceCode cpp">task<span class="op">&lt;</span><span class="dt">void</span>, C<span class="op">&gt;</span></code>
the body can’t contain <code class="sourceCode cpp"><span class="kw">co_return</span> with_error<span class="op">{</span>e<span class="op">}</span>;</code>:
the <code class="sourceCode cpp"><span class="dt">void</span></code>
result requires that the promise type contains a function <code class="sourceCode cpp">return_void<span class="op">()</span></code> and
if that is present it isn’t possible to also have a <code class="sourceCode cpp">return_value<span class="op">(</span>T<span class="op">)</span></code>.</p></li>
<li><p>When a coroutine uses
<code class="sourceCode cpp"><span class="kw">co_await</span> a;</code>
the coroutine is in a suspended state when <code class="sourceCode cpp">await_suspend<span class="op">(...)</span></code>
of some awaiter is entered. While the coroutine is suspended it can be
safely destroyed. It is possible to complete the coroutine in that state
and have the coroutine be cleaned up. This approach is used when the
awaited operation completes with <code class="sourceCode cpp">set_stopped<span class="op">()</span></code>. It
is possible to call <code class="sourceCode cpp">set_error<span class="op">(</span>std<span class="op">::</span>move<span class="op">(</span>rcvr<span class="op">)</span>, err<span class="op">)</span></code>
for some receiver <code class="sourceCode cpp">rcvr</code> and error
<code class="sourceCode cpp">err</code> obtained via the awaitable
<code class="sourceCode cpp">a</code>. Thus, using</p>
<div class="sourceCode" id="cb20"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb20-1"><a href="#cb20-1" aria-hidden="true" tabindex="-1"></a><span class="kw">co_await</span> with_error<span class="op">{</span>err<span class="op">}</span>;</span></code></pre></div>
<p>could complete with <code class="sourceCode cpp">set_error<span class="op">(</span>std<span class="op">::</span>move<span class="op">(</span>rcvr<span class="op">)</span>, err<span class="op">)</span></code>.</p>
<p>Using the same notation for awaiting outstanding operations and
returning results from a coroutine is, however, somewhat surprising. The
name of the awaiter may need to become more explicit like
<code class="sourceCode cpp">exist_coroutine_with_error</code> if this
approach should be supported.</p></li>
<li><p>When a coroutine uses
<code class="sourceCode cpp"><span class="kw">co_yield</span> v;</code>
the promise member <code class="sourceCode cpp">yield_value<span class="op">(</span>T<span class="op">)</span></code>
is called which can return an awaiter
<code class="sourceCode cpp">a</code>. When
<code class="sourceCode cpp">a</code>’s <code class="sourceCode cpp">await_suspend<span class="op">()</span></code> is
called, the coroutine is suspended and the operation can complete
accordingly. Thus, using</p>
<div class="sourceCode" id="cb21"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb21-1"><a href="#cb21-1" aria-hidden="true" tabindex="-1"></a><span class="kw">co_yield</span> with_error<span class="op">{</span>err<span class="op">}</span>;</span></code></pre></div>
<p>could complete with <code class="sourceCode cpp">set_error<span class="op">(</span>std<span class="op">::</span>move<span class="op">(</span>rcvr<span class="op">)</span>, err<span class="op">)</span></code>.
Using
<code class="sourceCode cpp"><span class="kw">co_yield</span></code> for
the purpose of returning from a coroutine with a specific result seems
more expected than using
<code class="sourceCode cpp"><span class="kw">co_await</span></code>.</p></li>
</ol>
<p>There are technically viable options for returning an error from a
coroutine without requiring exceptions. Whether any of them is
considered suitable from a readability point of view is a separate
question.</p>
<p>One concern which was raised with just not resuming the coroutine is
that the time of destruction of variables used by the coroutine is
different. The promise object can be destroyed before completing which
might address the concern.</p>
<p>Using
<code class="sourceCode cpp"><span class="kw">co_await</span></code> or
<code class="sourceCode cpp"><span class="kw">co_yield</span></code> to
propagate error results out of the coroutine has a possibly interesting
variation: in both of these case the error result may be conditionally
produced, i.e., it is possible to complete with an error sometimes and
to produce a value at other times. That could allow a pattern (using
<code class="sourceCode cpp"><span class="kw">co_yield</span></code> for
the potential error return):</p>
<div class="sourceCode" id="cb22"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb22-1"><a href="#cb22-1" aria-hidden="true" tabindex="-1"></a><span class="kw">auto</span> value <span class="op">=</span> <span class="kw">co_yield</span> when_error<span class="op">(</span><span class="kw">co_await</span> into_expected<span class="op">(</span>sender<span class="op">))</span>;</span></code></pre></div>
<p>The subexpression <code class="sourceCode cpp">into_expected<span class="op">(</span>sender<span class="op">)</span></code>
could turn the <code class="sourceCode cpp">set_value_t</code> and
<code class="sourceCode cpp">set_error_t</code> into a suitable <code class="sourceCode cpp">std<span class="op">::</span>expected<span class="op">&lt;</span>V, std<span class="op">::</span>variant<span class="op">&lt;</span>E<span class="op">...&gt;&gt;</span></code>
always reported using a <code class="sourceCode cpp">set_value_t</code>
completion (so the
<code class="sourceCode cpp"><span class="kw">co_await</span></code>
doesn’t throw). The corresponding <code class="sourceCode cpp">std<span class="op">::</span>expected</code>
becomes the result of the
<code class="sourceCode cpp"><span class="kw">co_await</span></code>.
Using
<code class="sourceCode cpp"><span class="kw">co_yield</span></code>
with <code class="sourceCode cpp">when_error<span class="op">(</span>exp<span class="op">)</span></code>
where <code class="sourceCode cpp">exp</code> is an expected can then
either produce <code class="sourceCode cpp">exp<span class="op">.</span>value<span class="op">()</span></code>
as the result of the
<code class="sourceCode cpp"><span class="kw">co_yield</span></code>
expression or it can result in the coroutine completing with the error
from <code class="sourceCode cpp">exp<span class="op">.</span>error<span class="op">()</span></code>.
Using this approach produces a fairly compact approach to propagating
the error retaining the type and without using exceptions.</p>
<h2 data-number="4.10" id="avoiding-stack-overflow"><span class="header-section-number">4.10</span> Avoiding Stack Overflow<a href="#avoiding-stack-overflow" class="self-link"></a></h2>
<p>It is easy to use a coroutine to accidentally create a stack overflow
because loops don’t really execute like loops. For example, a coroutine
like this can easily result in a stack overflow:</p>
<div class="sourceCode" id="cb23"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb23-1"><a href="#cb23-1" aria-hidden="true" tabindex="-1"></a>ex<span class="op">::</span>sync_wait<span class="op">(</span>ex<span class="op">::</span>write_env<span class="op">(</span></span>
<span id="cb23-2"><a href="#cb23-2" aria-hidden="true" tabindex="-1"></a>    <span class="op">[]()</span> <span class="op">-&gt;</span> ex<span class="op">::</span>task<span class="op">&lt;</span><span class="dt">void</span><span class="op">&gt;</span> <span class="op">{</span></span>
<span id="cb23-3"><a href="#cb23-3" aria-hidden="true" tabindex="-1"></a>        <span class="cf">for</span> <span class="op">(</span><span class="dt">int</span> i<span class="op">{}</span>; i <span class="op">&lt;</span> <span class="dv">1000000</span>; <span class="op">++</span>i<span class="op">)</span></span>
<span id="cb23-4"><a href="#cb23-4" aria-hidden="true" tabindex="-1"></a>            <span class="kw">co_await</span> ex<span class="op">::</span>just<span class="op">(</span>i<span class="op">)</span>;</span>
<span id="cb23-5"><a href="#cb23-5" aria-hidden="true" tabindex="-1"></a>    <span class="op">}()</span>,</span>
<span id="cb23-6"><a href="#cb23-6" aria-hidden="true" tabindex="-1"></a>    ex<span class="op">::</span>make_env<span class="op">(</span>ex<span class="op">::</span>get_scheduler, ex<span class="op">::</span>inline_scheduler<span class="op">{})</span></span>
<span id="cb23-7"><a href="#cb23-7" aria-hidden="true" tabindex="-1"></a><span class="op">))</span>;</span></code></pre></div>
<p>The reason this innocent looking code creates a stack overflow is
that the use of
<code class="sourceCode cpp"><span class="kw">co_await</span></code>
results in some function calls to suspend the coroutine and then further
function calls to resume the coroutine (for a proper explanation see,
e.g., Lewis Baker’s <a href="https://lewissbaker.github.io/2020/05/11/understanding_symmetric_transfer">Understanding
Symmetric Transfer</a>). As a result, the stack grows with each
iteration of the loop until it eventually overflows.</p>
<p>With senders it is also not possible to use symmetric transfer to
combat the problem: to achieve the full generality and composing
senders, there are still multiple function calls used, e.g., when
producing the completion signal. Using
<code class="sourceCode cpp">get_completion_behaviour</code> from the
proposal <a href="https://wg21.link/P3206">A sender query for completion
behaviour</a> could allow detecting senders which complete
synchronously. In these cases the stack overflow could be avoided
relying on symmetric transfer.</p>
<p>When using scheduler affinity the transfer of control via a scheduler
which doesn’t complete immediately does avoid the risk of stack
overflow: even when the
<code class="sourceCode cpp"><span class="kw">co_await</span></code>ed
work immediately completes as part of the
<code class="sourceCode cpp">await_suspend</code> call of the created
awaiter the coroutine isn’t immediately resumed. Instead, the work is
scheduled and the coroutine is suspended. The thread unwinds its stack
until it reaches its own scheduling and picks up the next entity to
execute.</p>
<p>When using <code class="sourceCode cpp">sync_wait<span class="op">(</span>sndr<span class="op">)</span></code>
the <code class="sourceCode cpp">run_loop</code>’s scheduler is used and
it may very well just resume the just suspended coroutine: when there is
scheduling happening as part of scheduler affinity it doesn’t mean that
work gets scheduled on a different thread!</p>
<p>The problem with stack overflows does remain when the work resumes
immediately despite using scheduler affinity. That may be the case when
using an inline scheduler, i.e., a scheduler with an operation state
whose
<code class="sourceCode cpp">start<span class="op">()</span></code>
immediately completes: the scheduled work gets executed as soon as <code class="sourceCode cpp">set_value<span class="op">(</span>std<span class="op">::</span>move<span class="op">(</span>rcvr<span class="op">))</span></code>
is called.</p>
<p>Another potential for stack overflows is when optimising the
behaviour for work which is known to not move to another scheduler: in
that case there isn’t really any need to use
<code class="sourceCode cpp">continue_on</code> to get back to the
scheduler where the operation was started! The execution remained on
that scheduler all along. However, not rescheduling the work means that
the stack isn’t unwound.</p>
<p>Since <code class="sourceCode cpp">task</code> uses scheduler
affinity by default, stack overflow shouldn’t be a problem and there is
no separate provision required to combat stack overflow. If the
implementation chooses to avoid rescheduling work it will need to make
sure that doing so doesn’t cause any problems, e.g., by rescheduling the
work sometimes. When using an inline scheduler the user will need to be
very careful to not overflow the stack or cause any of the various other
problems with executing immediately.</p>
<h2 data-number="4.11" id="asynchronous-clean-up"><span class="header-section-number">4.11</span> Asynchronous Clean-Up<a href="#asynchronous-clean-up" class="self-link"></a></h2>
<p>Asynchronous clean-up of objects is an important facility. Both <a href="https://github.com/facebookexperimental/libunifex"><code class="sourceCode cpp">unifex</code></a>
and <a href="https://github.com/NVIDIA/stdexec">stdexec</a> provide some
facilities for asynchronous clean-up in their respective coroutine task.
Based on the experience the recommendation is to do something
different!</p>
<p>The recommended direction is to support asynchronous resources
independent of a coroutine task. For example the <a href="https://wg21.link/p2849">async-object</a> proposal is in this
direction. There is similar work ongoing in the context of <a href="https://github.com/facebook/folly">Folly</a>. Thus, there is
currently no plan to support asynchronous clean-up as part of the
<code class="sourceCode cpp">task</code> implementation. Instead, it can
be composed based on other facilities.</p>
<h1 data-number="5" id="caveats"><span class="header-section-number">5</span> Caveats<a href="#caveats" class="self-link"></a></h1>
<p>The use of coroutines introduces some issues which are entirely
independent of how specific coroutines are defined. Some of these were
brought up on prior discussions but they aren’t anything which can be
solved as part of any particular coroutine implementation. In
particular:</p>
<ol type="1">
<li>As
<code class="sourceCode cpp"><span class="kw">co_await</span></code>ing
the result of an operation (or
<code class="sourceCode cpp"><span class="kw">co_yield</span></code>ing
a value) may suspend a coroutine, there is a potential to introduce
problems when resources which are meant to be held temporarily are held
when suspending. For example, holding a lock to a mutex while suspending
a coroutine can result in a different thread trying to release the lock
when the coroutine is resumed (scheduler affinity will move the resumed
coroutine to the same scheduler but not to the same thread).</li>
<li>Destroying a coroutine is only safe when it is suspended. For the
task implementation that means that it shall only call a completion
handler once the coroutine is suspended. That part is under the control
of the coroutine implementation. However, there is no way to guard
against users explicitly destroying a coroutine from within its
implementation or from another thread while it is not suspended: that’s
akin to destroying an object while it being used.</li>
<li>Debugging asynchronous code doesn’t work with the normal approaches:
there is generally no suitable stack as work gets resumed from some run
loop which doesn’t tell what set up the original work. To improve on
this situation, <em>async stack traces</em> linking different pieces of
outstanding work together can help. At <a href="https://cppcon.org/cppcon-2024-timeline/">CppCon 2024</a> Ian
Petersen and Jessica Wong presented how that may work (<a href="https://youtu.be/nHy2cA9ZDbw?si=RDFty43InNoJxJNN">watch the
video</a>). Implementations should consider adding corresponding support
and enhance tooling, e.g., debuggers, to pick up on async stack traces.
However, async stack support itself isn’t really something which one
coroutine implementation can enable.</li>
</ol>
<p>While these issues are important this proposal isn’t the right place
to discuss them. Discussion of these issues should be delegated to
suitable proposals wanting to improve this situation in some form.</p>
<h1 data-number="6" id="questions"><span class="header-section-number">6</span> Questions<a href="#questions" class="self-link"></a></h1>
<p>This section lists questions based on the design discussion above.
Each one has a recommendation and a vote is only needed if there
opinions deviating from the recommendation.</p>
<ul>
<li>Result type: expand <code class="sourceCode cpp">as_awaitable<span class="op">(</span>sndr<span class="op">)</span></code>
to support more than one <code class="sourceCode cpp">set_value_t<span class="op">(</span>T<span class="op">...)</span></code>
completion? Recommendation: no.</li>
<li>Result type: add transformation algorithms like
<code class="sourceCode cpp">into_optional</code>,
<code class="sourceCode cpp">into_expected</code>? Recommendation: no,
different proposals.</li>
<li>Scheduler affinity: should <code class="sourceCode cpp">task</code>
support scheduler affinity? Recommendation: yes.</li>
<li>Scheduler affinity: require a <code class="sourceCode cpp">get_scheduler<span class="op">()</span></code>
query on the receiver’s environments? Recommendation: yes.</li>
<li>Scheduler affinity: add a definition for
<code class="sourceCode cpp">inline_scheduler</code> (using whatever
name) to support disabling scheduler affinity? Recommendation: yes.</li>
<li>Allocator support: should <code class="sourceCode cpp">task</code>
support allocators (default <code class="sourceCode cpp">std<span class="op">::</span>allocator<span class="op">&lt;</span>std<span class="op">::</span>byte<span class="op">&gt;</span></code>)?
Recommendation: yes.</li>
<li>Error reporting: should it be possible to return an error without
throwing an exception? Recommendation: yes.</li>
<li>Error reporting: how should errors be reported? Recommendation:
using `co_yield with_error(e).</li>
<li>Error reporting: should <code class="sourceCode cpp"><span class="kw">co_yield</span> when_error<span class="op">(</span>expected<span class="op">)</span></code>
be supported? Recommendation: yes (although weakly).</li>
<li>Clean-up: should asynchronous clean-up be supported? Recommendation:
no.</li>
</ul>
<h1 data-number="7" id="implementation"><span class="header-section-number">7</span> Implementation<a href="#implementation" class="self-link"></a></h1>
<p>An implementation of <code class="sourceCode cpp">task</code> as
proposed in this document is available from <a href="https://github.com/bemanproject/task"><code class="sourceCode cpp">beman<span class="op">::</span>task</code></a>.
This implementation hasn’t received much use, yet, as it is fairly new.
It is setup to be buildable and provides some examples as a starting
point for experimentation.</p>
<p>Coroutine tasks very similar although not identical to the one
proposed are used in multiple projects. In particular, there are three
implementations in wide use:</p>
<ul>
<li><a href="https://github.com/facebook/folly/blob/main/folly/coro/Task.h"><code class="sourceCode cpp">Folly<span class="op">::</span>Task</code></a></li>
<li><a href="https://github.com/facebookexperimental/libunifex/blob/main/include/unifex/task.hpp"><code class="sourceCode cpp">unifex<span class="op">::</span>Task</code></a></li>
<li><a href="https://github.com/NVIDIA/stdexec/blob/main/include/exec/task.hpp"><code class="sourceCode cpp">stdexec<span class="op">::</span>task</code></a></li>
</ul>
<p>The first one (<a href="https://github.com/facebook/folly/blob/main/folly/coro/Task.h"><code class="sourceCode cpp">Folly<span class="op">::</span>Task</code></a>)
isn’t based on sender/receiver. Usage experience from all three have
influenced the design of <code class="sourceCode cpp">task</code>.</p>
<h1 data-number="8" id="acknowledgements"><span class="header-section-number">8</span> Acknowledgements<a href="#acknowledgements" class="self-link"></a></h1>
<p>We would like to thank Ian Petersen, Alexey Spiridonov, and Lee Howes
for comments on drafts of this proposal and general guidance.</p>
<h1 data-number="9" id="proposed-wording"><span class="header-section-number">9</span> Proposed Wording<a href="#proposed-wording" class="self-link"></a></h1>
<p>In [version.syn], add a row</p>
<div class="add" style="color: #006e28">

<div class="sourceCode" id="cb24"><pre class="sourceCode default default"><code class="sourceCode default"><span id="cb24-1"><a href="#cb24-1" aria-hidden="true" tabindex="-1"></a>#define __cpp_lib_task YYYMML // <em>also in</em> &lt;execution&gt;</span></code></pre></div>

</div>
<p>In [except.terminate] paragraph 1 add this bullet at the end of Note
1:</p>
<div class="note">
<p><span>[ <em>Note:</em> </span>These situations are</p>
<ul>
<li><p>…</p></li>
<li><p>when unhandled_stopped is called on a with_awaitable_senders<T>
object ([exec.with.awaitable.senders]) whose continuation is not a
handle to a coroutine whose promise type has an unhandled_stopped member
function<span class="rm" style="color: #bf0303"><del>.</del></span><span class="add" style="color: #006e28"><ins>, or</ins></span></p></li>
</ul>
<div class="add" style="color: #006e28">

<ul>
<li>when an exception is thrown from a coroutine
<code class="sourceCode default">std::execution::task</code> which
doesn’t support a <code class="sourceCode default">std::execution::set_error_t(std::execption_ptr)</code>
completion.</li>
</ul>

</div>
<span> — <em>end note</em> ]</span>
</div>
<p>In <span>33.4
<a href="https://wg21.link/execution.syn">[execution.syn]</a></span> add
declarations for the new classes:</p>
<div class="sourceCode" id="cb25"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb25-1"><a href="#cb25-1" aria-hidden="true" tabindex="-1"></a>namespace std::execution {</span>
<span id="cb25-2"><a href="#cb25-2" aria-hidden="true" tabindex="-1"></a>  ...</span>
<span id="cb25-3"><a href="#cb25-3" aria-hidden="true" tabindex="-1"></a>  // [exec.with.awaitable.senders]</span>
<span id="cb25-4"><a href="#cb25-4" aria-hidden="true" tabindex="-1"></a>  template&lt;class-type Promise&gt;</span>
<span id="cb25-5"><a href="#cb25-5" aria-hidden="true" tabindex="-1"></a>    struct with_awaitable_senders;</span></code></pre></div>
<div class="add" style="color: #006e28">

<div class="sourceCode" id="cb26"><pre class="sourceCode default default"><code class="sourceCode default"><span id="cb26-1"><a href="#cb26-1" aria-hidden="true" tabindex="-1"></a>  // <a href="https://wg21.link/exec.affine.on">[exec.affine.on]</a></span>
<span id="cb26-2"><a href="#cb26-2" aria-hidden="true" tabindex="-1"></a>  struct affine_on_t { <em>unspecified</em>  };</span>
<span id="cb26-3"><a href="#cb26-3" aria-hidden="true" tabindex="-1"></a>  inline constexpr affine_on_t affine_on;</span>
<span id="cb26-4"><a href="#cb26-4" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb26-5"><a href="#cb26-5" aria-hidden="true" tabindex="-1"></a>  // <a href="https://wg21.link/exec.inline.scheduler">[exec.inline.scheduler]</a></span>
<span id="cb26-6"><a href="#cb26-6" aria-hidden="true" tabindex="-1"></a>  class inline_scheduler;</span>
<span id="cb26-7"><a href="#cb26-7" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb26-8"><a href="#cb26-8" aria-hidden="true" tabindex="-1"></a>  // <a href="https://wg21.link/exec.task.scheduler">[exec.task.scheduler]</a></span>
<span id="cb26-9"><a href="#cb26-9" aria-hidden="true" tabindex="-1"></a>  class task_scheduler;</span>
<span id="cb26-10"><a href="#cb26-10" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb26-11"><a href="#cb26-11" aria-hidden="true" tabindex="-1"></a>  // <a href="https://wg21.link/exec.task">[exec.task]</a></span>
<span id="cb26-12"><a href="#cb26-12" aria-hidden="true" tabindex="-1"></a>  template &lt;class T, class Environment&gt;</span>
<span id="cb26-13"><a href="#cb26-13" aria-hidden="true" tabindex="-1"></a>  class task;</span></code></pre></div>

</div>
<div class="sourceCode" id="cb27"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb27-1"><a href="#cb27-1" aria-hidden="true" tabindex="-1"></a>}</span></code></pre></div>
<p>Add new subsections for the different classes at the end of <span>33
<a href="https://wg21.link/exec">[exec]</a></span>:</p>
<div class="draftnote" style="color: #0000ff">
<p>[ Drafting note: Evertyhing below is text meant to go to the end of
the <span>33
<a href="https://wg21.link/exec">[exec]</a></span> section without any
color highlight of what it being added. ]</p>
</div>
<h2 data-number="9.1" id="executionaffine_on-exec.affine.on"><span class="header-section-number">9.1</span> <code class="sourceCode cpp">execution<span class="op">::</span>affine_on</code>
[exec.affine.on]<a href="#executionaffine_on-exec.affine.on" class="self-link"></a></h2>
<p><span class="marginalizedparent"><a class="marginalized">1</a></span>
<code class="sourceCode cpp">affine_on</code> adapts a sender into one
that completes on the specified scheduler. If the algorithm determines
that the adapted sender already completes on the correct scheduler it
can avoid any scheduling operation.</p>
<p><span class="marginalizedparent"><a class="marginalized">2</a></span>
The name <code class="sourceCode cpp">affine_on</code> denotes a
pipeable sender adaptor object. For subexpressions
<code class="sourceCode cpp">sch</code> and
<code class="sourceCode cpp">sndr</code>, if <code class="sourceCode cpp"><span class="kw">decltype</span><span class="op">((</span>sch<span class="op">))</span></code>
does not satisfy <code class="sourceCode cpp">scheduler</code>, or <code class="sourceCode cpp"><span class="kw">decltype</span><span class="op">((</span>sndr<span class="op">))</span></code>
does not satisfy <code class="sourceCode cpp">sender</code>, <code class="sourceCode cpp">affine_on<span class="op">(</span>sndr, sch<span class="op">)</span></code>
is ill-formed.</p>
<p><span class="marginalizedparent"><a class="marginalized">3</a></span>
Otherwise, the expression <code class="sourceCode cpp">affine_on<span class="op">(</span>sndr, sch<span class="op">)</span></code>
is expression-equivalent to:</p>
<div class="sourceCode" id="cb28"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb28-1"><a href="#cb28-1" aria-hidden="true" tabindex="-1"></a>    transform_sender(<em>get-domain-early</em>(sndr), <em>make-sender</em>(affine_on, sch, sndr))</span></code></pre></div>
<p>except that <code class="sourceCode cpp">sndr</code> is evalutated
only once.</p>
<p><span class="marginalizedparent"><a class="marginalized">4</a></span>
The exposition-only class template
<code class="sourceCode cpp"><em>impls-for</em></code> is specialized
for <code class="sourceCode cpp">affine_on_t</code> as follows:</p>
<div class="sourceCode" id="cb29"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb29-1"><a href="#cb29-1" aria-hidden="true" tabindex="-1"></a>  namespace std::execution {</span>
<span id="cb29-2"><a href="#cb29-2" aria-hidden="true" tabindex="-1"></a>    template &lt;&gt;</span>
<span id="cb29-3"><a href="#cb29-3" aria-hidden="true" tabindex="-1"></a>    struct <em>impls-for</em>&lt;affine_on_t&gt;: <em>default-impls</em> {</span>
<span id="cb29-4"><a href="#cb29-4" aria-hidden="true" tabindex="-1"></a>      static constexpr auto <em>get-attrs</em> =</span>
<span id="cb29-5"><a href="#cb29-5" aria-hidden="true" tabindex="-1"></a>        [](const auto&amp; data, const auto&amp; child) noexcept -&gt; decltype(auto) {</span>
<span id="cb29-6"><a href="#cb29-6" aria-hidden="true" tabindex="-1"></a>          return <em>JOIN-ENV</em>(<em>SCHED-ATTRS</em>(data), <em>FWD-ENV</em>(get_env(child)));</span>
<span id="cb29-7"><a href="#cb29-7" aria-hidden="true" tabindex="-1"></a>        };</span>
<span id="cb29-8"><a href="#cb29-8" aria-hidden="true" tabindex="-1"></a>    };</span>
<span id="cb29-9"><a href="#cb29-9" aria-hidden="true" tabindex="-1"></a>  }</span></code></pre></div>
<p><span class="marginalizedparent"><a class="marginalized">5</a></span>
Let <code class="sourceCode cpp">out_sndr</code> be a subexpression
denoting a sender returned from <code class="sourceCode cpp">affine_on<span class="op">(</span>sndr, sch<span class="op">)</span></code>
or one equal to such, and let
<code class="sourceCode cpp">OutSndr</code> be the type <code class="sourceCode cpp"><span class="kw">decltype</span><span class="op">((</span>out_sndr<span class="op">))</span></code>.
Let <code class="sourceCode cpp">out_rcvr</code> be a subexpression
denoting a receiver that has an environment of type
<code class="sourceCode cpp">Env</code> such that <code class="sourceCode cpp">sender_in<span class="op">&lt;</span>OutSndr, Env<span class="op">&gt;</span></code>
is <code class="sourceCode cpp"><span class="kw">true</span></code>. Let
<code class="sourceCode cpp">op</code> be an lvalue referring to the
operation state that results from connecting
<code class="sourceCode cpp">out_sndr</code> to
<code class="sourceCode cpp">out_rcvr</code>. Calling <code class="sourceCode cpp">start<span class="op">(</span>op<span class="op">)</span></code>
will start <code class="sourceCode cpp">sndr</code> on the current
execution agent and execute completion operations on
<code class="sourceCode cpp">out_rcvr</code> on an execution agent of
the execution resource associated with
<code class="sourceCode cpp">sch</code>. If the current execution
resource is the same as the execution resource associated with
<code class="sourceCode cpp">sch</code>, the completion operation on
<code class="sourceCode cpp">out_rcvr</code> may be called before <code class="sourceCode cpp">start<span class="op">(</span>op<span class="op">)</span></code>
completes. If scheduling onto <code class="sourceCode cpp">sch</code>
fails, an error completion on
<code class="sourceCode cpp">out_rcvr</code> shall be executed on an
unspecified execution agent.</p>
<h2 data-number="9.2" id="executioninline_scheduler-exec.inline.scheduler"><span class="header-section-number">9.2</span> <code class="sourceCode cpp">execution<span class="op">::</span>inline_scheduler</code>
[exec.inline.scheduler]<a href="#executioninline_scheduler-exec.inline.scheduler" class="self-link"></a></h2>
<div class="sourceCode" id="cb30"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb30-1"><a href="#cb30-1" aria-hidden="true" tabindex="-1"></a>namespace std::execution {</span>
<span id="cb30-2"><a href="#cb30-2" aria-hidden="true" tabindex="-1"></a>  class inline_scheduler {</span>
<span id="cb30-3"><a href="#cb30-3" aria-hidden="true" tabindex="-1"></a>    class <em>inline-sender</em>; // exposition only</span>
<span id="cb30-4"><a href="#cb30-4" aria-hidden="true" tabindex="-1"></a>    template &lt;receiver R&gt;</span>
<span id="cb30-5"><a href="#cb30-5" aria-hidden="true" tabindex="-1"></a>    class <em>inline-state</em>;  // exposition only</span>
<span id="cb30-6"><a href="#cb30-6" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb30-7"><a href="#cb30-7" aria-hidden="true" tabindex="-1"></a>  public:</span>
<span id="cb30-8"><a href="#cb30-8" aria-hidden="true" tabindex="-1"></a>    using scheduler_concept = scheduler_t;</span>
<span id="cb30-9"><a href="#cb30-9" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb30-10"><a href="#cb30-10" aria-hidden="true" tabindex="-1"></a>    constexpr <em>inline-sender</em> schedule() noexcept { return {}; }</span>
<span id="cb30-11"><a href="#cb30-11" aria-hidden="true" tabindex="-1"></a>    constexpr bool operator== (const inline_scheduler&amp;) const noexcept = default;</span>
<span id="cb30-12"><a href="#cb30-12" aria-hidden="true" tabindex="-1"></a>  };</span>
<span id="cb30-13"><a href="#cb30-13" aria-hidden="true" tabindex="-1"></a>}</span></code></pre></div>
<p><span class="marginalizedparent"><a class="marginalized">1</a></span>
<code class="sourceCode cpp">inline_scheduler</code> is a class that
models <code class="sourceCode cpp">scheduler</code> <a href="https://wg21.link/exec.scheduler">[exec.scheduler]</a>. All
objects of type <code class="sourceCode cpp">inline_scheduler</code> are
equal.</p>
<p><span class="marginalizedparent"><a class="marginalized">2</a></span>
<code class="sourceCode cpp"><em>inline-sender</em></code> is an
exposition-only type that satisfies
<code class="sourceCode cpp">sender</code>. The type<br /> <code class="sourceCode cpp">completion_signatures_of_t<span class="op">&lt;</span><em>inline-sender</em><span class="op">&gt;</span></code>
is <code class="sourceCode cpp">completion_signatures<span class="op">&lt;</span>set_value_t<span class="op">()&gt;</span></code>.</p>
<p><span class="marginalizedparent"><a class="marginalized">3</a></span>
Let <code class="sourceCode cpp"><em>sndr</em></code> be an expression
of type <code class="sourceCode cpp"><em>inline-sender</em></code>, let
<code class="sourceCode cpp"><em>rcvr</em></code> be an expression such
that <code class="sourceCode cpp">receiver_of<span class="op">&lt;</span><span class="kw">decltype</span><span class="op">((</span><em>rcvr</em><span class="op">))</span>, CS<span class="op">&gt;</span></code>
is <code class="sourceCode cpp"><span class="kw">true</span></code>
where <code class="sourceCode cpp">CS</code> is <code class="sourceCode cpp">completion_signatures<span class="op">&lt;</span>set_value_t<span class="op">()&gt;</span></code>,
then:</p>
<ul>
<li><span class="marginalizedparent"><a class="marginalized">(3.1)</a></span> the
expression <code class="sourceCode cpp"><span class="fu">connect</span><span class="op">(</span><em>sndr</em>, <em>rcvr</em><span class="op">)</span></code>
has type <code class="sourceCode cpp"><em>inline-state</em><span class="op">&lt;</span>remove_cvref_t<span class="op">&lt;</span><span class="kw">decltype</span><span class="op">((</span><em>rcvr</em><span class="op">))&gt;&gt;</span></code>
and is potentially-throwing if and only if <code class="sourceCode cpp"><span class="op">((</span><span class="dt">void</span><span class="op">)</span><em>sndr</em>, <span class="kw">auto</span><span class="op">(</span><em>rcvr</em><span class="op">))</span></code>
is potentially-throwing, and</li>
<li><span class="marginalizedparent"><a class="marginalized">(3.2)</a></span> the
expression <code class="sourceCode cpp">get_completion_scheduler<span class="op">&lt;</span>set_value_t<span class="op">&gt;(</span>get_env<span class="op">(</span><em>sndr</em><span class="op">))</span></code>
has type <code class="sourceCode cpp">inline_scheduler</code> and is
potentially-throwing if and only if <code class="sourceCode cpp">get_env<span class="op">(</span><em>sndr</em><span class="op">)</span></code>
is potentially-throwing.</li>
</ul>
<p><span class="marginalizedparent"><a class="marginalized">4</a></span>
Let <code class="sourceCode cpp"><em>o</em></code> be a
non-<code class="sourceCode cpp"><span class="kw">const</span></code>
lvalue of type <code class="sourceCode cpp"><em>inline-state</em><span class="op">&lt;</span>Rcvr<span class="op">&gt;</span></code>,
and let <code class="sourceCode cpp">REC<span class="op">(</span><em>o</em><span class="op">)</span></code>
be a
non-<code class="sourceCode cpp"><span class="kw">const</span></code>
lvalue reference to an object of type
<code class="sourceCode cpp">Rcvr</code> that was initialized with the
expression <code class="sourceCode cpp"><em>rcvr</em></code> passed to
an invocation of
<code class="sourceCode cpp"><span class="fu">connect</span></code> that
returned <code class="sourceCode cpp"><em>o</em></code>, then:</p>
<ul>
<li><span class="marginalizedparent"><a class="marginalized">(4.1)</a></span> the
object to which <code class="sourceCode cpp">REC<span class="op">(</span><em>o</em><span class="op">)</span></code>
refers remains valid for the lifetime of the object to which
<code class="sourceCode cpp"><em>o</em></code> refers, and</li>
<li><span class="marginalizedparent"><a class="marginalized">(4.2)</a></span> the
expression <code class="sourceCode cpp">start<span class="op">(</span><em>o</em><span class="op">)</span></code>
is equivalent to <code class="sourceCode cpp">set_value<span class="op">(</span>std<span class="op">::</span>move<span class="op">(</span>REC<span class="op">(</span><em>o</em><span class="op">)))</span></code>.</li>
</ul>
<h2 data-number="9.3" id="executiontask_scheduler-exec.task.scheduler"><span class="header-section-number">9.3</span> <code class="sourceCode cpp">execution<span class="op">::</span>task_scheduler</code>
[exec.task.scheduler]<a href="#executiontask_scheduler-exec.task.scheduler" class="self-link"></a></h2>
<div class="sourceCode" id="cb31"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb31-1"><a href="#cb31-1" aria-hidden="true" tabindex="-1"></a>namespace std::execution {</span>
<span id="cb31-2"><a href="#cb31-2" aria-hidden="true" tabindex="-1"></a>  class task_scheduler {</span>
<span id="cb31-3"><a href="#cb31-3" aria-hidden="true" tabindex="-1"></a>    class <em>sender</em>; // exposition only</span>
<span id="cb31-4"><a href="#cb31-4" aria-hidden="true" tabindex="-1"></a>    template &lt;receiver R&gt;</span>
<span id="cb31-5"><a href="#cb31-5" aria-hidden="true" tabindex="-1"></a>    class <em>state</em>;  // exposition only</span>
<span id="cb31-6"><a href="#cb31-6" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb31-7"><a href="#cb31-7" aria-hidden="true" tabindex="-1"></a>  public:</span>
<span id="cb31-8"><a href="#cb31-8" aria-hidden="true" tabindex="-1"></a>    using scheduler_concept = scheduler_t;</span>
<span id="cb31-9"><a href="#cb31-9" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb31-10"><a href="#cb31-10" aria-hidden="true" tabindex="-1"></a>    template &lt;class Sch, class Allocator = allocator&lt;byte&gt;&gt;</span>
<span id="cb31-11"><a href="#cb31-11" aria-hidden="true" tabindex="-1"></a>      requires (!same_as&lt;task_scheduler, remove_cvref_t&lt;Sch&gt;&gt;)</span>
<span id="cb31-12"><a href="#cb31-12" aria-hidden="true" tabindex="-1"></a>        &amp;&amp; scheduler&lt;Sch&gt;</span>
<span id="cb31-13"><a href="#cb31-13" aria-hidden="true" tabindex="-1"></a>    explicit task_scheduler(Sch&amp;&amp; sch, Allocator alloc = {});</span>
<span id="cb31-14"><a href="#cb31-14" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb31-15"><a href="#cb31-15" aria-hidden="true" tabindex="-1"></a>    <em>sender</em> schedule();</span>
<span id="cb31-16"><a href="#cb31-16" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb31-17"><a href="#cb31-17" aria-hidden="true" tabindex="-1"></a>    friend bool operator== (const task_scheduler&amp; lhs, const task_scheduler&amp; rhs)</span>
<span id="cb31-18"><a href="#cb31-18" aria-hidden="true" tabindex="-1"></a>        noexcept;</span>
<span id="cb31-19"><a href="#cb31-19" aria-hidden="true" tabindex="-1"></a>    template &lt;class Sch&gt;</span>
<span id="cb31-20"><a href="#cb31-20" aria-hidden="true" tabindex="-1"></a>      requires (!same_as&lt;task_scheduler, Sch&gt;)</span>
<span id="cb31-21"><a href="#cb31-21" aria-hidden="true" tabindex="-1"></a>      &amp;&amp; scheduler&lt;Sch&gt;</span>
<span id="cb31-22"><a href="#cb31-22" aria-hidden="true" tabindex="-1"></a>    friend bool operator== (const task_scheduler&amp; lhs, const Sch&amp; rhs) noexcept;</span>
<span id="cb31-23"><a href="#cb31-23" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb31-24"><a href="#cb31-24" aria-hidden="true" tabindex="-1"></a>    private:</span>
<span id="cb31-25"><a href="#cb31-25" aria-hidden="true" tabindex="-1"></a>      shared_ptr&lt;void&gt; <em>sch</em>_; // <em>exposition only</em></span>
<span id="cb31-26"><a href="#cb31-26" aria-hidden="true" tabindex="-1"></a>  };</span>
<span id="cb31-27"><a href="#cb31-27" aria-hidden="true" tabindex="-1"></a>}</span></code></pre></div>
<p><span class="marginalizedparent"><a class="marginalized">1</a></span>
<code class="sourceCode cpp">task_scheduler</code> is a class that
models <code class="sourceCode cpp">scheduler</code> <a href="https://wg21.link/exec.scheduler">[exec.scheduler]</a>. Given on
object <code class="sourceCode cpp">s</code> of type
<code class="sourceCode cpp">task_scheduler</code>, let <code class="sourceCode cpp"><em>SCHED</em><span class="op">(</span>s<span class="op">)</span></code>
be the object owned by
<code class="sourceCode cpp">s<span class="op">.</span><em>sch</em>_</code>.</p>
<div class="sourceCode" id="cb32"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb32-1"><a href="#cb32-1" aria-hidden="true" tabindex="-1"></a>template &lt;class Sch, class Allocator = allocator&lt;void&gt;&gt;</span>
<span id="cb32-2"><a href="#cb32-2" aria-hidden="true" tabindex="-1"></a>  requires(!same_as&lt;task_scheduler, remove_cvref_t&lt;Sch&gt;&gt;) &amp;&amp; scheduler&lt;Sch&gt;</span>
<span id="cb32-3"><a href="#cb32-3" aria-hidden="true" tabindex="-1"></a>explicit task_scheduler(Sch&amp;&amp; sch, Allocator alloc = {});</span></code></pre></div>
<p><span class="marginalizedparent"><a class="marginalized">2</a></span>
<em>Effects</em>: Initialize
<code class="sourceCode cpp"><em>sch</em>_</code> with <code class="sourceCode cpp">allocate_shared<span class="op">&lt;</span>remove_cvref_t<span class="op">&lt;</span>Sch<span class="op">&gt;&gt;(</span>alloc, std<span class="op">::</span>forward<span class="op">&lt;</span>Sch<span class="op">&gt;(</span>sch<span class="op">))</span></code>.</p>
<p><span class="marginalizedparent"><a class="marginalized">3</a></span>
<em>Recommended practice</em>: Implementations should avoid the use of
dynamically allocated memory for small scheduler objects.</p>
<p><span class="marginalizedparent"><a class="marginalized">4</a></span>
<em>Remarks</em>: Any allocations performed by construction of
<code class="sourceCode cpp"><em>sender</em></code> or
<code class="sourceCode cpp"><em>state</em></code> objects resulting
from calls on <code class="sourceCode cpp"><span class="op">*</span><span class="kw">this</span></code>
are performed using a copy of
<code class="sourceCode cpp">alloc</code>.</p>
<div class="sourceCode" id="cb33"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb33-1"><a href="#cb33-1" aria-hidden="true" tabindex="-1"></a><em>sender</em> schedule();</span></code></pre></div>
<p><span class="marginalizedparent"><a class="marginalized">5</a></span>
<em>Effects</em>: Returns an object of type
<code class="sourceCode cpp"><em>sender</em></code> containing a sender
initialized with <code class="sourceCode cpp">schedule<span class="op">(</span><em>SCHED</em><span class="op">(*</span><span class="kw">this</span><span class="op">))</span></code>.</p>
<div class="sourceCode" id="cb34"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb34-1"><a href="#cb34-1" aria-hidden="true" tabindex="-1"></a>bool operator== (const task_scheduler&amp; lhs, const task_scheduler&amp; rhs) noexcept;</span></code></pre></div>
<p><span class="marginalizedparent"><a class="marginalized">6</a></span>
<em>Effects</em>: Equivalent to: <code class="sourceCode cpp"><span class="cf">return</span> lhs <span class="op">==</span> <em>SCHED</em><span class="op">(</span>rhs<span class="op">)</span>;</code></p>
<div class="sourceCode" id="cb35"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb35-1"><a href="#cb35-1" aria-hidden="true" tabindex="-1"></a>template &lt;class Sch&gt;</span>
<span id="cb35-2"><a href="#cb35-2" aria-hidden="true" tabindex="-1"></a>  requires (!same_as&lt;task_scheduler, Sch&gt;)</span>
<span id="cb35-3"><a href="#cb35-3" aria-hidden="true" tabindex="-1"></a>        &amp;&amp; scheduler&lt;Sch&gt;</span>
<span id="cb35-4"><a href="#cb35-4" aria-hidden="true" tabindex="-1"></a>bool operator== (const task_scheduler&amp; lhs, const Sch&amp; rhs) noexcept;</span></code></pre></div>
<p><span class="marginalizedparent"><a class="marginalized">7</a></span>
<em>Returns</em>:
<code class="sourceCode cpp"><span class="kw">false</span></code> if
type of <code class="sourceCode cpp"><em>SCHED</em><span class="op">(</span>lhs<span class="op">)</span></code>
is not <code class="sourceCode cpp">Sch</code>, otherwise <code class="sourceCode cpp"><em>SCHED</em><span class="op">(</span>lhs<span class="op">)</span> <span class="op">==</span> rhs;</code></p>
<div class="sourceCode" id="cb36"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb36-1"><a href="#cb36-1" aria-hidden="true" tabindex="-1"></a>class task_scheduler::<em>sender</em> { // <em>exposition only</em></span>
<span id="cb36-2"><a href="#cb36-2" aria-hidden="true" tabindex="-1"></a>public:</span>
<span id="cb36-3"><a href="#cb36-3" aria-hidden="true" tabindex="-1"></a>  using sender_concept = sender_t;</span>
<span id="cb36-4"><a href="#cb36-4" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb36-5"><a href="#cb36-5" aria-hidden="true" tabindex="-1"></a>  template &lt;receiver R&gt;</span>
<span id="cb36-6"><a href="#cb36-6" aria-hidden="true" tabindex="-1"></a>  <em>state</em>&lt;R&gt; connect(R&amp;&amp; rcvr);</span>
<span id="cb36-7"><a href="#cb36-7" aria-hidden="true" tabindex="-1"></a>};</span></code></pre></div>
<p><span class="marginalizedparent"><a class="marginalized">8</a></span>
<code class="sourceCode cpp"><em>sender</em></code> is an
exposition-only class that models
<code class="sourceCode cpp">sender</code> <a href="https://wg21.link/exec.sender">[exec.sender]</a> and for which
<code class="sourceCode cpp">completion_signatures_of_t<span class="op">&lt;</span><em>sender</em><span class="op">&gt;</span></code>
denotes:</p>
<div class="sourceCode" id="cb37"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb37-1"><a href="#cb37-1" aria-hidden="true" tabindex="-1"></a>completion_signatures&lt;</span>
<span id="cb37-2"><a href="#cb37-2" aria-hidden="true" tabindex="-1"></a>  set_value_t(),</span>
<span id="cb37-3"><a href="#cb37-3" aria-hidden="true" tabindex="-1"></a>  set_error_t(error_code),</span>
<span id="cb37-4"><a href="#cb37-4" aria-hidden="true" tabindex="-1"></a>  set_error_t(exception_ptr),</span>
<span id="cb37-5"><a href="#cb37-5" aria-hidden="true" tabindex="-1"></a>  set_stopped_t()&gt;</span></code></pre></div>
<p><span class="marginalizedparent"><a class="marginalized">9</a></span>
Let <code class="sourceCode cpp">sch</code> be an object of type
<code class="sourceCode cpp">task_scheduler</code> and let
<code class="sourceCode cpp">sndr</code> be an object of type
<code class="sourceCode cpp"><em>sender</em></code> obtained from <code class="sourceCode cpp">schedule<span class="op">(</span>sch<span class="op">)</span></code>.
Then <code class="sourceCode cpp">get_completion_scheduler<span class="op">&lt;</span>set_value_t<span class="op">&gt;(</span>get_env<span class="op">(</span>sndr<span class="op">))</span> <span class="op">==</span> sch</code>
is <code class="sourceCode cpp"><span class="kw">true</span></code>. The
object <code class="sourceCode cpp"><em>SENDER</em><span class="op">(</span>sndr<span class="op">)</span></code>
is the sender object contained by
<code class="sourceCode cpp">sndr</code> or an object move constructed
from it.</p>
<div class="sourceCode" id="cb38"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb38-1"><a href="#cb38-1" aria-hidden="true" tabindex="-1"></a>template&lt;receiver Rcvr&gt;</span>
<span id="cb38-2"><a href="#cb38-2" aria-hidden="true" tabindex="-1"></a><em>state</em>&lt;Rcvr&gt; connect(Rcvr&amp;&amp; rcvr);</span></code></pre></div>
<p><span class="marginalizedparent"><a class="marginalized">10</a></span>
<em>Effects</em>: Let <code class="sourceCode cpp">r</code> be an object
of a type that models <code class="sourceCode cpp">receiver</code> and
whose completion handlers result in invoking the corresponding
completion handlers of <code class="sourceCode cpp">rcvr</code> or copy
thereof. Returns an object of type <code class="sourceCode cpp"><em>state</em><span class="op">&lt;</span>Rcvr<span class="op">&gt;</span></code>
containing an operation state object initialized with <code class="sourceCode cpp"><span class="fu">connect</span><span class="op">(</span><em>SENDER</em><span class="op">(*</span><span class="kw">this</span><span class="op">)</span>, std<span class="op">::</span>move<span class="op">(</span>r<span class="op">))</span></code>.</p>
<div class="sourceCode" id="cb39"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb39-1"><a href="#cb39-1" aria-hidden="true" tabindex="-1"></a>template &lt;receiver R&gt;</span>
<span id="cb39-2"><a href="#cb39-2" aria-hidden="true" tabindex="-1"></a>class task_scheduler::<em>state</em> { // <em>exposition only</em></span>
<span id="cb39-3"><a href="#cb39-3" aria-hidden="true" tabindex="-1"></a>public:</span>
<span id="cb39-4"><a href="#cb39-4" aria-hidden="true" tabindex="-1"></a>  using operation_state_concept = operation_state_t;</span>
<span id="cb39-5"><a href="#cb39-5" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb39-6"><a href="#cb39-6" aria-hidden="true" tabindex="-1"></a>  void start() &amp; noexcept;</span>
<span id="cb39-7"><a href="#cb39-7" aria-hidden="true" tabindex="-1"></a>};</span></code></pre></div>
<p><span class="marginalizedparent"><a class="marginalized">11</a></span>
<code class="sourceCode cpp"><em>state</em></code> is an exposition-only
class template whose specializations model
<code class="sourceCode cpp">operation_state</code> <span>33.8
<a href="https://wg21.link/exec.opstate">[exec.opstate]</a></span>.</p>
<div class="sourceCode" id="cb40"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb40-1"><a href="#cb40-1" aria-hidden="true" tabindex="-1"></a>void start() &amp; noexcept;</span></code></pre></div>
<p><span class="marginalizedparent"><a class="marginalized">12</a></span>
<em>Effects</em>: Equivalent to <code class="sourceCode cpp">start<span class="op">(</span>st<span class="op">)</span></code>
where <code class="sourceCode cpp">st</code> is the operation state
object contained by <code class="sourceCode cpp"><span class="op">*</span><span class="kw">this</span></code>.</p>
<h2 data-number="9.4" id="executiontask-exec.task"><span class="header-section-number">9.4</span> <code class="sourceCode cpp">execution<span class="op">::</span>task</code>
[exec.task]<a href="#executiontask-exec.task" class="self-link"></a></h2>
<h3 data-number="9.4.1" id="task-overview-task.overview"><span class="header-section-number">9.4.1</span>
<code class="sourceCode cpp">task</code> Overview [task.overview]<a href="#task-overview-task.overview" class="self-link"></a></h3>
<p><span class="marginalizedparent"><a class="marginalized">1</a></span>
The <code class="sourceCode cpp">task</code> class template represents a
sender that can be used as the return type of coroutines. The first
template parameter <code class="sourceCode cpp">T</code> defines the
type value completion datum (<span>33.3
<a href="https://wg21.link/exec.async.ops">[exec.async.ops]</a></span>)
if <code class="sourceCode cpp">T</code> is not
<code class="sourceCode cpp"><span class="dt">void</span></code>.
Otherwise, there are no value completion datums. Inside coroutines
returning <code class="sourceCode cpp">task<span class="op">&lt;</span>T, E<span class="op">&gt;</span></code>
the operand of
<code class="sourceCode cpp"><span class="kw">co_return</span></code>
(if any) becomes the argument of
<code class="sourceCode cpp">set_value</code>. The second template
parameter <code class="sourceCode cpp">Environment</code> is used to
customize the behavior of <code class="sourceCode cpp">task</code>.</p>
<h3 data-number="9.4.2" id="class-template-task-task.class"><span class="header-section-number">9.4.2</span> Class template task
[task.class]<a href="#class-template-task-task.class" class="self-link"></a></h3>
<div class="sourceCode" id="cb41"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb41-1"><a href="#cb41-1" aria-hidden="true" tabindex="-1"></a>namespace std::execution {</span>
<span id="cb41-2"><a href="#cb41-2" aria-hidden="true" tabindex="-1"></a>  template &lt;class T, class Environment&gt;</span>
<span id="cb41-3"><a href="#cb41-3" aria-hidden="true" tabindex="-1"></a>  class task {</span>
<span id="cb41-4"><a href="#cb41-4" aria-hidden="true" tabindex="-1"></a>    // [task.state]</span>
<span id="cb41-5"><a href="#cb41-5" aria-hidden="true" tabindex="-1"></a>    template &lt;receiver R&gt;</span>
<span id="cb41-6"><a href="#cb41-6" aria-hidden="true" tabindex="-1"></a>    class <em>state</em>; // <em>exposition only</em></span>
<span id="cb41-7"><a href="#cb41-7" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb41-8"><a href="#cb41-8" aria-hidden="true" tabindex="-1"></a>  public:</span>
<span id="cb41-9"><a href="#cb41-9" aria-hidden="true" tabindex="-1"></a>    using sender_concept = sender_t;</span>
<span id="cb41-10"><a href="#cb41-10" aria-hidden="true" tabindex="-1"></a>    using completion_signatures = <em>see below</em>;</span>
<span id="cb41-11"><a href="#cb41-11" aria-hidden="true" tabindex="-1"></a>    using allocator_type = <em>see below</em>;</span>
<span id="cb41-12"><a href="#cb41-12" aria-hidden="true" tabindex="-1"></a>    using scheduler_type = <em>see below</em>;</span>
<span id="cb41-13"><a href="#cb41-13" aria-hidden="true" tabindex="-1"></a>    using stop_source_type = <em>see below</em>;</span>
<span id="cb41-14"><a href="#cb41-14" aria-hidden="true" tabindex="-1"></a>    using stop_token_type = decltype(declval&lt;stop_source_type&gt;().get_token());</span>
<span id="cb41-15"><a href="#cb41-15" aria-hidden="true" tabindex="-1"></a>    using error_types = <em>see below</em>;</span>
<span id="cb41-16"><a href="#cb41-16" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb41-17"><a href="#cb41-17" aria-hidden="true" tabindex="-1"></a>    // [task.promise]</span>
<span id="cb41-18"><a href="#cb41-18" aria-hidden="true" tabindex="-1"></a>    class promise_type;</span>
<span id="cb41-19"><a href="#cb41-19" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb41-20"><a href="#cb41-20" aria-hidden="true" tabindex="-1"></a>    task(task&amp;&amp;) noexcept;</span>
<span id="cb41-21"><a href="#cb41-21" aria-hidden="true" tabindex="-1"></a>    ~task();</span>
<span id="cb41-22"><a href="#cb41-22" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb41-23"><a href="#cb41-23" aria-hidden="true" tabindex="-1"></a>    template &lt;receiver R&gt;</span>
<span id="cb41-24"><a href="#cb41-24" aria-hidden="true" tabindex="-1"></a>    <em>state</em>&lt;R&gt; connect(R&amp;&amp; recv);</span>
<span id="cb41-25"><a href="#cb41-25" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb41-26"><a href="#cb41-26" aria-hidden="true" tabindex="-1"></a>  private:</span>
<span id="cb41-27"><a href="#cb41-27" aria-hidden="true" tabindex="-1"></a>    coroutine_handle&lt;promise_type&gt; <em>handle</em>; // <em>exposition only</em></span>
<span id="cb41-28"><a href="#cb41-28" aria-hidden="true" tabindex="-1"></a>  };</span>
<span id="cb41-29"><a href="#cb41-29" aria-hidden="true" tabindex="-1"></a>}</span></code></pre></div>
<p><span class="marginalizedparent"><a class="marginalized">1</a></span>
<code class="sourceCode cpp">task<span class="op">&lt;</span>T, E<span class="op">&gt;</span></code>
models <code class="sourceCode cpp">sender</code> <span>33.9
<a href="https://wg21.link/exec.snd">[exec.snd]</a></span> if
<code class="sourceCode cpp">T</code> is
<code class="sourceCode cpp"><span class="dt">void</span></code>, a
reference type, or an cv-unqualified non-array object type and
<code class="sourceCode cpp">E</code> is class type. Otherwise a program
that instantiates the definition of <code class="sourceCode cpp">task<span class="op">&lt;</span>T, E<span class="op">&gt;</span></code>
is ill-formed.</p>
<p><span class="marginalizedparent"><a class="marginalized">2</a></span>
The nested types of <code class="sourceCode cpp">task</code> template
specializations are determined based on the
<code class="sourceCode cpp">Environment</code> parameter:</p>
<ul>
<li><span class="marginalizedparent"><a class="marginalized">(2.1)</a></span>
<code class="sourceCode cpp">allocator_type</code> is <code class="sourceCode cpp">Environment<span class="op">::</span>allocator_type</code>
if that qualified-id is valid and denotes a type, <code class="sourceCode cpp">allocator<span class="op">&lt;</span>byte<span class="op">&gt;</span></code>
otherwise.</li>
<li><span class="marginalizedparent"><a class="marginalized">(2.2)</a></span>
<code class="sourceCode cpp">scheduler_type</code> is <code class="sourceCode cpp">Environment<span class="op">::</span>scheduler_type</code>
if that qualified-id is valid and denotes a type,
<code class="sourceCode cpp">task_scheduler</code> otherwise.</li>
<li><span class="marginalizedparent"><a class="marginalized">(2.3)</a></span>
<code class="sourceCode cpp">stop_source_type</code> is <code class="sourceCode cpp">Environment<span class="op">::</span>stop_source_type</code>
if that qualified-id is valid and denotes a type,
<code class="sourceCode cpp">inplace_stop_source</code> otherwise.</li>
<li><span class="marginalizedparent"><a class="marginalized">(2.4)</a></span>
<code class="sourceCode cpp">error_types</code> is <code class="sourceCode cpp">Environment<span class="op">::</span>error_types</code>
if that qualified-id is valid and denotes a type, <code class="sourceCode cpp">completion_signatures<span class="op">&lt;</span>set_error_t<span class="op">(</span>exception_ptr<span class="op">)&gt;</span></code>
otherwise.</li>
</ul>
<p><span class="marginalizedparent"><a class="marginalized">3</a></span>
A program is ill-formed if
<code class="sourceCode cpp">error_types</code> is not a specialization
of <code class="sourceCode cpp">completion_signatures<span class="op">&lt;</span>ErrorSigs<span class="op">...&gt;</span></code>
or <code class="sourceCode cpp">ErrorSigs</code> contains an element
which is not of the form <code class="sourceCode cpp">set_error_t<span class="op">(</span>E<span class="op">)</span></code>
for some type <code class="sourceCode cpp">E</code>.</p>
<p><span class="marginalizedparent"><a class="marginalized">4</a></span>
The type alias <code class="sourceCode cpp">completion_signatures</code>
is a specialization of <code class="sourceCode cpp">execution<span class="op">::</span>completion_signatures</code>
with the template arguments (in unspecified order):</p>
<ul>
<li><span class="marginalizedparent"><a class="marginalized">(4.1)</a></span>
<code class="sourceCode cpp">set_value_t<span class="op">()</span></code> if
<code class="sourceCode cpp">T</code> is
<code class="sourceCode cpp"><span class="dt">void</span></code>, and
<code class="sourceCode cpp">set_value_t<span class="op">(</span>T<span class="op">)</span></code>
otherwise;</li>
<li><span class="marginalizedparent"><a class="marginalized">(4.2)</a></span>
template arguments of the specialization of <code class="sourceCode cpp">execution<span class="op">::</span>completion_signatures</code>
denoted by <code class="sourceCode cpp">error_types</code>; and</li>
<li><span class="marginalizedparent"><a class="marginalized">(4.3)</a></span>
<code class="sourceCode cpp">set_stopped_t<span class="op">()</span></code>.</li>
</ul>
<p><span class="marginalizedparent"><a class="marginalized">5</a></span>
<code class="sourceCode cpp">allocator_type</code> shall meet the
<em>Cpp17Allocator</em> requirements.</p>
<h3 data-number="9.4.3" id="task-members-task.members"><span class="header-section-number">9.4.3</span> Task Members [task.members]<a href="#task-members-task.members" class="self-link"></a></h3>
<div class="sourceCode" id="cb42"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb42-1"><a href="#cb42-1" aria-hidden="true" tabindex="-1"></a>task(task&amp;&amp; other) noexcept;</span></code></pre></div>
<p><span class="marginalizedparent"><a class="marginalized">1</a></span>
<em>Effects:</em> Initializes
<code class="sourceCode cpp"><em>handle</em></code> with <code class="sourceCode cpp">exchange<span class="op">(</span>other<span class="op">.</span><em>handle</em>, <span class="op">{})</span></code>.</p>
<div class="sourceCode" id="cb43"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb43-1"><a href="#cb43-1" aria-hidden="true" tabindex="-1"></a>~task();</span></code></pre></div>
<p><span class="marginalizedparent"><a class="marginalized">2</a></span>
<em>Effects:</em> Equivalent to:</p>
<div class="sourceCode" id="cb44"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb44-1"><a href="#cb44-1" aria-hidden="true" tabindex="-1"></a>    if (<em>handle</em>)</span>
<span id="cb44-2"><a href="#cb44-2" aria-hidden="true" tabindex="-1"></a>      <em>handle</em>.destroy();</span></code></pre></div>
<div class="sourceCode" id="cb45"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb45-1"><a href="#cb45-1" aria-hidden="true" tabindex="-1"></a>template &lt;receiver R&gt;</span>
<span id="cb45-2"><a href="#cb45-2" aria-hidden="true" tabindex="-1"></a><em>state</em>&lt;R&gt; connect(R&amp;&amp; recv);</span></code></pre></div>
<p><span class="marginalizedparent"><a class="marginalized">3</a></span>
<em>Preconditions:</em> <code class="sourceCode cpp"><span class="dt">bool</span><span class="op">(</span><em>handle</em><span class="op">)</span></code>
is <code class="sourceCode cpp"><span class="kw">true</span></code>.</p>
<p><span class="marginalizedparent"><a class="marginalized">4</a></span>
<em>Effects:</em> Equivalent to: <code class="sourceCode cpp"><span class="cf">return</span> <em>state</em><span class="op">&lt;</span>R<span class="op">&gt;(</span>exchange<span class="op">(</span><em>handle</em>, <span class="op">{})</span>, std<span class="op">::</span>forward<span class="op">&lt;</span>R<span class="op">&gt;(</span>recv<span class="op">))</span>;</code></p>
<h3 data-number="9.4.4" id="class-template-taskstate-task.state"><span class="header-section-number">9.4.4</span> Class template task::state
[task.state]<a href="#class-template-taskstate-task.state" class="self-link"></a></h3>
<div class="sourceCode" id="cb46"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb46-1"><a href="#cb46-1" aria-hidden="true" tabindex="-1"></a>namespace std::execution {</span>
<span id="cb46-2"><a href="#cb46-2" aria-hidden="true" tabindex="-1"></a>  template &lt;class T, class Environment&gt;</span>
<span id="cb46-3"><a href="#cb46-3" aria-hidden="true" tabindex="-1"></a>    template &lt;receiver R&gt;</span>
<span id="cb46-4"><a href="#cb46-4" aria-hidden="true" tabindex="-1"></a>  class task&lt;T, Environment&gt;::<em>state</em> { // <em>exposition only</em></span>
<span id="cb46-5"><a href="#cb46-5" aria-hidden="true" tabindex="-1"></a>  public:</span>
<span id="cb46-6"><a href="#cb46-6" aria-hidden="true" tabindex="-1"></a>    using operation_state_concept = operation_state_t;</span>
<span id="cb46-7"><a href="#cb46-7" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb46-8"><a href="#cb46-8" aria-hidden="true" tabindex="-1"></a>    template &lt;class Rcvr&gt;</span>
<span id="cb46-9"><a href="#cb46-9" aria-hidden="true" tabindex="-1"></a>    <em>state</em>(coroutine_handle&lt;promise_type&gt; h, Rcvr&amp;&amp; rr);</span>
<span id="cb46-10"><a href="#cb46-10" aria-hidden="true" tabindex="-1"></a>    ~<em>state</em>();</span>
<span id="cb46-11"><a href="#cb46-11" aria-hidden="true" tabindex="-1"></a>    void start() &amp; noexcept;</span>
<span id="cb46-12"><a href="#cb46-12" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb46-13"><a href="#cb46-13" aria-hidden="true" tabindex="-1"></a>private:</span>
<span id="cb46-14"><a href="#cb46-14" aria-hidden="true" tabindex="-1"></a>    using <em>own-env-t</em> = <em>see below</em>;     // <em>exposition only</em></span>
<span id="cb46-15"><a href="#cb46-15" aria-hidden="true" tabindex="-1"></a>    coroutine_handle&lt;promise_type&gt; <em>handle</em>;  // <em>exposition only</em></span>
<span id="cb46-16"><a href="#cb46-16" aria-hidden="true" tabindex="-1"></a>    remove_cvref_t&lt;R&gt;              <em>rcvr</em>;    // <em>exposition only</em></span>
<span id="cb46-17"><a href="#cb46-17" aria-hidden="true" tabindex="-1"></a>    <em>own-env-t</em>                      <em>own-env</em>; // <em>exposition only</em></span>
<span id="cb46-18"><a href="#cb46-18" aria-hidden="true" tabindex="-1"></a>    Environment                    <em>environment</em>; // <em>exposition only</em></span>
<span id="cb46-19"><a href="#cb46-19" aria-hidden="true" tabindex="-1"></a>  };</span>
<span id="cb46-20"><a href="#cb46-20" aria-hidden="true" tabindex="-1"></a>}</span></code></pre></div>
<p><span class="marginalizedparent"><a class="marginalized">1</a></span>
The type <code class="sourceCode cpp"><em>own-env-t</em></code> is <code class="sourceCode cpp">Environment<span class="op">::</span><span class="kw">template</span> env_type<span class="op">&lt;</span><span class="kw">decltype</span><span class="op">(</span>get_env<span class="op">(</span>declval<span class="op">&lt;</span>R<span class="op">&gt;()))&gt;</span></code>
if that qualified-id is valid and denotes a type,
<code class="sourceCode cpp">env<span class="op">&lt;&gt;</span></code>
otherwise.</p>
<div class="sourceCode" id="cb47"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb47-1"><a href="#cb47-1" aria-hidden="true" tabindex="-1"></a>template &lt;class Rcvr&gt;</span>
<span id="cb47-2"><a href="#cb47-2" aria-hidden="true" tabindex="-1"></a><em>state</em>(coroutine_handle&lt;promise_type&gt; h, Rcvr&amp;&amp; rr);</span></code></pre></div>
<p><span class="marginalizedparent"><a class="marginalized">2</a></span>
<em>Effects:</em> Initializes</p>
<ul>
<li><span class="marginalizedparent"><a class="marginalized">(2.1)</a></span>
<code class="sourceCode cpp"><em>handle</em></code> with <code class="sourceCode cpp">std<span class="op">::</span>move<span class="op">(</span>h<span class="op">)</span></code>;</li>
<li><span class="marginalizedparent"><a class="marginalized">(2.2)</a></span>
<code class="sourceCode cpp"><em>rcvr</em></code> with <code class="sourceCode cpp">std<span class="op">::</span>forward<span class="op">&lt;</span>Rcvr<span class="op">&gt;(</span>rr<span class="op">)</span></code>;</li>
<li><span class="marginalizedparent"><a class="marginalized">(2.3)</a></span>
<code class="sourceCode cpp"><em>own-env</em></code> with <code class="sourceCode cpp"><em>own-env-t</em><span class="op">(</span>get_env<span class="op">(</span><em>rcvr</em><span class="op">))</span></code>
if that expression is valid and <code class="sourceCode cpp"><em>own-env-t</em><span class="op">()</span></code>
otherwise; If neither of these expressions is valid, the program is
ill-formed.</li>
<li><span class="marginalizedparent"><a class="marginalized">(2.4)</a></span>
<code class="sourceCode cpp"><em>environment</em></code> with <code class="sourceCode cpp">Environment<span class="op">(</span><em>own-env</em><span class="op">)</span></code>
if that expression is valid, otherwise <code class="sourceCode cpp">Environment<span class="op">(</span>get_env<span class="op">(</span><em>rcvr</em><span class="op">))</span></code>
if this expression is valid, otherwise <code class="sourceCode cpp">Environment<span class="op">()</span></code>. If
neither of these expressions is valid, the program is ill-formed.</li>
</ul>
<div class="sourceCode" id="cb48"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb48-1"><a href="#cb48-1" aria-hidden="true" tabindex="-1"></a>~<em>state</em>();</span></code></pre></div>
<p><span class="marginalizedparent"><a class="marginalized">3</a></span>
<em>Effects:</em> Equivalent to:</p>
<div class="sourceCode" id="cb49"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb49-1"><a href="#cb49-1" aria-hidden="true" tabindex="-1"></a>    if (<em>handle</em>)</span>
<span id="cb49-2"><a href="#cb49-2" aria-hidden="true" tabindex="-1"></a>      <em>handle</em>.destroy();</span></code></pre></div>
<div class="sourceCode" id="cb50"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb50-1"><a href="#cb50-1" aria-hidden="true" tabindex="-1"></a>void start() &amp; noexcept;</span></code></pre></div>
<p><span class="marginalizedparent"><a class="marginalized">4</a></span>
<em>Effects:</em> Let <code class="sourceCode cpp">prom</code> be the
object <code class="sourceCode cpp"><em>handle</em><span class="op">.</span>promise<span class="op">()</span></code>.
Associates <code class="sourceCode cpp"><em>STATE</em><span class="op">(</span>prom<span class="op">)</span></code>,
<code class="sourceCode cpp"><em>RCVR</em><span class="op">(</span>prom<span class="op">)</span></code>,
and <code class="sourceCode cpp"><em>SCHED</em><span class="op">(</span>prom<span class="op">)</span></code>
with <code class="sourceCode cpp"><span class="op">*</span><span class="kw">this</span></code>
as follows:</p>
<ul>
<li><span class="marginalizedparent"><a class="marginalized">(4.1)</a></span>
<code class="sourceCode cpp"><em>STATE</em><span class="op">(</span>prom<span class="op">)</span></code>
is <code class="sourceCode cpp"><span class="op">*</span><span class="kw">this</span></code>.</li>
<li><span class="marginalizedparent"><a class="marginalized">(4.2)</a></span>
<code class="sourceCode cpp"><em>RCVR</em><span class="op">(</span>prom<span class="op">)</span></code>
is <code class="sourceCode cpp"><em>rcvr</em></code>.</li>
<li><span class="marginalizedparent"><a class="marginalized">(4.3)</a></span>
<code class="sourceCode cpp"><em>SCHED</em><span class="op">(</span>prom<span class="op">)</span></code>
is the object initialized with <code class="sourceCode cpp">scheduler_type<span class="op">(</span>get_scheduler<span class="op">(</span>get_env<span class="op">(</span><em>rcvr</em><span class="op">)))</span></code>
if that expression is valid and <code class="sourceCode cpp">scheduler_type<span class="op">()</span></code>
otherwise. If neither of these expressions is valid, the program is
ill-formed.</li>
</ul>
<p>Let <code class="sourceCode cpp">st</code> be <code class="sourceCode cpp">get_stop_token<span class="op">(</span>get_env<span class="op">(</span><em>rcvr</em><span class="op">))</span></code>.
Initializes <code class="sourceCode cpp">prom<span class="op">.</span><em>token</em></code>
and <code class="sourceCode cpp">prom<span class="op">.</span><em>source</em></code>
such that</p>
<ul>
<li><span class="marginalizedparent"><a class="marginalized">(4.4)</a></span>
<code class="sourceCode cpp">prom<span class="op">.</span><em>token</em><span class="op">.</span>stop_requested<span class="op">()</span></code>
returns <code class="sourceCode cpp">st<span class="op">.</span>stop_requested<span class="op">()</span></code>;</li>
<li><span class="marginalizedparent"><a class="marginalized">(4.5)</a></span>
<code class="sourceCode cpp">prom<span class="op">.</span><em>token</em><span class="op">.</span>stop_possible<span class="op">()</span></code>
returns <code class="sourceCode cpp">st<span class="op">.</span>stop_possible<span class="op">()</span></code>;
and</li>
<li><span class="marginalizedparent"><a class="marginalized">(4.6)</a></span> for
types <code class="sourceCode cpp">Fn</code> and
<code class="sourceCode cpp">Init</code> such that both <code class="sourceCode cpp">invocable<span class="op">&lt;</span>Fn<span class="op">&gt;</span></code>
and <code class="sourceCode cpp">constructible_from<span class="op">&lt;</span>Fn, Init<span class="op">&gt;</span></code>
are modeled, <code class="sourceCode cpp">stop_token_type<span class="op">::</span>callback_type<span class="op">&lt;</span>Fn<span class="op">&gt;</span></code>
models <code class="sourceCode cpp"><em>stoppable-callback-for</em><span class="op">&lt;</span>Fn, stop_token_type, Init<span class="op">&gt;</span></code>.</li>
</ul>
<p>After that invokes <code class="sourceCode cpp"><em>handle</em><span class="op">.</span>resume<span class="op">()</span></code>.</p>
<h3 data-number="9.4.5" id="class-taskpromise_type-task.promise"><span class="header-section-number">9.4.5</span> Class task::promise_type
[task.promise]<a href="#class-taskpromise_type-task.promise" class="self-link"></a></h3>
<div class="sourceCode" id="cb51"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb51-1"><a href="#cb51-1" aria-hidden="true" tabindex="-1"></a>namespace std::execution {</span>
<span id="cb51-2"><a href="#cb51-2" aria-hidden="true" tabindex="-1"></a>  template &lt;class E&gt;</span>
<span id="cb51-3"><a href="#cb51-3" aria-hidden="true" tabindex="-1"></a>  struct with_error {</span>
<span id="cb51-4"><a href="#cb51-4" aria-hidden="true" tabindex="-1"></a>    using type = remove_cvref_t&lt;E&gt;;</span>
<span id="cb51-5"><a href="#cb51-5" aria-hidden="true" tabindex="-1"></a>    type error;</span>
<span id="cb51-6"><a href="#cb51-6" aria-hidden="true" tabindex="-1"></a>  };</span>
<span id="cb51-7"><a href="#cb51-7" aria-hidden="true" tabindex="-1"></a>  template &lt;class E&gt;</span>
<span id="cb51-8"><a href="#cb51-8" aria-hidden="true" tabindex="-1"></a>  with_error(E) -&gt; with_error&lt;E&gt;;</span>
<span id="cb51-9"><a href="#cb51-9" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb51-10"><a href="#cb51-10" aria-hidden="true" tabindex="-1"></a>  template &lt;scheduler Sch&gt;</span>
<span id="cb51-11"><a href="#cb51-11" aria-hidden="true" tabindex="-1"></a>  struct change_coroutine_scheduler {</span>
<span id="cb51-12"><a href="#cb51-12" aria-hidden="true" tabindex="-1"></a>    using type = remove_cvref_t&lt;Sch&gt;;</span>
<span id="cb51-13"><a href="#cb51-13" aria-hidden="true" tabindex="-1"></a>    type scheduler;</span>
<span id="cb51-14"><a href="#cb51-14" aria-hidden="true" tabindex="-1"></a>  };</span>
<span id="cb51-15"><a href="#cb51-15" aria-hidden="true" tabindex="-1"></a>  template &lt;scheduler Sch&gt;</span>
<span id="cb51-16"><a href="#cb51-16" aria-hidden="true" tabindex="-1"></a>  change_coroutine_scheduler(Sch) -&gt; change_coroutine_scheduler&lt;Sch&gt;;</span>
<span id="cb51-17"><a href="#cb51-17" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb51-18"><a href="#cb51-18" aria-hidden="true" tabindex="-1"></a>  template &lt;class T, class Environment&gt;</span>
<span id="cb51-19"><a href="#cb51-19" aria-hidden="true" tabindex="-1"></a>  class task&lt;T, Environment&gt;::promise_type {</span>
<span id="cb51-20"><a href="#cb51-20" aria-hidden="true" tabindex="-1"></a>  public:</span>
<span id="cb51-21"><a href="#cb51-21" aria-hidden="true" tabindex="-1"></a>    template &lt;class... Args&gt;</span>
<span id="cb51-22"><a href="#cb51-22" aria-hidden="true" tabindex="-1"></a>    promise_type(const Args&amp;... args);</span>
<span id="cb51-23"><a href="#cb51-23" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb51-24"><a href="#cb51-24" aria-hidden="true" tabindex="-1"></a>    task get_return_object() noexcept;</span>
<span id="cb51-25"><a href="#cb51-25" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb51-26"><a href="#cb51-26" aria-hidden="true" tabindex="-1"></a>    auto initial_suspend() noexcept;</span>
<span id="cb51-27"><a href="#cb51-27" aria-hidden="true" tabindex="-1"></a>    auto final_suspend() noexcept;</span>
<span id="cb51-28"><a href="#cb51-28" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb51-29"><a href="#cb51-29" aria-hidden="true" tabindex="-1"></a>    void uncaught_exception();</span>
<span id="cb51-30"><a href="#cb51-30" aria-hidden="true" tabindex="-1"></a>    coroutine_handle&lt;&gt; unhandled_stopped();</span>
<span id="cb51-31"><a href="#cb51-31" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb51-32"><a href="#cb51-32" aria-hidden="true" tabindex="-1"></a>    void return_void(); // present only if is_void_v&lt;T&gt; is true;</span>
<span id="cb51-33"><a href="#cb51-33" aria-hidden="true" tabindex="-1"></a>    template &lt;class V&gt;</span>
<span id="cb51-34"><a href="#cb51-34" aria-hidden="true" tabindex="-1"></a>    void return_value(V&amp;&amp; value); // present only if is_void_v&lt;T&gt; is false;</span>
<span id="cb51-35"><a href="#cb51-35" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb51-36"><a href="#cb51-36" aria-hidden="true" tabindex="-1"></a>    template &lt;class E&gt;</span>
<span id="cb51-37"><a href="#cb51-37" aria-hidden="true" tabindex="-1"></a>    <em>unspecified</em> yield_value(with_error&lt;E&gt; error);</span>
<span id="cb51-38"><a href="#cb51-38" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb51-39"><a href="#cb51-39" aria-hidden="true" tabindex="-1"></a>    template &lt;class A&gt;</span>
<span id="cb51-40"><a href="#cb51-40" aria-hidden="true" tabindex="-1"></a>    auto await_transform(A&amp;&amp; a);</span>
<span id="cb51-41"><a href="#cb51-41" aria-hidden="true" tabindex="-1"></a>    template &lt;class Sch&gt;</span>
<span id="cb51-42"><a href="#cb51-42" aria-hidden="true" tabindex="-1"></a>    auto await_transform(change_coroutine_scheduler&lt;Sch&gt; sch);</span>
<span id="cb51-43"><a href="#cb51-43" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb51-44"><a href="#cb51-44" aria-hidden="true" tabindex="-1"></a>    <em>unspecified</em> get_env() const noexcept;</span>
<span id="cb51-45"><a href="#cb51-45" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb51-46"><a href="#cb51-46" aria-hidden="true" tabindex="-1"></a>    template &lt;class... Args&gt;</span>
<span id="cb51-47"><a href="#cb51-47" aria-hidden="true" tabindex="-1"></a>    void* operator new(size_t size, Args&amp;&amp;... args);</span>
<span id="cb51-48"><a href="#cb51-48" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb51-49"><a href="#cb51-49" aria-hidden="true" tabindex="-1"></a>    void operator delete(void* pointer, size_t size) noexcept;</span>
<span id="cb51-50"><a href="#cb51-50" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb51-51"><a href="#cb51-51" aria-hidden="true" tabindex="-1"></a>  private:</span>
<span id="cb51-52"><a href="#cb51-52" aria-hidden="true" tabindex="-1"></a>    using <em>error-variant</em> = <em>see below</em>; // <em>exposition only</em></span>
<span id="cb51-53"><a href="#cb51-53" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb51-54"><a href="#cb51-54" aria-hidden="true" tabindex="-1"></a>    allocator_type    <em>alloc</em>;  // <em>exposition only</em></span>
<span id="cb51-55"><a href="#cb51-55" aria-hidden="true" tabindex="-1"></a>    stop_source_type  <em>source</em>; // <em>exposition only</em></span>
<span id="cb51-56"><a href="#cb51-56" aria-hidden="true" tabindex="-1"></a>    stop_token_type   <em>token</em>;  // <em>exposition only</em></span>
<span id="cb51-57"><a href="#cb51-57" aria-hidden="true" tabindex="-1"></a>    optional&lt;T&gt;       <em>result</em>; // <em>exposition only</em>; present only if is_void_v&lt;T&gt; is false;</span>
<span id="cb51-58"><a href="#cb51-58" aria-hidden="true" tabindex="-1"></a>    <em>error-variant</em>      <em>errors</em>; // <em>exposition only</em></span>
<span id="cb51-59"><a href="#cb51-59" aria-hidden="true" tabindex="-1"></a>  };</span>
<span id="cb51-60"><a href="#cb51-60" aria-hidden="true" tabindex="-1"></a>}</span></code></pre></div>
<p><span class="marginalizedparent"><a class="marginalized">1</a></span>
Let <code class="sourceCode cpp">prom</code> be an object of
<code class="sourceCode cpp">promise_type</code> and let
<code class="sourceCode cpp">tsk</code> be the
<code class="sourceCode cpp">task</code> object created by <code class="sourceCode cpp">prom<span class="op">.</span>get_return_object<span class="op">()</span></code>.
The description below refers to objects <code class="sourceCode cpp"><em>STATE</em><span class="op">(</span>prom<span class="op">)</span></code>,
<code class="sourceCode cpp"><em>RCVR</em><span class="op">(</span>prom<span class="op">)</span></code>,
and <code class="sourceCode cpp"><em>SCHED</em><span class="op">(</span>prom<span class="op">)</span></code>
associated with <code class="sourceCode cpp">tsk</code> during
evalutation of <code class="sourceCode cpp">task<span class="op">::</span><em>state</em><span class="op">&lt;</span>Rcvr<span class="op">&gt;::</span>start</code>
for some receiver <code class="sourceCode cpp">Rcvr</code> <a href="https://wg21.link/task.state">[task.state]</a>.</p>
<p><span class="marginalizedparent"><a class="marginalized">2</a></span>
<code class="sourceCode cpp"><em>error-variant</em></code> is a <code class="sourceCode cpp">variant<span class="op">&lt;</span>monostate, remove_cvref_t<span class="op">&lt;</span>E<span class="op">&gt;...&gt;</span></code>,
with duplicate types removed, where
<code class="sourceCode cpp">E<span class="op">...</span></code> are
template arguments of the specialization of <code class="sourceCode cpp">execution<span class="op">::</span>completion_signatures</code>
denoted by <code class="sourceCode cpp">error_types</code>.</p>
<div class="sourceCode" id="cb52"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb52-1"><a href="#cb52-1" aria-hidden="true" tabindex="-1"></a>template &lt;class... Args&gt;</span>
<span id="cb52-2"><a href="#cb52-2" aria-hidden="true" tabindex="-1"></a>promise_type(const Args&amp;... args);</span></code></pre></div>
<p><span class="marginalizedparent"><a class="marginalized">3</a></span>
<em>Mandates:</em> The first parameter of type
<code class="sourceCode cpp">allocator_arg_t</code> (if any) is not the
last parameter.</p>
<p><span class="marginalizedparent"><a class="marginalized">4</a></span>
<em>Effects:</em> If <code class="sourceCode cpp">Args</code> contains
an element of type <code class="sourceCode cpp">allocator_arg_t</code>
then <code class="sourceCode cpp"><em>alloc</em></code> is initialized
with the corresponding next element of
<code class="sourceCode cpp">args</code>. Otherwise,
<code class="sourceCode cpp"><em>alloc</em></code> is initialized with
<code class="sourceCode cpp">allocator_type<span class="op">()</span></code>.</p>
<div class="sourceCode" id="cb53"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb53-1"><a href="#cb53-1" aria-hidden="true" tabindex="-1"></a>task get_return_object() noexcept;</span></code></pre></div>
<p><span class="marginalizedparent"><a class="marginalized">5</a></span>
<em>Returns:</em> A <code class="sourceCode cpp">task</code> object
whose member <code class="sourceCode cpp"><em>handle</em></code> is
<code class="sourceCode cpp">coroutine_handle<span class="op">&lt;</span>promise_type<span class="op">&gt;::</span>from_promise<span class="op">(*</span><span class="kw">this</span><span class="op">)</span></code>.</p>
<div class="sourceCode" id="cb54"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb54-1"><a href="#cb54-1" aria-hidden="true" tabindex="-1"></a>auto initial_suspend() noexcept;</span></code></pre></div>
<p><span class="marginalizedparent"><a class="marginalized">6</a></span>
<em>Returns:</em> An awaitable object of unspecified type ([expr.await])
whose member functions arrange for</p>
<ul>
<li><span class="marginalizedparent"><a class="marginalized">(6.1)</a></span> the
calling coroutine to be suspended,</li>
<li><span class="marginalizedparent"><a class="marginalized">(6.2)</a></span> the
coroutine to be resumed on an execution agent of the execution resource
associated with <code class="sourceCode cpp"><em>SCHED</em><span class="op">(*</span><span class="kw">this</span><span class="op">)</span></code>.</li>
</ul>
<div class="sourceCode" id="cb55"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb55-1"><a href="#cb55-1" aria-hidden="true" tabindex="-1"></a>auto final_suspend() noexcept;</span></code></pre></div>
<p><span class="marginalizedparent"><a class="marginalized">7</a></span>
<em>Returns:</em> An awaitable object of unspecified type ([expr.await])
whose member functions arrange for the completion of the asynchronous
operation associated with <code class="sourceCode cpp"><em>STATE</em><span class="op">(*</span><span class="kw">this</span><span class="op">)</span></code>
by invoking:</p>
<ul>
<li><span class="marginalizedparent"><a class="marginalized">(7.1)</a></span>
<code class="sourceCode cpp">set_error<span class="op">(</span>std<span class="op">::</span>move<span class="op">(</span><em>RCVR</em><span class="op">(*</span><span class="kw">this</span><span class="op">))</span>,</code>
<code class="sourceCode cpp">std<span class="op">::</span>move<span class="op">(</span>e<span class="op">))</span></code>
if <code class="sourceCode cpp"><em>errors</em><span class="op">.</span>index<span class="op">()</span></code>
is greater than zero and <code class="sourceCode cpp">e</code> is the
value held by <code class="sourceCode cpp"><em>errors</em></code>,
otherwise</li>
<li><span class="marginalizedparent"><a class="marginalized">(7.2)</a></span>
<code class="sourceCode cpp">set_value<span class="op">(</span>std<span class="op">::</span>move<span class="op">(</span><em>RCVR</em><span class="op">(*</span><span class="kw">this</span><span class="op">)))</span></code>
if <code class="sourceCode cpp">is_void<span class="op">&lt;</span>T<span class="op">&gt;</span></code>
is <code class="sourceCode cpp"><span class="kw">true</span></code>, and
otherwise</li>
<li><span class="marginalizedparent"><a class="marginalized">(7.3)</a></span>
<code class="sourceCode cpp">set_value<span class="op">(</span>std<span class="op">::</span>move<span class="op">(</span><em>RCVR</em><span class="op">(*</span><span class="kw">this</span><span class="op">))</span>, <span class="op">*</span><em>result</em><span class="op">)</span></code>.</li>
</ul>
<div class="sourceCode" id="cb56"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb56-1"><a href="#cb56-1" aria-hidden="true" tabindex="-1"></a>template &lt;class Err&gt;</span>
<span id="cb56-2"><a href="#cb56-2" aria-hidden="true" tabindex="-1"></a>auto yield_value(with_error&lt;Err&gt; err);</span></code></pre></div>
<p><span class="marginalizedparent"><a class="marginalized">8</a></span>
<em>Mandates</em> <code class="sourceCode cpp">std<span class="op">::</span>move<span class="op">(</span>err<span class="op">.</span>error<span class="op">)</span></code>
is convertible to exactly one of the
<code class="sourceCode cpp">set_error_t</code> argument types of
<code class="sourceCode cpp">error_types</code>. Let
<code class="sourceCode cpp">Cerr</code> be that type.</p>
<p><span class="marginalizedparent"><a class="marginalized">9</a></span>
<em>Returns:</em> An awaitable object of unspecified type ([expr.await])
whose member functions arrange for the calling coroutine to be suspended
and then completes the asynchronous operation associated with <code class="sourceCode cpp"><em>STATE</em><span class="op">(*</span><span class="kw">this</span><span class="op">)</span></code>
by invoking <code class="sourceCode cpp">set_error<span class="op">(</span>std<span class="op">::</span>move<span class="op">(</span><em>RCVR</em><span class="op">(*</span><span class="kw">this</span><span class="op">))</span>, Cerr<span class="op">(</span>std<span class="op">::</span>move<span class="op">(</span>err<span class="op">.</span>error<span class="op">)))</span></code>.</p>
<div class="sourceCode" id="cb57"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb57-1"><a href="#cb57-1" aria-hidden="true" tabindex="-1"></a>template &lt;sender Sender&gt;</span>
<span id="cb57-2"><a href="#cb57-2" aria-hidden="true" tabindex="-1"></a>auto await_transform(Sender&amp;&amp; sndr) noexcept;</span></code></pre></div>
<p><span class="marginalizedparent"><a class="marginalized">10</a></span>
<em>Returns</em>: If <code class="sourceCode cpp">same_as<span class="op">&lt;</span>inline_scheduler, scheduler_type<span class="op">&gt;</span></code>
is <code class="sourceCode cpp"><span class="kw">true</span></code>
returns <code class="sourceCode cpp">as_awaitable<span class="op">(</span>std<span class="op">::</span>forward<span class="op">&lt;</span>Sender<span class="op">&gt;(</span>sndr<span class="op">)</span>,</code>
<code class="sourceCode cpp"><span class="op">*</span><span class="kw">this</span><span class="op">)</span></code>;
otherwise returns <code class="sourceCode cpp">as_awaitable<span class="op">(</span>affine_on<span class="op">(</span>std<span class="op">::</span>forward<span class="op">&lt;</span>Sender<span class="op">&gt;(</span>sndr<span class="op">)</span>, <em>SCHED</em><span class="op">(*</span><span class="kw">this</span><span class="op">))</span>, <span class="op">*</span><span class="kw">this</span><span class="op">)</span></code>.</p>
<div class="sourceCode" id="cb58"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb58-1"><a href="#cb58-1" aria-hidden="true" tabindex="-1"></a>template &lt;class Sch&gt;</span>
<span id="cb58-2"><a href="#cb58-2" aria-hidden="true" tabindex="-1"></a>auto await_transform(change_coroutine_scheduler&lt;Sch&gt; sch) noexcept;</span></code></pre></div>
<p><span class="marginalizedparent"><a class="marginalized">11</a></span>
<em>Effects:</em> Equivalent to: <code class="sourceCode cpp">returns await_transform<span class="op">(</span>just<span class="op">(</span>exchange<span class="op">(</span><em>SCHED</em><span class="op">(*</span><span class="kw">this</span><span class="op">)</span>, scheduler_type<span class="op">(</span>sch<span class="op">.</span>scheduler<span class="op">)))</span>, <span class="op">*</span><span class="kw">this</span><span class="op">)</span>;</code></p>
<div class="sourceCode" id="cb59"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb59-1"><a href="#cb59-1" aria-hidden="true" tabindex="-1"></a>void uncaught_exception();</span></code></pre></div>
<p><span class="marginalizedparent"><a class="marginalized">12</a></span>
<em>Effects:</em> If the signature <code class="sourceCode cpp">set_error_t<span class="op">(</span>exception_ptr<span class="op">)</span></code>
is not an element of <code class="sourceCode cpp">error_types</code>,
calls
<code class="sourceCode cpp">terminate<span class="op">()</span></code>
(<span>14.6.2
<a href="https://wg21.link/except.terminate">[except.terminate]</a></span>).
Otherwise, stores <code class="sourceCode cpp">current_exception<span class="op">()</span></code>
into <code class="sourceCode cpp"><em>errors</em></code>.</p>
<div class="sourceCode" id="cb60"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb60-1"><a href="#cb60-1" aria-hidden="true" tabindex="-1"></a>coroutine_handle&lt;&gt; unhandled_stopped();</span></code></pre></div>
<p><span class="marginalizedparent"><a class="marginalized">13</a></span>
<em>Effects:</em> Completes the asynchronous operation associated with
<code class="sourceCode cpp"><em>STATE</em><span class="op">(*</span><span class="kw">this</span><span class="op">)</span></code>
by invoking <code class="sourceCode cpp">set_stopped<span class="op">(</span>std<span class="op">::</span>move<span class="op">(</span><em>RCVR</em><span class="op">(*</span><span class="kw">this</span><span class="op">)))</span></code>.</p>
<p><span class="marginalizedparent"><a class="marginalized">14</a></span>
<em>Returns:</em> <code class="sourceCode cpp">noop_coroutine<span class="op">()</span></code>.</p>
<div class="sourceCode" id="cb61"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb61-1"><a href="#cb61-1" aria-hidden="true" tabindex="-1"></a><em>unspecified</em> get_env() const noexcept;</span></code></pre></div>
<p><span class="marginalizedparent"><a class="marginalized">15</a></span>
<em>Returns:</em> An object <code class="sourceCode cpp">env</code> such
that queries are forwarded as follows:</p>
<ul>
<li><span class="marginalizedparent"><a class="marginalized">(15.1)</a></span>
<code class="sourceCode cpp">env<span class="op">.</span>query<span class="op">(</span>get_scheduler<span class="op">)</span></code>
returns <code class="sourceCode cpp">scheduler_type<span class="op">(</span><em>SCHED</em><span class="op">(*</span><span class="kw">this</span><span class="op">))</span></code>.</li>
<li><span class="marginalizedparent"><a class="marginalized">(15.2)</a></span>
<code class="sourceCode cpp">env<span class="op">.</span>query<span class="op">(</span>get_allocator<span class="op">)</span></code>
returns <code class="sourceCode cpp"><em>alloc</em></code>.</li>
<li><span class="marginalizedparent"><a class="marginalized">(15.3)</a></span>
<code class="sourceCode cpp">env<span class="op">.</span>query<span class="op">(</span>get_stop_token<span class="op">)</span></code>
returns <code class="sourceCode cpp"><em>token</em></code>.</li>
<li><span class="marginalizedparent"><a class="marginalized">(15.4)</a></span> For
any other query <code class="sourceCode cpp">q</code> and arguments
<code class="sourceCode cpp">a<span class="op">...</span></code> a call
to <code class="sourceCode cpp">env<span class="op">.</span>query<span class="op">(</span>q, a<span class="op">...)</span></code>
returns <code class="sourceCode cpp"><em>STATE</em><span class="op">(*</span><span class="kw">this</span><span class="op">).</span>environment<span class="op">.</span>query<span class="op">(</span>q, a<span class="op">...)</span></code>
if this expression is well-formed and <code class="sourceCode cpp">forwarding_query<span class="op">(</span>q<span class="op">)</span></code>
is well-formed and is
<code class="sourceCode cpp"><span class="kw">true</span></code>.
Otherwise <code class="sourceCode cpp">env<span class="op">.</span>query<span class="op">(</span>q, a<span class="op">...)</span></code>
is ill-formed.</li>
</ul>
<div class="sourceCode" id="cb62"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb62-1"><a href="#cb62-1" aria-hidden="true" tabindex="-1"></a>template &lt;class... Args&gt;</span>
<span id="cb62-2"><a href="#cb62-2" aria-hidden="true" tabindex="-1"></a>void* operator new(size_t size, const Args&amp;... args);</span></code></pre></div>
<p><span class="marginalizedparent"><a class="marginalized">16</a></span> If
there is no parameter with type
<code class="sourceCode cpp">allocator_arg_t</code> then let
<code class="sourceCode cpp">alloc</code> be
<code class="sourceCode cpp">Allocator<span class="op">()</span></code>.
Let <code class="sourceCode cpp">arg_next</code> be the parameter
following the first <code class="sourceCode cpp">allocator_arg_t</code>
parameter (if any) and let <code class="sourceCode cpp">alloc</code> be
<code class="sourceCode cpp">Allocator<span class="op">(</span>arg_next<span class="op">)</span></code>.
Then <code class="sourceCode cpp">PAlloc</code> is <code class="sourceCode cpp">allocator_traits<span class="op">&lt;</span>Allocator<span class="op">&gt;::</span><span class="kw">template</span> rebind_alloc<span class="op">&lt;</span>U<span class="op">&gt;</span></code>
where <code class="sourceCode cpp">U</code> is an unspecified type whose
size and alignment are both <code class="sourceCode cpp"><span class="ot">__STDCPP_DEFAULT_NEW_ALIGNMENT__</span></code>.</p>
<p><span class="marginalizedparent"><a class="marginalized">17</a></span>
<em>Mandates:</em></p>
<ul>
<li><span class="marginalizedparent"><a class="marginalized">(17.1)</a></span> The
first parameter of type
<code class="sourceCode cpp">allocator_arg_t</code> (if any) is not the
last parameter.</li>
<li><span class="marginalizedparent"><a class="marginalized">(17.2)</a></span>
<code class="sourceCode cpp">Allocator<span class="op">(</span>arg_next<span class="op">)</span></code>
is a valid expression if there is a parameter of type
<code class="sourceCode cpp">allocator_arg_t</code>.</li>
<li><span class="marginalizedparent"><a class="marginalized">(17.3)</a></span>
<code class="sourceCode cpp">allocator_traits<span class="op">&lt;</span>PAlloc<span class="op">&gt;::</span>pointer</code>
is a pointer type.</li>
</ul>
<p><span class="marginalizedparent"><a class="marginalized">18</a></span>
<em>Effects:</em> Initializes an allocator
<code class="sourceCode cpp">palloc</code> of type
<code class="sourceCode cpp">PAlloc</code> with
<code class="sourceCode cpp">alloc</code>. Uses
<code class="sourceCode cpp">palloc</code> to allocate storage for the
smallest array of <code class="sourceCode cpp">U</code> sufficient to
provide storage for a coroutine state of size
<code class="sourceCode cpp">size</code>, and unspecified additional
state necessary to ensure that <code class="sourceCode cpp"><span class="kw">operator</span> <span class="kw">delete</span></code>
can later deallocate this memory block with an allocator equal to
<code class="sourceCode cpp">palloc</code>.</p>
<p><span class="marginalizedparent"><a class="marginalized">19</a></span>
<em>Returns:</em> A pointer to the allocated storage.</p>
<div class="sourceCode" id="cb63"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb63-1"><a href="#cb63-1" aria-hidden="true" tabindex="-1"></a>void operator delete(void* pointer, size_t size) noexcept;</span></code></pre></div>
<p><span class="marginalizedparent"><a class="marginalized">20</a></span>
<em>Preconditions:</em> <code class="sourceCode cpp">pointer</code> was
returned from an invocation of the above overload of <code class="sourceCode cpp"><span class="kw">operator</span> <span class="kw">new</span></code>
with a size argument equal to
<code class="sourceCode cpp">size</code>.</p>
<p><span class="marginalizedparent"><a class="marginalized">21</a></span>
<em>Effects:</em> Deallocates the storage pointed to by
<code class="sourceCode cpp">pointer</code> using an allocator equal to
that used to allocate it.</p>
</div>
</div>
</body>
</html>
