<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" lang xml:lang>
<head>
  <meta charset="utf-8" />
  <meta name="generator" content="mpark/wg21" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes" />
  <meta name="dcterms.date" content="2020-12-14" />
  <title>Composable cancellation for sender-based async operations</title>
  <style>
      code{white-space: pre-wrap;}
      span.smallcaps{font-variant: small-caps;}
      span.underline{text-decoration: underline;}
      div.column{display: inline-block; vertical-align: top; width: 50%;}
      div.hanging-indent{margin-left: 1.5em; text-indent: -1.5em;}
      ul.task-list{list-style: none;}
      pre > code.sourceCode { white-space: pre; position: relative; }
      pre > code.sourceCode > span { display: inline-block; line-height: 1.25; }
      pre > code.sourceCode > span:empty { height: 1.2em; }
      code.sourceCode > span { color: inherit; text-decoration: inherit; }
      div.sourceCode { margin: 1em 0; }
      pre.sourceCode { margin: 0; }
      @media screen {
      div.sourceCode { overflow: auto; }
      }
      @media print {
      pre > code.sourceCode { white-space: pre-wrap; }
      pre > code.sourceCode > span { text-indent: -5em; padding-left: 5em; }
      }
      pre.numberSource code
        { counter-reset: source-line 0; }
      pre.numberSource code > span
        { position: relative; left: -4em; counter-increment: source-line; }
      pre.numberSource code > span > a:first-child::before
        { content: counter(source-line);
          position: relative; left: -1em; text-align: right; vertical-align: baseline;
          border: none; display: inline-block;
          -webkit-touch-callout: none; -webkit-user-select: none;
          -khtml-user-select: none; -moz-user-select: none;
          -ms-user-select: none; user-select: none;
          padding: 0 4px; width: 4em;
          color: #aaaaaa;
        }
      pre.numberSource { margin-left: 3em; border-left: 1px solid #aaaaaa;  padding-left: 4px; }
      div.sourceCode
        {  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>
  <link rel="stylesheet" href="data:text/css,%0Abody%20%7B%0Amargin%3A%205em%3B%0Afont%2Dfamily%3A%20serif%3B%0A%0Ahyphens%3A%20auto%3B%0Aline%2Dheight%3A%201%2E35%3B%0A%7D%0Adiv%2Ewrapper%20%7B%0Amax%2Dwidth%3A%2060em%3B%0Amargin%3A%20auto%3B%0A%7D%0Aul%20%7B%0Alist%2Dstyle%2Dtype%3A%20none%3B%0Apadding%2Dleft%3A%202em%3B%0Amargin%2Dtop%3A%20%2D0%2E2em%3B%0Amargin%2Dbottom%3A%20%2D0%2E2em%3B%0A%7D%0Aa%20%7B%0Atext%2Ddecoration%3A%20none%3B%0Acolor%3A%20%234183C4%3B%0A%7D%0Aa%2Ehidden%5Flink%20%7B%0Atext%2Ddecoration%3A%20none%3B%0Acolor%3A%20inherit%3B%0A%7D%0Ali%20%7B%0Amargin%2Dtop%3A%200%2E6em%3B%0Amargin%2Dbottom%3A%200%2E6em%3B%0A%7D%0Ah1%2C%20h2%2C%20h3%2C%20h4%20%7B%0Aposition%3A%20relative%3B%0Aline%2Dheight%3A%201%3B%0A%7D%0Aa%2Eself%2Dlink%20%7B%0Aposition%3A%20absolute%3B%0Atop%3A%200%3B%0Aleft%3A%20calc%28%2D1%20%2A%20%283%2E5rem%20%2D%2026px%29%29%3B%0Awidth%3A%20calc%283%2E5rem%20%2D%2026px%29%3B%0Aheight%3A%202em%3B%0Atext%2Dalign%3A%20center%3B%0Aborder%3A%20none%3B%0Atransition%3A%20opacity%20%2E2s%3B%0Aopacity%3A%20%2E5%3B%0Afont%2Dfamily%3A%20sans%2Dserif%3B%0Afont%2Dweight%3A%20normal%3B%0Afont%2Dsize%3A%2083%25%3B%0A%7D%0Aa%2Eself%2Dlink%3Ahover%20%7B%20opacity%3A%201%3B%20%7D%0Aa%2Eself%2Dlink%3A%3Abefore%20%7B%20content%3A%20%22%C2%A7%22%3B%20%7D%0Aul%20%3E%20li%3Abefore%20%7B%0Acontent%3A%20%22%5C2014%22%3B%0Aposition%3A%20absolute%3B%0Amargin%2Dleft%3A%20%2D1%2E5em%3B%0A%7D%0A%3Atarget%20%7B%20background%2Dcolor%3A%20%23C9FBC9%3B%20%7D%0A%3Atarget%20%2Ecodeblock%20%7B%20background%2Dcolor%3A%20%23C9FBC9%3B%20%7D%0A%3Atarget%20ul%20%7B%20background%2Dcolor%3A%20%23C9FBC9%3B%20%7D%0A%2Eabbr%5Fref%20%7B%20float%3A%20right%3B%20%7D%0A%2Efolded%5Fabbr%5Fref%20%7B%20float%3A%20right%3B%20%7D%0A%3Atarget%20%2Efolded%5Fabbr%5Fref%20%7B%20display%3A%20none%3B%20%7D%0A%3Atarget%20%2Eunfolded%5Fabbr%5Fref%20%7B%20float%3A%20right%3B%20display%3A%20inherit%3B%20%7D%0A%2Eunfolded%5Fabbr%5Fref%20%7B%20display%3A%20none%3B%20%7D%0A%2Esecnum%20%7B%20display%3A%20inline%2Dblock%3B%20min%2Dwidth%3A%2035pt%3B%20%7D%0A%2Eheader%2Dsection%2Dnumber%20%7B%20display%3A%20inline%2Dblock%3B%20min%2Dwidth%3A%2035pt%3B%20%7D%0A%2Eannexnum%20%7B%20display%3A%20block%3B%20%7D%0Adiv%2EsourceLinkParent%20%7B%0Afloat%3A%20right%3B%0A%7D%0Aa%2EsourceLink%20%7B%0Aposition%3A%20absolute%3B%0Aopacity%3A%200%3B%0Amargin%2Dleft%3A%2010pt%3B%0A%7D%0Aa%2EsourceLink%3Ahover%20%7B%0Aopacity%3A%201%3B%0A%7D%0Aa%2EitemDeclLink%20%7B%0Aposition%3A%20absolute%3B%0Afont%2Dsize%3A%2075%25%3B%0Atext%2Dalign%3A%20right%3B%0Awidth%3A%205em%3B%0Aopacity%3A%200%3B%0A%7D%0Aa%2EitemDeclLink%3Ahover%20%7B%20opacity%3A%201%3B%20%7D%0Aspan%2Emarginalizedparent%20%7B%0Aposition%3A%20relative%3B%0Aleft%3A%20%2D5em%3B%0A%7D%0Ali%20span%2Emarginalizedparent%20%7B%20left%3A%20%2D7em%3B%20%7D%0Ali%20ul%20%3E%20li%20span%2Emarginalizedparent%20%7B%20left%3A%20%2D9em%3B%20%7D%0Ali%20ul%20%3E%20li%20ul%20%3E%20li%20span%2Emarginalizedparent%20%7B%20left%3A%20%2D11em%3B%20%7D%0Ali%20ul%20%3E%20li%20ul%20%3E%20li%20ul%20%3E%20li%20span%2Emarginalizedparent%20%7B%20left%3A%20%2D13em%3B%20%7D%0Adiv%2EfootnoteNumberParent%20%7B%0Aposition%3A%20relative%3B%0Aleft%3A%20%2D4%2E7em%3B%0A%7D%0Aa%2Emarginalized%20%7B%0Aposition%3A%20absolute%3B%0Afont%2Dsize%3A%2075%25%3B%0Atext%2Dalign%3A%20right%3B%0Awidth%3A%205em%3B%0A%7D%0Aa%2Eenumerated%5Fitem%5Fnum%20%7B%0Aposition%3A%20relative%3B%0Aleft%3A%20%2D3%2E5em%3B%0Adisplay%3A%20inline%2Dblock%3B%0Amargin%2Dright%3A%20%2D3em%3B%0Atext%2Dalign%3A%20right%3B%0Awidth%3A%203em%3B%0A%7D%0Adiv%2Epara%20%7B%20margin%2Dbottom%3A%200%2E6em%3B%20margin%2Dtop%3A%200%2E6em%3B%20text%2Dalign%3A%20justify%3B%20%7D%0Adiv%2Esection%20%7B%20text%2Dalign%3A%20justify%3B%20%7D%0Adiv%2Esentence%20%7B%20display%3A%20inline%3B%20%7D%0Aspan%2Eindexparent%20%7B%0Adisplay%3A%20inline%3B%0Aposition%3A%20relative%3B%0Afloat%3A%20right%3B%0Aright%3A%20%2D1em%3B%0A%7D%0Aa%2Eindex%20%7B%0Aposition%3A%20absolute%3B%0Adisplay%3A%20none%3B%0A%7D%0Aa%2Eindex%3Abefore%20%7B%20content%3A%20%22%E2%9F%B5%22%3B%20%7D%0A%0Aa%2Eindex%3Atarget%20%7B%0Adisplay%3A%20inline%3B%0A%7D%0A%2Eindexitems%20%7B%0Amargin%2Dleft%3A%202em%3B%0Atext%2Dindent%3A%20%2D2em%3B%0A%7D%0Adiv%2Eitemdescr%20%7B%0Amargin%2Dleft%3A%203em%3B%0A%7D%0A%2Ebnf%20%7B%0Afont%2Dfamily%3A%20serif%3B%0Amargin%2Dleft%3A%2040pt%3B%0Amargin%2Dtop%3A%200%2E5em%3B%0Amargin%2Dbottom%3A%200%2E5em%3B%0A%7D%0A%2Encbnf%20%7B%0Afont%2Dfamily%3A%20serif%3B%0Amargin%2Dtop%3A%200%2E5em%3B%0Amargin%2Dbottom%3A%200%2E5em%3B%0Amargin%2Dleft%3A%2040pt%3B%0A%7D%0A%2Encsimplebnf%20%7B%0Afont%2Dfamily%3A%20serif%3B%0Afont%2Dstyle%3A%20italic%3B%0Amargin%2Dtop%3A%200%2E5em%3B%0Amargin%2Dbottom%3A%200%2E5em%3B%0Amargin%2Dleft%3A%2040pt%3B%0Abackground%3A%20inherit%3B%20%0A%7D%0Aspan%2Etextnormal%20%7B%0Afont%2Dstyle%3A%20normal%3B%0Afont%2Dfamily%3A%20serif%3B%0Awhite%2Dspace%3A%20normal%3B%0Adisplay%3A%20inline%2Dblock%3B%0A%7D%0Aspan%2Erlap%20%7B%0Adisplay%3A%20inline%2Dblock%3B%0Awidth%3A%200px%3B%0A%7D%0Aspan%2Edescr%20%7B%20font%2Dstyle%3A%20normal%3B%20font%2Dfamily%3A%20serif%3B%20%7D%0Aspan%2Egrammarterm%20%7B%20font%2Dstyle%3A%20italic%3B%20%7D%0Aspan%2Eterm%20%7B%20font%2Dstyle%3A%20italic%3B%20%7D%0Aspan%2Eterminal%20%7B%20font%2Dfamily%3A%20monospace%3B%20font%2Dstyle%3A%20normal%3B%20%7D%0Aspan%2Enonterminal%20%7B%20font%2Dstyle%3A%20italic%3B%20%7D%0Aspan%2Etcode%20%7B%20font%2Dfamily%3A%20monospace%3B%20font%2Dstyle%3A%20normal%3B%20%7D%0Aspan%2Etextbf%20%7B%20font%2Dweight%3A%20bold%3B%20%7D%0Aspan%2Etextsc%20%7B%20font%2Dvariant%3A%20small%2Dcaps%3B%20%7D%0Aa%2Enontermdef%20%7B%20font%2Dstyle%3A%20italic%3B%20font%2Dfamily%3A%20serif%3B%20%7D%0Aspan%2Eemph%20%7B%20font%2Dstyle%3A%20italic%3B%20%7D%0Aspan%2Etechterm%20%7B%20font%2Dstyle%3A%20italic%3B%20%7D%0Aspan%2Emathit%20%7B%20font%2Dstyle%3A%20italic%3B%20%7D%0Aspan%2Emathsf%20%7B%20font%2Dfamily%3A%20sans%2Dserif%3B%20%7D%0Aspan%2Emathrm%20%7B%20font%2Dfamily%3A%20serif%3B%20font%2Dstyle%3A%20normal%3B%20%7D%0Aspan%2Etextrm%20%7B%20font%2Dfamily%3A%20serif%3B%20%7D%0Aspan%2Etextsl%20%7B%20font%2Dstyle%3A%20italic%3B%20%7D%0Aspan%2Emathtt%20%7B%20font%2Dfamily%3A%20monospace%3B%20font%2Dstyle%3A%20normal%3B%20%7D%0Aspan%2Embox%20%7B%20font%2Dfamily%3A%20serif%3B%20font%2Dstyle%3A%20normal%3B%20%7D%0Aspan%2Eungap%20%7B%20display%3A%20inline%2Dblock%3B%20width%3A%202pt%3B%20%7D%0Aspan%2Etextit%20%7B%20font%2Dstyle%3A%20italic%3B%20%7D%0Aspan%2Etexttt%20%7B%20font%2Dfamily%3A%20monospace%3B%20%7D%0Aspan%2Etcode%5Fin%5Fcodeblock%20%7B%20font%2Dfamily%3A%20monospace%3B%20font%2Dstyle%3A%20normal%3B%20%7D%0Aspan%2Ephantom%20%7B%20color%3A%20white%3B%20%7D%0A%0Aspan%2Emath%20%7B%20font%2Dstyle%3A%20normal%3B%20%7D%0Aspan%2Emathblock%20%7B%0Adisplay%3A%20block%3B%0Amargin%2Dleft%3A%20auto%3B%0Amargin%2Dright%3A%20auto%3B%0Amargin%2Dtop%3A%201%2E2em%3B%0Amargin%2Dbottom%3A%201%2E2em%3B%0Atext%2Dalign%3A%20center%3B%0A%7D%0Aspan%2Emathalpha%20%7B%0Afont%2Dstyle%3A%20italic%3B%0A%7D%0Aspan%2Esynopsis%20%7B%0Afont%2Dweight%3A%20bold%3B%0Amargin%2Dtop%3A%200%2E5em%3B%0Adisplay%3A%20block%3B%0A%7D%0Aspan%2Edefinition%20%7B%0Afont%2Dweight%3A%20bold%3B%0Adisplay%3A%20block%3B%0A%7D%0A%2Ecodeblock%20%7B%0Amargin%2Dleft%3A%201%2E2em%3B%0Aline%2Dheight%3A%20127%25%3B%0A%7D%0A%2Eoutputblock%20%7B%0Amargin%2Dleft%3A%201%2E2em%3B%0Aline%2Dheight%3A%20127%25%3B%0A%7D%0Adiv%2Eitemdecl%20%7B%0Amargin%2Dtop%3A%202ex%3B%0A%7D%0Acode%2Eitemdeclcode%20%7B%0Awhite%2Dspace%3A%20pre%3B%0Adisplay%3A%20block%3B%0A%7D%0Aspan%2Etextsuperscript%20%7B%0Avertical%2Dalign%3A%20super%3B%0Afont%2Dsize%3A%20smaller%3B%0Aline%2Dheight%3A%200%3B%0A%7D%0A%2Efootnotenum%20%7B%20vertical%2Dalign%3A%20super%3B%20font%2Dsize%3A%20smaller%3B%20line%2Dheight%3A%200%3B%20%7D%0A%2Efootnote%20%7B%0Afont%2Dsize%3A%20small%3B%0Amargin%2Dleft%3A%202em%3B%0Amargin%2Dright%3A%202em%3B%0Amargin%2Dtop%3A%200%2E6em%3B%0Amargin%2Dbottom%3A%200%2E6em%3B%0A%7D%0Adiv%2Eminipage%20%7B%0Adisplay%3A%20inline%2Dblock%3B%0Amargin%2Dright%3A%203em%3B%0A%7D%0Adiv%2EnumberedTable%20%7B%0Atext%2Dalign%3A%20center%3B%0Amargin%3A%202em%3B%0A%7D%0Adiv%2Efigure%20%7B%0Atext%2Dalign%3A%20center%3B%0Amargin%3A%202em%3B%0A%7D%0Atable%20%7B%0Aborder%3A%201px%20solid%20black%3B%0Aborder%2Dcollapse%3A%20collapse%3B%0Amargin%2Dleft%3A%20auto%3B%0Amargin%2Dright%3A%20auto%3B%0Amargin%2Dtop%3A%200%2E8em%3B%0Atext%2Dalign%3A%20left%3B%0Ahyphens%3A%20none%3B%20%0A%7D%0Atd%2C%20th%20%7B%0Apadding%2Dleft%3A%201em%3B%0Apadding%2Dright%3A%201em%3B%0Avertical%2Dalign%3A%20top%3B%0A%7D%0Atd%2Eempty%20%7B%0Apadding%3A%200px%3B%0Apadding%2Dleft%3A%201px%3B%0A%7D%0Atd%2Eleft%20%7B%0Atext%2Dalign%3A%20left%3B%0A%7D%0Atd%2Eright%20%7B%0Atext%2Dalign%3A%20right%3B%0A%7D%0Atd%2Ecenter%20%7B%0Atext%2Dalign%3A%20center%3B%0A%7D%0Atd%2Ejustify%20%7B%0Atext%2Dalign%3A%20justify%3B%0A%7D%0Atd%2Eborder%20%7B%0Aborder%2Dleft%3A%201px%20solid%20black%3B%0A%7D%0Atr%2Erowsep%2C%20td%2Ecline%20%7B%0Aborder%2Dtop%3A%201px%20solid%20black%3B%0A%7D%0Atr%2Eeven%2C%20tr%2Eodd%20%7B%0Aborder%2Dbottom%3A%201px%20solid%20black%3B%0A%7D%0Atr%2Ecapsep%20%7B%0Aborder%2Dtop%3A%203px%20solid%20black%3B%0Aborder%2Dtop%2Dstyle%3A%20double%3B%0A%7D%0Atr%2Eheader%20%7B%0Aborder%2Dbottom%3A%203px%20solid%20black%3B%0Aborder%2Dbottom%2Dstyle%3A%20double%3B%0A%7D%0Ath%20%7B%0Aborder%2Dbottom%3A%201px%20solid%20black%3B%0A%7D%0Aspan%2Ecentry%20%7B%0Afont%2Dweight%3A%20bold%3B%0A%7D%0Adiv%2Etable%20%7B%0Adisplay%3A%20block%3B%0Amargin%2Dleft%3A%20auto%3B%0Amargin%2Dright%3A%20auto%3B%0Atext%2Dalign%3A%20center%3B%0Awidth%3A%2090%25%3B%0A%7D%0Aspan%2Eindented%20%7B%0Adisplay%3A%20block%3B%0Amargin%2Dleft%3A%202em%3B%0Amargin%2Dbottom%3A%201em%3B%0Amargin%2Dtop%3A%201em%3B%0A%7D%0Aol%2Eenumeratea%20%7B%20list%2Dstyle%2Dtype%3A%20none%3B%20background%3A%20inherit%3B%20%7D%0Aol%2Eenumerate%20%7B%20list%2Dstyle%2Dtype%3A%20none%3B%20background%3A%20inherit%3B%20%7D%0A%0Acode%2EsourceCode%20%3E%20span%20%7B%20display%3A%20inline%3B%20%7D%0A" />
  <link rel="icon" 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//" />
  <!--[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">Composable cancellation for sender-based async operations</h1>

<table style="border:none;float:right">
  <tr>
    <td>Document #:</td>
    <td>P2175R0</td>
  </tr>
  <tr>
    <td>Date:</td>
    <td>2020-12-14</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>
      SG1 - Concurrency Sub-Group<br>
    </td>
  </tr>
  <tr>
    <td style="vertical-align:top">Reply-to:</td>
    <td>
      Lewis Baker<br>&lt;<a href="mailto:lewissbaker@gmail.com" class="email">lewissbaker@gmail.com</a>&gt;<br>
    </td>
  </tr>
</table>

</header>
<div style="clear:both">
<div id="TOC" role="doc-toc">
<h1 id="toctitle">Contents</h1>
<ul>
<li><a href="#abstract"><span class="toc-section-number">1</span> Abstract<span></span></a></li>
<li><a href="#motivation"><span class="toc-section-number">2</span> Motivation<span></span></a>
<ul>
<li><a href="#why-cancellation"><span class="toc-section-number">2.1</span> Why Cancellation?<span></span></a></li>
<li><a href="#structured-concurrency"><span class="toc-section-number">2.2</span> Structured Concurrency<span></span></a></li>
</ul></li>
<li><a href="#prior-art-in-cancellation-patterns"><span class="toc-section-number">3</span> Prior-art in cancellation patterns<span></span></a>
<ul>
<li><a href="#networking-ts-boostasio-cancellation-model"><span class="toc-section-number">3.1</span> Networking TS / boost::asio cancellation model<span></span></a></li>
<li><a href="#net-framework-cancellation-model"><span class="toc-section-number">3.2</span> .NET Framework cancellation model<span></span></a></li>
<li><a href="#cppcorotask-cancellation"><span class="toc-section-number">3.3</span> cppcoro::task cancellation<span></span></a></li>
<li><a href="#follycoro-cancellation-model"><span class="toc-section-number">3.4</span> folly::coro cancellation model<span></span></a></li>
<li><a href="#stdstop_token"><span class="toc-section-number">3.5</span> std::stop_token<span></span></a></li>
</ul></li>
<li><a href="#proposed-changes"><span class="toc-section-number">4</span> Proposed Changes<span></span></a>
<ul>
<li><a href="#add-stoppable_token-and-related-concepts"><span class="toc-section-number">4.1</span> Add <code class="sourceCode default">stoppable_token</code> and related concepts<span></span></a></li>
<li><a href="#tweaks-to-stdstop_token"><span class="toc-section-number">4.2</span> Tweaks to <code class="sourceCode default">std::stop_token</code><span></span></a></li>
<li><a href="#add-stdnever_stop_token"><span class="toc-section-number">4.3</span> Add <code class="sourceCode default">std::never_stop_token</code><span></span></a></li>
<li><a href="#add-stdin_place_stop_token-stdin_place_stop_source-stdin_place_stop_callbackcb"><span class="toc-section-number">4.4</span> Add <code class="sourceCode default">std::in_place_stop_token</code>, <code class="sourceCode default">std::in_place_stop_source</code>, <code class="sourceCode default">std::in_place_stop_callback&lt;CB&gt;</code><span></span></a></li>
<li><a href="#add-get_stop_token-customisation-point"><span class="toc-section-number">4.5</span> Add <code class="sourceCode default">get_stop_token()</code> customisation-point<span></span></a></li>
<li><a href="#type-traits"><span class="toc-section-number">4.6</span> Type-traits<span></span></a></li>
</ul></li>
<li><a href="#design-discussion"><span class="toc-section-number">5</span> Design Discussion<span></span></a>
<ul>
<li><a href="#naming"><span class="toc-section-number">5.1</span> Naming<span></span></a></li>
<li><a href="#why-do-we-need-a-stdstoppable_token-concept"><span class="toc-section-number">5.2</span> Why do we need a <code class="sourceCode default">std::stoppable_token</code> concept?<span></span></a></li>
<li><a href="#should-unstoppable_token-concept-just-be-a-trait"><span class="toc-section-number">5.3</span> Should <code class="sourceCode default">unstoppable_token</code> concept just be a trait?<span></span></a></li>
<li><a href="#can-stoppable_token_for-concept-be-recast-as-semantic-requirements"><span class="toc-section-number">5.4</span> Can <code class="sourceCode default">stoppable_token_for</code> concept be recast as semantic requirements?<span></span></a></li>
<li><a href="#why-do-we-need-the-callback_typecb-type-alias-on-the-stop-token-type"><span class="toc-section-number">5.5</span> Why do we need the <code class="sourceCode default">::callback_type&lt;CB&gt;</code> type-alias on the stop-token type?<span></span></a></li>
<li><a href="#why-doesnt-paper-this-propose-adding-a-stdnever_stop_callbackcb-type-name"><span class="toc-section-number">5.6</span> Why doesn’t paper this propose adding a <code class="sourceCode default">std::never_stop_callback&lt;CB&gt;</code> type-name?<span></span></a></li>
<li><a href="#should-get_stop_token-be-applicable-to-more-than-receivers"><span class="toc-section-number">5.7</span> Should <code class="sourceCode default">get_stop_token()</code> be applicable to more than receivers?<span></span></a></li>
<li><a href="#composability"><span class="toc-section-number">5.8</span> Composability<span></span></a></li>
<li><a href="#cancellation-is-optional-best-effort"><span class="toc-section-number">5.9</span> Cancellation is optional / best-effort<span></span></a></li>
<li><a href="#performance-considerations"><span class="toc-section-number">5.10</span> Performance Considerations<span></span></a></li>
<li><a href="#limitations-of-sender_traitssends_done"><span class="toc-section-number">5.11</span> Limitations of sender_traits::sends_done<span></span></a></li>
<li><a href="#cancellation-support-for-scheduler-implementations"><span class="toc-section-number">5.12</span> Cancellation support for <code class="sourceCode default">scheduler</code> implementations<span></span></a></li>
<li><a href="#impacts-to-other-proposals"><span class="toc-section-number">5.13</span> Impacts to other proposals<span></span></a></li>
<li><a href="#future-work"><span class="toc-section-number">5.14</span> Future work<span></span></a></li>
</ul></li>
<li><a href="#implementation-experience"><span class="toc-section-number">6</span> Implementation Experience<span></span></a></li>
<li><a href="#wording"><span class="toc-section-number">7</span> Wording<span></span></a></li>
<li><a href="#appendices"><span class="toc-section-number">8</span> Appendices<span></span></a>
<ul>
<li><a href="#appendix-a-the-stop_when-algorithm"><span class="toc-section-number">8.1</span> Appendix A: The <code class="sourceCode default">stop_when()</code> algorithm<span></span></a></li>
</ul></li>
</ul>
</div>
<h1 data-number="1" id="abstract"><span class="header-section-number">1</span> Abstract<a href="#abstract" class="self-link"></a></h1>
<p>This paper proposes a general-purpose, composable mechanism for requesting cancellation of in-flight sender-based async operations as an extension to <a href="https://wg21.link/P0443R14">P0443R14</a> - “A Unified Executors Proposal for C++.”</p>
<p>The paper <a href="https://wg21.link/P0443R14">P0443R14</a> proposes adding the sender/receiver concepts which provide a generic interface for asynchronous operations. As part of this interface, when an async operation completes it completes with one of three possible kinds of signals, delivered by invoking one of the <code class="sourceCode default">set_value</code>, <code class="sourceCode default">set_error</code> or <code class="sourceCode default">set_done</code> customisation-points on the receiver.</p>
<p>The first two correspond roughly to the async-equivalent of an ordinary function completing with a return-value or an exception, respectively, and the <code class="sourceCode default">set_done</code> completion-signal is a third kind of completion that represents a result that was neither success nor failure - typically used to signal that the operation completed early because it was asked to do so by the caller.</p>
<p>While <a href="https://wg21.link/P0443R14">P0443R14</a> provides the ability for an async operation to complete with an empty result in the case it was cancelled, <strong>there is currently no standard mechanism specified</strong> <strong>to allow the caller to communicate this request to stop an async operation once it has</strong> <strong>been started</strong>.</p>
<p>This leaves individual async operations having to use ad-hoc mechanisms to allow the caller to communicate a request to cancel. Examples include passing a <code class="sourceCode default">std::chrono::duration</code> as a timeout parameter for time-based cancellation, passing a <code class="sourceCode default">std::stop_token</code> as a parameter to the async function/coroutine, or adding a <code class="sourceCode default">.cancel()</code> method on the same object as the async method (as with the Networking TS).</p>
<p>For cancellation to compose well within an application, it generally needs to be supported at all levels. For example, for a request to cancel a high-level operation to be effective, that request to cancel an operation needs to be able to be communicated through each layer of the applictaion to the leaf-level operations. e.g. to a timer, I/O operation or compute loop on which the high-level operation’s completion is dependent.</p>
<p>The use of ad-hoc mechaninisms, however, makes it difficult to compose and propagate cancellation through intermediate layers. This is especially true for generic algorithms that compose already-constructed senders, such as the <code class="sourceCode default">when_all()</code> algorithm, and thus have no way to control the parameters parameters passed to the async operation that created the sender.</p>
<p>Defining a standard mechanism for communicating a request for cancellation of async operations is essential to allow building generic async algorithms that support cancellation.</p>
<p>The high-level goals of the cancellation design propose by this paper are:</p>
<ul>
<li>Define a standard cancellation mechanism that is generically composable. This allows algorithms to be built that can either be transparent to cancellation or that can introduce new cancellation scopes and inject cancellation requests into child operations. e.g. allow defininig a generic <code class="sourceCode default">timeout()</code> algorithm that can request cancellation of a child operation after a given time has elapsed.</li>
<li>Allow overhead of cancellation mechanisms to compile-out to nothing when we can statically determine that cancellation will never be requested. i.e. “Don’t pay for what you don’t use”</li>
<li>Don’t force callers or callees to have to support cancellation.
<ul>
<li>If an async operation doesn’t support cancellation then it doesn’t have to write any code to opt-out of cancellation. Supporting cancellation is opt-in.</li>
<li>If a caller will never request cancellation then they shouldn’t have to do any work to opt-out of support for cancellation.</li>
</ul></li>
</ul>
<p>This paper proposes the following changes to define a standard cancellation mechanism for sender-based operations:</p>
<ul>
<li>Add a named concept, <code class="sourceCode default">std::stoppable_token</code>, that matches types with the same shape as <code class="sourceCode default">std::stop_token</code>. This allows alternative implementations of the <code class="sourceCode default">stop_token</code> synchronisation primitive that can be more efficient for certain use-cases.</li>
<li>Add two additional concepts which are refinements of <code class="sourceCode default">std::stoppable_token</code>:
<ul>
<li><code class="sourceCode default">std::stoppable_token_for&lt;CB, Initializer&gt;</code> - tests that, in addition to satisfying the <code class="sourceCode default">std::stoppable_token</code> concept that you can also construct a <code class="sourceCode default">T::callback_type&lt;CB&gt;</code> from an instance of the stop-token and a value of type <code class="sourceCode default">Initializer</code>.</li>
<li><code class="sourceCode default">std::unstoppable_token</code> - A <code class="sourceCode default">std::stoppable_token</code> for which <code class="sourceCode default">stop_possible()</code> is static-constexpr and always returns <code class="sourceCode default">false</code>.</li>
</ul></li>
<li>Add <code class="sourceCode default">get_stop_token()</code> customisation-point, invocable on the receiver passed to <code class="sourceCode default">connect()</code> to obtain the stop-token to usefor that operation.</li>
<li>Add new type-traits for determining the stop-token type associated with a receiver:
<ul>
<li><code class="sourceCode default">std::stop_token_type_t&lt;T&gt;</code> obtains the decayed result-type of <code class="sourceCode default">get_stop_token()</code> invoked on an argument of type <code class="sourceCode default">T</code>.</li>
</ul></li>
<li>Add two new types that satisfy the <code class="sourceCode default">stoppable_token</code> concept:
<ul>
<li><code class="sourceCode default">std::never_stop_token</code> - used for cases where cancellation is not required</li>
<li><code class="sourceCode default">std::in_place_stop_token</code> - used for cases where the stop-source does not need to be movable/copyable and the lifetime of uses of the stop-token are strictly nested within the lifetime of the corresponding stop-source.</li>
</ul></li>
<li>Update <code class="sourceCode default">std::stop_token</code> to add the nested <code class="sourceCode default">::callback_type</code> type-alias required by the <code class="sourceCode default">stoppable_token</code> concept.</li>
</ul>
<p>This also has impacts on other proposals:</p>
<ul>
<li>Impacts on sender-based algorithms in <a href="https://wg21.link/P1897R3">P1897R3</a> which need to be updated to support cancellation.</li>
<li>Builds on top of <code class="sourceCode default">tag_invoke()</code> mechanism proposed in <a href="https://wg21.link/P1895R0">P1895R0</a> for customisation-points to generalise the mechanism for passing context from caller to callee through CPOs on the receiver.</li>
<li>Impacts on <code class="sourceCode default">std::task/lazy</code> propsed in in <a href="https://wg21.link/P1056R1">P1056R1</a> to allow cancellation requests to propagate through coroutines to awaited senders.</li>
<li>Potential impacts on interaction with cancellation in the Networking TS.</li>
</ul>
<p>The facilities proposed in this library have been implemented in the <a href="https://github.com/facebookexperimental/libunifex">libunifex</a> opensource library.</p>
<h1 data-number="2" id="motivation"><span class="header-section-number">2</span> Motivation<a href="#motivation" class="self-link"></a></h1>
<p>This section will attempt to explain why the author believes cancellation to be a fundamental building block of concurrent programs, and why it’s necessary to support writing concurrent code in a structured way.</p>
<h2 data-number="2.1" id="why-cancellation"><span class="header-section-number">2.1</span> Why Cancellation?<a href="#why-cancellation" class="self-link"></a></h2>
<h3 data-number="2.1.1" id="ordinary-functions"><span class="header-section-number">2.1.1</span> Ordinary functions<a href="#ordinary-functions" class="self-link"></a></h3>
<p>When we write sequential, single-threaded synchronous code in C++ we call a function when we want it to produce some result for us.</p>
<p>The caller presumably needs the result of that function to achieve its goal, otherwise it wouldn’t have called the function. You can think of “goal” here as roughly equivalent to “satisfying its post-conditions.”</p>
<p>If the function call completes with an error (i.e. an exception) then this generally indicates that the callee was unable to achieve its goal. Thus the caller’s current strategy for achieving its goal has failed and so the caller can either handle the error and try a different strategy for achieving its goal, or if the caller does not have an alternative strategy for achieving its goal then it can let the error propagate to its caller as a way of saying that it was not able to achieve its goal.</p>
<p>Either way, a function call will either complete with a value, indicating success, and the caller will continue executing along its value continuation-path, executing the next step towards achieving its goal, or it will complete with an error, indicating failure, and the caller will execute along the error continuation-path (typically by unwinding until it hits an exception handler).</p>
<p>As the caller is suspended while the callee is executing and the program is single-threaded, in many cases there is nothing that can change the fact that the caller needs that result in order to achieve its goal.</p>
<p>A sequential, single-threaded program can only pursue one strategy for achieving its goal at a time and so needs to wait until the result of the current strategy is known, at which point it can then make a choice about what to do next.</p>
<h3 data-number="2.1.2" id="concurrency-introduces-a-need-for-cancellation"><span class="header-section-number">2.1.2</span> Concurrency introduces a need for cancellation<a href="#concurrency-introduces-a-need-for-cancellation" class="self-link"></a></h3>
<p>Once we allow a program to execute multiple operations concurrently, it is possible that we might have multiple strategies for achieving the goal of a program that are executing at the same time.</p>
<p>For example, the goal of (a part of) the program might be “try to download this file or stop trying after 30s,” or it might be “try to connect to each of these 3 servers and send a request to whichever one connects first.”</p>
<p>If we take the file-downloading example, this program might concurrently try download the file and also start waiting until 30s has elapsed. If one of these strategies is successful (e.g. we finished waiting for 30s) then this means that the other strategy (i.e. downloading the file) is no longer needed as we have already satisfied the goal of this part of the program.</p>
<p>This now means that we have an operation that is currently executing whose result is no longer required. It would be a waste of resources to let this operation continue trying to fulfil its goal - it may still be consuming CPU-cycles, memory or network bandwidth.</p>
<p>Ideally, to avoid unnecessarily wasting these resources, we want this operation to stop executing as soon as possible so that it can free the resources it is using. This means we need a way to send a request to this operation to tell it to stop executing.</p>
<p>Note that concurrency here doesn’t necessarily require multiple threads in the current process. It might involve some kind of entity external to the process that is making progress independently and that communicates with this process somehow. e.g. via I/O.</p>
<p>For example, another process on the same host that communicates via a pipe, a process on another host that communicates over the network, the processor running on an attached storage device, or even a clock chip that keeps track of the current time.</p>
<h3 data-number="2.1.3" id="concurrency-algorithm-examples"><span class="header-section-number">2.1.3</span> Concurrency Algorithm Examples<a href="#concurrency-algorithm-examples" class="self-link"></a></h3>
<p>To give some more context, let’s look at a few concurrency-related algorithms that involve a need for cancellation:</p>
<p><strong>Example 1: <code class="sourceCode default">when_all(senders...)</code></strong></p>
<p>The first example is the <code class="sourceCode default">when_all()</code> algorithm, proposed in <a href="https://wg21.link/P1897R3">P1897R3</a>.</p>
<p>This algorithm takes a variadic number of input senders and returns a sender that launches each of those senders, allowing them to potentially execute concurrently. The operation completes when all of the input senders have completed, producing a pack of variants of tuples containing the value-result of each of the corresponding input senders.</p>
<p>However, if any of the input senders completes with either <code class="sourceCode default">set_error</code> or <code class="sourceCode default">set_done</code> then the whole operation completes with the first such result. Therefore, completing with <code class="sourceCode default">set_error</code> or <code class="sourceCode default">set_done</code> means that we have nowhere for the results of other operations to go; the results of other operations are just discarded.</p>
<p>In this case, it would be beneficial if we could request that any of the outstanding operations whose results will be discarded stop as soon as possible so that the overall operation can complete more quickly and avoid using more resources than it needs to. e.g. cpu, memory, network-bandwidth, etc.</p>
<p><strong>Example 2: <code class="sourceCode default">stop_when(source, trigger)</code></strong></p>
<p>The second example is the <code class="sourceCode default">stop_when()</code> algorithm, which is available in the libunifex library.</p>
<p>This algorithm takes as input two senders and returns a new sender that launches each of those senders, allowing them to potentially execute concurrently.</p>
<p>When one of the senders completes it requests cancellation of the other sender.</p>
<p>The whole operation completes with the result of <code class="sourceCode default">source</code> and the result of <code class="sourceCode default">trigger</code> is discarded. The <code class="sourceCode default">trigger</code> result is used purely as a trigger to request cancellation of <code class="sourceCode default">source</code>.</p>
<p>This algorithm can be used whenever a particular operation should be cancelled when some event occurs and that event is represented as a sender. For example, it could be an event that fires at a particular time, using <code class="sourceCode default">schedule_at()</code> on a scheduler.</p>
<p>Example:</p>
<div class="sourceCode" id="cb1"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a>task example(scheduler auto s) {</span>
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a>  auto dueTime = now(s) + 10s;</span>
<span id="cb1-3"><a href="#cb1-3" aria-hidden="true" tabindex="-1"></a>  co_await stop_when(some_operation(), schedule_at(s, dueTime));</span>
<span id="cb1-4"><a href="#cb1-4" aria-hidden="true" tabindex="-1"></a>}</span></code></pre></div>
<p><strong>Example 3: <code class="sourceCode default">timeout(source, duration)</code></strong></p>
<p>The <code class="sourceCode default">timeout()</code> algorithm is similar to <code class="sourceCode default">stop_when()</code> in that it requests that the <code class="sourceCode default">source</code> operation stop when some event triggers. However, it is different in that it is limited to time-based triggering of the cancellation and also, in the case that the timeout duration elapses, overrides the result of <code class="sourceCode default">source</code> and instead completes with a <code class="sourceCode default">timeout_error</code>.</p>
<p>You can apply the <code class="sourceCode default">timeout()</code> algorithm to any sender that supports cancellation and this will cause the operation to timeout after a given time without that operation having to have explicitly added support for timeouts.</p>
<p>The example from the “Concurrency introduces a need for cancellation” above might be written as:</p>
<div class="sourceCode" id="cb2"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a>sender auto download_file(url sourceUrl, path destinationPath);</span>
<span id="cb2-2"><a href="#cb2-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb2-3"><a href="#cb2-3" aria-hidden="true" tabindex="-1"></a>task example() {</span>
<span id="cb2-4"><a href="#cb2-4" aria-hidden="true" tabindex="-1"></a>  co_await timeout(download_file(&quot;https://example.com/file&quot;, &quot;./file.txt&quot;), 30s);</span>
<span id="cb2-5"><a href="#cb2-5" aria-hidden="true" tabindex="-1"></a>}</span></code></pre></div>
<p><strong>Example 4: <code class="sourceCode default">first_successful(rangeOfSenders)</code></strong></p>
<p>Another variation of a concurrency algorithm is the <code class="sourceCode default">first_successful()</code> algorithm, which takes a collection of input senders and returns a sender that starts each of the input senders, allowing them to execute concurrently.</p>
<p>If any of the operations complete successfully then it completes with the result of the first successful operation. Otherwise, if none of them complete successfully then completes with the result of the last sender to complete.</p>
<p>This algorithm can be used, for example, to attempt to connect to several candidate servers/end-points/transports concurrently and to use whichever connection was established first. This is often referred to as the “happy eyeballs” algorithm.</p>
<p>As soon as a successful result is obtained, the results of any other senders will be discarded. So ideally, the algorithm would request cancellation of any outstanding operations so that we can avoid wasting resources.</p>
<p><strong>Example 5: Composed concurrency/cancellation</strong></p>
<p>Note that with the above algorithms each of them introduce some form of concurrency in the program and have some condition under which they can request cancellation of one or more of their child operations.</p>
<p>However, for these operations to compose, they still need to also be responsive to cancellation requests coming from their parent.</p>
<p>For example, consider:</p>
<div class="sourceCode" id="cb3"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a>sender auto example() {</span>
<span id="cb3-2"><a href="#cb3-2" aria-hidden="true" tabindex="-1"></a>  return timeout(</span>
<span id="cb3-3"><a href="#cb3-3" aria-hidden="true" tabindex="-1"></a>    stop_when(</span>
<span id="cb3-4"><a href="#cb3-4" aria-hidden="true" tabindex="-1"></a>      when_all(do_a(), do_b(), do_c()),</span>
<span id="cb3-5"><a href="#cb3-5" aria-hidden="true" tabindex="-1"></a>      uiPanel.cancel_button_clicked()),</span>
<span id="cb3-6"><a href="#cb3-6" aria-hidden="true" tabindex="-1"></a>    30s);</span>
<span id="cb3-7"><a href="#cb3-7" aria-hidden="true" tabindex="-1"></a>}</span></code></pre></div>
<p>In this example, we want to concurrently execute 3 operations; <code class="sourceCode default">do_a()</code>, <code class="sourceCode default">do_b()</code> and <code class="sourceCode default">do_c()</code>, each of which return senders, and then complete once all 3 operations complete.</p>
<p>If we look at the <code class="sourceCode default">do_c()</code> operation, we want to stop this operation early:</p>
<ul>
<li>by <code class="sourceCode default">when_all()</code> if one of <code class="sourceCode default">do_a()</code> or <code class="sourceCode default">do_b()</code> fail; or</li>
<li>by <code class="sourceCode default">stop_when()</code> if the <code class="sourceCode default">cancel_button_clicked()</code> sender completes, triggered by the user clicking a cancel button on the user-interface; or</li>
<li>by <code class="sourceCode default">timeout()</code> if the operation takes longer than 30s to complete; or</li>
<li>by the parent/consumer of <code class="sourceCode default">example()</code> if it requests cancellation of the <code class="sourceCode default">example()</code> operation.</li>
</ul>
<p>A composable cancellation mechanism allows us to encapsulate concurrency and cancellation patterns in generic, reusable algorithms which can be combined in interesting ways without additional ceremony and boilerplate.</p>
<h3 data-number="2.1.4" id="leaf-operation-cancellation-examples"><span class="header-section-number">2.1.4</span> Leaf-operation Cancellation Examples<a href="#leaf-operation-cancellation-examples" class="self-link"></a></h3>
<p>Generally, a program will have some number of potentially-concurrent leaf operations that represent operations that are able to make the next forward step of the program towards its goals.</p>
<p>Non-leaf operations are the other operations, which are unable to make forward progress until one or more of the leaf operations makes forward progress. The algorithms listed in the section above are all non-leaf operations in the program.</p>
<p>At the end of the day, for cancellation to be effective, we need non-leaf operations to be able to forward requests for cancellation through to the leaf operations and we need the leaf operations to be able to respond to requests for cancellation in a timely manner.</p>
<p>Leaf operations that do not respond to cancellation requests should be permitted, however. Some operations may just not be cancellable, while others may have chosen to not implement this support for various reasons.</p>
<p>For context, here are some examples of leaf operations we might want to support cancellation:</p>
<p><strong>Example 1: Cancelling a <code class="sourceCode default">schedule()</code> operation</strong></p>
<p>The paper <a href="https://wg21.link/P0443R14">P0443R14</a> adds a basis operation named <code class="sourceCode default">schedule()</code> which allows you to schedule work onto the associated execution context of the scheduler.</p>
<p>When the schedule operation is started by calling the <code class="sourceCode default">start()</code> customisation-point, it typically enqueues a work-item on to a queue of some sort. A worker thread will eventually dequeue this work-item and when it does, signals completion of the <code class="sourceCode default">schedule()</code> operation by invoking <code class="sourceCode default">set_value()</code> on the receiver. As the operation completes on a worker thread associated with the scheduler, any work done inside the receiver’s <code class="sourceCode default">set_value()</code> method will be running on the scheduler’s execution context.</p>
<p>The <code class="sourceCode default">schedule()</code> operation here is considered a “leaf” operation and generally needs to be composed with other operations for it to be useful.</p>
<p>e.g. it might be composed with <code class="sourceCode default">transform()</code> to allow calling a function on the associated execution context.</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>execution<span class="op">::</span>transform<span class="op">(</span></span>
<span id="cb4-2"><a href="#cb4-2" aria-hidden="true" tabindex="-1"></a>  execution<span class="op">::</span>schedule<span class="op">(</span>scheduler<span class="op">)</span>,</span>
<span id="cb4-3"><a href="#cb4-3" aria-hidden="true" tabindex="-1"></a>  <span class="op">[]</span> <span class="op">{</span></span>
<span id="cb4-4"><a href="#cb4-4" aria-hidden="true" tabindex="-1"></a>    <span class="co">// This runs on &#39;scheduler&#39;s execution context.</span></span>
<span id="cb4-5"><a href="#cb4-5" aria-hidden="true" tabindex="-1"></a>    <span class="cf">return</span> compute_result<span class="op">()</span>;</span>
<span id="cb4-6"><a href="#cb4-6" aria-hidden="true" tabindex="-1"></a>  <span class="op">})</span>;</span></code></pre></div>
<p>This can further be composed with an algorithm like <code class="sourceCode default">when_all()</code> to allow multiple operations to be executed concurrently (e.g. on a thread-pool).</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>execution<span class="op">::</span>when_all<span class="op">(</span></span>
<span id="cb5-2"><a href="#cb5-2" aria-hidden="true" tabindex="-1"></a>  execution<span class="op">::</span>transform<span class="op">(</span></span>
<span id="cb5-3"><a href="#cb5-3" aria-hidden="true" tabindex="-1"></a>    execution<span class="op">::</span>schedule<span class="op">(</span>tpScheduler<span class="op">)</span>, <span class="op">[]</span> <span class="op">{</span> <span class="cf">return</span> compute_a<span class="op">()</span>; <span class="op">})</span>,</span>
<span id="cb5-4"><a href="#cb5-4" aria-hidden="true" tabindex="-1"></a>  execution<span class="op">::</span>transform<span class="op">(</span></span>
<span id="cb5-5"><a href="#cb5-5" aria-hidden="true" tabindex="-1"></a>    execution<span class="op">::</span>schedule<span class="op">(</span>tpScheduler<span class="op">)</span>, <span class="op">[]</span> <span class="op">{</span> <span class="cf">return</span> compute_b<span class="op">()</span>; <span class="op">}))</span>;</span></code></pre></div>
<p>If the result of that composed operation is no longer required, e.g. because one of the computations failed with an exception, then ideally we’d be able to cancel the <code class="sourceCode default">schedule()</code> operation for the remaining computation and remove the work-item from the scheduler’s queue immediately rather than wait until some worker-thread dequeues it.</p>
<p>If a scheduler is oversubscribed with a large queue depth then being able to cancel a <code class="sourceCode default">schedule()</code> operation that is currently at the back of the queue can reduce the latency of the composed operations.</p>
<p><strong>Example 2: Cancelling a timer</strong></p>
<p>In libunifex, schedulers that implement the <code class="sourceCode default">time_scheduler</code> concept provide <code class="sourceCode default">schedule_after()</code> and <code class="sourceCode default">schedule_at()</code> operations in adition to the required <code class="sourceCode default">schedule()</code> operation.</p>
<p>The <code class="sourceCode default">schedule_after(scheduler, duration)</code> operation will complete on the scheduler’s execution context with <code class="sourceCode default">set_value()</code> after a delay of <code class="sourceCode default">duration</code> time after the operation is started.</p>
<p>Often time-based scheduling is used for things like timeouts, where the timer will need to be cancelled if the operation completes before the timeout has elapsed. If the timer cannot be cancelled promptly then the timeout operation will have to wait until the timeout duration elapses before it can complete.</p>
<p><strong>Example 3: Cancelling async I/O on Windows</strong></p>
<p>Another common use-case for cancellation is cancelling async I/O.</p>
<p>For example, a user of an application might click on one button, which starts loading a large image from storage, but then immediately clicks on a different button to select a different image. The application should ideally avoid continuing to load the first image if the load had not yet completed to avoid wasting I/O bandwidth which could be better used for loading the second image.</p>
<p>Being able to cancel an async I/O operation that is no longer required can help with application responsiveness.</p>
<p>It can also be used to cause operations to complete that will otherwise never complete. e.g. a network application that is asychronously attempting to accept a connection may never receive an incoming connection attempt. We may want to stop accepting connections, say during shutdown, and thus cancel the async accept operation which would otherwise be waiting forever.</p>
<p>If we look at the basic async I/O APIs for Windows platforms, an async operation on a file associated with an I/O completion port is started by calling <code class="sourceCode default">ReadFile()</code> or <code class="sourceCode default">WriteFile()</code>, and passing a pointer to an <code class="sourceCode default">OVERLAPPED</code> structure.</p>
<p>The OS signals completion by posting a completion event to the I/O completion port with the corresponding <code class="sourceCode default">OVERLAPPED</code> pointer, which an event-loop typically receives by calling <code class="sourceCode default">GetQueuedCompletionStatus()</code>.</p>
<p>If we want to cancel the I/O operation early then we need to call <code class="sourceCode default">CancelIoEx()</code> and pass the same file-handle and <code class="sourceCode default">OVERLAPPED</code> pointer to issue a request to the OS to cancel the operation. To be able to do this, we need to be able to subscribe a callback that will be invoked when cancellation of the async read operation is requested so that we can actively call <code class="sourceCode default">CancelIoEx()</code>.</p>
<p>Note that the OS may or may not be able to actually cancel the operation depending on how far along the I/O operation has progressed (there may already be a completion event sitting the I/O completion port’s queue) and depending on the capabilities of the I/O device.</p>
<h2 data-number="2.2" id="structured-concurrency"><span class="header-section-number">2.2</span> Structured Concurrency<a href="#structured-concurrency" class="self-link"></a></h2>
<p>One of the design principles that has been guiding the design of sender/receiver, coroutines and of sender-based concurrency algorithms is that of “structured concurrency.”</p>
<p>The introduction of structured control-flow, which provided composable control-flow structures such as <code class="sourceCode default">if/else</code>, <code class="sourceCode default">do/while</code> and <code class="sourceCode default">switch</code> blocks, made it easier to reason about control-flow compared to code that exclusively used <code class="sourceCode default">goto</code> or conditional branches. These control-flow constructs can be composed arbitrarily to create complex logic and algorithms. Structured control-flow also tied control-flow to program scopes which made it easier to visually see the structure in the control-flow of your program.</p>
<p>Similarly, the introduction of structured-lifetime in C++, which tied lifetime of objects to program scopes through use of destructors, ensured that resources were cleaned up at the end of scopes. This made it much easier to reason about the lifetime of objects and write types and programs that can enlist the help of the compiler to ensure that resources are cleaned up, even in the presence of complicated control flow - like exceptions.</p>
<p>The combination of structured lifetime and structured control-flow and tying them both to the same program scopes makes it much easier to reason about the code.</p>
<blockquote>
<p>Structured concurrency revolves around the idea that (potentially) concurrent execution can be treated as a resource with a lifetime, much like how objects can have lifetimes.</p>
</blockquote>
<p>A potentially concurrent operation can be actively using resources it is given a reference to, and so the basic principle is that we need to make sure that the end of the lifetime of the concurrent operation “happens before” the destruction of any resources it might be accessing.</p>
<p>The way do this is by ensuring that all potentially concurrent operations that might be accessing a given resource are “joined” before that resource is destroyed (i.e. when it goes out of scope).</p>
<p>If we fail to join a potentially concurrent operation before resources it’s using goes out of scope then the program has undefined behaviour. Further, if we create a detached operation (work that can <em>never</em> be joined) then this makes it very difficult, and in some cases impossible, to know when it will be safe to destroy resources passed by-reference to that operation.</p>
<p>Fire and forget interfaces, such as the proposed <code class="sourceCode default">std::execution::execute()</code> from P0443R14, often require the programmer to manually ensure that they do some extra work at the end of the operation to signal completion so that the work can be joined.</p>
<p>This tends to lead to unstructured, ad-hoc mechanisms for signalling completion and joining concurrent work. Further, this logic is often mixed in with business logic for the operation and tends to be repeated each time a given concurrency pattern is used, leading to code that is more error-prone and hard to maintain.</p>
<h3 data-number="2.2.1" id="garbage-collection"><span class="header-section-number">2.2.1</span> Garbage collection<a href="#garbage-collection" class="self-link"></a></h3>
<p>One traditional approach to ensuring the correct order of destruction of resources used by concurrent programs is to use some form of garbage collection.</p>
<p>In garbage-collected languages, such as C#, the runtime ensures that the lifetimes of objects are maintained as long as they are reachable by some thread.</p>
<p>In C++ programs it is not uncommon for asynchronous code to make heavy use of <code class="sourceCode default">std::shared_ptr</code> to ensure that resources are kept alive until an async operation completes.</p>
<p>Much of the the asynchronous code at Facebook written using <code class="sourceCode default">folly::Future</code>-based APIs makes use of <code class="sourceCode default">std::shared_ptr</code>, usually captured within a lambda callback, to ensure that objects are kept alive until an asynchronous operation completes.</p>
<p>There are several downsides to using <code class="sourceCode default">std::shared_ptr</code> for this:</p>
<ul>
<li>Garbage collection approaches like <code class="sourceCode default">std::shared_ptr</code> tend to be viral, often requiring classes to inherit from <code class="sourceCode default">std::shared_from_this</code> or to hold child objects by <code class="sourceCode default">std::shared_ptr</code>.</li>
<li>The context in which the destruction occurs becomes non-deterministic. In cases where there might be a race between multiple threads releasing the last reference then the thread on which the object is destroyed can change from run-to-run, making it difficult to reason about the code. In some cases, this non-determinism can be a correctness issue, e.g. for objects that must be destroyed from a certain thread.</li>
<li>It forces heap-allocation and synchronisation (for the atomic refcount) which can impact the performance of an application.</li>
</ul>
<h3 data-number="2.2.2" id="coroutines"><span class="header-section-number">2.2.2</span> Coroutines<a href="#coroutines" class="self-link"></a></h3>
<p>Coroutines provide a language representation that allows developers to write asynchronous code that looks like normal code in terms of control-flow and lifetime. You can declare local variables, just like with normal functions, and the compiler makes sure that these variables are destroyed when control-flow exits the scope of that object.</p>
<p>However, this automatic destruction of local variables on scope-exit means that we need to ensure that we join any concurrent operations that may be holding a reference to those local variables before they go out of scope.</p>
<p>For example:</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>task<span class="op">&lt;</span><span class="dt">void</span><span class="op">&gt;</span> do_something<span class="op">(</span><span class="kw">const</span> resource<span class="op">&amp;</span> r<span class="op">)</span>;</span>
<span id="cb6-2"><a href="#cb6-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb6-3"><a href="#cb6-3" aria-hidden="true" tabindex="-1"></a>task<span class="op">&lt;</span><span class="dt">void</span><span class="op">&gt;</span> example<span class="op">()</span> <span class="op">{</span></span>
<span id="cb6-4"><a href="#cb6-4" aria-hidden="true" tabindex="-1"></a>  <span class="op">...</span></span>
<span id="cb6-5"><a href="#cb6-5" aria-hidden="true" tabindex="-1"></a>  <span class="op">{</span></span>
<span id="cb6-6"><a href="#cb6-6" aria-hidden="true" tabindex="-1"></a>    resource r;</span>
<span id="cb6-7"><a href="#cb6-7" aria-hidden="true" tabindex="-1"></a>    <span class="kw">co_await</span> do_something<span class="op">(</span>r<span class="op">)</span>;</span>
<span id="cb6-8"><a href="#cb6-8" aria-hidden="true" tabindex="-1"></a>  <span class="op">}</span></span>
<span id="cb6-9"><a href="#cb6-9" aria-hidden="true" tabindex="-1"></a>  <span class="op">...</span></span>
<span id="cb6-10"><a href="#cb6-10" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
<p>In this instance, we are calling an asynchronous child operation, <code class="sourceCode default">do_something()</code> and passing it a reference to a <code class="sourceCode default">resource</code> object that is a local variable in the calling coroutine.</p>
<p>Passing parameters by reference in this fashion is a natural way to write code as it mirrors very closely the way in which we write synchronous code.</p>
<p>When the <code class="sourceCode default">co_await</code> expression completes and the awaiting coroutine is resumed, execution will then exit the scope of the object, <code class="sourceCode default">r</code>, and it will be destroyed.</p>
<p>For this code to avoid running into undefined behaviour, it needs to ensure that any concurrent operations created within the <code class="sourceCode default">do_something()</code> operation that might be accessing <code class="sourceCode default">r</code> have all completed before it resumes the <code class="sourceCode default">example()</code> coroutine and <code class="sourceCode default">r</code> is destroyed.</p>
<p>To support this, we need <code class="sourceCode default">do_something()</code> to ensure that it provides the structured concurrency guarantee. i.e. that it does not leak any detached work that might still be accessing <code class="sourceCode default">r</code> after <code class="sourceCode default">do_something()</code> completes.</p>
<p>To expand on this further, let’s look at a possible implementation of <code class="sourceCode default">do_something()</code>.</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;</span>A<span class="op">&gt;</span> do_part1<span class="op">(</span><span class="kw">const</span> resource<span class="op">&amp;</span> r<span class="op">)</span>;</span>
<span id="cb7-2"><a href="#cb7-2" aria-hidden="true" tabindex="-1"></a>task<span class="op">&lt;</span>B<span class="op">&gt;</span> do_part2<span class="op">(</span><span class="kw">const</span> resource<span class="op">&amp;</span> r<span class="op">)</span>;</span>
<span id="cb7-3"><a href="#cb7-3" aria-hidden="true" tabindex="-1"></a><span class="dt">void</span> do_part3<span class="op">(</span><span class="kw">const</span> A<span class="op">&amp;</span> a, <span class="kw">const</span> B<span class="op">&amp;</span> b<span class="op">)</span></span>
<span id="cb7-4"><a href="#cb7-4" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb7-5"><a href="#cb7-5" aria-hidden="true" tabindex="-1"></a>task<span class="op">&lt;</span><span class="dt">void</span><span class="op">&gt;</span> do_something<span class="op">(</span><span class="kw">const</span> resource<span class="op">&amp;</span> r<span class="op">)</span> <span class="op">{</span></span>
<span id="cb7-6"><a href="#cb7-6" aria-hidden="true" tabindex="-1"></a>  <span class="kw">auto</span> <span class="op">[</span>a, b<span class="op">]</span> <span class="op">=</span> <span class="kw">co_await</span> when_all<span class="op">(</span></span>
<span id="cb7-7"><a href="#cb7-7" aria-hidden="true" tabindex="-1"></a>    do_part1<span class="op">(</span>r<span class="op">)</span>,</span>
<span id="cb7-8"><a href="#cb7-8" aria-hidden="true" tabindex="-1"></a>    do_part2<span class="op">(</span>r<span class="op">))</span>;</span>
<span id="cb7-9"><a href="#cb7-9" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb7-10"><a href="#cb7-10" aria-hidden="true" tabindex="-1"></a>  do_part3<span class="op">(</span>a, b<span class="op">)</span>;</span>
<span id="cb7-11"><a href="#cb7-11" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
<p>In this case, the <code class="sourceCode default">do_something()</code> operation is launching two potentially concurrent child operations, <code class="sourceCode default">do_part1()</code> and <code class="sourceCode default">do_part2()</code>, and then processing the results.</p>
<p>Both of these operations must complete successfully to be able to combine their results. However, if one of them fails with an error then we no longer need the results of the other operation since the operation as a whole will complete with an error.</p>
<p>In this case, we could theoretically imagine that the <code class="sourceCode default">when_all()</code> operation might be able to complete early with an error if the first operation fails, since at that point we know what its result will be.</p>
<p>However, if we don’t also wait for the other operation to complete then the <code class="sourceCode default">do_something()</code> operation may run to completion and then resume the calling <code class="sourceCode default">example()</code> coroutine which will then destroy the resource object, <code class="sourceCode default">r</code>, before the other child task completes.</p>
<p>Thus to avoid a dangling reference, the <code class="sourceCode default">when_all()</code> implementation still needs to ensure that all child-operations have run to completion, even though it has already computed its result and the results of the outstanding operations will just be discarded.</p>
<p><strong>In general, the ability to pass parameters by reference to coroutines safely</strong> <strong><em>requires</em> that we ensure concurrent child operations are all joined before returning.</strong>.</p>
<p>If we implement algorithms with this property then when we compose them, those higher level algorithms can also have this property. If any algorithms fail to preserve this “structured concurrency” property then any consumers of that algorithm will generally also fail to preserve that property.</p>
<p>Returning to the <code class="sourceCode default">when_all()</code> use-case above, it still has to wait for the other operation to finish if the first one fails early with an error. If it were to just wait for the operation to complete naturally then the operation as a whole will take longer to complete than necessary.</p>
<p>Ideally we’d have some way to ask that remaining child operation to complete as soon as it can because we are just going to discard its result. i.e. we need a way to request cancellation of that other operation.</p>
<h1 data-number="3" id="prior-art-in-cancellation-patterns"><span class="header-section-number">3</span> Prior-art in cancellation patterns<a href="#prior-art-in-cancellation-patterns" class="self-link"></a></h1>
<p>This section looks at some existing cancellation patterns used in C++ and in other languages to understand some of the limitations.</p>
<h2 data-number="3.1" id="networking-ts-boostasio-cancellation-model"><span class="header-section-number">3.1</span> Networking TS / boost::asio cancellation model<a href="#networking-ts-boostasio-cancellation-model" class="self-link"></a></h2>
<p>Many of the asynchronous facilities within boost::asio have support for cancellation.</p>
<p>The ability to request cancellation of operations is generally exposed to the user as a <code class="sourceCode default">.cancel()</code> method on the I/O object that was used to launch the asynchronous operation.</p>
<p>For example, the <code class="sourceCode default">basic_waitable_timer</code> type described in <a href="https://wg21.link/N4734">N4734</a> (C++ Extensions for Networking Working Draft) provides a <code class="sourceCode default">.cancel()</code> method that will cancel all outstanding <code class="sourceCode default">async_wait()</code> operations launched on the timer object.</p>
<p>For example:</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><span class="kw">using</span> <span class="kw">namespace</span> std<span class="op">::</span>experimental<span class="op">::</span>net;</span>
<span id="cb8-2"><a href="#cb8-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb8-3"><a href="#cb8-3" aria-hidden="true" tabindex="-1"></a>io_context<span class="op">&amp;</span> ioCtx <span class="op">=</span> <span class="op">...</span>;</span>
<span id="cb8-4"><a href="#cb8-4" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb8-5"><a href="#cb8-5" aria-hidden="true" tabindex="-1"></a>system_timer timer<span class="op">{</span>ioCtx<span class="op">}</span>;</span>
<span id="cb8-6"><a href="#cb8-6" aria-hidden="true" tabindex="-1"></a>timer<span class="op">.</span>expires_after<span class="op">(</span><span class="dv">500</span><span class="bu">ms</span><span class="op">)</span>;</span>
<span id="cb8-7"><a href="#cb8-7" aria-hidden="true" tabindex="-1"></a>timer<span class="op">.</span>async_wait<span class="op">([](</span>std<span class="op">::</span>error_code ec<span class="op">)</span> <span class="op">{</span></span>
<span id="cb8-8"><a href="#cb8-8" aria-hidden="true" tabindex="-1"></a>  <span class="cf">if</span> <span class="op">(!</span>ec<span class="op">)</span> <span class="op">{</span></span>
<span id="cb8-9"><a href="#cb8-9" aria-hidden="true" tabindex="-1"></a>    std<span class="op">::</span>cout <span class="op">&lt;&lt;</span> <span class="st">&quot;500ms has elapsed!&quot;</span>;</span>
<span id="cb8-10"><a href="#cb8-10" aria-hidden="true" tabindex="-1"></a>  <span class="op">}</span> <span class="cf">else</span> <span class="cf">if</span> <span class="op">(</span>ec <span class="op">==</span> std<span class="op">::</span>errc<span class="op">::</span>operation_canceled<span class="op">)</span> <span class="op">{</span></span>
<span id="cb8-11"><a href="#cb8-11" aria-hidden="true" tabindex="-1"></a>    std<span class="op">::</span>cout <span class="op">&lt;&lt;</span> <span class="st">&quot;timer was cancelled</span><span class="sc">\n</span><span class="st">&quot;</span>;</span>
<span id="cb8-12"><a href="#cb8-12" aria-hidden="true" tabindex="-1"></a>  <span class="op">}</span> <span class="cf">else</span> <span class="op">{</span></span>
<span id="cb8-13"><a href="#cb8-13" aria-hidden="true" tabindex="-1"></a>    std<span class="op">::</span>cout <span class="op">&lt;&lt;</span> <span class="st">&quot;error: &quot;</span> <span class="op">&lt;&lt;</span> ec<span class="op">.</span>message<span class="op">()</span> <span class="op">&lt;&lt;</span> <span class="st">&quot;</span><span class="sc">\n</span><span class="st">&quot;</span>;</span>
<span id="cb8-14"><a href="#cb8-14" aria-hidden="true" tabindex="-1"></a>  <span class="op">}</span></span>
<span id="cb8-15"><a href="#cb8-15" aria-hidden="true" tabindex="-1"></a><span class="op">})</span>;</span>
<span id="cb8-16"><a href="#cb8-16" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb8-17"><a href="#cb8-17" aria-hidden="true" tabindex="-1"></a><span class="co">// ... later</span></span>
<span id="cb8-18"><a href="#cb8-18" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb8-19"><a href="#cb8-19" aria-hidden="true" tabindex="-1"></a><span class="co">// Request cancellation of async_wait() operations.</span></span>
<span id="cb8-20"><a href="#cb8-20" aria-hidden="true" tabindex="-1"></a><span class="co">// Causes async_wait() to complete with std::errc::operation_canceled</span></span>
<span id="cb8-21"><a href="#cb8-21" aria-hidden="true" tabindex="-1"></a><span class="dt">size_t</span> numberCancelled <span class="op">=</span> timer<span class="op">.</span>cancel<span class="op">()</span>;</span></code></pre></div>
<p>The <code class="sourceCode default">basic_waitable_timer</code> class-template also supports a <code class="sourceCode default">.cancel_one()</code> method that cancels at most one single subscribed <code class="sourceCode default">async_wait()</code> operation rather than cancelling all subscriptions.</p>
<p>Other I/O objects in the Networking TS also provide a similar cancellation interface. For example, the <code class="sourceCode default">basic_socket</code> class-template provides both a <code class="sourceCode default">void cancel()</code> method, as well as a <code class="sourceCode default">void cancel(error_code&amp; ec)</code> method - the latter providing the ability to report an error on a failure to request cancellation.</p>
<p>Both of these <code class="sourceCode default">.cancel()</code> methods cancel all outstanding asynchronous operations associated with the socket object and cause their completion-handlers to be passed an error code that matches <code class="sourceCode default">std::errc::operation_canceled</code>.</p>
<p>Similarly, the <code class="sourceCode default">basic_socket_acceptor</code> and <code class="sourceCode default">basic_socket_resolver</code> class-templates also provide <code class="sourceCode default">void cancel()</code> and <code class="sourceCode default">void cancel(error_code&amp; ec)</code> methods that cancel all outstanding asynchronous operations.</p>
<p>It is also worth noting that the I/O objects do not support concurrent access and so it is the caller’s responsibility to ensure that any calls to <code class="sourceCode default">.cancel()</code> are serialised with respect to calls to other operations on the I/O object.</p>
<p>The typical approach to ensuring this requirement is satisfied when the source of a cancellation request occurs on another thread/executor is to schedule the call to <code class="sourceCode default">.cancel()</code> onto the I/O object’s associated strand executor.</p>
<p>One needs to be careful, then, to ensure that the I/O object is not destroyed in the meantime while the call to <code class="sourceCode default">.cancel()</code> is in the executor’s queue waiting to be processed.</p>
<h3 data-number="3.1.1" id="some-limitations"><span class="header-section-number">3.1.1</span> Some Limitations<a href="#some-limitations" class="self-link"></a></h3>
<p>While this approach to cancellation has worked successfully for users of boost::asio and the Networking TS in the domain of networking, there are a few limitations to this approach that make it difficult to generalise to all async operations.</p>
<p>It is also interesting to consider why things are designed this way.</p>
<p><strong>Inability to cancel an individual operation</strong></p>
<p>The semantics of the <code class="sourceCode default">.cancel()</code> methods is to cancel all pending asynchronous operations on the associated I/O object.</p>
<p>This makes it impossible to cancel just one specific asynchronous operation. e.g. cancelling the <code class="sourceCode default">async_read_some()</code> on a socket when there is also an outstanding <code class="sourceCode default">async_write_some()</code> on the same socket.</p>
<p>The ability to cancel individual operations may become more important if/when async facilities are extended to async file I/O.</p>
<p>For example, consider a database application that may have issued many concurrent read operations on a file, with each read associated with a different query. If some queries are cancelled then ideally only the read operations associated with those queries would be cancelled - not all read operations on the file.</p>
<p>A cancellation design that requires the program to cancel all outstanding operations associated with a given object would not work well for many use-cases.</p>
<p><strong>Requires an I/O object</strong></p>
<p>The ability to cancel async operations requires direct access to the I/O object that the operations are associated with.</p>
<p>While this generally maps closely to the requirements of the underlying OS calls for async I/O, this pattern of cancellation does not generalise well to asynchronous operations that are not methods of objects.</p>
<p>For example, say we want to write a high-level asynchronous operation that downloads a file over the network.</p>
<p>e.g.</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">template</span><span class="op">&lt;</span><span class="kw">typename</span> CompletionToken<span class="op">&gt;</span></span>
<span id="cb9-2"><a href="#cb9-2" aria-hidden="true" tabindex="-1"></a><span class="kw">auto</span> download_file<span class="op">(</span>io_context<span class="op">&amp;</span> ctx,</span>
<span id="cb9-3"><a href="#cb9-3" aria-hidden="true" tabindex="-1"></a>                   std<span class="op">:</span>string url,</span>
<span id="cb9-4"><a href="#cb9-4" aria-hidden="true" tabindex="-1"></a>                   std<span class="op">::</span>string path,</span>
<span id="cb9-5"><a href="#cb9-5" aria-hidden="true" tabindex="-1"></a>                   CompletionToken ct<span class="op">)</span>;</span></code></pre></div>
<p>This high-level algorithm might completely encapsulate creating and managing the lifetime of any sockets needed to communicate over the network internally, and never provide access to these to the caller.</p>
<p>The lack of any I/O object in this style of interface means that we have nothing that we can call <code class="sourceCode default">.cancel()</code> on to cancel all associated operations.</p>
<p>While the API could instead be exposed as a method on a hypothetical <code class="sourceCode default">file_downloader</code> class that acted as an I/O object and that kept track of outstanding async-operations and provided a <code class="sourceCode default">.cancel()</code> method on that object to cancel the requests, this approach makes building and using algorithms that support cancellation more cumbersome.</p>
<p>The granularity of cancellation being on the I/O object level also makes it difficult for generic concurrency algorithms that inject cancellation to limit their effect to only those operations that they are composing.</p>
<p><strong>Requires that every I/O operation supports cancellation</strong></p>
<p>The design of types such as <code class="sourceCode default">basic_waitable_timer</code> and <code class="sourceCode default">basic_socket</code> is such that the <code class="sourceCode default">.cancel()</code> method needs to be able to cancel all outstanding I/O operations associated with those I/O objects.</p>
<p>This means that, logically, the implementation needs to keep track of all outstanding, associated async operations so that they can be cancelled when the <code class="sourceCode default">.cancel()</code> method is called.</p>
<p>In general, this would tend to imply an increased amount of complexity in the implementation of these classes, as they need extra data-members to be able to store a list of the operations.</p>
<p>Whether this adds overhead in practice depends on the underlying platform APIs on which these types are mapping to.</p>
<p>For Windows IOCP-based sockets, the OS keeps track of the set of outstanding operations for each file-handle and calling <code class="sourceCode default">CancelIoEx(fileHandle, NULL)</code> will cancel all of them. So there is no overhead in the implementation to support this on Windows.</p>
<p>For a Linux epoll-based socket implementation, the list of outstanding operations needs to be maintained anyway as the event notifcation is per-file-handle / operation-type combination rather than per-async operation. Upon receipt of a notification from the OS that the socket is now readable, the implementation needs to be able iterate through the list of waiting <code class="sourceCode default">async_read_some()</code> operations to retry them.</p>
<p>The same lists of operations can be traversed by <code class="sourceCode default">.cancel()</code> when cancelling all outstanding I/O operations. Also, cancelling all of the operations at once can have a slight performance benefit compared to cancelling each operation individually as synchronisation is required to lock the internal data-structures in some cases and cancelling all operations can do this synchronisation once instead of once per operation.</p>
<p>A Linux io_uring-based socket implementation would be similar to the Windows IOCP in that io_uring provides notification on completion of individual async I/O operations and so does not intrinsically require the I/O object to maintain a list of outstanding async operations itself in the same way an epoll implementation does. However, it would be required to maintain this list in order to support a <code class="sourceCode default">.cancel()</code> method that cancels all outstanding operations as each individual io_uring operation must be cancelled separately. There would therefore be some additional complexity/overhead to support this.</p>
<p>The restriction that the I/O object must not be accessed concurrently from multiple threads and that calls to <code class="sourceCode default">.cancel()</code> must be sequenced with respect to other calls that start async operations avoids any synchronisation overhead that would be required if cancellation were able to be requested concurrently from other threads. This synchronisation burden is typically placed instead on the strand executor used to serialise access to the object.</p>
<p>In general, however, the pattern of supporting cancellation of all async operations associated with a given I/O object can result in additional bookkeeping needed to maintain the list of operations so that they can be cancelled.</p>
<p>Also, as a given async-operation does not necessarily know at the time it is launched whether or not it will be cancelled by a subsequent call to <code class="sourceCode default">.cancel()</code>, the implementation must assume it might and so perform the necessary bookkeeping to allow this, even if the operation will never be cancelled. There is no way for a caller to specify that this particular async-operation will never be cancelled.</p>
<p><strong>Lack of generic composability</strong></p>
<p>The Networking TS design does not require every async operation or I/O object to implement the same interface for cancellation - some have a <code class="sourceCode default">.cancel()</code> and a <code class="sourceCode default">.cancel_one()</code> which return a count, while others have a <code class="sourceCode default">.cancel()</code> and <code class="sourceCode default">.cancel(error_code&amp;)</code>.</p>
<p>The lack of a uniform interface to I/O objects and cancellation in general makes it impossible to compose arbitrary async operations generically with general-purpose algorithms that need to be able to to request cancellation.</p>
<p>For example, a generic <code class="sourceCode default">when_all()</code> or <code class="sourceCode default">timeout()</code> algorithm written to work with any child async operations cannot cancel those child operations unless there is a uniform/generic interface that it can be implemented in terms of.</p>
<p>This often leaves applications that compose Networking TS-based async operations and that want to support cancellation having to do so in an ad-hoc manner.</p>
<h2 data-number="3.2" id="net-framework-cancellation-model"><span class="header-section-number">3.2</span> .NET Framework cancellation model<a href="#net-framework-cancellation-model" class="self-link"></a></h2>
<p>The .NET Framework has had support for cancellation of async operations since the async model was greatly expanded in .NET v4.0.</p>
<p>Cancellation in the .NET Framework is generally built on top of the <code class="sourceCode default">CancellationToken</code>, <code class="sourceCode default">CancellationTokenSource</code> and <code class="sourceCode default">CancellationTokenRegistration</code> family of classes. These types closely correspond to the <code class="sourceCode default">std::stop_token</code> family of classes added to the C++ standard library in C++20.</p>
<p>Cancellable operations in .NET generally accept an additional parameter of type <code class="sourceCode default">CancellationToken</code>.</p>
<p>Many APIs also provide overloads that do not take a <code class="sourceCode default">CancellationToken</code> to make it easier to call in cases where the caller does not require cancellation. These APIs often just forward to the <code class="sourceCode default">CancellationToken</code>-taking version, passing <code class="sourceCode default">CancellationToken.None</code> - which is a token for which cancellation will never be requested.</p>
<p>For example:</p>
<div class="sourceCode" id="cb1"><pre class="sourceCode c#"><code class="sourceCode cs"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="kw">namespace</span> System.<span class="fu">Net</span>.<span class="fu">Http</span></span>
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a>{</span>
<span id="cb1-3"><a href="#cb1-3" aria-hidden="true" tabindex="-1"></a>  <span class="kw">class</span> HttpClient</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>
<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="co">// Non-cancellable version</span></span>
<span id="cb1-8"><a href="#cb1-8" aria-hidden="true" tabindex="-1"></a>    <span class="kw">public</span> Task&lt;HttpResponseMessage&gt; <span class="fu">GetAsync</span>(Uri requestUri) {</span>
<span id="cb1-9"><a href="#cb1-9" aria-hidden="true" tabindex="-1"></a>      <span class="kw">return</span> <span class="kw">this</span>.<span class="fu">GetAsync</span>(requestUri, CancellationToken.<span class="fu">None</span>);</span>
<span id="cb1-10"><a href="#cb1-10" aria-hidden="true" tabindex="-1"></a>    }</span>
<span id="cb1-11"><a href="#cb1-11" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-12"><a href="#cb1-12" aria-hidden="true" tabindex="-1"></a>    <span class="co">// Cancellable version</span></span>
<span id="cb1-13"><a href="#cb1-13" aria-hidden="true" tabindex="-1"></a>    <span class="kw">public</span> Task&lt;HttpResponseMessage&gt; <span class="fu">GetAsync</span>(Uri requestUri,</span>
<span id="cb1-14"><a href="#cb1-14" aria-hidden="true" tabindex="-1"></a>                                              CancellationToken cancellationToken);</span>
<span id="cb1-15"><a href="#cb1-15" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-16"><a href="#cb1-16" aria-hidden="true" tabindex="-1"></a>    ...</span>
<span id="cb1-17"><a href="#cb1-17" aria-hidden="true" tabindex="-1"></a>  }</span>
<span id="cb1-18"><a href="#cb1-18" aria-hidden="true" tabindex="-1"></a>}</span></code></pre></div>
<p>The implementation of cancellable async operations can either poll the token to see if cancellation has been requested by querying the <code class="sourceCode default">IsCancellationRequested</code> property, or can subscribe a callback to receive notification of a cancellation request using the <code class="sourceCode default">CancellationTokenRegistration</code> type.</p>
<p>The implementation of a cancellable async operation can also query whether or not cancellation will ever be requested by querying the <code class="sourceCode default">.CanBeCancelled</code> property. Some async operations can use a more efficient strategy if they don’t have to worry about supporting cancellation.</p>
<p>This is something that the Networking TS cancellation model above is not able to do.</p>
<p>The caller can request cancellation by creating a new <code class="sourceCode default">CancellationTokenSource</code> object and passing the <code class="sourceCode default">CancellationToken</code> obtained from its <code class="sourceCode default">.Token</code> property, then later calling <code class="sourceCode default">.Cancel()</code> method on the <code class="sourceCode default">CancellationTokenSource</code>.</p>
<p><strong>Task eagerness impacts cancellation design</strong></p>
<p>Note that the way <code class="sourceCode default">Task</code>-returning <code class="sourceCode default">async</code> methods work in the .NET Framework is that they start executing immediately when they are called and only return the <code class="sourceCode default">Task</code> object to the caller when they suspend, i.e. at an <code class="sourceCode default">await</code> expression, or when the method completes, either by returning a value or exiting via an exception.</p>
<p>For example:</p>
<div class="sourceCode" id="cb2"><pre class="sourceCode c#"><code class="sourceCode cs"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a>Task <span class="fu">Example</span>() {</span>
<span id="cb2-2"><a href="#cb2-2" aria-hidden="true" tabindex="-1"></a>  <span class="fu">Part1</span>();</span>
<span id="cb2-3"><a href="#cb2-3" aria-hidden="true" tabindex="-1"></a>  await <span class="fu">Part2</span>();</span>
<span id="cb2-4"><a href="#cb2-4" aria-hidden="true" tabindex="-1"></a>  <span class="fu">Part3</span>();</span>
<span id="cb2-5"><a href="#cb2-5" aria-hidden="true" tabindex="-1"></a>}</span>
<span id="cb2-6"><a href="#cb2-6" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb2-7"><a href="#cb2-7" aria-hidden="true" tabindex="-1"></a>Task <span class="fu">Caller</span>() {</span>
<span id="cb2-8"><a href="#cb2-8" aria-hidden="true" tabindex="-1"></a>  Task t = <span class="fu">Example</span>();</span>
<span id="cb2-9"><a href="#cb2-9" aria-hidden="true" tabindex="-1"></a>  <span class="co">// By here &#39;Example()&#39; has already called Part1() and Part2()</span></span>
<span id="cb2-10"><a href="#cb2-10" aria-hidden="true" tabindex="-1"></a>  <span class="co">// and is now suspended in &#39;await&#39; expression.</span></span>
<span id="cb2-11"><a href="#cb2-11" aria-hidden="true" tabindex="-1"></a>  </span>
<span id="cb2-12"><a href="#cb2-12" aria-hidden="true" tabindex="-1"></a>}</span></code></pre></div>
<p>One of the main reasons that .NET chose this eager execution model is to avoid heap-allocating storage for the local-variables in cases where the operation completes synchronously.</p>
<p>Instead of immediately heap-allocating storage for the async state, the implementation of an <code class="sourceCode default">async</code>-qualified method initially allocates the async state on the stack and then lazily moves that state to a heap-allocation only if the method suspends at an <code class="sourceCode default">await</code> expression.</p>
<p>This reduces the number of short-lived heap-allocations and thus reduces the frequency with which the garbage collector needs to run - at least for async function invocations that complete synchronously.</p>
<blockquote>
<p>This is something that is possible because of the garbage collected nature of .NET that allows relocating objects in memory.</p>
<p>The same strategy does not work for coroutines in C++ because the state can have internal pointers which cannot in general be automatically updated if the state is relocated.</p>
</blockquote>
<p>The fact that async functions execute eagerly then requires that the <code class="sourceCode default">CancellationToken</code> is available at the time the function is invoked so that it can be referenced during that initial sequential part of its execution, but also so that it can be passed down to other child tasks.</p>
<p>Thus, as the <code class="sourceCode default">CancellationToken</code> needs to be available at the time the operation starts executing, it <em>must</em> be provided as an argument to the function.</p>
<p><strong>Inability to inject cancellation</strong></p>
<p>One implication of this requirement to pass the <code class="sourceCode default">CancellationToken</code> as an argument when a <code class="sourceCode default">Task</code>-returning method is called, however, is that it means that you cannot use higher-order functions to compose <code class="sourceCode default">Task</code> objects and allow those higher-order functions to inject cancellation into those tasks.</p>
<p>For example, the <code class="sourceCode default">Task.WhenAll(params Task[] tasks)</code> algorithm is passed an array of <code class="sourceCode default">Task</code> objects and returns a <code class="sourceCode default">Task</code> that completes when all of the input tasks have completed. However, this algorithm is not able to request cancellation of the other <code class="sourceCode default">Task</code> objects if one of the tasks fails with an exception as it has no way to inject a <code class="sourceCode default">CancellationToken</code> into those async operations that it can use to communicate the request.</p>
<p>To be able to compose cancellation patterns generically, you would instead need to pass a lambda that takes a <code class="sourceCode default">CancellationToken</code> and returns a <code class="sourceCode default">Task</code> into the higher-order function so that the algorithm could inject its own <code class="sourceCode default">CancellationToken</code>.</p>
<p>The inability to compose cancellation patterns generically using higher-order algorithms in terms of <code class="sourceCode default">Task</code> will tend to mean that common concurrency/cancellation patterns will need to be manually re-implemented for each composition of child operations.</p>
<p><strong>Taking a CancellationToken argument advertises cancellability</strong></p>
<p>One of the interesting outcomes of the design choice for cancellation in .NET is that by requiring the caller to pass a <code class="sourceCode default">CancellationToken</code> as a parameter to cancellable async functions is that it advertises cancellability of that operation - it’s right there in the signature!</p>
<p>It also forces the implementation to think about cancellation. This can be a good thing as it means cancellability is more likely to be implemented. But it can also be a bad thing, as it forces the author of the code to do the busy-work of ensuring the <code class="sourceCode default">CancellationToken</code> is plumbed through to any child operations.</p>
<p><strong>Passing a CancellationToken tends to work well for await-style asynchrony</strong></p>
<p>With traditional callback-style async programming we often make a call to launch an operation and then execution returns to that caller which can then do something else, such as storing a handle to the operation somewhere that can be later used to request cancellation of that operation.</p>
<p>For example, code using <code class="sourceCode default">folly::Future</code>-based APIs might do the following:</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>folly<span class="op">::</span>Future<span class="op">&lt;</span><span class="dt">int</span><span class="op">&gt;</span> an_operation<span class="op">()</span>;</span>
<span id="cb10-2"><a href="#cb10-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb10-3"><a href="#cb10-3" aria-hidden="true" tabindex="-1"></a><span class="dt">void</span> some_class<span class="op">::</span>start_the_operation<span class="op">()</span> <span class="op">{</span></span>
<span id="cb10-4"><a href="#cb10-4" aria-hidden="true" tabindex="-1"></a>  <span class="co">// Launch an async operation.</span></span>
<span id="cb10-5"><a href="#cb10-5" aria-hidden="true" tabindex="-1"></a>  <span class="co">// Store the handle somewhere we can access it later.</span></span>
<span id="cb10-6"><a href="#cb10-6" aria-hidden="true" tabindex="-1"></a>  <span class="kw">this</span><span class="op">-&gt;</span>future <span class="op">=</span> on_operation<span class="op">()</span>;</span>
<span id="cb10-7"><a href="#cb10-7" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb10-8"><a href="#cb10-8" aria-hidden="true" tabindex="-1"></a>  <span class="co">// Attach a callback to be run when the operation completes.</span></span>
<span id="cb10-9"><a href="#cb10-9" aria-hidden="true" tabindex="-1"></a>  <span class="kw">this</span><span class="op">-&gt;</span>future<span class="op">.</span>addCallback_<span class="op">([&amp;](</span>folly<span class="op">::</span>Try<span class="op">&lt;</span><span class="dt">int</span><span class="op">&gt;&amp;&amp;</span> result<span class="op">)</span> <span class="op">{</span></span>
<span id="cb10-10"><a href="#cb10-10" aria-hidden="true" tabindex="-1"></a>    <span class="cf">if</span> <span class="op">(</span>result<span class="op">.</span>hasValue<span class="op">())</span> <span class="op">{</span></span>
<span id="cb10-11"><a href="#cb10-11" aria-hidden="true" tabindex="-1"></a>      <span class="co">// Succeeded</span></span>
<span id="cb10-12"><a href="#cb10-12" aria-hidden="true" tabindex="-1"></a>    <span class="op">}</span> <span class="cf">else</span> <span class="cf">if</span> <span class="op">(</span>result<span class="op">.</span>hasException<span class="op">&lt;</span>folly<span class="op">::</span>OperationCancelled<span class="op">&gt;())</span> <span class="op">{</span></span>
<span id="cb10-13"><a href="#cb10-13" aria-hidden="true" tabindex="-1"></a>      <span class="co">// Cancelled</span></span>
<span id="cb10-14"><a href="#cb10-14" aria-hidden="true" tabindex="-1"></a>    <span class="op">}</span> <span class="cf">else</span> <span class="op">{</span></span>
<span id="cb10-15"><a href="#cb10-15" aria-hidden="true" tabindex="-1"></a>      <span class="co">// Other error</span></span>
<span id="cb10-16"><a href="#cb10-16" aria-hidden="true" tabindex="-1"></a>    <span class="op">}</span></span>
<span id="cb10-17"><a href="#cb10-17" aria-hidden="true" tabindex="-1"></a>  <span class="op">})</span>;</span>
<span id="cb10-18"><a href="#cb10-18" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb10-19"><a href="#cb10-19" aria-hidden="true" tabindex="-1"></a>  <span class="co">// do something else that might trigger a cancellation...</span></span>
<span id="cb10-20"><a href="#cb10-20" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span>
<span id="cb10-21"><a href="#cb10-21" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb10-22"><a href="#cb10-22" aria-hidden="true" tabindex="-1"></a><span class="dt">void</span> some_class<span class="op">::</span>on_some_event<span class="op">()</span> <span class="op">{</span></span>
<span id="cb10-23"><a href="#cb10-23" aria-hidden="true" tabindex="-1"></a>  <span class="co">// Request cancellation of the operation by calling .cancel()</span></span>
<span id="cb10-24"><a href="#cb10-24" aria-hidden="true" tabindex="-1"></a>  <span class="co">// on the folly::future. The callback attached via setCallback_()</span></span>
<span id="cb10-25"><a href="#cb10-25" aria-hidden="true" tabindex="-1"></a>  <span class="co">// will still be run when the operation completes. </span></span>
<span id="cb10-26"><a href="#cb10-26" aria-hidden="true" tabindex="-1"></a>  <span class="kw">this</span><span class="op">-&gt;</span>future<span class="op">.</span>cancel<span class="op">()</span>;</span>
<span id="cb10-27"><a href="#cb10-27" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
<p>However, when writing coroutine-based async code we often immediately await the handle returned by invoking another coroutine.</p>
<p>For example:</p>
<div class="sourceCode" id="cb3"><pre class="sourceCode c#"><code class="sourceCode cs"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a>Task&lt;<span class="dt">int</span>&gt; <span class="fu">AnotherOperation</span>();</span>
<span id="cb3-2"><a href="#cb3-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-3"><a href="#cb3-3" aria-hidden="true" tabindex="-1"></a>Task&lt;<span class="dt">void</span>&gt; <span class="fu">Consumer</span>() {</span>
<span id="cb3-4"><a href="#cb3-4" aria-hidden="true" tabindex="-1"></a>  <span class="co">// Immediately awaitkng the returned task has two implications:</span></span>
<span id="cb3-5"><a href="#cb3-5" aria-hidden="true" tabindex="-1"></a>  <span class="co">// - it consumes the task, meaning we cannot subsequently use it to</span></span>
<span id="cb3-6"><a href="#cb3-6" aria-hidden="true" tabindex="-1"></a>  <span class="co">//   request cancellation.</span></span>
<span id="cb3-7"><a href="#cb3-7" aria-hidden="true" tabindex="-1"></a>  <span class="co">// - it suspends execution of the calling coroutine, meaning it is</span></span>
<span id="cb3-8"><a href="#cb3-8" aria-hidden="true" tabindex="-1"></a>  <span class="co">//   unable to execute any other code that might cancel the operation.</span></span>
<span id="cb3-9"><a href="#cb3-9" aria-hidden="true" tabindex="-1"></a>  <span class="dt">int</span> x = await <span class="fu">AnotherOperation</span>();</span>
<span id="cb3-10"><a href="#cb3-10" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-11"><a href="#cb3-11" aria-hidden="true" tabindex="-1"></a>  <span class="co">// Operation already completed here. No opportunity to cancel it.</span></span>
<span id="cb3-12"><a href="#cb3-12" aria-hidden="true" tabindex="-1"></a>}</span></code></pre></div>
<p>To allow the caller to communicate a cancellation request it can pass a <code class="sourceCode default">CancellationToken</code> (or <code class="sourceCode default">std::stop_token</code> in C++) into the child operation to allow the caller to communicate a request to cancel and for the child operation to receive that request.</p>
<p>In the cppcoro cancellation model and the .NET Framework cancellation model, the cancellation-token is usually passed as a parameter. This works well and is generally composable when making immediately invoked/awaited coroutine calls.</p>
<p>e.g.</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><span class="kw">using</span> <span class="kw">namespace</span> cppcoro;</span>
<span id="cb11-2"><a href="#cb11-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb11-3"><a href="#cb11-3" aria-hidden="true" tabindex="-1"></a>task<span class="op">&lt;</span><span class="dt">int</span><span class="op">&gt;</span> another_operation<span class="op">(</span>cancellation_token ct<span class="op">)</span>;</span>
<span id="cb11-4"><a href="#cb11-4" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb11-5"><a href="#cb11-5" aria-hidden="true" tabindex="-1"></a>task<span class="op">&lt;</span><span class="dt">void</span><span class="op">&gt;</span> consumer<span class="op">(</span>cancellation_token ct<span class="op">)</span> <span class="op">{</span></span>
<span id="cb11-6"><a href="#cb11-6" aria-hidden="true" tabindex="-1"></a>  <span class="co">// Explicitly passing the cancellation-token to child operations allows</span></span>
<span id="cb11-7"><a href="#cb11-7" aria-hidden="true" tabindex="-1"></a>  <span class="co">// us to communicate cancellation requests of the parent operation through</span></span>
<span id="cb11-8"><a href="#cb11-8" aria-hidden="true" tabindex="-1"></a>  <span class="co">// to child operations.</span></span>
<span id="cb11-9"><a href="#cb11-9" aria-hidden="true" tabindex="-1"></a>  <span class="dt">int</span> x <span class="op">=</span> <span class="kw">co_await</span> another_operation<span class="op">(</span>ct<span class="op">)</span>;</span>
<span id="cb11-10"><a href="#cb11-10" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb11-11"><a href="#cb11-11" aria-hidden="true" tabindex="-1"></a>  <span class="co">// use x</span></span>
<span id="cb11-12"><a href="#cb11-12" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
<h2 data-number="3.3" id="cppcorotask-cancellation"><span class="header-section-number">3.3</span> cppcoro::task cancellation<a href="#cppcorotask-cancellation" class="self-link"></a></h2>
<p>The <a href="https://github.com/lewissbaker/cppcoro">cppcoro</a> library takes a similar approach to cancellation that the .NET Framework takes.</p>
<p>Async operations that are cancellable can optionally take an extra <code class="sourceCode default">cancellation_token</code> parameter which the caller can use to later communicate a request to cancel the operation.</p>
<p>This approach suffers from the same composability limitations discussed in the section above on the .NET Framework cancellation model.</p>
<p>A key limitation here is that we cannot compose operations represented by <code class="sourceCode default">cppcoro::task</code> objects using generic concurrency algorithms and have those algorithms manage cancellation of the child operations.</p>
<p>The workaround has been to have these generic algorithms invoked with task factories that allow the algorithm to inject a <code class="sourceCode default">cancellation_token</code> that it controls.</p>
<p>For example, a generic <code class="sourceCode default">timeout()</code> algorithm:</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">using</span> <span class="kw">namespace</span> cppcoro;</span>
<span id="cb12-2"><a href="#cb12-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb12-3"><a href="#cb12-3" aria-hidden="true" tabindex="-1"></a><span class="kw">template</span><span class="op">&lt;</span></span>
<span id="cb12-4"><a href="#cb12-4" aria-hidden="true" tabindex="-1"></a>  <span class="kw">typename</span> AwaitableFactory,</span>
<span id="cb12-5"><a href="#cb12-5" aria-hidden="true" tabindex="-1"></a>  <span class="kw">typename</span> Scheduler,</span>
<span id="cb12-6"><a href="#cb12-6" aria-hidden="true" tabindex="-1"></a>  <span class="kw">typename</span> Duration,</span>
<span id="cb12-7"><a href="#cb12-7" aria-hidden="true" tabindex="-1"></a>  <span class="kw">typename</span> Result <span class="op">=</span> await_result_t<span class="op">&lt;</span>invoke_result_t<span class="op">&lt;</span>AwaitableFactory<span class="op">&amp;</span>, cancellation_token<span class="op">&gt;&gt;&gt;</span></span>
<span id="cb12-8"><a href="#cb12-8" aria-hidden="true" tabindex="-1"></a>task<span class="op">&lt;</span>Result<span class="op">&gt;</span> timeout<span class="op">(</span>AwaitableFactory f,</span>
<span id="cb12-9"><a href="#cb12-9" aria-hidden="true" tabindex="-1"></a>                     Scheduler s,</span>
<span id="cb12-10"><a href="#cb12-10" aria-hidden="true" tabindex="-1"></a>                     Duration d,</span>
<span id="cb12-11"><a href="#cb12-11" aria-hidden="true" tabindex="-1"></a>                     cancellation_token ct <span class="op">=</span> <span class="op">{})</span> <span class="op">{</span></span>
<span id="cb12-12"><a href="#cb12-12" aria-hidden="true" tabindex="-1"></a>  cancellation_source src;</span>
<span id="cb12-13"><a href="#cb12-13" aria-hidden="true" tabindex="-1"></a>  cancellation_registration cancelParent<span class="op">{</span>std<span class="op">::</span>move<span class="op">(</span>ct<span class="op">)</span>,</span>
<span id="cb12-14"><a href="#cb12-14" aria-hidden="true" tabindex="-1"></a>                                         <span class="op">[&amp;]</span> <span class="op">{</span> src<span class="op">.</span>request_cancellation<span class="op">()</span>; <span class="op">}}</span>;</span>
<span id="cb12-15"><a href="#cb12-15" aria-hidden="true" tabindex="-1"></a>  <span class="kw">auto</span> <span class="op">[</span>result, _<span class="op">]</span> <span class="op">=</span> <span class="kw">co_await</span> when_all<span class="op">(</span></span>
<span id="cb12-16"><a href="#cb12-16" aria-hidden="true" tabindex="-1"></a>    <span class="op">[&amp;]()</span> <span class="op">-&gt;</span> task<span class="op">&lt;</span>Result<span class="op">&gt;</span> <span class="op">{</span></span>
<span id="cb12-17"><a href="#cb12-17" aria-hidden="true" tabindex="-1"></a>      <span class="kw">auto</span> cancelOnExit <span class="op">=</span> on_scope_exit<span class="op">([&amp;]</span> <span class="op">{</span> src<span class="op">.</span>request_cancellation<span class="op">()</span>; <span class="op">})</span>;</span>
<span id="cb12-18"><a href="#cb12-18" aria-hidden="true" tabindex="-1"></a>      <span class="kw">co_return</span> <span class="kw">co_await</span> f<span class="op">(</span>src<span class="op">.</span>token<span class="op">())</span>;</span>
<span id="cb12-19"><a href="#cb12-19" aria-hidden="true" tabindex="-1"></a>    <span class="op">}()</span>,</span>
<span id="cb12-20"><a href="#cb12-20" aria-hidden="true" tabindex="-1"></a>    <span class="op">[&amp;]()</span> <span class="op">-&gt;</span> task<span class="op">&lt;</span><span class="dt">void</span><span class="op">&gt;</span> <span class="op">{</span></span>
<span id="cb12-21"><a href="#cb12-21" aria-hidden="true" tabindex="-1"></a>      <span class="kw">auto</span> cancelOnExit <span class="op">=</span> on_scope_exit<span class="op">([&amp;]</span> <span class="op">{</span> src<span class="op">.</span>request_cancellation<span class="op">()</span>; <span class="op">})</span>;</span>
<span id="cb12-22"><a href="#cb12-22" aria-hidden="true" tabindex="-1"></a>      <span class="cf">try</span> <span class="op">{</span></span>
<span id="cb12-23"><a href="#cb12-23" aria-hidden="true" tabindex="-1"></a>        <span class="kw">co_await</span> s<span class="op">.</span>schedule_after<span class="op">(</span>d, src<span class="op">.</span>token<span class="op">())</span>;</span>
<span id="cb12-24"><a href="#cb12-24" aria-hidden="true" tabindex="-1"></a>      <span class="op">}</span> <span class="cf">catch</span> <span class="op">(</span><span class="kw">const</span> operation_cancelled<span class="op">&amp;)</span> <span class="op">{}</span></span>
<span id="cb12-25"><a href="#cb12-25" aria-hidden="true" tabindex="-1"></a>    <span class="op">}())</span>;</span>
<span id="cb12-26"><a href="#cb12-26" aria-hidden="true" tabindex="-1"></a>  <span class="kw">co_return</span> std<span class="op">::</span>move<span class="op">(</span>result<span class="op">)</span>;</span>
<span id="cb12-27"><a href="#cb12-27" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
<p>The usage of such an algorithm is cumbersome as you end up having to wrap every call to an async operation that you want to compose with concurrency algorithms in a lambda that lets you inject the appropriate <code class="sourceCode default">cancellation_token</code>.</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">using</span> <span class="kw">namespace</span> cppcoro;</span>
<span id="cb13-2"><a href="#cb13-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb13-3"><a href="#cb13-3" aria-hidden="true" tabindex="-1"></a>task<span class="op">&lt;</span>response<span class="op">&gt;</span> query<span class="op">(</span>request req, cancellation_token ct <span class="op">=</span> <span class="op">{})</span>;</span>
<span id="cb13-4"><a href="#cb13-4" aria-hidden="true" tabindex="-1"></a>static_thread_pool threadPool;</span>
<span id="cb13-5"><a href="#cb13-5" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb13-6"><a href="#cb13-6" aria-hidden="true" tabindex="-1"></a>task<span class="op">&lt;</span><span class="dt">void</span><span class="op">&gt;</span> example_usage<span class="op">(</span>cancellation_token ct <span class="op">=</span> <span class="op">{})</span> <span class="op">{</span></span>
<span id="cb13-7"><a href="#cb13-7" aria-hidden="true" tabindex="-1"></a>  request req;</span>
<span id="cb13-8"><a href="#cb13-8" aria-hidden="true" tabindex="-1"></a>  req<span class="op">.</span>set_param<span class="op">(</span><span class="dv">123</span><span class="op">)</span>;</span>
<span id="cb13-9"><a href="#cb13-9" aria-hidden="true" tabindex="-1"></a>  <span class="co">//...</span></span>
<span id="cb13-10"><a href="#cb13-10" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb13-11"><a href="#cb13-11" aria-hidden="true" tabindex="-1"></a>  <span class="co">// Simple call without timeout() is relatively straigh-forward.</span></span>
<span id="cb13-12"><a href="#cb13-12" aria-hidden="true" tabindex="-1"></a>  response result1 <span class="op">=</span> <span class="kw">co_await</span> query<span class="op">(</span>req, ct<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">// Wrapping with a generic timeout() algorithm is painful.</span></span>
<span id="cb13-15"><a href="#cb13-15" aria-hidden="true" tabindex="-1"></a>  response result2 <span class="op">=</span> <span class="kw">co_await</span> timeout<span class="op">([&amp;](</span>cancellation_token ct<span class="op">)</span> <span class="op">{</span></span>
<span id="cb13-16"><a href="#cb13-16" aria-hidden="true" tabindex="-1"></a>    <span class="cf">return</span> query<span class="op">(</span>std<span class="op">::</span>move<span class="op">(</span>req<span class="op">)</span>, std<span class="op">::</span>move<span class="op">(</span>ct<span class="op">))</span>;</span>
<span id="cb13-17"><a href="#cb13-17" aria-hidden="true" tabindex="-1"></a>  <span class="op">}</span>, threadPool<span class="op">.</span>get_scheduler<span class="op">()</span>, <span class="dv">500</span><span class="bu">ms</span>, ct<span class="op">)</span>;</span>
<span id="cb13-18"><a href="#cb13-18" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
<p>This pattern generally scales poorly to large applications if you want to make use of generic concurrency algorithms that deal with cancellation.</p>
<h2 data-number="3.4" id="follycoro-cancellation-model"><span class="header-section-number">3.4</span> folly::coro cancellation model<a href="#follycoro-cancellation-model" class="self-link"></a></h2>
<p>The <code class="sourceCode default">folly::coro</code> library, which is the coroutine abstraction layer used within much of Facebook C++ code, takes a different approach to cancellation that greatly reduces the boiler-plate needed for handling cancellation.</p>
<p>Like cppcoro and the .NET Framework, <code class="sourceCode default">folly::coro</code> cancellation is build on a <code class="sourceCode default">CancellationToken</code>-based abstraction. The <code class="sourceCode default">folly::coro::Task</code> coroutine type is different, however, in that it is by-default transparent to cancellation.</p>
<p>Each <code class="sourceCode default">folly::coro::Task</code> has an implicitly associated <code class="sourceCode default">CancellationToken</code> that is automatically injected into any child operation that is <code class="sourceCode default">co_await</code>ed by that coroutine.</p>
<p>For example: Simple usage implicitly passes <code class="sourceCode default">CancellationToken</code> to child tasks.</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>folly<span class="op">::</span>coro<span class="op">::</span>Task<span class="op">&lt;</span>Response<span class="op">&gt;</span> query<span class="op">(</span>Request req<span class="op">)</span>;</span>
<span id="cb14-2"><a href="#cb14-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb14-3"><a href="#cb14-3" aria-hidden="true" tabindex="-1"></a>folly<span class="op">::</span>coro<span class="op">::</span>Task<span class="op">&lt;</span><span class="dt">void</span><span class="op">&gt;</span> example_usage<span class="op">()</span> <span class="op">{</span></span>
<span id="cb14-4"><a href="#cb14-4" aria-hidden="true" tabindex="-1"></a>  Request req;</span>
<span id="cb14-5"><a href="#cb14-5" aria-hidden="true" tabindex="-1"></a>  req<span class="op">.</span>setParam<span class="op">(</span><span class="dv">123</span><span class="op">)</span>;</span>
<span id="cb14-6"><a href="#cb14-6" aria-hidden="true" tabindex="-1"></a>  <span class="co">// ...</span></span>
<span id="cb14-7"><a href="#cb14-7" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb14-8"><a href="#cb14-8" aria-hidden="true" tabindex="-1"></a>  <span class="co">// Simple call is even simpler.</span></span>
<span id="cb14-9"><a href="#cb14-9" aria-hidden="true" tabindex="-1"></a>  Response result1 <span class="op">=</span> <span class="kw">co_await</span> query<span class="op">(</span>req<span class="op">)</span>;</span>
<span id="cb14-10"><a href="#cb14-10" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb14-11"><a href="#cb14-11" aria-hidden="true" tabindex="-1"></a>  <span class="co">// Wrapping call with a generic timeout() algorithm is much simpler.</span></span>
<span id="cb14-12"><a href="#cb14-12" aria-hidden="true" tabindex="-1"></a>  <span class="co">// This will cancel query() when either example_usage() is cancelled or</span></span>
<span id="cb14-13"><a href="#cb14-13" aria-hidden="true" tabindex="-1"></a>  <span class="co">// when the timeout expires.</span></span>
<span id="cb14-14"><a href="#cb14-14" aria-hidden="true" tabindex="-1"></a>  Response result2 <span class="op">=</span> <span class="kw">co_await</span> folly<span class="op">::</span>coro<span class="op">::</span>timeout<span class="op">(</span>query<span class="op">(</span>req<span class="op">)</span>, <span class="dv">500</span><span class="bu">ms</span><span class="op">)</span>;</span>
<span id="cb14-15"><a href="#cb14-15" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb14-16"><a href="#cb14-16" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
<p>Having the <code class="sourceCode default">CancellationToken</code> implicitly passed down to child operations removes much of the burden of manually plumbing cancellation parameters, greatly simplifying writing cancellation-correct code with coroutines.</p>
<p>A coroutine can obtain the current <code class="sourceCode default">CancellationToken</code> as follows:</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>folly<span class="op">::</span>coro<span class="op">::</span>Task<span class="op">&lt;</span><span class="dt">void</span><span class="op">&gt;</span> current_token_example<span class="op">()</span> <span class="op">{</span></span>
<span id="cb15-2"><a href="#cb15-2" aria-hidden="true" tabindex="-1"></a>  <span class="kw">const</span> folly<span class="op">::</span>CancellationToken<span class="op">&amp;</span> ct <span class="op">=</span></span>
<span id="cb15-3"><a href="#cb15-3" aria-hidden="true" tabindex="-1"></a>    <span class="kw">co_await</span> folly<span class="op">::</span>coro<span class="op">::</span>co_current_cancellation_token;</span>
<span id="cb15-4"><a href="#cb15-4" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb15-5"><a href="#cb15-5" aria-hidden="true" tabindex="-1"></a>  <span class="co">// later ...</span></span>
<span id="cb15-6"><a href="#cb15-6" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb15-7"><a href="#cb15-7" aria-hidden="true" tabindex="-1"></a>  <span class="co">// Poll for cancellation</span></span>
<span id="cb15-8"><a href="#cb15-8" aria-hidden="true" tabindex="-1"></a>  <span class="cf">if</span> <span class="op">(</span>ct<span class="op">.</span>isCancellationRequested<span class="op">())</span> <span class="op">{</span></span>
<span id="cb15-9"><a href="#cb15-9" aria-hidden="true" tabindex="-1"></a>    <span class="co">// Exit early.</span></span>
<span id="cb15-10"><a href="#cb15-10" aria-hidden="true" tabindex="-1"></a>    <span class="kw">co_yield</span> folly<span class="op">::</span>coro<span class="op">::</span>co_error<span class="op">(</span>folly<span class="op">::</span>OperationCancelled<span class="op">{})</span>;</span>
<span id="cb15-11"><a href="#cb15-11" aria-hidden="true" tabindex="-1"></a>  <span class="op">}</span></span>
<span id="cb15-12"><a href="#cb15-12" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb15-13"><a href="#cb15-13" aria-hidden="true" tabindex="-1"></a>  <span class="co">// Or attach a CancellationCallback</span></span>
<span id="cb15-14"><a href="#cb15-14" aria-hidden="true" tabindex="-1"></a>  <span class="op">{</span></span>
<span id="cb15-15"><a href="#cb15-15" aria-hidden="true" tabindex="-1"></a>    <span class="kw">auto</span> handle <span class="op">=</span> startSomeOperation<span class="op">()</span>;    </span>
<span id="cb15-16"><a href="#cb15-16" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb15-17"><a href="#cb15-17" aria-hidden="true" tabindex="-1"></a>    folly<span class="op">::</span>CancellationCallback cb<span class="op">{</span>ct, <span class="op">[&amp;]()</span> <span class="kw">noexcept</span> <span class="op">{</span></span>
<span id="cb15-18"><a href="#cb15-18" aria-hidden="true" tabindex="-1"></a>      cancelOperation<span class="op">(</span>handle<span class="op">)</span>;</span>
<span id="cb15-19"><a href="#cb15-19" aria-hidden="true" tabindex="-1"></a>    <span class="op">}}</span>;</span>
<span id="cb15-20"><a href="#cb15-20" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb15-21"><a href="#cb15-21" aria-hidden="true" tabindex="-1"></a>    <span class="co">// Wait until cancellation is requested.</span></span>
<span id="cb15-22"><a href="#cb15-22" aria-hidden="true" tabindex="-1"></a>    <span class="kw">co_await</span> waitForOperation<span class="op">(</span>handle<span class="op">)</span>;</span>
<span id="cb15-23"><a href="#cb15-23" aria-hidden="true" tabindex="-1"></a>  <span class="op">}</span></span>
<span id="cb15-24"><a href="#cb15-24" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
<p>You can manually inject your own <code class="sourceCode default">CancellationToken</code> to allow you to request cancellation of a child operation by manually calling the <code class="sourceCode default">co_withCancellation()</code> function. This overrides the implicit <code class="sourceCode default">CancellationToken</code> from the parent coroutine.</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>folly<span class="op">::</span>coro<span class="op">::</span>Task<span class="op">&lt;</span><span class="dt">void</span><span class="op">&gt;</span> do_something<span class="op">()</span>;</span>
<span id="cb16-2"><a href="#cb16-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb16-3"><a href="#cb16-3" aria-hidden="true" tabindex="-1"></a>folly<span class="op">::</span>coro<span class="op">::</span>Task<span class="op">&lt;</span><span class="dt">void</span><span class="op">&gt;</span> manual_override_example<span class="op">()</span> <span class="op">{</span></span>
<span id="cb16-4"><a href="#cb16-4" aria-hidden="true" tabindex="-1"></a>  folly<span class="op">::</span>CancellationSource cancelSrc;</span>
<span id="cb16-5"><a href="#cb16-5" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb16-6"><a href="#cb16-6" aria-hidden="true" tabindex="-1"></a>  <span class="co">// Manually hook up the parent coroutine&#39;s CancellationToken to</span></span>
<span id="cb16-7"><a href="#cb16-7" aria-hidden="true" tabindex="-1"></a>  <span class="co">// forward through to &#39;cancelSrc&#39;.</span></span>
<span id="cb16-8"><a href="#cb16-8" aria-hidden="true" tabindex="-1"></a>  folly<span class="op">::</span>CancellationCallback cancelWhenParentCancelled<span class="op">{</span></span>
<span id="cb16-9"><a href="#cb16-9" aria-hidden="true" tabindex="-1"></a>    <span class="kw">co_await</span> folly<span class="op">::</span>coro<span class="op">::</span>co_current_cancellation_token,</span>
<span id="cb16-10"><a href="#cb16-10" aria-hidden="true" tabindex="-1"></a>    <span class="op">[&amp;]()</span> <span class="kw">noexcept</span> <span class="op">{</span> cancelSrc<span class="op">.</span>requestCancellation<span class="op">()</span>; <span class="op">}}</span>;</span>
<span id="cb16-11"><a href="#cb16-11" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb16-12"><a href="#cb16-12" aria-hidden="true" tabindex="-1"></a>  <span class="co">// Inject a different CancellationToken in to the child</span></span>
<span id="cb16-13"><a href="#cb16-13" aria-hidden="true" tabindex="-1"></a>  <span class="kw">co_await</span> folly<span class="op">::</span>coro<span class="op">::</span>co_withCancellation<span class="op">(</span>cancelSrc<span class="op">.</span>getToken<span class="op">()</span>, do_something<span class="op">())</span>;</span>
<span id="cb16-14"><a href="#cb16-14" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
<p>The implementation of <code class="sourceCode default">folly::coro::Task</code> works as follows:</p>
<ul>
<li>The <code class="sourceCode default">Task</code>’s promise object holds the coroutine’s associated <code class="sourceCode default">CancellationToken</code> as a data-member.</li>
<li>The <code class="sourceCode default">Task</code>s’ coroutine type uses the <code class="sourceCode default">await_transform()</code> mechanism to automatically apply the <code class="sourceCode default">co_withCancellation()</code> customisation-point to the argument of every <code class="sourceCode default">co_await</code> expression in the coroutine-body - this is invoked with the current <code class="sourceCode default">CancellationToken</code> and the awaitable.</li>
<li>Awaitable types can customise <code class="sourceCode default">co_withCancellation()</code> to handle injecting the <code class="sourceCode default">CancellationToken</code> and thus opt-in to support for cancellability.</li>
<li>The <code class="sourceCode default">Task</code> type itself customises <code class="sourceCode default">co_withCancellation()</code> to inject the parent coroutine’s <code class="sourceCode default">CancellationToken</code> into the child coroutine’s promise.</li>
</ul>
<p>A simplified sketch of how the implementation fits together:</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">template</span><span class="op">&lt;</span><span class="kw">typename</span> T<span class="op">&gt;</span></span>
<span id="cb17-2"><a href="#cb17-2" aria-hidden="true" tabindex="-1"></a><span class="kw">class</span> TaskPromise <span class="op">{</span></span>
<span id="cb17-3"><a href="#cb17-3" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb17-4"><a href="#cb17-4" aria-hidden="true" tabindex="-1"></a>  <span class="co">// Every co_await expression injects the CancellationToken by applying</span></span>
<span id="cb17-5"><a href="#cb17-5" aria-hidden="true" tabindex="-1"></a>  <span class="co">// co_withCancellation() to the operand to the co_await operator,</span></span>
<span id="cb17-6"><a href="#cb17-6" aria-hidden="true" tabindex="-1"></a>  <span class="co">// passing the parent coroutine&#39;s current Cance</span></span>
<span id="cb17-7"><a href="#cb17-7" aria-hidden="true" tabindex="-1"></a>  <span class="kw">template</span><span class="op">&lt;</span><span class="kw">typename</span> T<span class="op">&gt;</span></span>
<span id="cb17-8"><a href="#cb17-8" aria-hidden="true" tabindex="-1"></a>  <span class="kw">decltype</span><span class="op">(</span><span class="kw">auto</span><span class="op">)</span> await_transform<span class="op">(</span>T<span class="op">&amp;&amp;</span> value<span class="op">)</span> <span class="op">{</span></span>
<span id="cb17-9"><a href="#cb17-9" aria-hidden="true" tabindex="-1"></a>    <span class="cf">return</span> folly<span class="op">::</span>coro<span class="op">::</span>co_withCancellation<span class="op">(</span>cancelToken_, <span class="kw">static_cast</span><span class="op">&lt;</span>T<span class="op">&amp;&amp;&gt;(</span>value<span class="op">))</span>;</span>
<span id="cb17-10"><a href="#cb17-10" aria-hidden="true" tabindex="-1"></a>  <span class="op">}</span></span>
<span id="cb17-11"><a href="#cb17-11" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb17-12"><a href="#cb17-12" aria-hidden="true" tabindex="-1"></a><span class="kw">private</span><span class="op">:</span></span>
<span id="cb17-13"><a href="#cb17-13" aria-hidden="true" tabindex="-1"></a>  folly<span class="op">::</span>CancellationToken cancelToken_;</span>
<span id="cb17-14"><a href="#cb17-14" aria-hidden="true" tabindex="-1"></a>  <span class="dt">bool</span> hasCancelToken_ <span class="op">=</span> <span class="kw">false</span>;</span>
<span id="cb17-15"><a href="#cb17-15" aria-hidden="true" tabindex="-1"></a>  <span class="op">...</span></span>
<span id="cb17-16"><a href="#cb17-16" aria-hidden="true" tabindex="-1"></a><span class="op">}</span>;</span>
<span id="cb17-17"><a href="#cb17-17" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb17-18"><a href="#cb17-18" aria-hidden="true" tabindex="-1"></a><span class="kw">template</span> <span class="op">&lt;</span><span class="kw">typename</span> T<span class="op">&gt;</span></span>
<span id="cb17-19"><a href="#cb17-19" aria-hidden="true" tabindex="-1"></a><span class="kw">class</span> Task <span class="op">{</span></span>
<span id="cb17-20"><a href="#cb17-20" aria-hidden="true" tabindex="-1"></a><span class="kw">public</span><span class="op">:</span></span>
<span id="cb17-21"><a href="#cb17-21" aria-hidden="true" tabindex="-1"></a>  <span class="kw">using</span> promise_type <span class="op">=</span> TaskPromise<span class="op">&lt;</span>T<span class="op">&gt;</span>;</span>
<span id="cb17-22"><a href="#cb17-22" aria-hidden="true" tabindex="-1"></a>  <span class="op">...</span></span>
<span id="cb17-23"><a href="#cb17-23" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb17-24"><a href="#cb17-24" aria-hidden="true" tabindex="-1"></a><span class="kw">private</span><span class="op">:</span></span>
<span id="cb17-25"><a href="#cb17-25" aria-hidden="true" tabindex="-1"></a>  <span class="kw">friend</span> Task co_withCancellation<span class="op">(</span><span class="kw">const</span> CancellationToken<span class="op">&amp;</span> cancelToken,</span>
<span id="cb17-26"><a href="#cb17-26" aria-hidden="true" tabindex="-1"></a>                                  Task<span class="op">&amp;&amp;</span> task<span class="op">)</span> <span class="kw">noexcept</span> <span class="op">{</span></span>
<span id="cb17-27"><a href="#cb17-27" aria-hidden="true" tabindex="-1"></a>    <span class="kw">auto</span><span class="op">&amp;</span> promise <span class="op">=</span> task<span class="op">.</span>coro_<span class="op">.</span>promise<span class="op">()</span>;</span>
<span id="cb17-28"><a href="#cb17-28" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb17-29"><a href="#cb17-29" aria-hidden="true" tabindex="-1"></a>    <span class="co">// Don&#39;t override previous CancellationToken if set.</span></span>
<span id="cb17-30"><a href="#cb17-30" aria-hidden="true" tabindex="-1"></a>    <span class="cf">if</span> <span class="op">(!</span>promise<span class="op">.</span>hasCancelToken_<span class="op">)</span> <span class="op">{</span></span>
<span id="cb17-31"><a href="#cb17-31" aria-hidden="true" tabindex="-1"></a>      task<span class="op">.</span>coro_<span class="op">.</span>promise<span class="op">().</span>cancelToken_ <span class="op">=</span> cancelToken;</span>
<span id="cb17-32"><a href="#cb17-32" aria-hidden="true" tabindex="-1"></a>      hasCancelToken_ <span class="op">=</span> <span class="kw">true</span>;</span>
<span id="cb17-33"><a href="#cb17-33" aria-hidden="true" tabindex="-1"></a>    <span class="op">}</span></span>
<span id="cb17-34"><a href="#cb17-34" aria-hidden="true" tabindex="-1"></a>    <span class="cf">return</span> std<span class="op">::</span>move<span class="op">(</span>task<span class="op">)</span>;</span>
<span id="cb17-35"><a href="#cb17-35" aria-hidden="true" tabindex="-1"></a>  <span class="op">}</span></span>
<span id="cb17-36"><a href="#cb17-36" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb17-37"><a href="#cb17-37" aria-hidden="true" tabindex="-1"></a>  std<span class="op">::</span>coroutine_handle<span class="op">&lt;</span>promise_type<span class="op">&gt;</span> coro_;</span>
<span id="cb17-38"><a href="#cb17-38" aria-hidden="true" tabindex="-1"></a><span class="op">}</span>;</span></code></pre></div>
<p>This mechanism for automatically passing the <code class="sourceCode default">CancellationToken</code> to child operations relies on the fact that <code class="sourceCode default">Task</code>-returning coroutines are lazily started only when the returned task is awaited. This allow us to safely inject the <code class="sourceCode default">CancellationToken</code> into the child coroutine as part of the <code class="sourceCode default">co_await</code> expression, just before the child coroutine is launched. It also allows passing these tasks to higher-order concurrency algorithms that can then create their own cancellation scopes and inject their own <code class="sourceCode default">CancellationToken</code> that they can use to request cancellation.</p>
<p>This would not be possible if the task was started eagerly when the coroutine function was first invoked.</p>
<p><strong>Benefits</strong></p>
<p>The ability to have cancellation-tokens implcitly passed through by the coroutine mechanics greatly simplifies a lot of application code that generally only needs to be transparent to cancellation.</p>
<p>Cancellation is generally either requested by high-level handlers (e.g. RPC request framework request might request cancellation if the connection is dropped) or by general-purpose concurrency algorithms that introduce new cancellation-scopes (e.g. a <code class="sourceCode default">timeout()</code> algorithm). A handful of leaf operations can then be built that respond to cancellation (e.g. RPC requests, timers, etc.)</p>
<p>This allows centralising the handling of cancellation to a relatively small fraction of the code-base with the bulk of the application code supporting cancellation without needing to write any additional code to do so.</p>
<p><strong>Limitations</strong></p>
<p>With this approach, where every Task always has a (possibly null) CancellationToken, we cannot statically determine whether cancellation will be requested and thus cannot statically eliminate all overhead related to cancellation support. However, we can still determine at runtime whether or not cancellation might be requested by calling <code class="sourceCode default">.canBeCancelled()</code> on the <code class="sourceCode default">CancellationToken</code>.</p>
<p>One example of the runtime overhead this adds is that we need an extra pointer of storage for every coroutine frame to store the CancellationToken as it is passed down.</p>
<p>The general-purpose nature of <code class="sourceCode default">CancellationToken</code> requires allocating some shared-storage on the heap with the lifetime managed through use of atomic reference-counting. This allows it to be used safely in a wide variety of scenarios.</p>
<p>However, for many of the coroutine scenarios we have a structured concurrency model where the <code class="sourceCode default">CancellationToken</code> passed to child coroutines is never used after those child coroutines complete. Also, there are cases where a coroutine creates a new <code class="sourceCode default">CancellationSource</code> as a local variable and never moves or copies it.</p>
<p>A more efficient implementation that takes advantage of this more restrictive, structured use of cancellation-tokens could potentially avoid the allocation and reference counting by using a different cancellation-token type that allocates the shared-state inline in the cancellation-source object and has cancellation-tokens simply hold a non-reference-counted pointer to this shared-state.</p>
<p>Finally, the mechanism used to propagate the cancellation context from the parent coroutine to child coroutines is currently hard-coded. It calls the cancellation-specific CPO <code class="sourceCode default">co_withCancellation()</code> and uses hard-coded <code class="sourceCode default">CancellationToken</code> type. However, there may be other kinds of context that an application wants to propagate automatically to child coroutines using similar mechanics to the cancellation-token propagation. It’s possible that this facility can be generalised to support passing other kinds of context through implicitly. e.g. an allocator, executor, logging context.</p>
<h2 data-number="3.5" id="stdstop_token"><span class="header-section-number">3.5</span> std::stop_token<a href="#stdstop_token" class="self-link"></a></h2>
<p>C++20 added three new types to the standard library: <code class="sourceCode default">std::stop_token</code>, <code class="sourceCode default">std::stop_source</code> and <code class="sourceCode default">std::stop_callback&lt;CB&gt;</code>.</p>
<p>These types were added to C++20 to support cancellation as part of the <code class="sourceCode default">std::jthread</code> abstraction and have some integration with the <code class="sourceCode default">std::condition_variable_any</code> wait-functions.</p>
<p>Example: A simple usage of jthread/stop_token/condition_variable_any</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="pp">#include </span><span class="im">&lt;mutex&gt;</span></span>
<span id="cb18-2"><a href="#cb18-2" aria-hidden="true" tabindex="-1"></a><span class="pp">#include </span><span class="im">&lt;thread&gt;</span></span>
<span id="cb18-3"><a href="#cb18-3" aria-hidden="true" tabindex="-1"></a><span class="pp">#include </span><span class="im">&lt;stop_token&gt;</span></span>
<span id="cb18-4"><a href="#cb18-4" aria-hidden="true" tabindex="-1"></a><span class="pp">#include </span><span class="im">&lt;condition_variable&gt;</span></span>
<span id="cb18-5"><a href="#cb18-5" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb18-6"><a href="#cb18-6" aria-hidden="true" tabindex="-1"></a><span class="dt">int</span> main<span class="op">()</span> <span class="op">{</span></span>
<span id="cb18-7"><a href="#cb18-7" aria-hidden="true" tabindex="-1"></a>  std<span class="op">::</span>jthread worker<span class="op">{[](</span>std<span class="op">::</span>stop_token st<span class="op">)</span> <span class="op">{</span></span>
<span id="cb18-8"><a href="#cb18-8" aria-hidden="true" tabindex="-1"></a>    std<span class="op">::</span>condition_variable_any cv;</span>
<span id="cb18-9"><a href="#cb18-9" aria-hidden="true" tabindex="-1"></a>    std<span class="op">::</span>mutex m;</span>
<span id="cb18-10"><a href="#cb18-10" aria-hidden="true" tabindex="-1"></a>    std<span class="op">::</span>unique_lock lk<span class="op">{</span>m<span class="op">}</span>;</span>
<span id="cb18-11"><a href="#cb18-11" 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> <span class="dv">0</span>; <span class="op">!</span>st<span class="op">.</span>stop_requested<span class="op">()</span> ; <span class="op">++</span>i<span class="op">)</span> <span class="op">{</span></span>
<span id="cb18-12"><a href="#cb18-12" aria-hidden="true" tabindex="-1"></a>      std<span class="op">::</span>cout <span class="op">&lt;&lt;</span> <span class="st">&quot;tick &quot;</span> <span class="op">&lt;&lt;</span> i <span class="op">&lt;&lt;</span> std<span class="op">::</span>endl;</span>
<span id="cb18-13"><a href="#cb18-13" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb18-14"><a href="#cb18-14" aria-hidden="true" tabindex="-1"></a>      <span class="co">// An interruptible sleep_for()</span></span>
<span id="cb18-15"><a href="#cb18-15" aria-hidden="true" tabindex="-1"></a>      cv<span class="op">.</span>wait_for<span class="op">(</span>lk, st, <span class="dv">100</span><span class="bu">ms</span>, std<span class="op">::</span>false_type<span class="op">{})</span>;</span>
<span id="cb18-16"><a href="#cb18-16" aria-hidden="true" tabindex="-1"></a>    <span class="op">}</span></span>
<span id="cb18-17"><a href="#cb18-17" aria-hidden="true" tabindex="-1"></a>  <span class="op">}}</span>;</span>
<span id="cb18-18"><a href="#cb18-18" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb18-19"><a href="#cb18-19" aria-hidden="true" tabindex="-1"></a>  <span class="co">// Do something on the main() thread</span></span>
<span id="cb18-20"><a href="#cb18-20" aria-hidden="true" tabindex="-1"></a>  std<span class="op">::</span>this_thread<span class="op">::</span>sleep_for<span class="op">(</span><span class="dv">1</span><span class="bu">s</span><span class="op">)</span>;</span>
<span id="cb18-21"><a href="#cb18-21" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb18-22"><a href="#cb18-22" aria-hidden="true" tabindex="-1"></a>  <span class="co">// When &#39;worker&#39; goes out of scope its destructor will call</span></span>
<span id="cb18-23"><a href="#cb18-23" aria-hidden="true" tabindex="-1"></a>  <span class="co">// worker.request_stop() before joining the thread.</span></span>
<span id="cb18-24"><a href="#cb18-24" aria-hidden="true" tabindex="-1"></a>  <span class="co">// This will communicate the request to stop via the std::stop_token</span></span>
<span id="cb18-25"><a href="#cb18-25" aria-hidden="true" tabindex="-1"></a>  <span class="co">// passed to the thread&#39;s entry-point function and this will interrupt</span></span>
<span id="cb18-26"><a href="#cb18-26" aria-hidden="true" tabindex="-1"></a>  <span class="co">// the cv.wait_for() call and cause the thread to exit promptly.</span></span>
<span id="cb18-27"><a href="#cb18-27" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
<p>However, the <code class="sourceCode default">stop_token</code> abstraction is more general than <code class="sourceCode default">std::jthread</code> and can also be used for cancellation of asynchronous code in much the same way that <code class="sourceCode default">CancellationToken</code> is used in <code class="sourceCode default">folly::coro</code> and in the .NET Framework.</p>
<p>A general purpose cancellation mechanism for sender/receiver would ideally integrate with the <code class="sourceCode default">std::stop_token</code> abstraction. Although it is worth noting that the design of <code class="sourceCode default">std::stop_token</code>, like <code class="sourceCode default">folly::CancellationToken</code>, requires a heap-allocation and reference-counting, which is not strictly necessary for structured concurrency use-cases.</p>
<h1 data-number="4" id="proposed-changes"><span class="header-section-number">4</span> Proposed Changes<a href="#proposed-changes" class="self-link"></a></h1>
<p>This section will describe the facilities being proposed by this paper. The following section, ‘Design Discussion’ will discuss some of the design considerations relating to this proposal.</p>
<h2 data-number="4.1" id="add-stoppable_token-and-related-concepts"><span class="header-section-number">4.1</span> Add <code class="sourceCode default">stoppable_token</code> and related concepts<a href="#add-stoppable_token-and-related-concepts" class="self-link"></a></h2>
<p>Add the following concept definition to the <code class="sourceCode default">&lt;concepts&gt;</code> header:</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">namespace</span> std</span>
<span id="cb19-2"><a href="#cb19-2" aria-hidden="true" tabindex="-1"></a><span class="op">{</span></span>
<span id="cb19-3"><a href="#cb19-3" aria-hidden="true" tabindex="-1"></a>  <span class="kw">template</span><span class="op">&lt;</span><span class="kw">template</span><span class="op">&lt;</span><span class="kw">typename</span><span class="op">&gt;</span> <span class="kw">class</span><span class="op">&gt;</span></span>
<span id="cb19-4"><a href="#cb19-4" aria-hidden="true" tabindex="-1"></a>  <span class="kw">struct</span> __check_type_alias_exists;  <span class="co">// exposition-only</span></span>
<span id="cb19-5"><a href="#cb19-5" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb19-6"><a href="#cb19-6" aria-hidden="true" tabindex="-1"></a>  <span class="kw">template</span><span class="op">&lt;</span><span class="kw">typename</span> T<span class="op">&gt;</span></span>
<span id="cb19-7"><a href="#cb19-7" aria-hidden="true" tabindex="-1"></a>  <span class="kw">concept</span> stoppable_token <span class="op">=</span></span>
<span id="cb19-8"><a href="#cb19-8" aria-hidden="true" tabindex="-1"></a>    copy_constructible<span class="op">&lt;</span>T<span class="op">&gt;</span> <span class="op">&amp;&amp;</span></span>
<span id="cb19-9"><a href="#cb19-9" aria-hidden="true" tabindex="-1"></a>    move_constructible<span class="op">&lt;</span>T<span class="op">&gt;</span> <span class="op">&amp;&amp;</span></span>
<span id="cb19-10"><a href="#cb19-10" aria-hidden="true" tabindex="-1"></a>    is_nothrow_copy_constructible_v<span class="op">&lt;</span>T<span class="op">&gt;</span> <span class="op">&amp;&amp;</span></span>
<span id="cb19-11"><a href="#cb19-11" aria-hidden="true" tabindex="-1"></a>    is_nothrow_move_constructible_v<span class="op">&lt;</span>T<span class="op">&gt;</span> <span class="op">&amp;&amp;</span></span>
<span id="cb19-12"><a href="#cb19-12" aria-hidden="true" tabindex="-1"></a>    equality_comparable<span class="op">&lt;</span>T<span class="op">&gt;</span> <span class="op">&amp;&amp;</span></span>
<span id="cb19-13"><a href="#cb19-13" aria-hidden="true" tabindex="-1"></a>    <span class="kw">requires</span> <span class="op">(</span><span class="kw">const</span> T<span class="op">&amp;</span> token<span class="op">)</span> <span class="op">{</span></span>
<span id="cb19-14"><a href="#cb19-14" aria-hidden="true" tabindex="-1"></a>      <span class="op">{</span> token<span class="op">.</span>stop_requested<span class="op">()</span> <span class="op">}</span> <span class="kw">noexcept</span> <span class="op">-&gt;</span> boolean<span class="op">-</span>testable;</span>
<span id="cb19-15"><a href="#cb19-15" aria-hidden="true" tabindex="-1"></a>      <span class="op">{</span> token<span class="op">.</span>stop_possible<span class="op">()</span> <span class="op">}</span> <span class="kw">noexcept</span> <span class="op">-&gt;</span> boolean<span class="op">-</span>testable;</span>
<span id="cb19-16"><a href="#cb19-16" aria-hidden="true" tabindex="-1"></a>      <span class="kw">typename</span> __check_type_alias_exists<span class="op">&lt;</span>T<span class="op">::</span><span class="kw">template</span> callback_type<span class="op">&gt;</span>;</span>
<span id="cb19-17"><a href="#cb19-17" aria-hidden="true" tabindex="-1"></a>    <span class="op">}</span>;</span>
<span id="cb19-18"><a href="#cb19-18" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb19-19"><a href="#cb19-19" aria-hidden="true" tabindex="-1"></a>  <span class="kw">template</span><span class="op">&lt;</span><span class="kw">typename</span> T, <span class="kw">typename</span> CB, <span class="kw">typename</span> Initializer <span class="op">=</span> CB<span class="op">&gt;</span></span>
<span id="cb19-20"><a href="#cb19-20" aria-hidden="true" tabindex="-1"></a>  <span class="kw">concept</span> stoppable_token_for <span class="op">=</span></span>
<span id="cb19-21"><a href="#cb19-21" aria-hidden="true" tabindex="-1"></a>    stoppable_token<span class="op">&lt;</span>T<span class="op">&gt;</span> <span class="op">&amp;&amp;</span></span>
<span id="cb19-22"><a href="#cb19-22" aria-hidden="true" tabindex="-1"></a>    invocable<span class="op">&lt;</span>CB<span class="op">&gt;</span> <span class="op">&amp;&amp;</span></span>
<span id="cb19-23"><a href="#cb19-23" aria-hidden="true" tabindex="-1"></a>    <span class="kw">requires</span> <span class="op">{</span></span>
<span id="cb19-24"><a href="#cb19-24" aria-hidden="true" tabindex="-1"></a>      <span class="kw">typename</span> T<span class="op">::</span><span class="kw">template</span> callback_type<span class="op">&lt;</span>CB<span class="op">&gt;</span>;</span>
<span id="cb19-25"><a href="#cb19-25" aria-hidden="true" tabindex="-1"></a>    <span class="op">}</span> <span class="op">&amp;&amp;</span></span>
<span id="cb19-26"><a href="#cb19-26" aria-hidden="true" tabindex="-1"></a>    constructible_from<span class="op">&lt;</span>CB, Initializer<span class="op">&gt;</span> <span class="op">&amp;&amp;</span></span>
<span id="cb19-27"><a href="#cb19-27" aria-hidden="true" tabindex="-1"></a>    constructible_from<span class="op">&lt;</span><span class="kw">typename</span> T<span class="op">::</span><span class="kw">template</span> callback_type<span class="op">&lt;</span>CB<span class="op">&gt;</span>, T, Initializer<span class="op">&gt;</span> <span class="op">&amp;&amp;</span></span>
<span id="cb19-28"><a href="#cb19-28" aria-hidden="true" tabindex="-1"></a>    constructible_from<span class="op">&lt;</span><span class="kw">typename</span> T<span class="op">::</span><span class="kw">template</span> callback_type<span class="op">&lt;</span>CB<span class="op">&gt;</span>, T<span class="op">&amp;</span>, Initializer<span class="op">&gt;</span> <span class="op">&amp;&amp;</span></span>
<span id="cb19-29"><a href="#cb19-29" aria-hidden="true" tabindex="-1"></a>    constructible_from<span class="op">&lt;</span><span class="kw">typename</span> T<span class="op">::</span><span class="kw">template</span> callback_type<span class="op">&lt;</span>CB<span class="op">&gt;</span>, <span class="kw">const</span> T, Initializer<span class="op">&gt;</span> <span class="op">&amp;&amp;</span></span>
<span id="cb19-30"><a href="#cb19-30" aria-hidden="true" tabindex="-1"></a>    constructible_from<span class="op">&lt;</span><span class="kw">typename</span> T<span class="op">::</span><span class="kw">template</span> callback_type<span class="op">&lt;</span>CB<span class="op">&gt;</span>, <span class="kw">const</span> T<span class="op">&amp;</span>, Initializer<span class="op">&gt;</span>;</span>
<span id="cb19-31"><a href="#cb19-31" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb19-32"><a href="#cb19-32" aria-hidden="true" tabindex="-1"></a>  <span class="kw">template</span><span class="op">&lt;</span><span class="kw">typename</span> T<span class="op">&gt;</span></span>
<span id="cb19-33"><a href="#cb19-33" aria-hidden="true" tabindex="-1"></a>  <span class="kw">concept</span> unstoppable_token <span class="op">=</span></span>
<span id="cb19-34"><a href="#cb19-34" aria-hidden="true" tabindex="-1"></a>    stoppable_token<span class="op">&lt;</span>T<span class="op">&gt;</span> <span class="op">&amp;&amp;</span></span>
<span id="cb19-35"><a href="#cb19-35" aria-hidden="true" tabindex="-1"></a>    <span class="kw">requires</span> <span class="op">{</span></span>
<span id="cb19-36"><a href="#cb19-36" aria-hidden="true" tabindex="-1"></a>      <span class="op">{</span> T<span class="op">::</span>stop_possible<span class="op">()</span> <span class="op">}</span> <span class="op">-&gt;</span> boolean<span class="op">-</span>testable;</span>
<span id="cb19-37"><a href="#cb19-37" aria-hidden="true" tabindex="-1"></a>    <span class="op">}</span> <span class="op">&amp;&amp;</span></span>
<span id="cb19-38"><a href="#cb19-38" aria-hidden="true" tabindex="-1"></a>    <span class="op">(!</span>T<span class="op">::</span>stop_possible<span class="op">())</span>;</span>
<span id="cb19-39"><a href="#cb19-39" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
<p>The <code class="sourceCode default">stoppable_token</code> concept checks for the basic interface of a “stop token” which is copyable and allows polling to see if stop has been requested and also whether a stop request is possible.</p>
<p>It also provides an associated nested template-type-alias, <code class="sourceCode default">T::callback_type&lt;CB&gt;</code>, that identifies the stop-callback type to use to register a callback to be executed if a stop-request is ever made on a <code class="sourceCode default">stoppable_token</code> of type, <code class="sourceCode default">T</code>.</p>
<p>The <code class="sourceCode default">stoppable_token</code> concept has a number of semantic requirements on types:</p>
<ul>
<li>All copies of a <code class="sourceCode default">stoppable_token</code> reference the same logical shared stop state and shall report values consistent with each other.</li>
<li>Given a token, <code class="sourceCode default">t</code>, if <code class="sourceCode default">t.stop_possible()</code> evaluates to <code class="sourceCode default">false</code> then for any token, <code class="sourceCode default">u</code>, that references the same logical shared stop state, <code class="sourceCode default">u.stop_possible()</code> shall also subsequently evaluate to <code class="sourceCode default">false</code> and <code class="sourceCode default">u.stop_requested()</code> shall also subsequently evaluate to <code class="sourceCode default">false</code>.</li>
<li>Given a token, <code class="sourceCode default">t</code>, if <code class="sourceCode default">t.stop_requested()</code> evaluates to <code class="sourceCode default">true</code> then for any token, <code class="sourceCode default">u</code>, that references the same logical shared stop state, <code class="sourceCode default">u.stop_requested()</code> shall also subsequently evaluate to <code class="sourceCode default">true</code> and <code class="sourceCode default">u.stop_possible()</code> shall also subsequently evaluate to <code class="sourceCode default">true</code>.</li>
<li>Given a token, <code class="sourceCode default">t</code>, of type <code class="sourceCode default">T</code>, a callback-type, <code class="sourceCode default">CB</code>, and a callback-initializer argument, <code class="sourceCode default">init</code>, of type <code class="sourceCode default">Initializer</code> then constructing an instance, <code class="sourceCode default">cb</code>, of type <code class="sourceCode default">T::callback_type&lt;CB&gt;</code>, passing <code class="sourceCode default">t</code> as the first argument and <code class="sourceCode default">init</code> as the second argument to the constructor, shall, if <code class="sourceCode default">t.stop_possible()</code> is <code class="sourceCode default">true</code>, construct an instance, <code class="sourceCode default">callback</code>, of type <code class="sourceCode default">CB</code>, direct-initialized with <code class="sourceCode default">init</code>, and register <code class="sourceCode default">callback</code> with <code class="sourceCode default">t</code>’s shared stop state such that <code class="sourceCode default">callback</code> will be invoked with an empty argument list if a stop request is made on the shared stop state.
<ul>
<li>If <code class="sourceCode default">t.stop_requested()</code> is <code class="sourceCode default">true</code> at the time <code class="sourceCode default">callback</code> is registered then <code class="sourceCode default">callback</code> <em>may</em> be invoked immediately inline inside the call to <code class="sourceCode default">cb</code>’s constructor.</li>
<li>If <code class="sourceCode default">callback</code> is invoked then for any token, <code class="sourceCode default">u</code>, that references the same shared stop state as <code class="sourceCode default">t</code>, an evaluation of <code class="sourceCode default">u.stop_requested()</code> will be <code class="sourceCode default">true</code> if the beginning of the invocation of <code class="sourceCode default">callback</code> strongly-happens-before the evaluation of <code class="sourceCode default">u.stop_requested()</code>.</li>
<li>If <code class="sourceCode default">t.stop_possible()</code> evaluates to <code class="sourceCode default">false</code> then the construction of <code class="sourceCode default">cb</code> is not required to construct and initialize <code class="sourceCode default">callback</code>.</li>
</ul></li>
<li>Construction of a <code class="sourceCode default">T::callback_type&lt;CB&gt;</code> instance shall only throw exceptions thrown by the initialization of the <code class="sourceCode default">CB</code> instance from the value of type <code class="sourceCode default">Initializer</code>.</li>
<li>Destruction of the <code class="sourceCode default">T::callback_type&lt;CB&gt;</code> object, <code class="sourceCode default">cb</code>, deregisters <code class="sourceCode default">callback</code> from the shared stop state such that <code class="sourceCode default">callback</code> will not be invoked after the destructor returns.
<ul>
<li>If <code class="sourceCode default">callback</code> is currently being invoked on another thread then the destructor of <code class="sourceCode default">cb</code> will block until the invocation of <code class="sourceCode default">callback</code> returns such that the return from the invocation of <code class="sourceCode default">callback</code> strongly-happens-before the destruction of <code class="sourceCode default">callback</code>.</li>
<li>Destruction of a callback <code class="sourceCode default">cb</code> shall not block on the completion of the invocation of some other callback registered with the same shared stop state.</li>
</ul></li>
</ul>
<h2 data-number="4.2" id="tweaks-to-stdstop_token"><span class="header-section-number">4.2</span> Tweaks to <code class="sourceCode default">std::stop_token</code><a href="#tweaks-to-stdstop_token" class="self-link"></a></h2>
<p>Modify <code class="sourceCode default">std::stop_token</code> to add the nested <code class="sourceCode default">callback_type</code> template type-alias.</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">namespace</span> std</span>
<span id="cb20-2"><a href="#cb20-2" aria-hidden="true" tabindex="-1"></a><span class="op">{</span></span>
<span id="cb20-3"><a href="#cb20-3" aria-hidden="true" tabindex="-1"></a>  <span class="kw">class</span> stop_token <span class="op">{</span></span>
<span id="cb20-4"><a href="#cb20-4" aria-hidden="true" tabindex="-1"></a>  <span class="kw">public</span><span class="op">:</span></span>
<span id="cb20-5"><a href="#cb20-5" aria-hidden="true" tabindex="-1"></a>    <span class="kw">template</span><span class="op">&lt;</span><span class="kw">typename</span> T<span class="op">&gt;</span></span>
<span id="cb20-6"><a href="#cb20-6" aria-hidden="true" tabindex="-1"></a>    <span class="kw">using</span> callback_type <span class="op">=</span> stop_callback<span class="op">&lt;</span>T<span class="op">&gt;</span>;</span>
<span id="cb20-7"><a href="#cb20-7" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb20-8"><a href="#cb20-8" aria-hidden="true" tabindex="-1"></a>    <span class="co">// ... remainder of stop_token definition as before</span></span>
<span id="cb20-9"><a href="#cb20-9" aria-hidden="true" tabindex="-1"></a>  <span class="op">}</span>;</span>
<span id="cb20-10"><a href="#cb20-10" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
<p>This type alias is required for the <code class="sourceCode default">std::stop_token</code> type to satisfy the <code class="sourceCode default">stoppable_token</code> concept.</p>
<h2 data-number="4.3" id="add-stdnever_stop_token"><span class="header-section-number">4.3</span> Add <code class="sourceCode default">std::never_stop_token</code><a href="#add-stdnever_stop_token" class="self-link"></a></h2>
<p>The <code class="sourceCode default">never_stop_token</code> type implements the <code class="sourceCode default">unstoppable_token</code> concept. i.e. <code class="sourceCode default">stop_possible()</code> and <code class="sourceCode default">stop_requested()</code> are static constexpr member functions that always return <code class="sourceCode default">false</code>.</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="co">// &lt;stop_token&gt; header</span></span>
<span id="cb21-2"><a href="#cb21-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb21-3"><a href="#cb21-3" aria-hidden="true" tabindex="-1"></a><span class="kw">namespace</span> std</span>
<span id="cb21-4"><a href="#cb21-4" aria-hidden="true" tabindex="-1"></a><span class="op">{</span></span>
<span id="cb21-5"><a href="#cb21-5" aria-hidden="true" tabindex="-1"></a>  <span class="kw">class</span> never_stop_token <span class="op">{</span></span>
<span id="cb21-6"><a href="#cb21-6" aria-hidden="true" tabindex="-1"></a>    <span class="co">// exposition only</span></span>
<span id="cb21-7"><a href="#cb21-7" aria-hidden="true" tabindex="-1"></a>    <span class="kw">class</span> callback <span class="op">{</span></span>
<span id="cb21-8"><a href="#cb21-8" aria-hidden="true" tabindex="-1"></a>    <span class="kw">public</span><span class="op">:</span></span>
<span id="cb21-9"><a href="#cb21-9" aria-hidden="true" tabindex="-1"></a>      <span class="kw">template</span><span class="op">&lt;</span><span class="kw">typename</span> C<span class="op">&gt;</span></span>
<span id="cb21-10"><a href="#cb21-10" aria-hidden="true" tabindex="-1"></a>      <span class="kw">explicit</span> callback_type<span class="op">(</span>never_stop_token, C<span class="op">&amp;&amp;)</span> <span class="kw">noexcept</span> <span class="op">{}</span></span>
<span id="cb21-11"><a href="#cb21-11" aria-hidden="true" tabindex="-1"></a>    <span class="op">}</span>;</span>
<span id="cb21-12"><a href="#cb21-12" aria-hidden="true" tabindex="-1"></a>  <span class="kw">public</span><span class="op">:</span></span>
<span id="cb21-13"><a href="#cb21-13" aria-hidden="true" tabindex="-1"></a>    <span class="kw">template</span><span class="op">&lt;</span>invocable CB<span class="op">&gt;</span></span>
<span id="cb21-14"><a href="#cb21-14" aria-hidden="true" tabindex="-1"></a>    <span class="kw">using</span> callback_type <span class="op">=</span> callback;</span>
<span id="cb21-15"><a href="#cb21-15" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb21-16"><a href="#cb21-16" aria-hidden="true" tabindex="-1"></a>    <span class="kw">static</span> <span class="kw">constexpr</span> <span class="dt">bool</span> stop_requested<span class="op">()</span> <span class="kw">const</span> <span class="kw">noexcept</span> <span class="op">{</span> <span class="cf">return</span> <span class="kw">false</span>; <span class="op">}</span></span>
<span id="cb21-17"><a href="#cb21-17" aria-hidden="true" tabindex="-1"></a>    <span class="kw">static</span> <span class="kw">constexpr</span> <span class="dt">bool</span> stop_possible<span class="op">()</span> <span class="kw">const</span> <span class="kw">noexcept</span> <span class="op">{</span> <span class="cf">return</span> <span class="kw">false</span>; <span class="op">}</span></span>
<span id="cb21-18"><a href="#cb21-18" aria-hidden="true" tabindex="-1"></a>  <span class="op">}</span>;</span>
<span id="cb21-19"><a href="#cb21-19" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
<p>This can be returned from <code class="sourceCode default">get_stop_token()</code> customisation-point to indicate statically that you will never submit a stop-request to the operation - this is the default behaviour of the <code class="sourceCode default">get_stop_token()</code> customisation-point.</p>
<p>Child operations that attempt to use this type as a stop-token to detect and respond to cancellation requests will generally optimise out those code-paths and avoid using any storage for the stop-callback.</p>
<p>Note that <code class="sourceCode default">unstoppable_token&lt;never_stop_token&gt;</code> will evaluate to <code class="sourceCode default">true</code>. This trait should be used to detect never-cancellable use-cases instead of testing for <code class="sourceCode default">same_as&lt;never_stop_token&gt;</code> to allow other libraries to also define stop-token types that may be unstoppable.</p>
<h2 data-number="4.4" id="add-stdin_place_stop_token-stdin_place_stop_source-stdin_place_stop_callbackcb"><span class="header-section-number">4.4</span> Add <code class="sourceCode default">std::in_place_stop_token</code>, <code class="sourceCode default">std::in_place_stop_source</code>, <code class="sourceCode default">std::in_place_stop_callback&lt;CB&gt;</code><a href="#add-stdin_place_stop_token-stdin_place_stop_source-stdin_place_stop_callbackcb" class="self-link"></a></h2>
<p>The <code class="sourceCode default">in_place_stop_token</code> type implements the <code class="sourceCode default">stoppable_token</code> concept, similarly to <code class="sourceCode default">stop_token</code>, but places more restrictions on its usage and in doing so permits a more efficient implementation that does not require heap allocations or reference-counting to manage the lifetime of the shared stop-state.</p>
<p>Instead, the shared stop-state is stored inline inside the <code class="sourceCode default">in_place_stop_source</code> object and its lifetime is tied to the lifetime of that object. The <code class="sourceCode default">in_place_stop_token</code> objects can then just hold a raw pointer to the shared stop-state and copying the stop-token objects no longer requires atomic ref-counting. However, this then means that applications must ensure all usage of <code class="sourceCode default">in_place_stop_token</code> and <code class="sourceCode default">in_place_stop_callback</code> objects occurs prior to the invocation of the destructor of the associated <code class="sourceCode default">in_place_stop_source</code>.</p>
<p>These restrictions match the typical use-cases for sender-based structured concurrency algorithms that need to introduce new cancellation scopes. And by carefully placing semantic constraints on usages of the <code class="sourceCode default">get_stop_token()</code> customisation-point (described below) we can enforce that all usages of stop-tokens used for cancellation in sender-based operations are constrained to work within the limitations of the <code class="sourceCode default">in_place_stop_token</code> type.</p>
<p>This proposal adds the following to the <code class="sourceCode default">&lt;stop_token&gt;</code> header:</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">namespace</span> std</span>
<span id="cb22-2"><a href="#cb22-2" aria-hidden="true" tabindex="-1"></a><span class="op">{</span></span>
<span id="cb22-3"><a href="#cb22-3" aria-hidden="true" tabindex="-1"></a>  <span class="kw">class</span> in_place_stop_token;</span>
<span id="cb22-4"><a href="#cb22-4" aria-hidden="true" tabindex="-1"></a>  <span class="kw">template</span><span class="op">&lt;</span><span class="kw">typename</span> CB<span class="op">&gt;</span></span>
<span id="cb22-5"><a href="#cb22-5" aria-hidden="true" tabindex="-1"></a>  <span class="kw">class</span> in_place_stop_callback;</span>
<span id="cb22-6"><a href="#cb22-6" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb22-7"><a href="#cb22-7" aria-hidden="true" tabindex="-1"></a>  <span class="kw">class</span> in_place_stop_source</span>
<span id="cb22-8"><a href="#cb22-8" aria-hidden="true" tabindex="-1"></a>  <span class="op">{</span></span>
<span id="cb22-9"><a href="#cb22-9" aria-hidden="true" tabindex="-1"></a>  <span class="kw">public</span><span class="op">:</span></span>
<span id="cb22-10"><a href="#cb22-10" aria-hidden="true" tabindex="-1"></a>    in_place_stop_source<span class="op">()</span> <span class="kw">noexcept</span>;</span>
<span id="cb22-11"><a href="#cb22-11" aria-hidden="true" tabindex="-1"></a>    <span class="op">~</span>in_place_stop_source<span class="op">()</span>;</span>
<span id="cb22-12"><a href="#cb22-12" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb22-13"><a href="#cb22-13" aria-hidden="true" tabindex="-1"></a>    <span class="co">// Not copyable/movable</span></span>
<span id="cb22-14"><a href="#cb22-14" aria-hidden="true" tabindex="-1"></a>    in_place_stop_source<span class="op">(</span><span class="kw">const</span> in_place_stop_source<span class="op">&amp;)</span> <span class="op">=</span> <span class="kw">delete</span>;</span>
<span id="cb22-15"><a href="#cb22-15" aria-hidden="true" tabindex="-1"></a>    in_place_stop_source<span class="op">(</span>in_place_stop_source<span class="op">&amp;&amp;)</span> <span class="op">=</span> <span class="kw">delete</span>;</span>
<span id="cb22-16"><a href="#cb22-16" aria-hidden="true" tabindex="-1"></a>    in_place_stop_source<span class="op">&amp;</span> <span class="kw">operator</span><span class="op">=(</span><span class="kw">const</span> in_place_stop_source<span class="op">&amp;)</span> <span class="op">=</span> <span class="kw">delete</span>;</span>
<span id="cb22-17"><a href="#cb22-17" aria-hidden="true" tabindex="-1"></a>    in_place_stop_source<span class="op">&amp;</span> <span class="kw">operator</span><span class="op">=(</span>in_place_stop_source<span class="op">&amp;&amp;)</span> <span class="op">=</span> <span class="kw">delete</span>;</span>
<span id="cb22-18"><a href="#cb22-18" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb22-19"><a href="#cb22-19" aria-hidden="true" tabindex="-1"></a>    <span class="dt">bool</span> request_stop<span class="op">()</span> <span class="kw">noexcept</span>;</span>
<span id="cb22-20"><a href="#cb22-20" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb22-21"><a href="#cb22-21" aria-hidden="true" tabindex="-1"></a>    <span class="op">[[</span><span class="at">nodiscard</span><span class="op">]]</span> <span class="dt">bool</span> stop_requested<span class="op">()</span> <span class="kw">const</span> <span class="kw">noexcept</span> <span class="op">{</span></span>
<span id="cb22-22"><a href="#cb22-22" aria-hidden="true" tabindex="-1"></a>      <span class="cf">return</span> stop_requested_flag<span class="op">.</span>load<span class="op">(</span>std<span class="op">::</span>memory_order_acquire<span class="op">)</span>; <span class="co">// exposition-only</span></span>
<span id="cb22-23"><a href="#cb22-23" aria-hidden="true" tabindex="-1"></a>    <span class="op">}</span></span>
<span id="cb22-24"><a href="#cb22-24" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb22-25"><a href="#cb22-25" aria-hidden="true" tabindex="-1"></a>    <span class="op">[[</span><span class="at">nodiscard</span><span class="op">]]</span> in_place_stop_token get_token<span class="op">()</span> <span class="kw">const</span> <span class="kw">noexcept</span>;</span>
<span id="cb22-26"><a href="#cb22-26" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb22-27"><a href="#cb22-27" aria-hidden="true" tabindex="-1"></a>  <span class="kw">private</span><span class="op">:</span></span>
<span id="cb22-28"><a href="#cb22-28" aria-hidden="true" tabindex="-1"></a>    atomic<span class="op">&lt;</span><span class="dt">bool</span><span class="op">&gt;</span> stop_requested_flag<span class="op">{</span><span class="kw">false</span><span class="op">}</span>; <span class="co">// exposition-only</span></span>
<span id="cb22-29"><a href="#cb22-29" aria-hidden="true" tabindex="-1"></a>  <span class="op">}</span>;</span>
<span id="cb22-30"><a href="#cb22-30" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb22-31"><a href="#cb22-31" aria-hidden="true" tabindex="-1"></a>  <span class="kw">class</span> in_place_stop_token</span>
<span id="cb22-32"><a href="#cb22-32" aria-hidden="true" tabindex="-1"></a>  <span class="op">{</span></span>
<span id="cb22-33"><a href="#cb22-33" aria-hidden="true" tabindex="-1"></a>  <span class="kw">public</span><span class="op">:</span></span>
<span id="cb22-34"><a href="#cb22-34" aria-hidden="true" tabindex="-1"></a>    <span class="kw">template</span><span class="op">&lt;</span><span class="kw">typename</span> CB<span class="op">&gt;</span></span>
<span id="cb22-35"><a href="#cb22-35" aria-hidden="true" tabindex="-1"></a>    <span class="kw">using</span> callback_type <span class="op">=</span> in_place_stop_callback<span class="op">&lt;</span>CB<span class="op">&gt;</span>;</span>
<span id="cb22-36"><a href="#cb22-36" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb22-37"><a href="#cb22-37" aria-hidden="true" tabindex="-1"></a>    in_place_stop_token<span class="op">()</span> <span class="kw">noexcept</span></span>
<span id="cb22-38"><a href="#cb22-38" aria-hidden="true" tabindex="-1"></a>    <span class="op">:</span> src<span class="op">(</span><span class="kw">nullptr</span><span class="op">)</span> <span class="co">// exposition-only</span></span>
<span id="cb22-39"><a href="#cb22-39" aria-hidden="true" tabindex="-1"></a>    <span class="op">{}</span></span>
<span id="cb22-40"><a href="#cb22-40" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb22-41"><a href="#cb22-41" aria-hidden="true" tabindex="-1"></a>    in_place_stop_token<span class="op">(</span><span class="kw">const</span> in_place_stop_token<span class="op">&amp;</span> other<span class="op">)</span> <span class="kw">noexcept</span></span>
<span id="cb22-42"><a href="#cb22-42" aria-hidden="true" tabindex="-1"></a>    <span class="op">:</span> src<span class="op">(</span>other<span class="op">.</span>src<span class="op">)</span> <span class="co">// exposition-only</span></span>
<span id="cb22-43"><a href="#cb22-43" aria-hidden="true" tabindex="-1"></a>    <span class="op">{}</span></span>
<span id="cb22-44"><a href="#cb22-44" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb22-45"><a href="#cb22-45" aria-hidden="true" tabindex="-1"></a>    <span class="op">[[</span><span class="at">nodiscard</span><span class="op">]]</span> <span class="dt">bool</span> stop_possible<span class="op">()</span> <span class="kw">const</span> <span class="kw">noexcept</span> <span class="op">{</span></span>
<span id="cb22-46"><a href="#cb22-46" aria-hidden="true" tabindex="-1"></a>      <span class="cf">return</span> src <span class="op">!=</span> <span class="kw">nullptr</span>; <span class="co">// exposition-only</span></span>
<span id="cb22-47"><a href="#cb22-47" aria-hidden="true" tabindex="-1"></a>    <span class="op">}</span></span>
<span id="cb22-48"><a href="#cb22-48" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb22-49"><a href="#cb22-49" aria-hidden="true" tabindex="-1"></a>    <span class="op">[[</span><span class="at">nodiscard</span><span class="op">]]</span> <span class="dt">bool</span> stop_requested<span class="op">()</span> <span class="kw">const</span> <span class="kw">noexcept</span> <span class="op">{</span></span>
<span id="cb22-50"><a href="#cb22-50" aria-hidden="true" tabindex="-1"></a>      <span class="cf">return</span> src <span class="op">!=</span> <span class="kw">nullptr</span> <span class="op">&amp;&amp;</span> src<span class="op">-&gt;</span>stop_requested<span class="op">()</span>; <span class="co">// exposition-only</span></span>
<span id="cb22-51"><a href="#cb22-51" aria-hidden="true" tabindex="-1"></a>    <span class="op">}</span></span>
<span id="cb22-52"><a href="#cb22-52" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb22-53"><a href="#cb22-53" aria-hidden="true" tabindex="-1"></a>    <span class="kw">friend</span> <span class="op">[[</span><span class="at">nodiscard</span><span class="op">]]</span> <span class="dt">bool</span> <span class="kw">operator</span><span class="op">==(</span>in_place_stop_token a,</span>
<span id="cb22-54"><a href="#cb22-54" aria-hidden="true" tabindex="-1"></a>                                         in_place_stop_token b<span class="op">)</span> <span class="kw">noexcept</span> <span class="op">{</span></span>
<span id="cb22-55"><a href="#cb22-55" aria-hidden="true" tabindex="-1"></a>      <span class="cf">return</span> a<span class="op">.</span>src <span class="op">==</span> b<span class="op">.</span>src; <span class="co">// exposition-only</span></span>
<span id="cb22-56"><a href="#cb22-56" aria-hidden="true" tabindex="-1"></a>    <span class="op">}</span></span>
<span id="cb22-57"><a href="#cb22-57" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb22-58"><a href="#cb22-58" aria-hidden="true" tabindex="-1"></a>    <span class="dt">void</span> swap<span class="op">(</span>in_place_stop_token<span class="op">&amp;</span> other<span class="op">)</span> <span class="kw">noexcept</span> <span class="op">{</span></span>
<span id="cb22-59"><a href="#cb22-59" aria-hidden="true" tabindex="-1"></a>      std<span class="op">::</span>swap<span class="op">(</span>src, other<span class="op">.</span>src<span class="op">)</span>; <span class="co">// exposition-only</span></span>
<span id="cb22-60"><a href="#cb22-60" aria-hidden="true" tabindex="-1"></a>    <span class="op">}</span></span>
<span id="cb22-61"><a href="#cb22-61" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb22-62"><a href="#cb22-62" aria-hidden="true" tabindex="-1"></a>    <span class="kw">friend</span> <span class="dt">void</span> swap<span class="op">(</span>in_place_stop_token<span class="op">&amp;</span> a, in_place_stop_token<span class="op">&amp;</span> b<span class="op">)</span> <span class="kw">noexcept</span> <span class="op">{</span></span>
<span id="cb22-63"><a href="#cb22-63" aria-hidden="true" tabindex="-1"></a>      a<span class="op">.</span>swap<span class="op">(</span>b<span class="op">)</span>;</span>
<span id="cb22-64"><a href="#cb22-64" aria-hidden="true" tabindex="-1"></a>    <span class="op">}</span></span>
<span id="cb22-65"><a href="#cb22-65" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb22-66"><a href="#cb22-66" aria-hidden="true" tabindex="-1"></a>  <span class="kw">private</span><span class="op">:</span></span>
<span id="cb22-67"><a href="#cb22-67" aria-hidden="true" tabindex="-1"></a>    in_place_stop_source<span class="op">*</span> src; <span class="co">// exposition-only</span></span>
<span id="cb22-68"><a href="#cb22-68" aria-hidden="true" tabindex="-1"></a>  <span class="op">}</span>;</span>
<span id="cb22-69"><a href="#cb22-69" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb22-70"><a href="#cb22-70" aria-hidden="true" tabindex="-1"></a>  <span class="kw">template</span><span class="op">&lt;</span><span class="kw">typename</span> CB<span class="op">&gt;</span></span>
<span id="cb22-71"><a href="#cb22-71" aria-hidden="true" tabindex="-1"></a>  <span class="kw">class</span> in_place_stop_callback <span class="op">{</span></span>
<span id="cb22-72"><a href="#cb22-72" aria-hidden="true" tabindex="-1"></a>  <span class="kw">public</span><span class="op">:</span></span>
<span id="cb22-73"><a href="#cb22-73" aria-hidden="true" tabindex="-1"></a>    <span class="kw">template</span><span class="op">&lt;</span><span class="kw">typename</span> Initializer<span class="op">&gt;</span></span>
<span id="cb22-74"><a href="#cb22-74" aria-hidden="true" tabindex="-1"></a>      <span class="kw">requires</span> constructible_from<span class="op">&lt;</span>CB, Initializer<span class="op">&gt;</span></span>
<span id="cb22-75"><a href="#cb22-75" aria-hidden="true" tabindex="-1"></a>    <span class="kw">explicit</span> in_place_stop_callback<span class="op">(</span>in_place_stop_token st, Initializer<span class="op">&amp;&amp;</span> init<span class="op">)</span></span>
<span id="cb22-76"><a href="#cb22-76" aria-hidden="true" tabindex="-1"></a>      <span class="kw">noexcept</span><span class="op">(</span>is_nothrow_constructible_v<span class="op">&lt;</span>CB, Initializer<span class="op">&gt;)</span>;</span>
<span id="cb22-77"><a href="#cb22-77" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb22-78"><a href="#cb22-78" aria-hidden="true" tabindex="-1"></a>    <span class="op">~</span>in_place_stop_callback<span class="op">()</span>;</span>
<span id="cb22-79"><a href="#cb22-79" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb22-80"><a href="#cb22-80" aria-hidden="true" tabindex="-1"></a>    <span class="co">// Not movable/copyable</span></span>
<span id="cb22-81"><a href="#cb22-81" aria-hidden="true" tabindex="-1"></a>    in_place_stop_callback<span class="op">(</span><span class="kw">const</span> in_place_stop_callback<span class="op">&amp;)</span> <span class="op">=</span> <span class="kw">delete</span>;</span>
<span id="cb22-82"><a href="#cb22-82" aria-hidden="true" tabindex="-1"></a>    in_place_stop_callback<span class="op">(</span>in_place_stop_callback<span class="op">&amp;&amp;)</span> <span class="op">=</span> <span class="kw">delete</span>;</span>
<span id="cb22-83"><a href="#cb22-83" aria-hidden="true" tabindex="-1"></a>    in_place_stop_callback<span class="op">&amp;</span> <span class="kw">operator</span><span class="op">=(</span>in_place_stop_callback<span class="op">&amp;&amp;)</span> <span class="op">=</span> <span class="kw">delete</span>;</span>
<span id="cb22-84"><a href="#cb22-84" aria-hidden="true" tabindex="-1"></a>    in_place_stop_callback<span class="op">&amp;</span> <span class="kw">operator</span><span class="op">=(</span>in_place_stop_callback<span class="op">&amp;&amp;)</span> <span class="op">=</span> <span class="kw">delete</span>;</span>
<span id="cb22-85"><a href="#cb22-85" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb22-86"><a href="#cb22-86" aria-hidden="true" tabindex="-1"></a>  <span class="kw">private</span><span class="op">:</span></span>
<span id="cb22-87"><a href="#cb22-87" aria-hidden="true" tabindex="-1"></a>    in_place_stop_source<span class="op">*</span> src; <span class="co">// exposition-only</span></span>
<span id="cb22-88"><a href="#cb22-88" aria-hidden="true" tabindex="-1"></a>    CB callback;</span>
<span id="cb22-89"><a href="#cb22-89" aria-hidden="true" tabindex="-1"></a>  <span class="op">}</span>;</span>
<span id="cb22-90"><a href="#cb22-90" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
<p>The semantics with regards to callback registration, deregistration and invocation of callbacks with respect to calls to <code class="sourceCode default">in_place_stop_source::request_stop()</code> is identical to that of <code class="sourceCode default">stop_source</code> and <code class="sourceCode default">stop_callback</code>.</p>
<p>However there are some additional semantic constraints placed on the usage of the proposed in-place versions of the stop token types compared to the existing types:</p>
<ul>
<li>Any <code class="sourceCode default">in_place_stop_token</code> obtained from a call to <code class="sourceCode default">in_place_stop_source::get_token()</code> is associated with that <code class="sourceCode default">in_place_stop_source</code>. Any <code class="sourceCode default">in_place_stop_token</code> copied from an <code class="sourceCode default">in_place_stop_token</code> associated with an <code class="sourceCode default">in_place_stop_source</code> is also associated with that <code class="sourceCode default">in_place_stop_source</code>.</li>
<li>Any <code class="sourceCode default">in_place_stop_token</code> associated with an <code class="sourceCode default">in_place_stop_source</code> is invalidated by the beginning of the invocation of the destructor of the <code class="sourceCode default">in_place_stop_source</code>. The only valid operations on an invalidated <code class="sourceCode default">in_place_stop_token</code> are to call the destructor or to assign a new <code class="sourceCode default">in_place_stop_token</code> value to the token. [[Note: this means that any calls to <code class="sourceCode default">stop_requested()</code> or <code class="sourceCode default">stop_possible()</code> must strongly happen-before the beginning of the invocation of the destructor of the associated <code class="sourceCode default">in_place_stop_source</code> object.]].</li>
<li>Any <code class="sourceCode default">in_place_stop_callback</code> constructed using an <code class="sourceCode default">in_place_stop_token</code> associated with an <code class="sourceCode default">in_place_stop_source</code> is also associated with that <code class="sourceCode default">in_place_stop_source</code>.</li>
<li>It is undefined behaviour if the program does not ensure that the return from the call to the destructor of all <code class="sourceCode default">in_place_stop_callback</code> objects associated with an <code class="sourceCode default">in_place_stop_source</code> object strongly happens before the beginning of the invocation of the destructor of the associated <code class="sourceCode default">in_place_stop_source</code> object.</li>
</ul>
<h2 data-number="4.5" id="add-get_stop_token-customisation-point"><span class="header-section-number">4.5</span> Add <code class="sourceCode default">get_stop_token()</code> customisation-point<a href="#add-get_stop_token-customisation-point" class="self-link"></a></h2>
<p>Add the following definition to the <code class="sourceCode default">&lt;execution&gt;</code> header:</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><span class="co">// &lt;execution&gt;</span></span>
<span id="cb23-2"><a href="#cb23-2" aria-hidden="true" tabindex="-1"></a><span class="kw">namespace</span> std<span class="op">::</span>execution</span>
<span id="cb23-3"><a href="#cb23-3" aria-hidden="true" tabindex="-1"></a><span class="op">{</span></span>
<span id="cb23-4"><a href="#cb23-4" aria-hidden="true" tabindex="-1"></a>  <span class="kw">inline</span> <span class="kw">namespace</span> unspecified <span class="op">{</span></span>
<span id="cb23-5"><a href="#cb23-5" aria-hidden="true" tabindex="-1"></a>    <span class="kw">inline</span> <span class="kw">constexpr</span> unspecified get_stop_token <span class="op">=</span> unspecified;</span>
<span id="cb23-6"><a href="#cb23-6" aria-hidden="true" tabindex="-1"></a>  <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>Where <code class="sourceCode default">execution::get_stop_token</code> defines a customisation-point object that is invocable with a single argument that is an lvalue referencing an object whose type satisfies the <code class="sourceCode default">execution::receiver</code> concept such that <code class="sourceCode default">execution::get_stop_token(r)</code> is equivalent to:</p>
<ul>
<li><code class="sourceCode default">std::tag_invoke(std::tag_t&lt;get_stop_token&gt;, std::as_const(r))</code> if this expression is valid;</li>
<li>otherwise is equivalent to <code class="sourceCode default">std::never_stop_token{}</code></li>
</ul>
<p>The program is ill-formed if the decay-copied result of the expression <code class="sourceCode default">get_stop_token(r)</code> has a type that does not model the <code class="sourceCode default">std::stoppable_token</code> concept.</p>
<p>The program is ill-formed if customisations of the <code class="sourceCode default">get_stop_token()</code> customisation-point are not declared <code class="sourceCode default">noexcept</code>. i.e. <code class="sourceCode default">noexcept(execution::get_stop_token(declval&lt;decltype((r))&gt;()))</code> must be <code class="sourceCode default">true</code>.</p>
<p>Note: See <a href="https://wg21.link/P1895R0">P1895R0</a> “tag_invoke: A general pattern for supporting customisable functions” for details of the proposed <code class="sourceCode default">std::tag_invoke()</code></p>
<p><strong>Semantic Constraints</strong></p>
<p>There are some additional semantic constraints applied to customisations of the <code class="sourceCode default">get_stop_token()</code> customisation-point and also to usage of this customisation point.</p>
<p>The <code class="sourceCode default">get_stop_token()</code> customisation-point is intended for use by implementations of <code class="sourceCode default">execution::connect()</code> for the sender to query the stop-token to use for receiving notification of stop-requests by calling <code class="sourceCode default">get_stop_token()</code> on the receiver passed as the second argument to <code class="sourceCode default">connect()</code>.</p>
<blockquote>
<p>Note that the receiver represents the calling context. Other contextual information may also be passed to the operation implicitly via queries on the receiver. See the “Design Discussion” section on generalising context propagation.</p>
</blockquote>
<p>The stop-token returned by <code class="sourceCode default">get_stop_token(receiver)</code> may only be assumed to be valid until the operation-state object returned by the call to <code class="sourceCode default">execution::connect()</code> is destroyed.</p>
<p>Conversely, customisations of <code class="sourceCode default">get_stop_token()</code> for a given receiver must ensure that the stop-token returned is valid until at least after the operation-state constructed when it was connected to a sender is destroyed.</p>
<p>Note that the operation-state object is allowed to be destroyed either before <code class="sourceCode default">execution::start()</code> is called or, if <code class="sourceCode default">execution::start()</code> is called, then after the beginning of a successful invocation of the completion-signalling operation on the receiver (i.e. <code class="sourceCode default">set_value</code>, <code class="sourceCode default">set_error</code> or <code class="sourceCode default">set_done</code>).</p>
<p>A receiver’s completion-signal handler will often go on to execute logic that ends up destroying the parent operation-state. And for many algorithms that introduce new cancellation-scopes, they will often be implemented by storing an <code class="sourceCode default">in_place_stop_source</code> in the parent operation-state and will customise the receiver passed to child operations so that its <code class="sourceCode default">get_stop_token()</code> will return an <code class="sourceCode default">in_place_stop_token</code> associated with this <code class="sourceCode default">in_place_stop_source</code>.</p>
<p>So when writing generic code that supports responding to stop-requests we have to assume that when we call <code class="sourceCode default">set_value()</code>, <code class="sourceCode default">set_error()</code> or <code class="sourceCode default">set_done()</code> on the receiver that this may end up destroying the shared stop-state and thus invalidate any stop-tokens obtained from the receiver’s <code class="sourceCode default">get_stop_token()</code> implementation.</p>
<p>This means that an operation that obtains stop-tokens from a receiver, <code class="sourceCode default">r</code>, by calling <code class="sourceCode default">get_stop_token(r)</code>, will need to ensure that:</p>
<ul>
<li>any use of stop-tokens obtained by calling <code class="sourceCode default">get_stop_token(r)</code> happens-before the return from the operation-state destructor; and</li>
<li>the calls to destructors of any stop-callbacks constructed using stop-tokens obtained by calling <code class="sourceCode default">get_stop_token(r)</code> happen before the return from the operation-state destructor.</li>
</ul>
<p>In many cases, the safest way to do this is to defer calling <code class="sourceCode default">get_stop_token()</code> and construction stop-callback objects until <code class="sourceCode default">execution::start()</code> is called and to ensure that stop-callback objects are destroyed before calling one of the completion-signalling operations (<code class="sourceCode default">set_value</code>, <code class="sourceCode default">set_error</code> or <code class="sourceCode default">set_done</code>).</p>
<h2 data-number="4.6" id="type-traits"><span class="header-section-number">4.6</span> Type-traits<a href="#type-traits" class="self-link"></a></h2>
<p>Add the following helper-trait to the <code class="sourceCode default">&lt;execution&gt;</code> header.</p>
<div class="sourceCode" id="cb24"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb24-1"><a href="#cb24-1" aria-hidden="true" tabindex="-1"></a><span class="kw">namespace</span> std<span class="op">::</span>execution</span>
<span id="cb24-2"><a href="#cb24-2" aria-hidden="true" tabindex="-1"></a><span class="op">{</span></span>
<span id="cb24-3"><a href="#cb24-3" aria-hidden="true" tabindex="-1"></a>  <span class="kw">template</span><span class="op">&lt;</span><span class="kw">typename</span> Source<span class="op">&gt;</span></span>
<span id="cb24-4"><a href="#cb24-4" aria-hidden="true" tabindex="-1"></a>  <span class="kw">using</span> stop_token_type_t <span class="op">=</span></span>
<span id="cb24-5"><a href="#cb24-5" aria-hidden="true" tabindex="-1"></a>    std<span class="op">::</span>remove_cvref_t<span class="op">&lt;</span><span class="kw">decltype</span><span class="op">(</span>execution<span class="op">::</span>get_stop_token<span class="op">(</span>std<span class="op">::</span>declval<span class="op">&lt;</span>Source<span class="op">&gt;()))&gt;</span>;</span>
<span id="cb24-6"><a href="#cb24-6" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb24-7"><a href="#cb24-7" aria-hidden="true" tabindex="-1"></a>  <span class="kw">template</span><span class="op">&lt;</span><span class="kw">typename</span> Source<span class="op">&gt;</span></span>
<span id="cb24-8"><a href="#cb24-8" aria-hidden="true" tabindex="-1"></a>  <span class="kw">struct</span> stop_token_type</span>
<span id="cb24-9"><a href="#cb24-9" aria-hidden="true" tabindex="-1"></a>  <span class="op">{</span></span>
<span id="cb24-10"><a href="#cb24-10" aria-hidden="true" tabindex="-1"></a>    <span class="kw">using</span> type <span class="op">=</span> stop_token_type_t<span class="op">&lt;</span>Source<span class="op">&gt;</span>;</span>
<span id="cb24-11"><a href="#cb24-11" aria-hidden="true" tabindex="-1"></a>  <span class="op">}</span>;</span>
<span id="cb24-12"><a href="#cb24-12" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
<p>This trait is typically used to simplify the implementation of <code class="sourceCode default">operation_state</code> types returned from <code class="sourceCode default">execution::connect()</code>.</p>
<p>For example: An operation-state object may choose to declare a member that stores a stop-callback that is used to subscribe to the receiver’s stop-token.</p>
<div class="sourceCode" id="cb25"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb25-1"><a href="#cb25-1" aria-hidden="true" tabindex="-1"></a><span class="kw">template</span><span class="op">&lt;</span><span class="kw">typename</span> Receiver<span class="op">&gt;</span></span>
<span id="cb25-2"><a href="#cb25-2" aria-hidden="true" tabindex="-1"></a><span class="kw">struct</span> schedule_operation_state <span class="op">{</span></span>
<span id="cb25-3"><a href="#cb25-3" aria-hidden="true" tabindex="-1"></a>  <span class="kw">struct</span> cancel_callback <span class="op">{</span></span>
<span id="cb25-4"><a href="#cb25-4" aria-hidden="true" tabindex="-1"></a>    schedule_operation_state<span class="op">&amp;</span> op;</span>
<span id="cb25-5"><a href="#cb25-5" aria-hidden="true" tabindex="-1"></a>    <span class="dt">void</span> <span class="kw">operator</span><span class="op">()()</span> <span class="kw">noexcept</span> <span class="op">{</span></span>
<span id="cb25-6"><a href="#cb25-6" aria-hidden="true" tabindex="-1"></a>      op<span class="op">.</span>context<span class="op">.</span>cancel<span class="op">(&amp;</span>op<span class="op">)</span>;</span>
<span id="cb25-7"><a href="#cb25-7" aria-hidden="true" tabindex="-1"></a>    <span class="op">}</span></span>
<span id="cb25-8"><a href="#cb25-8" aria-hidden="true" tabindex="-1"></a>  <span class="op">}</span>;</span>
<span id="cb25-9"><a href="#cb25-9" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb25-10"><a href="#cb25-10" aria-hidden="true" tabindex="-1"></a>  <span class="dt">void</span> start<span class="op">()</span> <span class="kw">noexcept</span> <span class="op">{</span></span>
<span id="cb25-11"><a href="#cb25-11" aria-hidden="true" tabindex="-1"></a>    stopCallback<span class="op">.</span>construct<span class="op">(</span>execution<span class="op">::</span>get_stop_token<span class="op">(</span>receiver<span class="op">)</span>, cancel_callback<span class="op">{*</span><span class="kw">this</span><span class="op">})</span>;</span>
<span id="cb25-12"><a href="#cb25-12" aria-hidden="true" tabindex="-1"></a>    context<span class="op">.</span>enqueue<span class="op">(</span><span class="kw">this</span><span class="op">)</span>;</span>
<span id="cb25-13"><a href="#cb25-13" aria-hidden="true" tabindex="-1"></a>  <span class="op">}</span></span>
<span id="cb25-14"><a href="#cb25-14" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb25-15"><a href="#cb25-15" aria-hidden="true" tabindex="-1"></a>  thread_context<span class="op">&amp;</span> context;</span>
<span id="cb25-16"><a href="#cb25-16" aria-hidden="true" tabindex="-1"></a>  Receiver receiver;</span>
<span id="cb25-17"><a href="#cb25-17" aria-hidden="true" tabindex="-1"></a>  manual_lifetime<span class="op">&lt;</span><span class="kw">typename</span> std<span class="op">::</span>execution<span class="op">::</span>stop_token_type_t<span class="op">&lt;</span>Receiver<span class="op">&amp;&gt;</span></span>
<span id="cb25-18"><a href="#cb25-18" aria-hidden="true" tabindex="-1"></a>     <span class="op">::</span><span class="kw">template</span> callback_type<span class="op">&lt;</span>cancel_callback<span class="op">&gt;&gt;</span> stopCallback;</span>
<span id="cb25-19"><a href="#cb25-19" aria-hidden="true" tabindex="-1"></a><span class="op">}</span>;</span></code></pre></div>
<p>It can also be used in conjunction with the <code class="sourceCode default">unstoppable_token</code> concept to check statically whether the receiver’s stop-token can ever be cancelled.</p>
<div class="sourceCode" id="cb26"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb26-1"><a href="#cb26-1" aria-hidden="true" tabindex="-1"></a><span class="dt">void</span> my_operation_state<span class="op">&lt;</span>Receiver<span class="op">&gt;::</span>start<span class="op">()</span> <span class="kw">noexcept</span> <span class="op">{</span></span>
<span id="cb26-2"><a href="#cb26-2" aria-hidden="true" tabindex="-1"></a>  <span class="co">// Avoid instantiating receiver&#39;s `set_done()` if stop can never be requested. </span></span>
<span id="cb26-3"><a href="#cb26-3" aria-hidden="true" tabindex="-1"></a>  <span class="cf">if</span> <span class="kw">constexpr</span> <span class="op">(!</span>std<span class="op">::</span>unstoppable_token<span class="op">&lt;</span>std<span class="op">::</span>execution<span class="op">::</span>stop_token_type_t<span class="op">&lt;</span>Receiver<span class="op">&gt;&gt;)</span> <span class="op">{</span></span>
<span id="cb26-4"><a href="#cb26-4" aria-hidden="true" tabindex="-1"></a>    <span class="cf">if</span> <span class="op">(</span>std<span class="op">::</span>execution<span class="op">::</span>get_stop_token<span class="op">(</span>receiver<span class="op">).</span>stop_requested<span class="op">())</span> <span class="op">{</span></span>
<span id="cb26-5"><a href="#cb26-5" aria-hidden="true" tabindex="-1"></a>      <span class="co">// Stop already requested.</span></span>
<span id="cb26-6"><a href="#cb26-6" aria-hidden="true" tabindex="-1"></a>      <span class="co">// Complete immediately with set_done().</span></span>
<span id="cb26-7"><a href="#cb26-7" aria-hidden="true" tabindex="-1"></a>      std<span class="op">::</span>execution<span class="op">::</span>set_done<span class="op">(</span>std<span class="op">::</span>move<span class="op">(</span>receiver<span class="op">))</span>;</span>
<span id="cb26-8"><a href="#cb26-8" aria-hidden="true" tabindex="-1"></a>      <span class="cf">return</span>;</span>
<span id="cb26-9"><a href="#cb26-9" aria-hidden="true" tabindex="-1"></a>    <span class="op">}</span></span>
<span id="cb26-10"><a href="#cb26-10" aria-hidden="true" tabindex="-1"></a>  <span class="op">}</span></span>
<span id="cb26-11"><a href="#cb26-11" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb26-12"><a href="#cb26-12" aria-hidden="true" tabindex="-1"></a>  <span class="co">// ... else start the operation</span></span>
<span id="cb26-13"><a href="#cb26-13" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
<h1 data-number="5" id="design-discussion"><span class="header-section-number">5</span> Design Discussion<a href="#design-discussion" class="self-link"></a></h1>
<h2 data-number="5.1" id="naming"><span class="header-section-number">5.1</span> Naming<a href="#naming" class="self-link"></a></h2>
<p>This paper proposes several new names, all centred around the idea of a “stop token” that represents the thing an operation can poll or subscribe to to tell whether or not there has been a request to stop the operation.</p>
<p>However, the obvious name, <code class="sourceCode default">std::stop_token</code>, has already been taken by the type added to the standard library in C++20 which means we cannot use this as the name for the generalised concept.</p>
<p>To distinguish the concept of a “stop token” from the concrete <code class="sourceCode default">std::stop_token</code> type this paper chose to use the term <code class="sourceCode default">std::stoppable_token</code> as something close but distinct from the existing name.</p>
<p>The <code class="sourceCode default">std::stoppable_token</code> name is not ideal, however, for a couple of reasons:</p>
<ul>
<li>Not all types that model this concept are able to produce a stop request. i.e. not all types that match <code class="sourceCode default">std::stoppble_token</code> are “stoppable,” such as the proposed <code class="sourceCode default">std::never_stop_token</code>.</li>
<li>Technically the token is not “stoppable” but rather, “able to produce a stop request.” If anything were “stoppable” it would be the operation itself, but that is not what this concept is describing.</li>
</ul>
<p>However, the author was unable to find a more suitable name than <code class="sourceCode default">stoppable_token</code> for this concept. The guidance for naming of concepts listed in <a href="https://wg21.link/P1851R0">P1851R0</a> “Guidelines For snake_case Concept Naming” does not seem to prescribe a standard way for resolving concept name conflicts when an existing concrete type has already taken the obvious abstraction name other than by “using creativity.”</p>
<p>The <code class="sourceCode default">std::unstoppable_token</code> name is also similarly not ideal as the name suggests that this concept would match a set of types mutually exclusive from the types matched by <code class="sourceCode default">std::stoppable_token</code>, but actually the <code class="sourceCode default">std::unstoppable_token</code> concept subsumes <code class="sourceCode default">std::stoppable_token</code>. i.e. all <code class="sourceCode default">std::unstoppable_token</code> types are also <code class="sourceCode default">std::stoppable_token</code> types.</p>
<p>Another possible name for <code class="sourceCode default">std::unstoppable_token</code> is <code class="sourceCode default">std::never_stoppable_token</code> which uses the “never” terminology consistent with <code class="sourceCode default">std::never_stop_token</code>.</p>
<p>Alternatively, this potential confusion between <code class="sourceCode default">std::stoppable_token</code> and <code class="sourceCode default">std::unstoppable_token</code> could be resolved by replacing <code class="sourceCode default">std::unstoppable_token</code> concept with a <code class="sourceCode default">std::is_stop_ever_possible_v&lt;StopToken&gt;</code> trait (see further discussion below).</p>
<p>The naming of the new concrete types that model <code class="sourceCode default">std::stoppable_token</code> are:</p>
<ul>
<li><code class="sourceCode default">std::in_place_stop_token</code></li>
<li><code class="sourceCode default">std::never_stop_token</code></li>
</ul>
<p>These follow the general pattern of <code class="sourceCode default">&lt;some_adjective&gt;_stop_token</code>.</p>
<p>Associated types (if any) also follow the pattern of <code class="sourceCode default">&lt;some_adjective&gt;_stop_source</code> and <code class="sourceCode default">&lt;some_adjective&gt;_stop_callback</code> to mirror the naming conventions of the existing <code class="sourceCode default">std::stop_source</code> and <code class="sourceCode default">std::stop_callback</code> types added in C++20.</p>
<h2 data-number="5.2" id="why-do-we-need-a-stdstoppable_token-concept"><span class="header-section-number">5.2</span> Why do we need a <code class="sourceCode default">std::stoppable_token</code> concept?<a href="#why-do-we-need-a-stdstoppable_token-concept" class="self-link"></a></h2>
<p>The <code class="sourceCode default">std::stop_token</code> type added in C++20 is a vocabulary type that can be passed to an operation and later used to communicate an asynchronous request to stop that operation.</p>
<p>The design of <code class="sourceCode default">std::stop_token</code> defined a shared-ownership model that allows the lifetimes of associated <code class="sourceCode default">std::stop_source</code> and <code class="sourceCode default">std::stop_token</code> objects to be independent of each other. This is necessary for some use-cases, including their use in the <code class="sourceCode default">std::jthread</code> type, which allows a <code class="sourceCode default">std::jthread</code> to detach from the thread resource and destroy its <code class="sourceCode default">std::stop_source</code> before the thread has finished using the <code class="sourceCode default">std::stop_token</code>.</p>
<p>This shared-ownership model implies some runtime overhead, however, typically requiring a heap-allocation and atomic-reference counting of the shared state.</p>
<p>To avoid forcing this overhead on all async operations we want to allow other implementations to use other stop-token types that make different performance tradeoffs.</p>
<p>For example, allowing more efficient implementations, such as <code class="sourceCode default">std::in_place_stop_token</code>, for cases where usage is more structured - as sender/receiver usage is.</p>
<p>Or allowing no-op implementations, such as <code class="sourceCode default">std::never_stop_token</code>, for cases where cancellation is not required.</p>
<p>Thus, in cases where we want to write algorithms that work generically over different stop-token types it would be beneficial to allow parameters accepting a stop-token to be constrained with a concept that checks that the argument fulfills the syntactic requirements of the concept.</p>
<p>Finally, having a concept for this gives us somewhere to describe the semantic constraints on implementations of <code class="sourceCode default">std::stoppable_token</code> types.</p>
<h2 data-number="5.3" id="should-unstoppable_token-concept-just-be-a-trait"><span class="header-section-number">5.3</span> Should <code class="sourceCode default">unstoppable_token</code> concept just be a trait?<a href="#should-unstoppable_token-concept-just-be-a-trait" class="self-link"></a></h2>
<p>This paper has proposed the addition of a concept named <code class="sourceCode default">std::unstoppable_token</code> that refines the proposed <code class="sourceCode default">std::stoppable_token</code> concept to match only those stop-token types that statically guarantee they will never issue a stop-request.</p>
<p>This concept can be used in <code class="sourceCode default">if constexpr</code> predicates to avoid instantiating code-paths that would only be necessary in responding to stop-requests.</p>
<p>For example:</p>
<div class="sourceCode" id="cb27"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb27-1"><a href="#cb27-1" aria-hidden="true" tabindex="-1"></a><span class="kw">template</span><span class="op">&lt;</span><span class="kw">typename</span> Receiver<span class="op">&gt;</span></span>
<span id="cb27-2"><a href="#cb27-2" aria-hidden="true" tabindex="-1"></a><span class="dt">void</span> my_operation_state<span class="op">&lt;</span>Receiver<span class="op">&gt;::</span>start<span class="op">()</span> <span class="op">&amp;</span> <span class="kw">noexcept</span> <span class="op">{</span></span>
<span id="cb27-3"><a href="#cb27-3" aria-hidden="true" tabindex="-1"></a>  <span class="co">// Avoid instantiating Receiver&#39;s set_done() if unstoppable</span></span>
<span id="cb27-4"><a href="#cb27-4" aria-hidden="true" tabindex="-1"></a>  <span class="cf">if</span> <span class="kw">constexpr</span> <span class="op">(!</span>std<span class="op">::</span>unstoppable_token<span class="op">&lt;</span>std<span class="op">::</span>execution<span class="op">::</span>stop_token_type_t<span class="op">&lt;</span>Receiver<span class="op">&gt;&gt;)</span></span>
<span id="cb27-5"><a href="#cb27-5" aria-hidden="true" tabindex="-1"></a>  <span class="op">{</span></span>
<span id="cb27-6"><a href="#cb27-6" aria-hidden="true" tabindex="-1"></a>    <span class="cf">if</span> <span class="op">(</span>std<span class="op">::</span>execution<span class="op">::</span>get_stop_token<span class="op">(</span><span class="kw">this</span><span class="op">-&gt;</span>receiver<span class="op">)).</span>stop_requested<span class="op">())</span></span>
<span id="cb27-7"><a href="#cb27-7" aria-hidden="true" tabindex="-1"></a>    <span class="op">{</span></span>
<span id="cb27-8"><a href="#cb27-8" aria-hidden="true" tabindex="-1"></a>      std<span class="op">::</span>execution<span class="op">::</span>set_done<span class="op">(</span>std<span class="op">::</span>move<span class="op">(</span><span class="kw">this</span><span class="op">-&gt;</span>receiver<span class="op">))</span>;</span>
<span id="cb27-9"><a href="#cb27-9" aria-hidden="true" tabindex="-1"></a>      <span class="cf">return</span>;</span>
<span id="cb27-10"><a href="#cb27-10" aria-hidden="true" tabindex="-1"></a>    <span class="op">}</span></span>
<span id="cb27-11"><a href="#cb27-11" aria-hidden="true" tabindex="-1"></a>  <span class="op">}</span></span>
<span id="cb27-12"><a href="#cb27-12" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb27-13"><a href="#cb27-13" aria-hidden="true" tabindex="-1"></a>  <span class="co">// ... rest of start() implementation</span></span>
<span id="cb27-14"><a href="#cb27-14" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
<p>It can also be used to constrain specialisations of an operation-state type to give a more efficient implementation if cancellation will never be requested.</p>
<p>For example:</p>
<div class="sourceCode" id="cb28"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb28-1"><a href="#cb28-1" aria-hidden="true" tabindex="-1"></a><span class="kw">template</span><span class="op">&lt;</span><span class="kw">typename</span> Receiver<span class="op">&gt;</span></span>
<span id="cb28-2"><a href="#cb28-2" aria-hidden="true" tabindex="-1"></a><span class="kw">class</span> my_operation_state <span class="op">{</span></span>
<span id="cb28-3"><a href="#cb28-3" aria-hidden="true" tabindex="-1"></a>  <span class="co">// Default implementation...</span></span>
<span id="cb28-4"><a href="#cb28-4" aria-hidden="true" tabindex="-1"></a><span class="op">}</span>;</span>
<span id="cb28-5"><a href="#cb28-5" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb28-6"><a href="#cb28-6" aria-hidden="true" tabindex="-1"></a><span class="kw">template</span><span class="op">&lt;</span><span class="kw">typename</span> Receiver<span class="op">&gt;</span></span>
<span id="cb28-7"><a href="#cb28-7" aria-hidden="true" tabindex="-1"></a>  <span class="kw">requires</span> std<span class="op">::</span>unstoppable_token<span class="op">&lt;</span>std<span class="op">::</span>execution<span class="op">::</span>stop_token_type_t<span class="op">&lt;</span>Receiver<span class="op">&gt;&gt;</span></span>
<span id="cb28-8"><a href="#cb28-8" aria-hidden="true" tabindex="-1"></a><span class="kw">class</span> my_operation_state <span class="op">{</span></span>
<span id="cb28-9"><a href="#cb28-9" aria-hidden="true" tabindex="-1"></a>  <span class="co">// Implementation optimised for no-cancellation...</span></span>
<span id="cb28-10"><a href="#cb28-10" aria-hidden="true" tabindex="-1"></a><span class="op">}</span>;</span></code></pre></div>
<p>However it’s unclear whether or not this needs to be a concept or whether it could just be a predicate type-trait.</p>
<p>For example, defining a <code class="sourceCode default">std::is_stop_ever_possible_v&lt;T&gt;</code> trait equivalent to:</p>
<div class="sourceCode" id="cb29"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb29-1"><a href="#cb29-1" aria-hidden="true" tabindex="-1"></a><span class="kw">namespace</span> std</span>
<span id="cb29-2"><a href="#cb29-2" aria-hidden="true" tabindex="-1"></a><span class="op">{</span></span>
<span id="cb29-3"><a href="#cb29-3" aria-hidden="true" tabindex="-1"></a>  <span class="kw">template</span><span class="op">&lt;</span>stoppable_token T<span class="op">&gt;</span></span>
<span id="cb29-4"><a href="#cb29-4" aria-hidden="true" tabindex="-1"></a>  <span class="kw">inline</span> <span class="kw">constexpr</span> <span class="dt">bool</span> is_stop_ever_possible_v <span class="op">=</span> <span class="op">!</span>unstoppable_token<span class="op">&lt;</span>T<span class="op">&gt;</span>;</span>
<span id="cb29-5"><a href="#cb29-5" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
<p>Naming this as the positive “is stop ever possible” would help avoid a double negative for the <code class="sourceCode default">if constexpr</code> use-cases where the code-path is only taken if a stop-request is possible, but would then require adding a negation to the <code class="sourceCode default">requires</code>-clause for class specialisations for the no-cancellation case.</p>
<h2 data-number="5.4" id="can-stoppable_token_for-concept-be-recast-as-semantic-requirements"><span class="header-section-number">5.4</span> Can <code class="sourceCode default">stoppable_token_for</code> concept be recast as semantic requirements?<a href="#can-stoppable_token_for-concept-be-recast-as-semantic-requirements" class="self-link"></a></h2>
<p>This paper proposes adding the multi-type concept, <code class="sourceCode default">stoppable_token_for</code>, which refines the <code class="sourceCode default">stoppable_token</code> concept by checking that we can construct a stop-callback for a specific given stop-token type, callback-type and callback-initializer type.</p>
<p>This concept can be used to constrain customisations of the <code class="sourceCode default">execution::connect()</code> method for particular senders to require that the stop-token obtained from the receiver can have a stop-callback attached that takes a particular callback-type.</p>
<p>For example: Constraining the <code class="sourceCode default">execution::connect()</code> customisation for a sender</p>
<div class="sourceCode" id="cb30"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb30-1"><a href="#cb30-1" aria-hidden="true" tabindex="-1"></a><span class="kw">template</span><span class="op">&lt;</span><span class="kw">typename</span> Operation<span class="op">&gt;</span></span>
<span id="cb30-2"><a href="#cb30-2" aria-hidden="true" tabindex="-1"></a><span class="kw">struct</span> cancel_callback <span class="op">{</span></span>
<span id="cb30-3"><a href="#cb30-3" aria-hidden="true" tabindex="-1"></a>  Operation<span class="op">*</span> op;</span>
<span id="cb30-4"><a href="#cb30-4" aria-hidden="true" tabindex="-1"></a>  <span class="dt">void</span> <span class="kw">operator</span><span class="op">()()</span> <span class="kw">noexcept</span> <span class="op">{</span> <span class="co">/* logic for handling a stop-request. */</span> <span class="op">}</span></span>
<span id="cb30-5"><a href="#cb30-5" aria-hidden="true" tabindex="-1"></a><span class="op">}</span>;</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><span class="kw">template</span><span class="op">&lt;</span><span class="kw">typename</span> Receiver<span class="op">&gt;</span></span>
<span id="cb30-8"><a href="#cb30-8" aria-hidden="true" tabindex="-1"></a><span class="kw">struct</span> operation_state <span class="op">{</span></span>
<span id="cb30-9"><a href="#cb30-9" aria-hidden="true" tabindex="-1"></a>  <span class="kw">explicit</span> operation_state<span class="op">(</span>Receiver<span class="op">&amp;&amp;</span> r<span class="op">)</span></span>
<span id="cb30-10"><a href="#cb30-10" aria-hidden="true" tabindex="-1"></a>  <span class="op">:</span> receiver<span class="op">(</span>std<span class="op">::</span>move<span class="op">(</span>r<span class="op">))</span></span>
<span id="cb30-11"><a href="#cb30-11" aria-hidden="true" tabindex="-1"></a>  , stopCallback_<span class="op">(</span>std<span class="op">::</span>execution<span class="op">::</span>get_stop_token<span class="op">(</span>receiver<span class="op">)</span>, <span class="kw">this</span><span class="op">)</span></span>
<span id="cb30-12"><a href="#cb30-12" aria-hidden="true" tabindex="-1"></a>  <span class="op">{}</span></span>
<span id="cb30-13"><a href="#cb30-13" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb30-14"><a href="#cb30-14" aria-hidden="true" tabindex="-1"></a>  <span class="dt">void</span> start<span class="op">()</span> <span class="op">{</span> <span class="co">/* logic for launching operation */</span> <span class="op">}</span></span>
<span id="cb30-15"><a href="#cb30-15" aria-hidden="true" tabindex="-1"></a>  </span>
<span id="cb30-16"><a href="#cb30-16" aria-hidden="true" tabindex="-1"></a>  Receiver receiver;</span>
<span id="cb30-17"><a href="#cb30-17" aria-hidden="true" tabindex="-1"></a>  <span class="kw">typename</span> std<span class="op">::</span>execution<span class="op">::</span>stop_token_type_t<span class="op">&lt;</span>Receiver<span class="op">&gt;::</span></span>
<span id="cb30-18"><a href="#cb30-18" aria-hidden="true" tabindex="-1"></a>    <span class="kw">template</span> callback_type<span class="op">&lt;</span>cancel_callback<span class="op">&lt;</span>operation_state<span class="op">&gt;&gt;</span> stopCallback_;</span>
<span id="cb30-19"><a href="#cb30-19" aria-hidden="true" tabindex="-1"></a><span class="op">}</span>;</span>
<span id="cb30-20"><a href="#cb30-20" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb30-21"><a href="#cb30-21" aria-hidden="true" tabindex="-1"></a><span class="kw">class</span> my_sender <span class="op">{</span></span>
<span id="cb30-22"><a href="#cb30-22" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb30-23"><a href="#cb30-23" aria-hidden="true" tabindex="-1"></a>  <span class="kw">template</span><span class="op">&lt;</span>std<span class="op">::</span>execution<span class="op">::</span>receiver R<span class="op">&gt;</span></span>
<span id="cb30-24"><a href="#cb30-24" aria-hidden="true" tabindex="-1"></a>    <span class="kw">requires</span> std<span class="op">::</span>stoppable_token_for<span class="op">&lt;</span></span>
<span id="cb30-25"><a href="#cb30-25" aria-hidden="true" tabindex="-1"></a>      std<span class="op">::</span>execution<span class="op">::</span>stop_token_type_t<span class="op">&lt;</span>R<span class="op">&gt;</span>,</span>
<span id="cb30-26"><a href="#cb30-26" aria-hidden="true" tabindex="-1"></a>      cancel_callback<span class="op">&lt;</span>operation_state<span class="op">&lt;</span>R<span class="op">&gt;&gt;</span>,</span>
<span id="cb30-27"><a href="#cb30-27" aria-hidden="true" tabindex="-1"></a>      operation_state<span class="op">&lt;</span>R<span class="op">&gt;*&gt;</span></span>
<span id="cb30-28"><a href="#cb30-28" aria-hidden="true" tabindex="-1"></a>  <span class="kw">friend</span> operation_state<span class="op">&lt;</span>R<span class="op">&gt;</span> tag_invoke<span class="op">(</span>std<span class="op">::</span>tag_t<span class="op">&lt;</span>std<span class="op">::</span>execution<span class="op">::</span><span class="fu">connect</span><span class="op">&gt;</span>,</span>
<span id="cb30-29"><a href="#cb30-29" aria-hidden="true" tabindex="-1"></a>                                       my_sender<span class="op">&amp;&amp;</span> self,</span>
<span id="cb30-30"><a href="#cb30-30" aria-hidden="true" tabindex="-1"></a>                                       R<span class="op">&amp;&amp;</span> receiver<span class="op">)</span> <span class="op">{</span></span>
<span id="cb30-31"><a href="#cb30-31" aria-hidden="true" tabindex="-1"></a>    <span class="cf">return</span> operation_state<span class="op">&lt;</span>R<span class="op">&gt;{(</span>R<span class="op">&amp;&amp;)</span>receiver<span class="op">}</span>;</span>
<span id="cb30-32"><a href="#cb30-32" aria-hidden="true" tabindex="-1"></a>  <span class="op">}</span></span>
<span id="cb30-33"><a href="#cb30-33" aria-hidden="true" tabindex="-1"></a><span class="op">}</span>;</span></code></pre></div>
<p>However, it’s unlikely that there would be value in constraining <code class="sourceCode default">connect()</code> implementations like this. All <code class="sourceCode default">std::stoppable_token</code> types should have a nested <code class="sourceCode default">callback_type&lt;CB&gt;</code> type alias that can be instantiated with any type <code class="sourceCode default">CB</code> for which <code class="sourceCode default">std::invocable&lt;CB&gt; &amp;&amp; std::destructible&lt;CB&gt;</code> is <code class="sourceCode default">true</code>.</p>
<p>Unfortunately, this kind of “universal quantification” is not something that we can currently express in a concept definition. So if we do want to express these constraints we are left with having to define a multi-type concept and then check this concept only once we know all of the concrete types.</p>
<p>One alternative direction to explore for specification could be to consider adding a semantic requirement that for a type, <code class="sourceCode default">T</code>, to satisfy the <code class="sourceCode default">std::stoppable_token</code> concept that the exposition-only concept <code class="sourceCode default">stoppable_token_for&lt;T, CB, Initializer&gt;</code> must be satisfied for all hypothetical pairs of types <code class="sourceCode default">CB</code> and <code class="sourceCode default">Initializer</code> where <code class="sourceCode default">CB</code> meets the requirements for <code class="sourceCode default">std::invocable</code> and <code class="sourceCode default">std::constructible_from&lt;Initializer&gt;</code>.</p>
<h2 data-number="5.5" id="why-do-we-need-the-callback_typecb-type-alias-on-the-stop-token-type"><span class="header-section-number">5.5</span> Why do we need the <code class="sourceCode default">::callback_type&lt;CB&gt;</code> type-alias on the stop-token type?<a href="#why-do-we-need-the-callback_typecb-type-alias-on-the-stop-token-type" class="self-link"></a></h2>
<p>As generic code needs to be able to support arbitrary <code class="sourceCode default">std::stoppable_token</code> types, and each of these types can have a different stop-callback type, we need some way for generic code to obtain the associated stop-callback type for a given stop-token type.</p>
<p>The way that generic code obtains this stop-callback type is through the nested template type-alias <code class="sourceCode default">T::callback_type&lt;CB&gt;</code>.</p>
<h2 data-number="5.6" id="why-doesnt-paper-this-propose-adding-a-stdnever_stop_callbackcb-type-name"><span class="header-section-number">5.6</span> Why doesn’t paper this propose adding a <code class="sourceCode default">std::never_stop_callback&lt;CB&gt;</code> type-name?<a href="#why-doesnt-paper-this-propose-adding-a-stdnever_stop_callbackcb-type-name" class="self-link"></a></h2>
<p>In cases where you know statically that you’re using a <code class="sourceCode default">std::stop_token</code> you can explicitly name the <code class="sourceCode default">std::stop_callback</code> type-name directly to construct a stop-callback that subscribes to notification of stop-requests.</p>
<p>Similarly, if you know statically that you’re using a <code class="sourceCode default">std::in_place_stop_token</code>, you can explicitly name the <code class="sourceCode default">std::in_place_stop_callback</code> type-name directly.</p>
<p>In cases where you need to operate generically on any <code class="sourceCode default">std::stoppable_token</code>, you will need to use the <code class="sourceCode default">ST::callback_type&lt;CB&gt;</code> type-alias to lookup the corresponding stop-callback type for a given <code class="sourceCode default">std::stoppable_token</code> type, <code class="sourceCode default">ST</code>.</p>
<p>In cases where you know statically that you have a <code class="sourceCode default">std::never_stop_token</code>, there is no point to constructing a hypothetical <code class="sourceCode default">std::never_stop_callback</code> since you know the callback will never be invoked.</p>
<h2 data-number="5.7" id="should-get_stop_token-be-applicable-to-more-than-receivers"><span class="header-section-number">5.7</span> Should <code class="sourceCode default">get_stop_token()</code> be applicable to more than receivers?<a href="#should-get_stop_token-be-applicable-to-more-than-receivers" class="self-link"></a></h2>
<p>As proposed in this paper, the <code class="sourceCode default">execution::get_stop_token()</code> customisation point is limited to being applied to objects that model the <code class="sourceCode default">receiver</code> concept.</p>
<p>The reason for this is so that we can apply the semantic constraints on the validity of the stop-token returned from <code class="sourceCode default">execution::get_stop_token(r)</code> with relation to lifetime of the operation-state returned by <code class="sourceCode default">execution::connect(s, r)</code>.</p>
<p>However, it’s possible that we may also find uses for the <code class="sourceCode default">execution::get_stop_token()</code> CPO as a mechanism for obtaining a stop-token from other kinds of objects. For example, there may be use-cases where we want to be able to apply the <code class="sourceCode default">execution::get_stop_token()</code> customisation-point to a coroutine promise-type to obtain the current coroutine’s stop-token.</p>
<p>Thus we should consider specifying <code class="sourceCode default">get_stop_token()</code> to allow it to be called on other kinds of objects but done in such a way that the receiver-related semantic requirements are enforced when applied to a receiver passed to <code class="sourceCode default">execution::connect()</code>.</p>
<h2 data-number="5.8" id="composability"><span class="header-section-number">5.8</span> Composability<a href="#composability" class="self-link"></a></h2>
<p>One of the key design goals of this proposal is to allow generic composition of cancellable async operations. This section discusses some of the considerations around supporting this.</p>
<h3 data-number="5.8.1" id="algorithms-should-be-aware-of-or-transparent-to-cancellation"><span class="header-section-number">5.8.1</span> Algorithms should be aware of or transparent to cancellation<a href="#algorithms-should-be-aware-of-or-transparent-to-cancellation" class="self-link"></a></h3>
<p>For cancellation to be effective in an application that composes async operations using senders, we need to be able to issue a stop-request to a high-level operation and have that request propagated through to the leaf-operations. However, for this to be possible, every intervening algorithm that composes the senders needs to be forwarding the stop-request on to its child operations.</p>
<p>For simpler algorithms that do not introduce new cancellation scopes (ie. that do not generate their own stop-requests) they simply need to be transparent to cancellation.</p>
<p>The easiest way for algorithms to do this is to pass a receiver into child operations that forwards calls to <code class="sourceCode default">get_stop_token()</code> to the parent operation’s recever.</p>
<p>For example: The <code class="sourceCode default">transform()</code> algorithm is transparent to cancellation.</p>
<div class="sourceCode" id="cb31"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb31-1"><a href="#cb31-1" aria-hidden="true" tabindex="-1"></a><span class="kw">template</span><span class="op">&lt;</span><span class="kw">typename</span> Src, <span class="kw">typename</span> Func<span class="op">&gt;</span></span>
<span id="cb31-2"><a href="#cb31-2" aria-hidden="true" tabindex="-1"></a><span class="kw">class</span> transform_sender <span class="op">{</span></span>
<span id="cb31-3"><a href="#cb31-3" aria-hidden="true" tabindex="-1"></a>  Src source;</span>
<span id="cb31-4"><a href="#cb31-4" aria-hidden="true" tabindex="-1"></a>  Func func;</span>
<span id="cb31-5"><a href="#cb31-5" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb31-6"><a href="#cb31-6" aria-hidden="true" tabindex="-1"></a>  <span class="kw">template</span><span class="op">&lt;</span><span class="kw">typename</span> Receiver<span class="op">&gt;</span></span>
<span id="cb31-7"><a href="#cb31-7" aria-hidden="true" tabindex="-1"></a>  <span class="kw">struct</span> operation_state <span class="op">{</span></span>
<span id="cb31-8"><a href="#cb31-8" aria-hidden="true" tabindex="-1"></a>    <span class="kw">struct</span> receiver <span class="op">{</span></span>
<span id="cb31-9"><a href="#cb31-9" aria-hidden="true" tabindex="-1"></a>      operation_state<span class="op">*</span> state;</span>
<span id="cb31-10"><a href="#cb31-10" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb31-11"><a href="#cb31-11" aria-hidden="true" tabindex="-1"></a>      <span class="kw">template</span><span class="op">&lt;</span><span class="kw">typename</span><span class="op">...</span> Values<span class="op">&gt;</span></span>
<span id="cb31-12"><a href="#cb31-12" aria-hidden="true" tabindex="-1"></a>      <span class="dt">void</span> set_value<span class="op">(</span>Values<span class="op">&amp;&amp;...</span> values<span class="op">)</span> <span class="op">&amp;&amp;</span> <span class="op">{</span></span>
<span id="cb31-13"><a href="#cb31-13" aria-hidden="true" tabindex="-1"></a>        std<span class="op">::</span>execution<span class="op">::</span>set_value<span class="op">(</span>std<span class="op">::</span>move<span class="op">(</span>state<span class="op">-&gt;</span>receiver<span class="op">)</span>,</span>
<span id="cb31-14"><a href="#cb31-14" aria-hidden="true" tabindex="-1"></a>                                  std<span class="op">::</span>invoke<span class="op">(</span>state<span class="op">-&gt;</span>func, <span class="op">(</span>Values<span class="op">&amp;&amp;)</span>values<span class="op">...))</span>;</span>
<span id="cb31-15"><a href="#cb31-15" aria-hidden="true" tabindex="-1"></a>      <span class="op">}</span></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>      <span class="kw">template</span><span class="op">&lt;</span><span class="kw">typename</span> Error<span class="op">&gt;</span></span>
<span id="cb31-18"><a href="#cb31-18" aria-hidden="true" tabindex="-1"></a>      <span class="dt">void</span> set_error<span class="op">(</span>Error<span class="op">&amp;&amp;</span> error<span class="op">)</span> <span class="op">&amp;&amp;</span> <span class="kw">noexcept</span> <span class="op">{</span></span>
<span id="cb31-19"><a href="#cb31-19" aria-hidden="true" tabindex="-1"></a>        std<span class="op">::</span>execution<span class="op">::</span>set_error<span class="op">(</span>std<span class="op">::</span>move<span class="op">(</span>state<span class="op">-&gt;</span>receiver<span class="op">)</span>, <span class="op">(</span>Error<span class="op">&amp;&amp;)</span>error<span class="op">)</span>;</span>
<span id="cb31-20"><a href="#cb31-20" aria-hidden="true" tabindex="-1"></a>      <span class="op">}</span></span>
<span id="cb31-21"><a href="#cb31-21" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb31-22"><a href="#cb31-22" aria-hidden="true" tabindex="-1"></a>      <span class="dt">void</span> set_done<span class="op">()</span> <span class="op">&amp;&amp;</span> <span class="kw">noexcept</span> <span class="op">{</span></span>
<span id="cb31-23"><a href="#cb31-23" aria-hidden="true" tabindex="-1"></a>        std<span class="op">::</span>execution<span class="op">::</span>set_done<span class="op">(</span>std<span class="op">::</span>move<span class="op">(</span>state<span class="op">-&gt;</span>receiver<span class="op">))</span>;</span>
<span id="cb31-24"><a href="#cb31-24" aria-hidden="true" tabindex="-1"></a>      <span class="op">}</span></span>
<span id="cb31-25"><a href="#cb31-25" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb31-26"><a href="#cb31-26" aria-hidden="true" tabindex="-1"></a>      <span class="co">// Forward get_stop_token() to the parent receiver</span></span>
<span id="cb31-27"><a href="#cb31-27" aria-hidden="true" tabindex="-1"></a>      <span class="kw">friend</span> <span class="kw">auto</span> tag_invoke<span class="op">(</span>tag_t<span class="op">&lt;</span>execution<span class="op">::</span>get_stop_token<span class="op">&gt;</span>, <span class="kw">const</span> receiver<span class="op">&amp;</span> self<span class="op">)</span> <span class="kw">noexcept</span></span>
<span id="cb31-28"><a href="#cb31-28" aria-hidden="true" tabindex="-1"></a>        <span class="op">-&gt;</span> std<span class="op">::</span>invoke_result_t<span class="op">&lt;</span>execution<span class="op">::</span>get_stop_token, <span class="kw">const</span> Receiver<span class="op">&amp;&gt;</span> <span class="op">{</span></span>
<span id="cb31-29"><a href="#cb31-29" aria-hidden="true" tabindex="-1"></a>        <span class="cf">return</span> std<span class="op">::</span>execution<span class="op">::</span>get_stop_token<span class="op">(</span>self<span class="op">.</span>state<span class="op">-&gt;</span>receiver<span class="op">)</span>;</span>
<span id="cb31-30"><a href="#cb31-30" aria-hidden="true" tabindex="-1"></a>      <span class="op">}</span></span>
<span id="cb31-31"><a href="#cb31-31" aria-hidden="true" tabindex="-1"></a>    <span class="op">}</span>;</span>
<span id="cb31-32"><a href="#cb31-32" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb31-33"><a href="#cb31-33" aria-hidden="true" tabindex="-1"></a>    operation_state<span class="op">(</span>Src<span class="op">&amp;&amp;</span> source, Func<span class="op">&amp;&amp;</span> func, Receiver<span class="op">&amp;&amp;</span> r<span class="op">)</span></span>
<span id="cb31-34"><a href="#cb31-34" aria-hidden="true" tabindex="-1"></a>    <span class="op">:</span> receiver<span class="op">(</span>std<span class="op">::</span>move<span class="op">(</span>r<span class="op">))</span></span>
<span id="cb31-35"><a href="#cb31-35" aria-hidden="true" tabindex="-1"></a>    , func<span class="op">(</span>std<span class="op">::</span>move<span class="op">(</span>func<span class="op">))</span></span>
<span id="cb31-36"><a href="#cb31-36" aria-hidden="true" tabindex="-1"></a>    , innerState<span class="op">(</span>std<span class="op">::</span>execution<span class="op">::</span><span class="fu">connect</span><span class="op">(</span>std<span class="op">::</span>move<span class="op">(</span>source<span class="op">)</span>, receiver<span class="op">{</span><span class="kw">this</span><span class="op">}))</span></span>
<span id="cb31-37"><a href="#cb31-37" aria-hidden="true" tabindex="-1"></a>    <span class="op">{}</span></span>
<span id="cb31-38"><a href="#cb31-38" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb31-39"><a href="#cb31-39" aria-hidden="true" tabindex="-1"></a>    <span class="dt">void</span> start<span class="op">()</span> <span class="kw">noexcept</span> <span class="op">{</span></span>
<span id="cb31-40"><a href="#cb31-40" aria-hidden="true" tabindex="-1"></a>      std<span class="op">::</span>execution<span class="op">::</span>start<span class="op">(</span>innerState<span class="op">)</span>;</span>
<span id="cb31-41"><a href="#cb31-41" aria-hidden="true" tabindex="-1"></a>    <span class="op">}</span></span>
<span id="cb31-42"><a href="#cb31-42" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb31-43"><a href="#cb31-43" aria-hidden="true" tabindex="-1"></a>    Receiver receiver;</span>
<span id="cb31-44"><a href="#cb31-44" aria-hidden="true" tabindex="-1"></a>    Func func;</span>
<span id="cb31-45"><a href="#cb31-45" aria-hidden="true" tabindex="-1"></a>    std<span class="op">::</span>execution<span class="op">::</span>connect_result_t<span class="op">&lt;</span>Src, receiver<span class="op">&gt;</span> innerState;</span>
<span id="cb31-46"><a href="#cb31-46" aria-hidden="true" tabindex="-1"></a>  <span class="op">}</span>;</span>
<span id="cb31-47"><a href="#cb31-47" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb31-48"><a href="#cb31-48" aria-hidden="true" tabindex="-1"></a>  <span class="kw">template</span><span class="op">&lt;</span><span class="kw">typename</span> Receiver<span class="op">&gt;</span></span>
<span id="cb31-49"><a href="#cb31-49" aria-hidden="true" tabindex="-1"></a>  operation_state<span class="op">&lt;</span>Receiver<span class="op">&gt;</span> <span class="fu">connect</span><span class="op">(</span>Receiver<span class="op">&amp;&amp;</span> r<span class="op">)</span> <span class="op">&amp;&amp;</span> <span class="op">{</span></span>
<span id="cb31-50"><a href="#cb31-50" aria-hidden="true" tabindex="-1"></a>    <span class="cf">return</span> operation_state<span class="op">&lt;</span>Receiver<span class="op">&gt;{</span>std<span class="op">::</span>move<span class="op">(</span>source<span class="op">)</span>, std<span class="op">::</span>move<span class="op">(</span>func<span class="op">)</span>, std<span class="op">::</span>move<span class="op">(</span>r<span class="op">)}</span>;</span>
<span id="cb31-51"><a href="#cb31-51" aria-hidden="true" tabindex="-1"></a>  <span class="op">}</span></span>
<span id="cb31-52"><a href="#cb31-52" aria-hidden="true" tabindex="-1"></a><span class="op">}</span>;</span></code></pre></div>
<p>By forwarding the <code class="sourceCode default">get_stop_token()</code> CPO call to the parent receiver and returning the parent receiver’s stop token, this means that if the transform-sender’s child operation asks for the stop-token it will get the parent operation’s stop-token and thus will observe any stop-requests send to the parent operation - stop requests transparently pass through the transform-operation.</p>
<p>Note that this forwarding of query/getter-style CPOs on the receiver can be further generalised to allow forwarding other kinds of queries on the receiver by adding a <code class="sourceCode default">tag_invoke()</code> overload that is generic over the CPO being forwarded.</p>
<p>For example, instead of writing the following overload for the receiver</p>
<div class="sourceCode" id="cb32"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb32-1"><a href="#cb32-1" aria-hidden="true" tabindex="-1"></a><span class="co">// Forward get_stop_token() to the parent receiver</span></span>
<span id="cb32-2"><a href="#cb32-2" aria-hidden="true" tabindex="-1"></a><span class="kw">friend</span> <span class="kw">auto</span> tag_invoke<span class="op">(</span>tag_t<span class="op">&lt;</span>execution<span class="op">::</span>get_stop_token<span class="op">&gt;</span>, <span class="kw">const</span> receiver<span class="op">&amp;</span> self<span class="op">)</span> <span class="kw">noexcept</span></span>
<span id="cb32-3"><a href="#cb32-3" aria-hidden="true" tabindex="-1"></a>  <span class="op">-&gt;</span> std<span class="op">::</span>invoke_result_t<span class="op">&lt;</span>execution<span class="op">::</span>get_stop_token, <span class="kw">const</span> Receiver<span class="op">&amp;&gt;</span> <span class="op">{</span></span>
<span id="cb32-4"><a href="#cb32-4" aria-hidden="true" tabindex="-1"></a>  <span class="cf">return</span> std<span class="op">::</span>execution<span class="op">::</span>get_stop_token<span class="op">(</span>self<span class="op">.</span>state<span class="op">-&gt;</span>receiver<span class="op">)</span>;</span>
<span id="cb32-5"><a href="#cb32-5" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
<p>we can write:</p>
<div class="sourceCode" id="cb33"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb33-1"><a href="#cb33-1" aria-hidden="true" tabindex="-1"></a><span class="kw">template</span><span class="op">&lt;</span><span class="kw">typename</span> CPO<span class="op">&gt;</span></span>
<span id="cb33-2"><a href="#cb33-2" aria-hidden="true" tabindex="-1"></a>  <span class="kw">requires</span> std<span class="op">::</span>invocable<span class="op">&lt;</span>CPO, <span class="kw">const</span> Receiver<span class="op">&amp;&gt;</span></span>
<span id="cb33-3"><a href="#cb33-3" aria-hidden="true" tabindex="-1"></a><span class="kw">friend</span> <span class="kw">auto</span> tag_invoke<span class="op">(</span>CPO cpo, <span class="kw">const</span> receiver<span class="op">&amp;</span> self<span class="op">)</span></span>
<span id="cb33-4"><a href="#cb33-4" aria-hidden="true" tabindex="-1"></a>  <span class="kw">noexcept</span><span class="op">(</span>std<span class="op">::</span>is_nothrow_invocable_v<span class="op">&lt;</span>CPO, <span class="kw">const</span> Receiver<span class="op">&amp;&gt;)</span></span>
<span id="cb33-5"><a href="#cb33-5" aria-hidden="true" tabindex="-1"></a>  <span class="op">-&gt;</span> std<span class="op">::</span>invoke_result_t<span class="op">&lt;</span>CPO, <span class="kw">const</span> Receiver<span class="op">&amp;&gt;</span> <span class="op">{</span></span>
<span id="cb33-6"><a href="#cb33-6" aria-hidden="true" tabindex="-1"></a>  <span class="cf">return</span> <span class="kw">static_cast</span><span class="op">&lt;</span>CPO<span class="op">&amp;&amp;&gt;(</span>cpo<span class="op">)(</span>self<span class="op">.</span>state<span class="op">-&gt;</span>receiver<span class="op">)</span>;</span>
<span id="cb33-7"><a href="#cb33-7" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
<p>This will still succeed in forwarding calls to <code class="sourceCode default">get_stop_token()</code> on the receiver but will now also support forwarding calls to other query-like CPOs. e.g. <code class="sourceCode default">get_scheduler(r)</code> to get the current scheduler, or <code class="sourceCode default">get_allocator(r)</code> to get the current allocator, or <code class="sourceCode default">get_priority(r)</code> to get the priority of a particular operation.</p>
<p>Thus if we ensure that sender-based algorithms added to the standard library are specified in such a way that receivers that pass to child operations will forward receiver-query-like CPO-calls to the parent</p>
<p>Generalising this forwarding mechanism for receiver queries should be explored further but is a topic for another paper.</p>
<h3 data-number="5.8.2" id="introducing-new-cancellation-scopes-in-a-sender-algorithm"><span class="header-section-number">5.8.2</span> Introducing new cancellation-scopes in a sender algorithm<a href="#introducing-new-cancellation-scopes-in-a-sender-algorithm" class="self-link"></a></h3>
<p>Not every algorithm is going to be transparent to cancellation. Algorithms that introduce concurrency will often also introduce a new cancellation scope.</p>
<p>A cancellation scope allows cancellation of child operations independently of cancellation of the operation as a whole, while usually still allowing cancellation of the parent operation to propagate to cancellation of child operations.</p>
<p>For example, consider the <code class="sourceCode default">stop_when()</code> algorithm. It accepts two input senders; a <code class="sourceCode default">source</code> and a <code class="sourceCode default">trigger</code>, such that:</p>
<ul>
<li>If <code class="sourceCode default">source</code> completes before <code class="sourceCode default">trigger</code> it will cancel the <code class="sourceCode default">trigger</code> operation</li>
<li>If <code class="sourceCode default">trigger</code> completes before <code class="sourceCode default">source</code> then it cancels the <code class="sourceCode default">source</code> operation.</li>
<li>If the composed operation is cancelled then both <code class="sourceCode default">source</code> and <code class="sourceCode default">trigger</code> are cancelled.</li>
<li>Once both <code class="sourceCode default">source</code> and <code class="sourceCode default">trigger</code> complete then the composed operation completes with the result from <code class="sourceCode default">source</code>.</li>
</ul>
<p>In this instance, the <code class="sourceCode default">stop_when()</code> algorithm introduces a new cancellation scope so that it can independently request cancellation of the child operations.</p>
<p>A possible implementation strategy for such a <code class="sourceCode default">stop_when()</code> algorithm would be to do the following:</p>
<ul>
<li>Have the stop_when operation-state hold:
<ul>
<li>The receiver connected to the <code class="sourceCode default">stop_when</code> sender</li>
<li>A <code class="sourceCode default">std::in_place_stop_source</code></li>
<li>A stop-callback that subscribes to the parent receiver’s stop-token and that calls <code class="sourceCode default">.request_stop()</code> on the <code class="sourceCode default">in_place_stop_source</code> when a stop-request is made on the parent receiver’s stop-token.</li>
<li>Operation-states for each of the <code class="sourceCode default">source</code> and <code class="sourceCode default">trigger</code> operations, connected to receivers generated internally by the stop_when algorithm.</li>
<li>A <code class="sourceCode default">std::atomic&lt;uint8_t&gt;</code> that is decremented when either of the operations completes and is used to determine when both operations have completed.</li>
<li>Some storage space for holding the result of the <code class="sourceCode default">source</code> sender while waiting for the <code class="sourceCode default">trigger</code> sender to complete (if the <code class="sourceCode default">source</code> sender completes first)</li>
</ul></li>
<li>The receivers passed to <code class="sourceCode default">connect()</code> on both the <code class="sourceCode default">source</code> and <code class="sourceCode default">trigger</code> operations customise <code class="sourceCode default">get_stop_token()</code> to return a <code class="sourceCode default">std::in_place_stop_token</code> obtained from the stop_when operation’s <code class="sourceCode default">std::in_place_stop_source</code>.</li>
</ul>
<p>This pattern of an operation-state owning a stop-source, subscribing to the parent operation’s stop-token to forward the stop-request onto the stop-source, and then passing a stop-token referencing the stop-source on to child operations by customising <code class="sourceCode default">get_stop_token()</code> on the receivers passed to those operations is a common pattern when implementing concurrency patterns that introduce a new cancellation scope.</p>
<p>See Appendix A for details of the implementation of <code class="sourceCode default">stop_when()</code>.</p>
<h3 data-number="5.8.3" id="inhibiting-cancellation-propagation"><span class="header-section-number">5.8.3</span> Inhibiting cancellation propagation<a href="#inhibiting-cancellation-propagation" class="self-link"></a></h3>
<p>There are sometimes cases where we don’t want a stop-request issued to the parent operation to propagate to a child operation. For example, the child operation might be a cleanup operation which we want to run to completion regardless of whether the parent operation is cancelled or not.</p>
<p>This can be achieved by building a sender that wraps the connected receiver in a new receiver type that customises <code class="sourceCode default">get_stop_token()</code> to return <code class="sourceCode default">std::never_stop_token</code>.</p>
<p>For example:</p>
<div class="sourceCode" id="cb34"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb34-1"><a href="#cb34-1" aria-hidden="true" tabindex="-1"></a><span class="kw">template</span><span class="op">&lt;</span><span class="kw">typename</span> Sender<span class="op">&gt;</span></span>
<span id="cb34-2"><a href="#cb34-2" aria-hidden="true" tabindex="-1"></a><span class="kw">struct</span> unstoppable_sender <span class="op">{</span></span>
<span id="cb34-3"><a href="#cb34-3" aria-hidden="true" tabindex="-1"></a>  Sender inner;</span>
<span id="cb34-4"><a href="#cb34-4" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb34-5"><a href="#cb34-5" aria-hidden="true" tabindex="-1"></a>  <span class="kw">template</span><span class="op">&lt;</span><span class="kw">template</span><span class="op">&lt;</span><span class="kw">typename</span><span class="op">...&gt;</span> <span class="kw">class</span> Variant,</span>
<span id="cb34-6"><a href="#cb34-6" aria-hidden="true" tabindex="-1"></a>           <span class="kw">template</span><span class="op">&lt;</span><span class="kw">typename</span><span class="op">...&gt;</span> <span class="kw">class</span> Tuple<span class="op">&gt;</span></span>
<span id="cb34-7"><a href="#cb34-7" aria-hidden="true" tabindex="-1"></a>  <span class="kw">using</span> value_types <span class="op">=</span> <span class="kw">typename</span> Sender<span class="op">::</span><span class="kw">template</span> value_types<span class="op">&lt;</span>Variant, Tuple<span class="op">&gt;</span>;</span>
<span id="cb34-8"><a href="#cb34-8" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb34-9"><a href="#cb34-9" aria-hidden="true" tabindex="-1"></a>  <span class="kw">template</span><span class="op">&lt;</span><span class="kw">template</span><span class="op">&lt;</span><span class="kw">typename</span><span class="op">...&gt;</span> <span class="kw">class</span> Variant<span class="op">&gt;</span></span>
<span id="cb34-10"><a href="#cb34-10" aria-hidden="true" tabindex="-1"></a>  <span class="kw">using</span> error_types <span class="op">=</span> <span class="kw">typename</span> Sender<span class="op">::</span><span class="kw">template</span> error_types<span class="op">&lt;</span>Variant<span class="op">&gt;</span>;</span>
<span id="cb34-11"><a href="#cb34-11" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb34-12"><a href="#cb34-12" aria-hidden="true" tabindex="-1"></a>  <span class="kw">static</span> <span class="kw">constexpr</span> <span class="dt">bool</span> sends_done <span class="op">=</span> Sender<span class="op">::</span>sends_done;</span>
<span id="cb34-13"><a href="#cb34-13" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb34-14"><a href="#cb34-14" aria-hidden="true" tabindex="-1"></a>  <span class="kw">template</span><span class="op">&lt;</span><span class="kw">typename</span> Receiver<span class="op">&gt;</span></span>
<span id="cb34-15"><a href="#cb34-15" aria-hidden="true" tabindex="-1"></a>  <span class="kw">struct</span> receiver <span class="op">{</span></span>
<span id="cb34-16"><a href="#cb34-16" aria-hidden="true" tabindex="-1"></a>    Receiver inner;</span>
<span id="cb34-17"><a href="#cb34-17" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb34-18"><a href="#cb34-18" aria-hidden="true" tabindex="-1"></a>    <span class="co">// Override get_stop_token()</span></span>
<span id="cb34-19"><a href="#cb34-19" aria-hidden="true" tabindex="-1"></a>    <span class="kw">friend</span> std<span class="op">::</span>never_stop_token tag_invoke<span class="op">(</span></span>
<span id="cb34-20"><a href="#cb34-20" aria-hidden="true" tabindex="-1"></a>        std<span class="op">::</span>tag_t<span class="op">&lt;</span>std<span class="op">::</span>execution<span class="op">::</span>get_stop_token<span class="op">&gt;</span>,</span>
<span id="cb34-21"><a href="#cb34-21" aria-hidden="true" tabindex="-1"></a>        <span class="kw">const</span> receiver<span class="op">&amp;</span> self<span class="op">)</span> <span class="kw">noexcept</span> <span class="op">{</span></span>
<span id="cb34-22"><a href="#cb34-22" aria-hidden="true" tabindex="-1"></a>      <span class="cf">return</span> <span class="op">{}</span>;</span>
<span id="cb34-23"><a href="#cb34-23" aria-hidden="true" tabindex="-1"></a>    <span class="op">}</span></span>
<span id="cb34-24"><a href="#cb34-24" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb34-25"><a href="#cb34-25" aria-hidden="true" tabindex="-1"></a>    <span class="co">// Pass through other CPOs</span></span>
<span id="cb34-26"><a href="#cb34-26" aria-hidden="true" tabindex="-1"></a>    <span class="kw">template</span><span class="op">&lt;</span><span class="kw">typename</span> CPO, <span class="kw">typename</span> Self, <span class="kw">typename</span><span class="op">...</span> Args<span class="op">&gt;</span></span>
<span id="cb34-27"><a href="#cb34-27" aria-hidden="true" tabindex="-1"></a>      <span class="kw">requires</span> <span class="op">(!</span>std<span class="op">::</span>same_as<span class="op">&lt;</span>CPO, std<span class="op">::</span>tag_t<span class="op">&lt;</span>std<span class="op">::</span>execution<span class="op">::</span>get_stop_token<span class="op">&gt;&gt;)</span> <span class="op">&amp;&amp;</span></span>
<span id="cb34-28"><a href="#cb34-28" aria-hidden="true" tabindex="-1"></a>               std<span class="op">::</span>same_as<span class="op">&lt;</span>std<span class="op">::</span>remove_cvref_t<span class="op">&lt;</span>Self<span class="op">&gt;</span>, receiver<span class="op">&gt;</span> <span class="op">&amp;&amp;</span></span>
<span id="cb34-29"><a href="#cb34-29" aria-hidden="true" tabindex="-1"></a>               std<span class="op">::</span>invocable<span class="op">&lt;</span>CPO, member_t<span class="op">&lt;</span>Self, Receiver<span class="op">&gt;</span>, Args<span class="op">...&gt;</span></span>
<span id="cb34-30"><a href="#cb34-30" aria-hidden="true" tabindex="-1"></a>    <span class="kw">friend</span> <span class="kw">auto</span> tag_invoke<span class="op">(</span>CPO cpo, Self<span class="op">&amp;&amp;</span> self, Args<span class="op">&amp;&amp;...</span> args<span class="op">)</span></span>
<span id="cb34-31"><a href="#cb34-31" aria-hidden="true" tabindex="-1"></a>        <span class="kw">noexcept</span><span class="op">(</span>std<span class="op">::</span>is_nothrow_invocable_v<span class="op">&lt;</span>CPO, member_t<span class="op">&lt;</span>Self, Receiver<span class="op">&gt;</span>, Args<span class="op">...&gt;)</span></span>
<span id="cb34-32"><a href="#cb34-32" aria-hidden="true" tabindex="-1"></a>        <span class="op">-&gt;</span> std<span class="op">::</span>invoke_result_t<span class="op">&lt;</span>CPO, member_t<span class="op">&lt;</span>Self, Receiver<span class="op">&gt;</span>, Args<span class="op">...&gt;</span> <span class="op">{</span></span>
<span id="cb34-33"><a href="#cb34-33" aria-hidden="true" tabindex="-1"></a>      <span class="cf">return</span> std<span class="op">::</span>move<span class="op">(</span>cpo<span class="op">)(</span><span class="kw">static_cast</span><span class="op">&lt;</span>Self<span class="op">&amp;&amp;&gt;(</span>self<span class="op">).</span>inner, <span class="kw">static_cast</span><span class="op">&lt;</span>Args<span class="op">&amp;&amp;&gt;(</span>args<span class="op">)...)</span>;</span>
<span id="cb34-34"><a href="#cb34-34" aria-hidden="true" tabindex="-1"></a>    <span class="op">}</span></span>
<span id="cb34-35"><a href="#cb34-35" aria-hidden="true" tabindex="-1"></a>  <span class="op">}</span>;</span>
<span id="cb34-36"><a href="#cb34-36" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb34-37"><a href="#cb34-37" aria-hidden="true" tabindex="-1"></a>  <span class="kw">template</span><span class="op">&lt;</span><span class="kw">typename</span> Self, <span class="kw">typename</span> Receiver<span class="op">&gt;</span></span>
<span id="cb34-38"><a href="#cb34-38" aria-hidden="true" tabindex="-1"></a>    <span class="kw">requires</span> std<span class="op">::</span>same_as<span class="op">&lt;</span>std<span class="op">::</span>remove_cvref_t<span class="op">&lt;</span>Self<span class="op">&gt;</span>, unstoppable_sender<span class="op">&gt;</span> <span class="op">&amp;&amp;</span></span>
<span id="cb34-39"><a href="#cb34-39" aria-hidden="true" tabindex="-1"></a>             std<span class="op">::</span>execution<span class="op">::</span>receiver<span class="op">&lt;</span>Receiver<span class="op">&gt;</span> <span class="op">&amp;&amp;</span></span>
<span id="cb34-40"><a href="#cb34-40" aria-hidden="true" tabindex="-1"></a>             std<span class="op">::</span>constructible_from<span class="op">&lt;</span>std<span class="op">::</span>remove_cvref_t<span class="op">&lt;</span>Receiver<span class="op">&gt;</span>, Receiver<span class="op">&gt;</span> <span class="op">&amp;&amp;</span></span>
<span id="cb34-41"><a href="#cb34-41" aria-hidden="true" tabindex="-1"></a>             std<span class="op">::</span>sender_to<span class="op">&lt;</span>member_t<span class="op">&lt;</span>Self, Sender<span class="op">&gt;</span>, receiver<span class="op">&lt;</span>std<span class="op">::</span>remove_cvref_t<span class="op">&lt;</span>Receiver<span class="op">&gt;&gt;&gt;</span></span>
<span id="cb34-42"><a href="#cb34-42" aria-hidden="true" tabindex="-1"></a>  <span class="kw">friend</span> <span class="kw">auto</span> tag_invoke<span class="op">(</span>std<span class="op">::</span>tag_t<span class="op">&lt;</span>std<span class="op">::</span>execution<span class="op">::</span><span class="fu">connect</span><span class="op">&gt;</span>, Self<span class="op">&amp;&amp;</span> self, Receiver<span class="op">&amp;&amp;</span> receiver<span class="op">)</span></span>
<span id="cb34-43"><a href="#cb34-43" aria-hidden="true" tabindex="-1"></a>      <span class="kw">noexcept</span><span class="op">(</span></span>
<span id="cb34-44"><a href="#cb34-44" aria-hidden="true" tabindex="-1"></a>        std<span class="op">::</span>is_nothrow_constructible_v<span class="op">&lt;</span>std<span class="op">::</span>remove_cvref_t<span class="op">&lt;</span>Receiver<span class="op">&gt;</span>, Receiver<span class="op">&gt;</span> <span class="op">&amp;&amp;</span></span>
<span id="cb34-45"><a href="#cb34-45" aria-hidden="true" tabindex="-1"></a>        std<span class="op">::</span>is_nothrow_invocable_v<span class="op">&lt;</span><span class="kw">decltype</span><span class="op">(</span>std<span class="op">::</span>execution<span class="op">::</span><span class="fu">connect</span><span class="op">)</span>,</span>
<span id="cb34-46"><a href="#cb34-46" aria-hidden="true" tabindex="-1"></a>                                    member_t<span class="op">&lt;</span>Self, Sender<span class="op">&gt;</span>,</span>
<span id="cb34-47"><a href="#cb34-47" aria-hidden="true" tabindex="-1"></a>                                    receiver<span class="op">&lt;</span>std<span class="op">::</span>remove_cvref_t<span class="op">&lt;</span>Receiver<span class="op">&gt;&gt;&gt;)</span></span>
<span id="cb34-48"><a href="#cb34-48" aria-hidden="true" tabindex="-1"></a>      <span class="op">-&gt;</span> std<span class="op">::</span>invoke_result_t<span class="op">&lt;</span><span class="kw">decltype</span><span class="op">(</span>std<span class="op">::</span>execution<span class="op">::</span><span class="fu">connect</span><span class="op">)</span>,</span>
<span id="cb34-49"><a href="#cb34-49" aria-hidden="true" tabindex="-1"></a>                              member_t<span class="op">&lt;</span>Self, Sender<span class="op">&gt;</span>,</span>
<span id="cb34-50"><a href="#cb34-50" aria-hidden="true" tabindex="-1"></a>                              receiver<span class="op">&lt;</span>std<span class="op">::</span>remove_cvref_t<span class="op">&lt;</span>Receiver<span class="op">&gt;&gt;&gt;</span> <span class="op">{</span></span>
<span id="cb34-51"><a href="#cb34-51" aria-hidden="true" tabindex="-1"></a>    <span class="co">// Wrap the incoming receiver and forward through to Sender&#39;s connect().</span></span>
<span id="cb34-52"><a href="#cb34-52" aria-hidden="true" tabindex="-1"></a>    <span class="cf">return</span> std<span class="op">::</span>execution<span class="op">::</span><span class="fu">connect</span><span class="op">(</span></span>
<span id="cb34-53"><a href="#cb34-53" aria-hidden="true" tabindex="-1"></a>      <span class="kw">static_cast</span><span class="op">&lt;</span>Self<span class="op">&amp;&amp;&gt;(</span>self<span class="op">).</span>inner,</span>
<span id="cb34-54"><a href="#cb34-54" aria-hidden="true" tabindex="-1"></a>      receiver<span class="op">&lt;</span>std<span class="op">::</span>remove_cvref_t<span class="op">&lt;</span>Receiver<span class="op">&gt;&gt;{</span><span class="kw">static_cast</span><span class="op">&lt;</span>Receiver<span class="op">&amp;&amp;&gt;(</span>receiver<span class="op">)})</span>;</span>
<span id="cb34-55"><a href="#cb34-55" aria-hidden="true" tabindex="-1"></a>  <span class="op">}</span></span>
<span id="cb34-56"><a href="#cb34-56" aria-hidden="true" tabindex="-1"></a><span class="op">}</span>;</span></code></pre></div>
<p>Then you can wrap your operation in the <code class="sourceCode default">unstoppable_sender</code> and stop-requests from the parent will no longer propagate to the child operation.</p>
<h3 data-number="5.8.4" id="coroutine-integration-limitations-of-stdtask"><span class="header-section-number">5.8.4</span> Coroutine integration / limitations of std::task<a href="#coroutine-integration-limitations-of-stdtask" class="self-link"></a></h3>
<p>The design of the <code class="sourceCode default">std::task/lazy</code> coroutine type proposed in <a href="https://wg21.link/P1056R1">P1056R1</a> does not support the design goal of generically composable, cancellable operations.</p>
<p>This P1056R1 design was largely modelled on the design of <code class="sourceCode default">cppcoro::task</code> and the limitations of this with regards to cancellation have been discussed in prior sections.</p>
<p>The implementation and usage experience of <code class="sourceCode default">folly::coro::Task</code> in Facebook has shown that the model of implicit propagation of a <code class="sourceCode default">CancellationToken</code> can be used to provide a simple interface for ensuring that cancellation of a high-level operation implicitly propagates that request to child operations.</p>
<p>We have implemented a prototype of a <code class="sourceCode default">task&lt;T&gt;</code> coroutine-type in <a href="https://github.com/facebookexperimental/libunifex">libunifex</a> that supports the same ideas for implicit propagation of cancellation-context from parent coroutine to child coroutine as <code class="sourceCode default">folly::coro::Task</code> but with the implementation revised to integrate with the sender/receiver concepts and the cancellation-mechanism proposed in this paper.</p>
<p>This implementation needs to support 3 cases for propagating stop-requests through a <code class="sourceCode default">task</code> coroutine: 1. Where a <code class="sourceCode default">task</code> is used as a child of a sender-based algorithm. The receiver passed to the <code class="sourceCode default">task</code>’s <code class="sourceCode default">connect()</code> operation needs to have its stop-token’s stop-requests forwarded into child operations of the <code class="sourceCode default">task</code>. 2. Where a <code class="sourceCode default">task</code> coroutine awaits a sender we need to make sure that the <code class="sourceCode default">task</code> injects a receiver into the call to <code class="sourceCode default">connect()</code> on the awaited sender that has customised <code class="sourceCode default">get_stop_token()</code> to return the <code class="sourceCode default">task</code>’s stop-token inherited from its parent. 3. Where a <code class="sourceCode default">task</code> awaits an awaitable type, such as another <code class="sourceCode default">task</code>, the <code class="sourceCode default">task</code>’s stop-token needs to be propagated to that awaitable while still preserving the ability to symmetrically-transfer execution to a child coroutine - something that is not possible if indirecting through a sender’s <code class="sourceCode default">connect</code>/<code class="sourceCode default">start</code> interface.</p>
<p>For example:</p>
<div class="sourceCode" id="cb35"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb35-1"><a href="#cb35-1" aria-hidden="true" tabindex="-1"></a>static_thread_pool tp;</span>
<span id="cb35-2"><a href="#cb35-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb35-3"><a href="#cb35-3" aria-hidden="true" tabindex="-1"></a>task<span class="op">&lt;</span><span class="dt">int</span><span class="op">&gt;</span> child1<span class="op">(</span><span class="kw">auto</span> scheduler<span class="op">)</span> <span class="op">{</span></span>
<span id="cb35-4"><a href="#cb35-4" aria-hidden="true" tabindex="-1"></a>  <span class="kw">co_await</span> schedule_after<span class="op">(</span>tp, <span class="dv">1</span><span class="bu">s</span><span class="op">)</span>;</span>
<span id="cb35-5"><a href="#cb35-5" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span>
<span id="cb35-6"><a href="#cb35-6" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb35-7"><a href="#cb35-7" aria-hidden="true" tabindex="-1"></a>task<span class="op">&lt;</span><span class="dt">int</span><span class="op">&gt;</span> child2<span class="op">(</span><span class="kw">auto</span> scheduler<span class="op">)</span> <span class="op">{</span></span>
<span id="cb35-8"><a href="#cb35-8" aria-hidden="true" tabindex="-1"></a>  <span class="co">// </span></span>
<span id="cb35-9"><a href="#cb35-9" aria-hidden="true" tabindex="-1"></a>  <span class="kw">co_await</span> schedule_after<span class="op">(</span>tp, <span class="dv">10</span><span class="bu">ms</span><span class="op">)</span>;</span>
<span id="cb35-10"><a href="#cb35-10" aria-hidden="true" tabindex="-1"></a>  <span class="cf">throw</span> std<span class="op">::</span>runtime_error<span class="op">{</span><span class="st">&quot;failed&quot;</span><span class="op">}</span>;</span>
<span id="cb35-11"><a href="#cb35-11" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span>
<span id="cb35-12"><a href="#cb35-12" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb35-13"><a href="#cb35-13" aria-hidden="true" tabindex="-1"></a>task<span class="op">&lt;</span><span class="dt">int</span><span class="op">&gt;</span> parent<span class="op">(</span><span class="kw">auto</span> scheduler<span class="op">)</span> <span class="op">{</span></span>
<span id="cb35-14"><a href="#cb35-14" aria-hidden="true" tabindex="-1"></a>  <span class="co">// Passing tasks into when_all() which treats the task as a sender.</span></span>
<span id="cb35-15"><a href="#cb35-15" aria-hidden="true" tabindex="-1"></a>  <span class="co">// when_all() will inject its own stop-token into the child tasks.</span></span>
<span id="cb35-16"><a href="#cb35-16" aria-hidden="true" tabindex="-1"></a>  <span class="co">//</span></span>
<span id="cb35-17"><a href="#cb35-17" aria-hidden="true" tabindex="-1"></a>  <span class="co">// When child2() completes with an error after 10ms this should</span></span>
<span id="cb35-18"><a href="#cb35-18" aria-hidden="true" tabindex="-1"></a>  <span class="co">// cancel child1() quickly rather than waiting for the full 1s.</span></span>
<span id="cb35-19"><a href="#cb35-19" aria-hidden="true" tabindex="-1"></a>  <span class="co">//</span></span>
<span id="cb35-20"><a href="#cb35-20" aria-hidden="true" tabindex="-1"></a>  <span class="co">// Awaiting sender-result of when_all() - this needs to have the</span></span>
<span id="cb35-21"><a href="#cb35-21" aria-hidden="true" tabindex="-1"></a>  <span class="co">// stop-token from the parent() task injected into the sender so</span></span>
<span id="cb35-22"><a href="#cb35-22" aria-hidden="true" tabindex="-1"></a>  <span class="co">// that cancelling parent() cancels the when_all() operation.</span></span>
<span id="cb35-23"><a href="#cb35-23" aria-hidden="true" tabindex="-1"></a>  <span class="kw">co_await</span> when_all<span class="op">(</span>child1<span class="op">(</span>scheduler<span class="op">)</span>, child2<span class="op">(</span>scheduler<span class="op">))</span>;</span>
<span id="cb35-24"><a href="#cb35-24" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
<p>The libunifex prototype also explores some strategies for representing a ‘done’ result from a sender when awaited within a <code class="sourceCode default">task</code>-coroutine similar to an exception unwind but one that is not catchable with a try/catch.</p>
<p>Even though the ‘done’ signal cannot be caught with a try/catch, we can still apply sender-algorithms to translate the ‘done’ signal into either the value-channel, e.g. by returning a <code class="sourceCode default">std::optional</code>, or into the error-channel, e.g. by throwing an <code class="sourceCode default">operation_cancelled</code> exception.</p>
<p>For example:</p>
<div class="sourceCode" id="cb36"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb36-1"><a href="#cb36-1" aria-hidden="true" tabindex="-1"></a>sender_of<span class="op">&lt;</span><span class="dt">int</span><span class="op">&gt;</span> <span class="kw">auto</span> some_cancellable_operation<span class="op">()</span>;</span>
<span id="cb36-2"><a href="#cb36-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb36-3"><a href="#cb36-3" aria-hidden="true" tabindex="-1"></a>task<span class="op">&lt;</span><span class="dt">void</span><span class="op">&gt;</span> example<span class="op">()</span> <span class="op">{</span></span>
<span id="cb36-4"><a href="#cb36-4" aria-hidden="true" tabindex="-1"></a>  <span class="co">// If this completes with &#39;set_done&#39; then this will unwind the</span></span>
<span id="cb36-5"><a href="#cb36-5" aria-hidden="true" tabindex="-1"></a>  <span class="co">// awaiting coroutine and it will also complete with the &#39;done&#39;</span></span>
<span id="cb36-6"><a href="#cb36-6" aria-hidden="true" tabindex="-1"></a>  <span class="co">// signal.</span></span>
<span id="cb36-7"><a href="#cb36-7" aria-hidden="true" tabindex="-1"></a>  <span class="dt">int</span> x <span class="op">=</span> <span class="kw">co_await</span> some_cancellable_operation<span class="op">()</span>;</span>
<span id="cb36-8"><a href="#cb36-8" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb36-9"><a href="#cb36-9" aria-hidden="true" tabindex="-1"></a>  <span class="co">// But we can apply an algorithm that translates the &#39;done&#39;</span></span>
<span id="cb36-10"><a href="#cb36-10" aria-hidden="true" tabindex="-1"></a>  <span class="co">// signal into a value.</span></span>
<span id="cb36-11"><a href="#cb36-11" aria-hidden="true" tabindex="-1"></a>  std<span class="op">::</span>optional<span class="op">&lt;</span><span class="dt">int</span><span class="op">&gt;</span> y <span class="op">=</span> <span class="kw">co_await</span> done_as_optional<span class="op">(</span>some_cancellable_operation<span class="op">())</span>;</span>
<span id="cb36-12"><a href="#cb36-12" aria-hidden="true" tabindex="-1"></a>  <span class="cf">if</span> <span class="op">(!</span>y<span class="op">.</span>has_value<span class="op">())</span> <span class="op">{</span></span>
<span id="cb36-13"><a href="#cb36-13" aria-hidden="true" tabindex="-1"></a>    <span class="co">// Completed with cancellation.</span></span>
<span id="cb36-14"><a href="#cb36-14" aria-hidden="true" tabindex="-1"></a>  <span class="op">}</span></span>
<span id="cb36-15"><a href="#cb36-15" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb36-16"><a href="#cb36-16" aria-hidden="true" tabindex="-1"></a>  <span class="co">// Or we can translate the &#39;done&#39; signal into an error.</span></span>
<span id="cb36-17"><a href="#cb36-17" aria-hidden="true" tabindex="-1"></a>  <span class="cf">try</span> <span class="op">{</span></span>
<span id="cb36-18"><a href="#cb36-18" aria-hidden="true" tabindex="-1"></a>    <span class="dt">int</span> z <span class="op">=</span> <span class="kw">co_await</span> done_as_error<span class="op">&lt;</span>operation_cancelled<span class="op">&gt;(</span>some_cancellable_operation<span class="op">())</span>;</span>
<span id="cb36-19"><a href="#cb36-19" aria-hidden="true" tabindex="-1"></a>  <span class="op">}</span> <span class="cf">catch</span> <span class="op">(</span><span class="kw">const</span> operation_cancelled<span class="op">&amp;)</span> <span class="op">{</span></span>
<span id="cb36-20"><a href="#cb36-20" aria-hidden="true" tabindex="-1"></a>    <span class="co">// Handle cancellation</span></span>
<span id="cb36-21"><a href="#cb36-21" aria-hidden="true" tabindex="-1"></a>  <span class="op">}</span></span>
<span id="cb36-22"><a href="#cb36-22" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
<p>Note that options for treating the ‘done’ signal as first-class within a coroutine and in non-coroutine functions have been explored in <a href="https://wg21.link/P1677R2">P1677R2</a> - “Cancellation is Serendipitous Success” (by Kirk Shoop and Lisa Lippincott).</p>
<p>There is still some further design work required to investigate and incorporate other capabilities into a revised <code class="sourceCode default">task</code> design before a revision to P1056 can be produced, including:</p>
<ul>
<li>generalising the context-propagation support within <code class="sourceCode default">task</code> to allow propagating other kinds of context from caller to callee. e.g. an allocator, a scheduler, or some application-specific context</li>
<li>supporting a <code class="sourceCode default">task</code> having an associated scheduler and always resuming on the execution context of that task</li>
<li>allocator customisation - this is also explored by the paper <a href="https://wg21.link/P1681R0">P1681R0</a>
<ul>
<li>“Revisiting allocator model for coroutine lazy/task/generator” (by Gor Nishanov)</li>
</ul></li>
</ul>
<p>Support for propagating cancellation signals through coroutines is not part of this proposal. However, cancellation support <em>should</em> be incorporated into a subsequent revision of <a href="https:/wg21.link/P1056">P1056</a>. The author does not believe that <a href="https://wg21.link/P1056R1">P1056R1</a> should be accepted as-is due to its poor support for composable cancellation.</p>
<h2 data-number="5.9" id="cancellation-is-optional-best-effort"><span class="header-section-number">5.9</span> Cancellation is optional / best-effort<a href="#cancellation-is-optional-best-effort" class="self-link"></a></h2>
<p>The intention is to allow cancellation to be opt-in for both the implementation of a sender-based async operation and for the consumer of that operation (i.e. the author of the receiver).</p>
<p>If a receiver, <code class="sourceCode default">r</code>, does not to customise the <code class="sourceCode default">get_stop_token()</code> customisation-point to return a stop-token that would allow it to communicate a stop-request then when the sender calls <code class="sourceCode default">get_stop_token(r)</code> on the receiver it will dispatch to the default version, which returns <code class="sourceCode default">std::never_stop_token</code>.</p>
<p>If the sender tries to use this stop-token to respond to a stop-request the compiler will see an empty type with both <code class="sourceCode default">stop_possible()</code> and <code class="sourceCode default">stop_requested()</code> statically returning <code class="sourceCode default">false</code>. This should allow the compiler to optimise out most code-paths that would normally be dealing with stop-requests.</p>
<p>Conversely, if a sender does not support cancellation it does not need to call <code class="sourceCode default">get_stop_token()</code> and does not need to respond to stop-requests. In this case there is no overhead or extra complexity required in the sender implementation to ignore stop-requests, it can simply just ignore the <code class="sourceCode default">get_stop_token()</code> customisation-point altogether and let the async operation naturally run to completion.</p>
<p>Note that, in general, responding to a request to stop is inherently racy as the source of the request to stop is potentially executing concurrently with the natural completion of the operation (cancellation almost always involves some form of concurrency). So it’s always possible that a request to stop comes too late and is ignored because the operation has progressed past the point where it can be cancelled.</p>
<p>Thus async operations often only respond to cancellation on a best-effort basis.</p>
<p>For example, at the time that you request cancellation of an async I/O operation for which you have not yet received notification of its completion, the I/O may actually have already have completed and the OS has posted the completion notification and it’s just sitting in a queue waiting for you to process it. In this situation, the OS will almost certainly just ignore the request to cancel an already-complete I/O operation.</p>
<p>Stop-requests can also be ignored simply because the operation does not support cancellation.</p>
<p>Thus applications will generally need to be able to cope with stop-requests that are ignored. The timeliness of responding to a stop-request, or whether it responds at all to a stop-request, can often be a QoI decision for the implementation of that operation.</p>
<p>However, there are some operations that may require support for cancellation to be able to build a correct application. For example, a server that listens for incoming connections on a socket may need to be able to cancel the <code class="sourceCode default">accept()</code> operation during shutdown to handle the case where no more clients will attempt to establish connections. If the <code class="sourceCode default">accept()</code> operation did not respond to a stop-request then the program may never terminate.</p>
<h2 data-number="5.10" id="performance-considerations"><span class="header-section-number">5.10</span> Performance Considerations<a href="#performance-considerations" class="self-link"></a></h2>
<p>This section discusses several of the performance considerations that went into the design of this proposal.</p>
<h3 data-number="5.10.1" id="dont-pay-for-what-you-dont-use"><span class="header-section-number">5.10.1</span> Don’t pay for what you don’t use<a href="#dont-pay-for-what-you-dont-use" class="self-link"></a></h3>
<p>Supporting cancellation of an async operation generally has runtime overhead compared to operations that do not support cancellation. Extra synchronisation, extra branches and extra storage for stop-callbacks is often required when supporting cancellation.</p>
<p>If we know at compile-time that a caller will never request cancellation of an operation then we’d like to be able to avoid the runtime overhead that comes with supporting cancellation.</p>
<p>The default implementation of the <code class="sourceCode default">get_stop_token()</code> customisation-point returns a <code class="sourceCode default">std::never_stop_token</code> which has constexpr <code class="sourceCode default">stop_possible()</code> and <code class="sourceCode default">stop_requested()</code> methods and also has an empty, no-op stop-callback type.</p>
<p>Consumers that do not opt-in to the ability to submit a stop-request by customising the <code class="sourceCode default">get_stop_token()</code> customisation-point will therefore end up providing a <code class="sourceCode default">std::never_stop_token</code> to the operation. Even if the operation does support cancellation, attempts to use this token type will compile out most cancellation-handling code-paths as dead-code and thus eliminate runtime cancellation overhead.</p>
<p>For cases where a fundamentally different and more efficient implementation is possible when cancellation is not required to be supported, the implementation can specialise on or use <code class="sourceCode default">if constexpr</code> in conjunction with the <code class="sourceCode default">std::unstoppable_token</code> concept to dispatch to the different implementations.</p>
<p>For example, the <code class="sourceCode default">libunifex::win32::windows_thread_pool</code> schedule operation adds two overloads of <code class="sourceCode default">connect()</code>, one for receivers whose stop-token is never going to produce a stop-request and which returns an operation-state that takes a more efficient approach, and another overload for receivers that might issue a stop-request.</p>
<div class="sourceCode" id="cb37"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb37-1"><a href="#cb37-1" aria-hidden="true" tabindex="-1"></a><span class="kw">template</span><span class="op">&lt;</span><span class="kw">typename</span> Receiver<span class="op">&gt;</span></span>
<span id="cb37-2"><a href="#cb37-2" aria-hidden="true" tabindex="-1"></a><span class="kw">class</span> schedule_op <span class="op">{</span></span>
<span id="cb37-3"><a href="#cb37-3" aria-hidden="true" tabindex="-1"></a>  <span class="co">// non-cancellable version ...</span></span>
<span id="cb37-4"><a href="#cb37-4" aria-hidden="true" tabindex="-1"></a><span class="op">}</span>;</span>
<span id="cb37-5"><a href="#cb37-5" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb37-6"><a href="#cb37-6" aria-hidden="true" tabindex="-1"></a><span class="kw">template</span><span class="op">&lt;</span><span class="kw">typename</span> Receiver<span class="op">&gt;</span></span>
<span id="cb37-7"><a href="#cb37-7" aria-hidden="true" tabindex="-1"></a><span class="kw">class</span> cancellable_schedule_op <span class="op">{</span></span>
<span id="cb37-8"><a href="#cb37-8" aria-hidden="true" tabindex="-1"></a>  <span class="co">// cancellable version ...</span></span>
<span id="cb37-9"><a href="#cb37-9" aria-hidden="true" tabindex="-1"></a><span class="op">}</span>;</span>
<span id="cb37-10"><a href="#cb37-10" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb37-11"><a href="#cb37-11" aria-hidden="true" tabindex="-1"></a><span class="kw">class</span> windows_thread_pool<span class="op">::</span>schedule_sender <span class="op">{</span></span>
<span id="cb37-12"><a href="#cb37-12" aria-hidden="true" tabindex="-1"></a><span class="kw">public</span><span class="op">:</span></span>
<span id="cb37-13"><a href="#cb37-13" aria-hidden="true" tabindex="-1"></a>  <span class="co">// Dispatch to cancellable implementation if unstoppable</span></span>
<span id="cb37-14"><a href="#cb37-14" aria-hidden="true" tabindex="-1"></a>  <span class="kw">template</span><span class="op">&lt;</span><span class="kw">typename</span> Receiver<span class="op">&gt;</span></span>
<span id="cb37-15"><a href="#cb37-15" aria-hidden="true" tabindex="-1"></a>      <span class="kw">requires</span> std<span class="op">::</span>execution<span class="op">::</span>receiver_of<span class="op">&lt;</span>Receiver<span class="op">&gt;</span> <span class="op">&amp;&amp;</span></span>
<span id="cb37-16"><a href="#cb37-16" aria-hidden="true" tabindex="-1"></a>               std<span class="op">::</span>unstoppable_token<span class="op">&lt;</span>std<span class="op">::</span>execution<span class="op">::</span>stop_token_type_t<span class="op">&lt;</span>Receiver<span class="op">&gt;&gt;)</span></span>
<span id="cb37-17"><a href="#cb37-17" aria-hidden="true" tabindex="-1"></a>  schedule_op<span class="op">&lt;</span>unifex<span class="op">::</span>remove_cvref_t<span class="op">&lt;</span>Receiver<span class="op">&gt;&gt;</span> <span class="fu">connect</span><span class="op">(</span>Receiver<span class="op">&amp;&amp;</span> r<span class="op">)</span> <span class="kw">const</span> <span class="op">{</span></span>
<span id="cb37-18"><a href="#cb37-18" aria-hidden="true" tabindex="-1"></a>      <span class="cf">return</span> schedule_op<span class="op">&lt;</span>unifex<span class="op">::</span>remove_cvref_t<span class="op">&lt;</span>Receiver<span class="op">&gt;&gt;{</span></span>
<span id="cb37-19"><a href="#cb37-19" aria-hidden="true" tabindex="-1"></a>          <span class="op">*</span>pool_, <span class="op">(</span>Receiver<span class="op">&amp;&amp;)</span>r<span class="op">}</span>;</span>
<span id="cb37-20"><a href="#cb37-20" aria-hidden="true" tabindex="-1"></a>  <span class="op">}</span></span>
<span id="cb37-21"><a href="#cb37-21" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb37-22"><a href="#cb37-22" aria-hidden="true" tabindex="-1"></a>  <span class="co">// Dispatch to cancellable implementation if not unstoppable</span></span>
<span id="cb37-23"><a href="#cb37-23" aria-hidden="true" tabindex="-1"></a>  <span class="kw">template</span><span class="op">&lt;</span><span class="kw">typename</span> Receiver<span class="op">&gt;</span></span>
<span id="cb37-24"><a href="#cb37-24" aria-hidden="true" tabindex="-1"></a>      <span class="kw">requires</span> receiver_of<span class="op">&lt;</span>Receiver<span class="op">&gt;</span> <span class="op">&amp;&amp;</span></span>
<span id="cb37-25"><a href="#cb37-25" aria-hidden="true" tabindex="-1"></a>               <span class="op">(!</span>std<span class="op">::</span>unstoppable_token<span class="op">&lt;</span>std<span class="op">::</span>execution<span class="op">::</span>stop_token_type_t<span class="op">&lt;</span>Receiver<span class="op">&gt;&gt;)</span></span>
<span id="cb37-26"><a href="#cb37-26" aria-hidden="true" tabindex="-1"></a>  cancellable_schedule_op<span class="op">&lt;</span>unifex<span class="op">::</span>remove_cvref_t<span class="op">&lt;</span>Receiver<span class="op">&gt;&gt;</span> <span class="fu">connect</span><span class="op">(</span>Receiver<span class="op">&amp;&amp;</span> r<span class="op">)</span> <span class="kw">const</span> <span class="op">{</span></span>
<span id="cb37-27"><a href="#cb37-27" aria-hidden="true" tabindex="-1"></a>      <span class="cf">return</span> cancellable_schedule_op<span class="op">&lt;</span>unifex<span class="op">::</span>remove_cvref_t<span class="op">&lt;</span>Receiver<span class="op">&gt;&gt;{</span></span>
<span id="cb37-28"><a href="#cb37-28" aria-hidden="true" tabindex="-1"></a>          <span class="op">*</span>pool_, <span class="op">(</span>Receiver<span class="op">&amp;&amp;)</span>r<span class="op">}</span>;</span>
<span id="cb37-29"><a href="#cb37-29" aria-hidden="true" tabindex="-1"></a>  <span class="op">}</span></span>
<span id="cb37-30"><a href="#cb37-30" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb37-31"><a href="#cb37-31" aria-hidden="true" tabindex="-1"></a>  <span class="op">...</span></span>
<span id="cb37-32"><a href="#cb37-32" aria-hidden="true" tabindex="-1"></a><span class="op">}</span>;</span></code></pre></div>
<p>Implementations can also avoid expensive operations needed to support cancellation at runtime, even if this is not known statically, by calling the <code class="sourceCode default">.stop_possible()</code> member function on the stop-token.</p>
<p>For example, the <code class="sourceCode default">cancellable_schedule_op</code> type mentioned above requires a heap allocation to support cancellation, but only conditionally allocates this memory if <code class="sourceCode default">get_stop_token(receiver).stop_possible()</code> is <code class="sourceCode default">true</code>.</p>
<h4 data-number="5.10.1.1" id="trying-to-cancel-uncancellable-operations"><span class="header-section-number">5.10.1.1</span> Trying to cancel uncancellable operations<a href="#trying-to-cancel-uncancellable-operations" class="self-link"></a></h4>
<p>The one case where it is more difficult to eliminate all runtime overhead is where a consumer of an operation that introduces a new cancellation scope might request cancellation of that operation but where the operation does not support cancellation and never calls <code class="sourceCode default">get_stop_token()</code> on the receiver.</p>
<p>As there is no query available to ask a sender whether or not it will respond to a stop-request the consumer will have to assume it might and reserve storage for a stop-source, e.g. a <code class="sourceCode default">std::in_place_stop_source</code> which could be 16 bytes.</p>
<p>In the case where a stop-request is made there will still usually involve at least one atomic operation to signal the stop-request, although if <code class="sourceCode default">get_stop_token()</code> was never called it would never need to execute any stop-callbacks.</p>
<h3 data-number="5.10.2" id="avoiding-heap-allocations-and-reference-counting"><span class="header-section-number">5.10.2</span> Avoiding heap-allocations and reference counting<a href="#avoiding-heap-allocations-and-reference-counting" class="self-link"></a></h3>
<p>The design of <code class="sourceCode default">std::stop_token</code> uses a shared-ownership model where the ownership of the stop-state is shared between all <code class="sourceCode default">std::stop_token</code>, <code class="sourceCode default">std::stop_source</code> and <code class="sourceCode default">std::stop_callback</code> objects associated with that stop-state.</p>
<p>This design was necessry to support independence in the respective lifetimes of <code class="sourceCode default">stop_source</code>-owners and <code class="sourceCode default">stop_token</code>-owners which is required for some use cases. For example, the <code class="sourceCode default">detach()</code> method included in <code class="sourceCode default">std::jthread</code> allows destruction of the <code class="sourceCode default">std::jthread</code> (which owns a <code class="sourceCode default">stop_source</code>) before the thread completes.</p>
<p>This shared-ownership model generally requires implementations to heap-allocate and atomically reference-count this shared stop-state. The overhead of this can make <code class="sourceCode default">std::stop_token</code> unsuitable for some high-performance use-cases requiring support for cancellation.</p>
<p>Ideally, we’d like to be able to avoid the runtime overhead of both the heap-allocation and reference-counting, but doing so requires placing more restriction on the use of a stop-token abstraction.</p>
<p>For example, if we did not allow the stop-source object to be movable or copyable and we required that the lifetime of stop-token/stop-callback objects was nested within the lifetime of a single, associated stop-source object then this would allow storing the stop-state inline inside the stop-source object, avoiding the need for a heap-allocation. It would also eliminate the need for reference counting since we know, by construction, that the stop-source will always be the last reference to the shared stop-state.</p>
<p>It just so happens that sender-based algorithms that provide the structured concurrency guarantee have a usage model that exactly matches this more restrictive interface requirements. i.e. that child operations (users of stop-tokens/stop-callbacks) are required to complete before the parent operation (owner of the stop-source) completes. The stop-source can also be constructed in-place in the parent operation’s operation-state object. As the operation-state object itself is not movable/copyable, the stop-source object does not need to be movable/copyable.</p>
<p>The <code class="sourceCode default">std::in_place_stop_source</code> type and its associated <code class="sourceCode default">std::in_place_stop_token</code> and <code class="sourceCode default">std::in_place_stop_callback</code> types proposed in this paper provide an implementation of the <code class="sourceCode default">std::stoppable_token</code> concept that has this more restrictive usage model compatible with usage in sender-based algorithms that adhere to the structured concurrency guarantee.</p>
<p>This allows sender algorithms introducing new cancellation scopes to use a stop-token based cancellation-model without the need for heap-allocations or atomic reference counting. This should allow more efficient implementations compared to what is possible with the more general <code class="sourceCode default">std::stop_token</code> interface.</p>
<p>It is worth noting, however, that implementations are still free to use <code class="sourceCode default">std::stop_token</code> if desired, as it is also a valid implementation of the <code class="sourceCode default">std::stoppable_token</code> concept.</p>
<h3 data-number="5.10.3" id="type-erasure-of-stop-callbacks"><span class="header-section-number">5.10.3</span> Type-erasure of stop callbacks<a href="#type-erasure-of-stop-callbacks" class="self-link"></a></h3>
<p>The stop-token design supports registering multiple stop-callbacks to receive notification of stop requests made from a given stop-source. Implementations of the stop-token concept will therefore generally need to maintain a list of registered callbacks and as each callback can potentially have a different type, this will require some form of type-erasure of the callbacks.</p>
<p>This type-erasure can be implemented without the need for heap-allocations, although it does mean that making a stop-request will involve making an indirect function-call to invoke each registered callback.</p>
<p>While the cost of this indirect call is not expected to be significant, it is worth noting that the cancellation model used by the Networking TS, which involves a parent operation directly calling a <code class="sourceCode default">.cancel()</code> method on a child object, does not have this same inherent need for type-erasure of the cancellation logic.</p>
<h3 data-number="5.10.4" id="synchronisation-required-to-support-stop-requests-coming-from-other-threads"><span class="header-section-number">5.10.4</span> Synchronisation required to support stop-requests coming from other threads<a href="#synchronisation-required-to-support-stop-requests-coming-from-other-threads" class="self-link"></a></h3>
<p>The existing <code class="sourceCode default">std::stop_token</code> family of types, as well as the proposed <code class="sourceCode default">std::in_place_stop_token</code> family of types, are both designed to allow stop-requests to be made from one thread while another thread is either polling for stop-requests or registering a stop-callback.</p>
<p>Supporting the ability to make a stop-request from any thread makes it easier to build cancellation algorithms as you don’t have to worry about figuring out whether or not it’s safe to issue a stop-request from the current thread and if not then figuring out which execution context is associated with the child operation you want to cancel and then scheduling work onto that execution context.</p>
<p>However, this capability means that the implementation of these types necessarily involves some form of thread synchronisation to ensure that this is safe - typically some atomic operations and a spin-lock held for a short period of time.</p>
<p>This design approach for cancellation is different to that of the Networking TS, which usually requires that calls to request cancellation are serialised with respect to calls to other operations on a given I/O object. Serialisation of these calls is usually handled by scheduling all work that might access the I/O object onto a strand-executor. There is still thread-synchronisation here, it’s just been moved out of the I/O object/operation and into the strand executor.</p>
<p>Note that even with this model, care needs to be taken to correctly handle the case where a call to the <code class="sourceCode default">.cancel()</code> method is scheduled onto the strand-executor’s queue and the operation completes concurrently before the <code class="sourceCode default">.cancel()</code> call can be evaluated.</p>
<p>The design of stop-callbacks and the requirements placed on their implementations by the <code class="sourceCode default">std::stoppable_token</code> concept is intended to solve this problem in a different way by allowing you to synchronously deregister a callback in such a way that you are guaranteed that after the deregistration completes that there is no other thread that is or will concurrently execute that stop-callback.</p>
<h3 data-number="5.10.5" id="allowing-optimised-implementations-for-single-threaded-use-cases"><span class="header-section-number">5.10.5</span> Allowing optimised implementations for single-threaded use-cases<a href="#allowing-optimised-implementations-for-single-threaded-use-cases" class="self-link"></a></h3>
<p>For use-cases where an application is only ever running logic on a single thread and we know that all stop requests will be made on that thread and all usages of the stop-token will also occur on that thread then the overhead of the thread-synchronisation inherent in the <code class="sourceCode default">std::stop_token</code> and <code class="sourceCode default">std::in_place_stop_token</code> types is unnecessary and may wish to be avoided.</p>
<p>However, if a given algorithm that introduces a new cancellation-scope in this environment has been written in terms of <code class="sourceCode default">std::in_place_stop_token</code> then it becomes difficult to avoid its inherent synchronisation, even if it’s only ever accessed from a single thread.</p>
<p>It’s an open-question whether or not we need to support some kind of mechanism to allow applications that only perform single-threaded cancellation to avoid the thread-synchronisation overhead.</p>
<p>More design investigation is required to be able to determine how best to do this within the sender/receiver framework.</p>
<h3 data-number="5.10.6" id="cost-of-get_stop_token-for-deep-operation-stacks"><span class="header-section-number">5.10.6</span> Cost of <code class="sourceCode default">get_stop_token()</code> for deep operation stacks<a href="#cost-of-get_stop_token-for-deep-operation-stacks" class="self-link"></a></h3>
<p>The design proposed in this paper, where an operation obtains the stop-token to use by calling <code class="sourceCode default">get_stop_token()</code> on the receiver passed to <code class="sourceCode default">connect()</code>, will potentially have many algorithms that are transparent to cancellation. i.e. where it just forwards the <code class="sourceCode default">get_stop_token()</code> through to the parent receiver</p>
<p>Usually, the receiver passed to child operations of a transparent-to-cancellation algorithm will hold a pointer to the parent operation-state and the parent receiver will be held as a data-member of the parent operation-state.</p>
<p>This means that customising the <code class="sourceCode default">get_stop_token()</code> call on the child receiver to forward to the parent receiver will often involve a pointer indirection.</p>
<p>If many sender-operations have been composed into a deep hierarchy then this can mean that each call to <code class="sourceCode default">get_stop_token()</code> at the leaf-level may end up needing to walk O(depth) pointer indirections before we get to the receiver that is able to provide the concrete stop-token object.</p>
<p>If a particular high-level operation ends up having a large number of leaf operations, each of which call <code class="sourceCode default">get_stop_token()</code> then this chain of receivers may end up needing to be walked many times, which could be a performance bottleneck for some applications.</p>
<p>This can be overcome, however, by introducing special context-caching adapters in the call-stack that can cache these values and store them closer (in terms of number of pointer indirections) to the usage of that context.</p>
<p>Such an adapter would, upon initialisation in <code class="sourceCode default">connect()</code>, obtain the context from the parent receiver and then store a cached copy of that context in either the receiver or operation-state of that node.</p>
<p>For example: A <code class="sourceCode default">context_caching_sender</code> adapter that caches the value in a receiver adapter.</p>
<div class="sourceCode" id="cb38"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb38-1"><a href="#cb38-1" aria-hidden="true" tabindex="-1"></a><span class="kw">template</span><span class="op">&lt;</span><span class="kw">typename</span> CPO, <span class="kw">typename</span> InnerSender<span class="op">&gt;</span></span>
<span id="cb38-2"><a href="#cb38-2" aria-hidden="true" tabindex="-1"></a><span class="kw">struct</span> context_caching_sender <span class="op">{</span></span>
<span id="cb38-3"><a href="#cb38-3" aria-hidden="true" tabindex="-1"></a>  InnerSender inner;</span>
<span id="cb38-4"><a href="#cb38-4" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb38-5"><a href="#cb38-5" aria-hidden="true" tabindex="-1"></a>  <span class="kw">template</span><span class="op">&lt;</span><span class="kw">typename</span> InnerReceiver<span class="op">&gt;</span></span>
<span id="cb38-6"><a href="#cb38-6" aria-hidden="true" tabindex="-1"></a>  <span class="kw">struct</span> receiver_wrapper <span class="op">{</span></span>
<span id="cb38-7"><a href="#cb38-7" aria-hidden="true" tabindex="-1"></a>    <span class="kw">using</span> value_type <span class="op">=</span> std<span class="op">::</span>invoke_result_t<span class="op">&lt;</span>CPO, <span class="kw">const</span> InnerReceiver<span class="op">&amp;&gt;</span>;</span>
<span id="cb38-8"><a href="#cb38-8" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb38-9"><a href="#cb38-9" aria-hidden="true" tabindex="-1"></a>    <span class="co">// Populates the cached value by invoking the CPO on the receiver.</span></span>
<span id="cb38-10"><a href="#cb38-10" aria-hidden="true" tabindex="-1"></a>    <span class="kw">explicit</span> receiver_wrapper<span class="op">(</span>InnerReceiver r<span class="op">)</span></span>
<span id="cb38-11"><a href="#cb38-11" aria-hidden="true" tabindex="-1"></a>    <span class="op">:</span> inner<span class="op">(</span>std<span class="op">::</span>move<span class="op">(</span>r<span class="op">))</span></span>
<span id="cb38-12"><a href="#cb38-12" aria-hidden="true" tabindex="-1"></a>    , cached_value<span class="op">(</span>CPO<span class="op">{}(</span>inner<span class="op">))</span></span>
<span id="cb38-13"><a href="#cb38-13" aria-hidden="true" tabindex="-1"></a>    <span class="op">{}</span></span>
<span id="cb38-14"><a href="#cb38-14" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb38-15"><a href="#cb38-15" aria-hidden="true" tabindex="-1"></a>    InnerReceiver inner;</span>
<span id="cb38-16"><a href="#cb38-16" aria-hidden="true" tabindex="-1"></a>    value_type cached_value;</span>
<span id="cb38-17"><a href="#cb38-17" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb38-18"><a href="#cb38-18" aria-hidden="true" tabindex="-1"></a>    <span class="kw">template</span><span class="op">&lt;</span><span class="kw">typename</span> OtherCPO, <span class="kw">typename</span> Self, <span class="kw">typename</span><span class="op">...</span> Args<span class="op">&gt;</span></span>
<span id="cb38-19"><a href="#cb38-19" aria-hidden="true" tabindex="-1"></a>      <span class="kw">requires</span></span>
<span id="cb38-20"><a href="#cb38-20" aria-hidden="true" tabindex="-1"></a>        std<span class="op">::</span>same_as<span class="op">&lt;</span>std<span class="op">::</span>remove_cvref_t<span class="op">&lt;</span>Self<span class="op">&gt;</span>, receiver_wrapper<span class="op">&gt;</span> <span class="op">&amp;&amp;</span></span>
<span id="cb38-21"><a href="#cb38-21" aria-hidden="true" tabindex="-1"></a>        std<span class="op">::</span>invocable<span class="op">&lt;</span>OtherCPO, member_t<span class="op">&lt;</span>Self, InnerReceiver<span class="op">&gt;</span>, Args<span class="op">...&gt;</span></span>
<span id="cb38-22"><a href="#cb38-22" aria-hidden="true" tabindex="-1"></a>    <span class="kw">friend</span> <span class="kw">auto</span> tag_invoke<span class="op">(</span>OtherCPO cpo, Self<span class="op">&amp;&amp;</span> self, Args<span class="op">&amp;&amp;...</span> args<span class="op">)</span> <span class="op">{</span></span>
<span id="cb38-23"><a href="#cb38-23" aria-hidden="true" tabindex="-1"></a>      <span class="cf">return</span> cpo<span class="op">(</span><span class="kw">static_cast</span><span class="op">&lt;</span>Self<span class="op">&amp;&amp;&gt;(</span>self<span class="op">).</span>inner, <span class="kw">static_cast</span><span class="op">&lt;</span>Args<span class="op">&amp;&amp;&gt;(</span>args<span class="op">)...)</span>;</span>
<span id="cb38-24"><a href="#cb38-24" aria-hidden="true" tabindex="-1"></a>    <span class="op">}</span></span>
<span id="cb38-25"><a href="#cb38-25" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb38-26"><a href="#cb38-26" aria-hidden="true" tabindex="-1"></a>    <span class="co">// Hook that CPO to return the cached value instead of forwarding on to</span></span>
<span id="cb38-27"><a href="#cb38-27" aria-hidden="true" tabindex="-1"></a>    <span class="co">// the wrapped receiver.</span></span>
<span id="cb38-28"><a href="#cb38-28" aria-hidden="true" tabindex="-1"></a>    <span class="kw">template</span><span class="op">&lt;</span><span class="kw">typename</span> Self<span class="op">&gt;</span></span>
<span id="cb38-29"><a href="#cb38-29" aria-hidden="true" tabindex="-1"></a>    <span class="kw">friend</span> <span class="kw">const</span> value_type<span class="op">&amp;</span> tag_invoke<span class="op">(</span>CPO cpo, <span class="kw">const</span> receiver_wrapper<span class="op">&amp;</span> r<span class="op">)</span> <span class="op">{</span></span>
<span id="cb38-30"><a href="#cb38-30" aria-hidden="true" tabindex="-1"></a>      <span class="cf">return</span> r<span class="op">.</span>cached_value;</span>
<span id="cb38-31"><a href="#cb38-31" aria-hidden="true" tabindex="-1"></a>    <span class="op">}</span></span>
<span id="cb38-32"><a href="#cb38-32" aria-hidden="true" tabindex="-1"></a>  <span class="op">}</span>;</span>
<span id="cb38-33"><a href="#cb38-33" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb38-34"><a href="#cb38-34" aria-hidden="true" tabindex="-1"></a>  <span class="kw">template</span><span class="op">&lt;</span><span class="kw">typename</span> Self, <span class="kw">typename</span> Receiver<span class="op">&gt;</span></span>
<span id="cb38-35"><a href="#cb38-35" aria-hidden="true" tabindex="-1"></a>    <span class="kw">requires</span> std<span class="op">::</span>same_as<span class="op">&lt;</span>std<span class="op">::</span>remove_cvref_t<span class="op">&lt;</span>Self<span class="op">&gt;</span>, context_caching_sender<span class="op">&gt;</span></span>
<span id="cb38-36"><a href="#cb38-36" aria-hidden="true" tabindex="-1"></a>  <span class="kw">friend</span> <span class="kw">auto</span> tag_invoke<span class="op">(</span>std<span class="op">::</span>tag_t<span class="op">&lt;</span>std<span class="op">::</span>execution<span class="op">::</span><span class="fu">connect</span><span class="op">&gt;</span>, Self<span class="op">&amp;&amp;</span> self, Receiver r<span class="op">)</span> <span class="op">{</span></span>
<span id="cb38-37"><a href="#cb38-37" aria-hidden="true" tabindex="-1"></a>    <span class="cf">return</span> std<span class="op">::</span>execution<span class="op">::</span><span class="fu">connect</span><span class="op">(</span></span>
<span id="cb38-38"><a href="#cb38-38" aria-hidden="true" tabindex="-1"></a>      <span class="kw">static_cast</span><span class="op">&lt;</span>Self<span class="op">&amp;&amp;&gt;(</span>self<span class="op">)</span>,</span>
<span id="cb38-39"><a href="#cb38-39" aria-hidden="true" tabindex="-1"></a>      receiver_wrapper<span class="op">&lt;</span>R<span class="op">&gt;{</span>std<span class="op">::</span>move<span class="op">(</span>r<span class="op">)})</span>;</span>
<span id="cb38-40"><a href="#cb38-40" aria-hidden="true" tabindex="-1"></a>  <span class="op">}</span></span>
<span id="cb38-41"><a href="#cb38-41" aria-hidden="true" tabindex="-1"></a><span class="op">}</span>;</span></code></pre></div>
<p>Applying this adapter at key points within your application should allow you to address any O(depth)-related performance problems that arise.</p>
<h2 data-number="5.11" id="limitations-of-sender_traitssends_done"><span class="header-section-number">5.11</span> Limitations of sender_traits::sends_done<a href="#limitations-of-sender_traitssends_done" class="self-link"></a></h2>
<p>The <code class="sourceCode default">sender_traits</code> facility proposed in P0443R14 allows you to query what signals a give sender might complete with.</p>
<p>For example, the <code class="sourceCode default">sender_traits::error_types</code> type-alias lets you query what overloads of <code class="sourceCode default">set_error()</code> might be invoked on the receiver connected to it, and similarly, the <code class="sourceCode default">value_types</code> type-alias lets you query what overloads of <code class="sourceCode default">set_value()</code> might be invoked.</p>
<p>There is also the <code class="sourceCode default">sender_traits::sends_done</code> boolean static member that indicates whether or not the operation might complete with <code class="sourceCode default">set_done()</code>.</p>
<p>These queries can be used to pre-reserve storage for results, generate vtables, avoid template instantiations and apply various other optimisations. They are the equivalent to the <code class="sourceCode default">noexcept</code> and <code class="sourceCode default">decltype</code> expressions for regular functions, but in the async domain.</p>
<p>For many senders, whether or not the operation will complete with <code class="sourceCode default">set_done()</code> depends entirely on whether or not the receiver that it is connected to returns a <code class="sourceCode default">std::unstoppable_token</code> from its <code class="sourceCode default">get_stop_token()</code> customisation or not. If you never cancel the operation then it never completes with <code class="sourceCode default">set_done()</code>.</p>
<p>However, when querying information about the sender we do not yet know what receiver type it will be connected to and so the calculation of <code class="sourceCode default">sends_done</code> needs to assume that it <em>might</em> be connected to a receiver whose stop token is able to deliver a stop-request.</p>
<p>As senders need to conservatively report that they might complete with <code class="sourceCode default">set_done</code> this can lead to some missed optimisation opportunities for algorithms that might otherwise be able to</p>
<p>There are other similar limitations with respect to the <code class="sourceCode default">value_types</code> and <code class="sourceCode default">error_types</code> members of <code class="sourceCode default">sender_traits</code>.</p>
<p>The list of error-types that a sender produces may be dependent on the receiver in that whether or not it completes with a <code class="sourceCode default">std::exception_ptr</code> may be dependent on whether or not the <code class="sourceCode default">set_value()</code> overloads on the receiver that receive the result of the operation declared <code class="sourceCode default">noexcept</code> or not.</p>
<p>The value result-type of a sender might depend on some type-information obtained from the receiver it is connected to. For example, the sender might call a <code class="sourceCode default">get_allocator()</code> customisation point on the receiver to obtain an allocator of type, <code class="sourceCode default">A</code>, and then produce a result of type <code class="sourceCode default">std::vector&lt;int, A&gt;</code> constructed using that allocator.</p>
<p>One possible avenue for investigation here is to defer calculating the completion signals of a sender until we know what concrete receiver type is going to be connected to it. For example, we could replace <code class="sourceCode default">sender_traits&lt;S&gt;</code> with an <code class="sourceCode default">operation_traits&lt;S, R&gt;</code> type that allowed computing the result-type with full knowledge of the receiver type.</p>
<h2 data-number="5.12" id="cancellation-support-for-scheduler-implementations"><span class="header-section-number">5.12</span> Cancellation support for <code class="sourceCode default">scheduler</code> implementations<a href="#cancellation-support-for-scheduler-implementations" class="self-link"></a></h2>
<p>The <code class="sourceCode default">executor</code> concept proposed by P0443R14 and earlier revisions describe an interface for scheduling work onto a given execution context by calling the <code class="sourceCode default">std::execution::execute()</code> customisation point and passing the executor and an invocable object.</p>
<p>However, once a call to <code class="sourceCode default">execute()</code> is made, the work is enqueued and there is no standard way for the caller to be able to cancel this work to remove it from the queue if the caller later determines that this work no longer needs to be performed.</p>
<p>The <code class="sourceCode default">scheduler</code> concept proposed by P0443R14 also describes the ability to schedule work onto its associated execution context, but does so using the same sender/receiver concepts used for other async operations.</p>
<p>This means that the <code class="sourceCode default">schedule()</code> operation produced by a scheduler can make use of the same mechanisms used for other senders to support cancellation of the operation. i.e. by connecting the sender returned from <code class="sourceCode default">schedule()</code> to a receiver that has customised the <code class="sourceCode default">get_stop_token()</code> customisation-point.</p>
<h2 data-number="5.13" id="impacts-to-other-proposals"><span class="header-section-number">5.13</span> Impacts to other proposals<a href="#impacts-to-other-proposals" class="self-link"></a></h2>
<p>This paper has impacts on or is related to a number of other proposals.</p>
<p>This paper is primarily proposing an extension to the sender/receiver model proposed in P0443R14 - “A Unified Executors Proposal for C++” to add a standard, composable mechanism for cancellation of in-flight sender-based asynchronous operations.</p>
<p>While this paper could be applied as-is on top of P0443, the <code class="sourceCode default">get_stop_token()</code> customisation point is currently specified in terms of <code class="sourceCode default">tag_invoke()</code> facilities proposed in P1895R0 “tag_invoke: A general mechanism for supporting customisable functions” and so merging the two would also introduce a dependency on P1895.</p>
<p>The dependency on <code class="sourceCode default">tag_invoke()</code> should also be considered in conjunction with the papers P2221R0 “define P0443 cpos with tag_invoke” and P2220R0 “redefine properties in P0443,” which also propose adoption of <code class="sourceCode default">tag_invoke()</code> facilites within the executors proposal.</p>
<p>Some of the code examples in this paper assume that changes proposed in P2221R0 have been applied.</p>
<p>This paper highlights some limitations with the current design proposed for the <code class="sourceCode default">std::lazy</code>/<code class="sourceCode default">std::task</code> type in P1056R1 with regards to the inability for these coroutine types to be able to be composed using generic algorithms in a way that allows them to participate in cancellation.</p>
<p>It is recommended that P1056 be updated to incorporate support for composable cancellation as proposed in this paper, although there are also other general changes required to support integration with sender/receiver.</p>
<p>The paper P1897 “Towards C++23 executors: A proposal for an initial set of algorithms” includes a number of sender-based algorithms. We should ensure that these algorithms are specified in a way that makes them either transparent to cancellation (e.g. <code class="sourceCode default">transform()</code>) or explicitly specify their cancellation behaviour if they introduce new cancellation scopes (e.g. <code class="sourceCode default">when_all()</code>) - In general, any algorithm that introduces concurrency should be evaluated for whether or not it should be introducing a new cancellation scope.</p>
<h2 data-number="5.14" id="future-work"><span class="header-section-number">5.14</span> Future work<a href="#future-work" class="self-link"></a></h2>
<p>There are also some areas for futher investigation related to this paper.</p>
<ul>
<li>Investigate cancellability of <code class="sourceCode default">execution::execute()</code> operations.</li>
<li>Extend <code class="sourceCode default">std::condition_variable_any::wait()</code> functions with support for arbitrary <code class="sourceCode default">std::stoppable_token</code> types rather than just <code class="sourceCode default">std::stop_token</code>.</li>
<li>Add some basic cancellation-related algorithms:
<ul>
<li><code class="sourceCode default">stop_when(src, trigger)</code> - issues a stop-request to <code class="sourceCode default">src</code> when <code class="sourceCode default">trigger</code> completes</li>
<li><code class="sourceCode default">first_successful(inputs...)</code> - completes with first successful (ie. ‘value’) result, otherwise last result</li>
<li><code class="sourceCode default">timeout(src, scheduler, duration)</code> -</li>
</ul></li>
</ul>
<h1 data-number="6" id="implementation-experience"><span class="header-section-number">6</span> Implementation Experience<a href="#implementation-experience" class="self-link"></a></h1>
<p>The general model of having a CancellationToken implicitly propagated to child coroutines has been implemented in <code class="sourceCode default">folly::coro</code>, Facebook’s library of coroutine abstractions, which has been used extensively in production.</p>
<p>The specific model for cancellation described in this paper, which integrates with the sender/receiver model for async computation proposed in P0443R14, has been implemented in Facebook’s libunifex opensource library.</p>
<p>A prototype implementation of a coroutine task-type that implicitly propagates a stop-token object to child coroutines and child senders has also been implemeted as part of the libunifex library as <code class="sourceCode default">unifex::task&lt;T&gt;</code>. However, this coroutine type is not being proposed by this paper.</p>
<h1 data-number="7" id="wording"><span class="header-section-number">7</span> Wording<a href="#wording" class="self-link"></a></h1>
<p>Will be provided in a future revision of this paper.</p>
<h1 data-number="8" id="appendices"><span class="header-section-number">8</span> Appendices<a href="#appendices" class="self-link"></a></h1>
<h2 data-number="8.1" id="appendix-a-the-stop_when-algorithm"><span class="header-section-number">8.1</span> Appendix A: The <code class="sourceCode default">stop_when()</code> algorithm<a href="#appendix-a-the-stop_when-algorithm" class="self-link"></a></h2>
<p>This example shows how to implement libunifex’s <code class="sourceCode default">stop_when()</code> algorithm which introduces a new cancellation-scope - where child operations can be cancelled independently of whether the parent operation was cancelled or not.</p>
<div class="sourceCode" id="cb39"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb39-1"><a href="#cb39-1" aria-hidden="true" tabindex="-1"></a><span class="pp">#include </span><span class="im">&lt;stop_token&gt;</span></span>
<span id="cb39-2"><a href="#cb39-2" aria-hidden="true" tabindex="-1"></a><span class="pp">#include </span><span class="im">&lt;execution&gt;</span></span>
<span id="cb39-3"><a href="#cb39-3" aria-hidden="true" tabindex="-1"></a><span class="pp">#include </span><span class="im">&lt;atomic&gt;</span></span>
<span id="cb39-4"><a href="#cb39-4" aria-hidden="true" tabindex="-1"></a><span class="pp">#include </span><span class="im">&lt;optional&gt;</span></span>
<span id="cb39-5"><a href="#cb39-5" aria-hidden="true" tabindex="-1"></a><span class="pp">#include </span><span class="im">&lt;variant&gt;</span></span>
<span id="cb39-6"><a href="#cb39-6" aria-hidden="true" tabindex="-1"></a><span class="pp">#include </span><span class="im">&lt;tuple&gt;</span></span>
<span id="cb39-7"><a href="#cb39-7" aria-hidden="true" tabindex="-1"></a><span class="pp">#include </span><span class="im">&lt;type_traits&gt;</span></span>
<span id="cb39-8"><a href="#cb39-8" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb39-9"><a href="#cb39-9" aria-hidden="true" tabindex="-1"></a><span class="kw">namespace</span> _stop_when <span class="op">{</span></span>
<span id="cb39-10"><a href="#cb39-10" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb39-11"><a href="#cb39-11" aria-hidden="true" tabindex="-1"></a><span class="kw">struct</span> forward_stop_request <span class="op">{</span></span>
<span id="cb39-12"><a href="#cb39-12" aria-hidden="true" tabindex="-1"></a>  std<span class="op">::</span>in_place_stop_source<span class="op">&amp;</span> stopSource;</span>
<span id="cb39-13"><a href="#cb39-13" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb39-14"><a href="#cb39-14" aria-hidden="true" tabindex="-1"></a>  <span class="dt">void</span> <span class="kw">operator</span><span class="op">()()</span> <span class="kw">noexcept</span> <span class="op">{</span></span>
<span id="cb39-15"><a href="#cb39-15" aria-hidden="true" tabindex="-1"></a>    stopSource<span class="op">.</span>request_stop<span class="op">()</span>;</span>
<span id="cb39-16"><a href="#cb39-16" aria-hidden="true" tabindex="-1"></a>  <span class="op">}</span></span>
<span id="cb39-17"><a href="#cb39-17" aria-hidden="true" tabindex="-1"></a><span class="op">}</span>;</span>
<span id="cb39-18"><a href="#cb39-18" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb39-19"><a href="#cb39-19" aria-hidden="true" tabindex="-1"></a><span class="kw">template</span><span class="op">&lt;</span><span class="kw">typename</span> Source, <span class="kw">typename</span> Trigger, <span class="kw">typename</span> Receiver<span class="op">&gt;</span></span>
<span id="cb39-20"><a href="#cb39-20" aria-hidden="true" tabindex="-1"></a><span class="kw">struct</span> _op;</span>
<span id="cb39-21"><a href="#cb39-21" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb39-22"><a href="#cb39-22" aria-hidden="true" tabindex="-1"></a><span class="kw">template</span><span class="op">&lt;</span><span class="kw">typename</span> Source, <span class="kw">typename</span> Trigger, <span class="kw">typename</span> Receiver<span class="op">&gt;</span></span>
<span id="cb39-23"><a href="#cb39-23" aria-hidden="true" tabindex="-1"></a><span class="kw">struct</span> _source_receiver <span class="op">{</span></span>
<span id="cb39-24"><a href="#cb39-24" aria-hidden="true" tabindex="-1"></a>  <span class="kw">using</span> op_t <span class="op">=</span> _op<span class="op">&lt;</span>Source, Trigger, Receiver<span class="op">&gt;</span>;</span>
<span id="cb39-25"><a href="#cb39-25" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb39-26"><a href="#cb39-26" aria-hidden="true" tabindex="-1"></a>  <span class="kw">template</span><span class="op">&lt;</span><span class="kw">typename</span><span class="op">...</span> Values<span class="op">&gt;</span></span>
<span id="cb39-27"><a href="#cb39-27" aria-hidden="true" tabindex="-1"></a>  <span class="dt">void</span> set_value<span class="op">(</span>Values<span class="op">&amp;&amp;...</span> values<span class="op">)</span> <span class="op">&amp;&amp;</span></span>
<span id="cb39-28"><a href="#cb39-28" aria-hidden="true" tabindex="-1"></a>      <span class="kw">noexcept</span><span class="op">(</span>std<span class="op">::</span>is_nothrow_constructible_v<span class="op">&lt;</span>std<span class="op">::</span>decay_t<span class="op">&lt;</span>Values<span class="op">&gt;</span>, Values<span class="op">&gt;</span> <span class="op">&amp;&amp;</span> <span class="op">...)</span> <span class="op">{</span></span>
<span id="cb39-29"><a href="#cb39-29" aria-hidden="true" tabindex="-1"></a>    op<span class="op">-&gt;</span>result<span class="op">.</span><span class="kw">template</span> emplace<span class="op">&lt;</span>std<span class="op">::</span>tuple<span class="op">&lt;</span></span>
<span id="cb39-30"><a href="#cb39-30" aria-hidden="true" tabindex="-1"></a>        std<span class="op">::</span>tag_t<span class="op">&lt;</span>std<span class="op">::</span>execution<span class="op">::</span>set_value<span class="op">&gt;</span>,</span>
<span id="cb39-31"><a href="#cb39-31" aria-hidden="true" tabindex="-1"></a>        std<span class="op">::</span>decay_t<span class="op">&lt;</span>Values<span class="op">&gt;...&gt;&gt;(</span></span>
<span id="cb39-32"><a href="#cb39-32" aria-hidden="true" tabindex="-1"></a>      std<span class="op">::</span>execution<span class="op">::</span>set_value, <span class="op">(</span>Values<span class="op">&amp;&amp;)</span>values<span class="op">...)</span>;</span>
<span id="cb39-33"><a href="#cb39-33" aria-hidden="true" tabindex="-1"></a>    op<span class="op">-&gt;</span>notify_child_complete<span class="op">()</span>;</span>
<span id="cb39-34"><a href="#cb39-34" aria-hidden="true" tabindex="-1"></a>  <span class="op">}</span></span>
<span id="cb39-35"><a href="#cb39-35" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb39-36"><a href="#cb39-36" aria-hidden="true" tabindex="-1"></a>  <span class="kw">template</span><span class="op">&lt;</span><span class="kw">typename</span> Error<span class="op">&gt;</span></span>
<span id="cb39-37"><a href="#cb39-37" aria-hidden="true" tabindex="-1"></a>  <span class="dt">void</span> set_error<span class="op">(</span>Error<span class="op">&amp;&amp;</span> error<span class="op">)</span> <span class="op">&amp;&amp;</span> <span class="kw">noexcept</span> <span class="op">{</span></span>
<span id="cb39-38"><a href="#cb39-38" aria-hidden="true" tabindex="-1"></a>    op<span class="op">-&gt;</span>result<span class="op">.</span><span class="kw">template</span> emplace<span class="op">&lt;</span>std<span class="op">::</span>tuple<span class="op">&lt;</span></span>
<span id="cb39-39"><a href="#cb39-39" aria-hidden="true" tabindex="-1"></a>        std<span class="op">::</span>tag_t<span class="op">&lt;</span>std<span class="op">::</span>execution<span class="op">::</span>set_error<span class="op">&gt;</span>, std<span class="op">::</span>decay_t<span class="op">&lt;</span>Error<span class="op">&gt;&gt;&gt;(</span></span>
<span id="cb39-40"><a href="#cb39-40" aria-hidden="true" tabindex="-1"></a>      std<span class="op">::</span>execution<span class="op">::</span>set_error, <span class="op">(</span>Error<span class="op">&amp;&amp;)</span>error<span class="op">)</span>;</span>
<span id="cb39-41"><a href="#cb39-41" aria-hidden="true" tabindex="-1"></a>    op<span class="op">-&gt;</span>notify_child_complete<span class="op">()</span>;</span>
<span id="cb39-42"><a href="#cb39-42" aria-hidden="true" tabindex="-1"></a>  <span class="op">}</span></span>
<span id="cb39-43"><a href="#cb39-43" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb39-44"><a href="#cb39-44" aria-hidden="true" tabindex="-1"></a>  <span class="dt">void</span> set_done<span class="op">()</span> <span class="op">&amp;&amp;</span> <span class="kw">noexcept</span> <span class="op">{</span></span>
<span id="cb39-45"><a href="#cb39-45" aria-hidden="true" tabindex="-1"></a>    op<span class="op">-&gt;</span>result<span class="op">.</span><span class="kw">template</span> emplace<span class="op">&lt;</span>std<span class="op">::</span>tuple<span class="op">&lt;</span>std<span class="op">::</span>tag_t<span class="op">&lt;</span>std<span class="op">::</span>execution<span class="op">::</span>set_done<span class="op">&gt;&gt;&gt;(</span></span>
<span id="cb39-46"><a href="#cb39-46" aria-hidden="true" tabindex="-1"></a>      std<span class="op">::</span>execution<span class="op">::</span>set_done<span class="op">)</span>;</span>
<span id="cb39-47"><a href="#cb39-47" aria-hidden="true" tabindex="-1"></a>    op<span class="op">-&gt;</span>notify_child_complete<span class="op">()</span>;</span>
<span id="cb39-48"><a href="#cb39-48" aria-hidden="true" tabindex="-1"></a>  <span class="op">}</span></span>
<span id="cb39-49"><a href="#cb39-49" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb39-50"><a href="#cb39-50" aria-hidden="true" tabindex="-1"></a>  op_t<span class="op">*</span> op;</span>
<span id="cb39-51"><a href="#cb39-51" aria-hidden="true" tabindex="-1"></a><span class="op">}</span>;</span>
<span id="cb39-52"><a href="#cb39-52" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb39-53"><a href="#cb39-53" aria-hidden="true" tabindex="-1"></a><span class="kw">template</span><span class="op">&lt;</span><span class="kw">typename</span> Source, <span class="kw">typename</span> Trigger, <span class="kw">typename</span> Receiver<span class="op">&gt;</span></span>
<span id="cb39-54"><a href="#cb39-54" aria-hidden="true" tabindex="-1"></a><span class="kw">struct</span> _trigger_receiver <span class="op">{</span></span>
<span id="cb39-55"><a href="#cb39-55" aria-hidden="true" tabindex="-1"></a>  <span class="kw">using</span> op_t <span class="op">=</span> _op<span class="op">&lt;</span>Source, Trigger, Receiver<span class="op">&gt;</span>;</span>
<span id="cb39-56"><a href="#cb39-56" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb39-57"><a href="#cb39-57" aria-hidden="true" tabindex="-1"></a>  <span class="kw">template</span><span class="op">&lt;</span><span class="kw">typename</span><span class="op">...</span> Values<span class="op">&gt;</span></span>
<span id="cb39-58"><a href="#cb39-58" aria-hidden="true" tabindex="-1"></a>  <span class="dt">void</span> set_value<span class="op">(</span>Values<span class="op">&amp;&amp;...)</span> <span class="op">&amp;&amp;</span> <span class="kw">noexcept</span> <span class="op">{</span></span>
<span id="cb39-59"><a href="#cb39-59" aria-hidden="true" tabindex="-1"></a>    op<span class="op">-&gt;</span>notify_child_complete<span class="op">()</span>;</span>
<span id="cb39-60"><a href="#cb39-60" aria-hidden="true" tabindex="-1"></a>  <span class="op">}</span></span>
<span id="cb39-61"><a href="#cb39-61" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb39-62"><a href="#cb39-62" aria-hidden="true" tabindex="-1"></a>  <span class="kw">template</span><span class="op">&lt;</span><span class="kw">typename</span> Error<span class="op">&gt;</span></span>
<span id="cb39-63"><a href="#cb39-63" aria-hidden="true" tabindex="-1"></a>  <span class="dt">void</span> set_error<span class="op">(</span>Error<span class="op">&amp;&amp;)</span> <span class="op">&amp;&amp;</span> <span class="kw">noexcept</span> <span class="op">{</span></span>
<span id="cb39-64"><a href="#cb39-64" aria-hidden="true" tabindex="-1"></a>    op<span class="op">-&gt;</span>notify_child_complete<span class="op">()</span>;</span>
<span id="cb39-65"><a href="#cb39-65" aria-hidden="true" tabindex="-1"></a>  <span class="op">}</span></span>
<span id="cb39-66"><a href="#cb39-66" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb39-67"><a href="#cb39-67" aria-hidden="true" tabindex="-1"></a>  <span class="dt">void</span> set_done<span class="op">()</span> <span class="op">&amp;&amp;</span> <span class="kw">noexcept</span> <span class="op">{</span></span>
<span id="cb39-68"><a href="#cb39-68" aria-hidden="true" tabindex="-1"></a>    op<span class="op">-&gt;</span>notify_child_complete<span class="op">()</span>;</span>
<span id="cb39-69"><a href="#cb39-69" aria-hidden="true" tabindex="-1"></a>  <span class="op">}</span></span>
<span id="cb39-70"><a href="#cb39-70" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb39-71"><a href="#cb39-71" aria-hidden="true" tabindex="-1"></a>  <span class="kw">friend</span> std<span class="op">::</span>in_place_stop_token tag_invoke<span class="op">(</span></span>
<span id="cb39-72"><a href="#cb39-72" aria-hidden="true" tabindex="-1"></a>      std<span class="op">::</span>tag_t<span class="op">&lt;</span>std<span class="op">::</span>execution<span class="op">::</span>get_stop_token<span class="op">&gt;</span>,</span>
<span id="cb39-73"><a href="#cb39-73" aria-hidden="true" tabindex="-1"></a>      <span class="kw">const</span> _trigger_receiver<span class="op">&amp;</span> self<span class="op">)</span> <span class="kw">noexcept</span> <span class="op">{</span></span>
<span id="cb39-74"><a href="#cb39-74" aria-hidden="true" tabindex="-1"></a>    <span class="cf">return</span> self<span class="op">.</span>op<span class="op">-&gt;</span>stopSource<span class="op">.</span>get_token<span class="op">()</span>;</span>
<span id="cb39-75"><a href="#cb39-75" aria-hidden="true" tabindex="-1"></a>  <span class="op">}</span></span>
<span id="cb39-76"><a href="#cb39-76" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb39-77"><a href="#cb39-77" aria-hidden="true" tabindex="-1"></a>  op_t<span class="op">*</span> op;</span>
<span id="cb39-78"><a href="#cb39-78" aria-hidden="true" tabindex="-1"></a><span class="op">}</span>;</span>
<span id="cb39-79"><a href="#cb39-79" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb39-80"><a href="#cb39-80" aria-hidden="true" tabindex="-1"></a><span class="kw">template</span><span class="op">&lt;</span><span class="kw">typename</span><span class="op">...</span> Values<span class="op">&gt;</span></span>
<span id="cb39-81"><a href="#cb39-81" aria-hidden="true" tabindex="-1"></a><span class="kw">using</span> value_result_tuple_t <span class="op">=</span></span>
<span id="cb39-82"><a href="#cb39-82" aria-hidden="true" tabindex="-1"></a>  std<span class="op">::</span>tuple<span class="op">&lt;</span>std<span class="op">::</span>tag_t<span class="op">&lt;</span>std<span class="op">::</span>execution<span class="op">::</span>set_value<span class="op">&gt;</span>, std<span class="op">::</span>decay_t<span class="op">&lt;</span>Values<span class="op">&gt;...&gt;</span>;</span>
<span id="cb39-83"><a href="#cb39-83" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb39-84"><a href="#cb39-84" aria-hidden="true" tabindex="-1"></a><span class="kw">template</span><span class="op">&lt;</span><span class="kw">typename</span> Source, <span class="kw">typename</span> Trigger, <span class="kw">typename</span> Receiver<span class="op">&gt;</span></span>
<span id="cb39-85"><a href="#cb39-85" aria-hidden="true" tabindex="-1"></a><span class="kw">struct</span> _op <span class="op">{</span></span>
<span id="cb39-86"><a href="#cb39-86" aria-hidden="true" tabindex="-1"></a>  <span class="kw">using</span> source_receiver_t <span class="op">=</span> _source_receiver<span class="op">&lt;</span>Source, Trigger, Receiver<span class="op">&gt;</span>;</span>
<span id="cb39-87"><a href="#cb39-87" aria-hidden="true" tabindex="-1"></a>  <span class="kw">using</span> trigger_receiver_t <span class="op">=</span> _trigger_receiver<span class="op">&lt;</span>Source, Trigger, Receiver<span class="op">&gt;</span>;</span>
<span id="cb39-88"><a href="#cb39-88" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb39-89"><a href="#cb39-89" aria-hidden="true" tabindex="-1"></a>  <span class="kw">template</span><span class="op">&lt;</span><span class="kw">typename</span> Receiver2<span class="op">&gt;</span></span>
<span id="cb39-90"><a href="#cb39-90" aria-hidden="true" tabindex="-1"></a>  <span class="kw">explicit</span> _op<span class="op">(</span>Source<span class="op">&amp;&amp;</span> source, Trigger<span class="op">&amp;&amp;</span> trigger, Receiver2<span class="op">&amp;&amp;</span> receiver<span class="op">)</span></span>
<span id="cb39-91"><a href="#cb39-91" aria-hidden="true" tabindex="-1"></a>  <span class="op">:</span> receiver<span class="op">(</span><span class="kw">static_cast</span><span class="op">&lt;</span>Receiver2<span class="op">&amp;&amp;&gt;(</span>receiver<span class="op">))</span></span>
<span id="cb39-92"><a href="#cb39-92" aria-hidden="true" tabindex="-1"></a>  , sourceOp<span class="op">(</span>std<span class="op">::</span>execution<span class="op">::</span><span class="fu">connect</span><span class="op">((</span>Source<span class="op">&amp;&amp;)</span>source, source_receiver_t<span class="op">{</span><span class="kw">this</span><span class="op">}))</span></span>
<span id="cb39-93"><a href="#cb39-93" aria-hidden="true" tabindex="-1"></a>  , triggerOp<span class="op">(</span>std<span class="op">::</span>execution<span class="op">::</span><span class="fu">connect</span><span class="op">((</span>Trigger<span class="op">&amp;&amp;)</span>trigger, trigger_receiver_t<span class="op">{</span><span class="kw">this</span><span class="op">}))</span></span>
<span id="cb39-94"><a href="#cb39-94" aria-hidden="true" tabindex="-1"></a>  <span class="op">{}</span></span>
<span id="cb39-95"><a href="#cb39-95" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb39-96"><a href="#cb39-96" aria-hidden="true" tabindex="-1"></a>  <span class="dt">void</span> start<span class="op">()</span> <span class="op">&amp;&amp;</span> <span class="kw">noexcept</span> <span class="op">{</span></span>
<span id="cb39-97"><a href="#cb39-97" aria-hidden="true" tabindex="-1"></a>    <span class="co">// Subscribe to stop-requests from the parent.</span></span>
<span id="cb39-98"><a href="#cb39-98" aria-hidden="true" tabindex="-1"></a>    stopCallback<span class="op">.</span>emplace<span class="op">(</span></span>
<span id="cb39-99"><a href="#cb39-99" aria-hidden="true" tabindex="-1"></a>      std<span class="op">::</span>execution<span class="op">::</span>get_stop_token<span class="op">(</span>receiver<span class="op">)</span>,</span>
<span id="cb39-100"><a href="#cb39-100" aria-hidden="true" tabindex="-1"></a>      forward_stop_request<span class="op">{</span>stopSource<span class="op">})</span>;</span>
<span id="cb39-101"><a href="#cb39-101" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb39-102"><a href="#cb39-102" aria-hidden="true" tabindex="-1"></a>    <span class="co">// And start child operations.</span></span>
<span id="cb39-103"><a href="#cb39-103" aria-hidden="true" tabindex="-1"></a>    std<span class="op">::</span>execution<span class="op">::</span>start<span class="op">(</span>triggerOp<span class="op">)</span>;</span>
<span id="cb39-104"><a href="#cb39-104" aria-hidden="true" tabindex="-1"></a>    std<span class="op">::</span>execution<span class="op">::</span>start<span class="op">(</span>sourceOp<span class="op">)</span>;</span>
<span id="cb39-105"><a href="#cb39-105" aria-hidden="true" tabindex="-1"></a>  <span class="op">}</span></span>
<span id="cb39-106"><a href="#cb39-106" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb39-107"><a href="#cb39-107" aria-hidden="true" tabindex="-1"></a>  <span class="dt">void</span> notify_child_complete<span class="op">()</span> <span class="kw">noexcept</span> <span class="op">{</span></span>
<span id="cb39-108"><a href="#cb39-108" aria-hidden="true" tabindex="-1"></a>    stopSource<span class="op">.</span>request_stop<span class="op">()</span>;</span>
<span id="cb39-109"><a href="#cb39-109" aria-hidden="true" tabindex="-1"></a>    <span class="cf">if</span> <span class="op">(</span>remaining<span class="op">.</span>fetch_sub<span class="op">(</span><span class="dv">1</span>, std<span class="op">::</span>memory_order_acq_rel<span class="op">)</span> <span class="op">==</span> <span class="dv">1</span><span class="op">)</span> <span class="op">{</span></span>
<span id="cb39-110"><a href="#cb39-110" aria-hidden="true" tabindex="-1"></a>      stopCallback<span class="op">.</span>reset<span class="op">()</span>;</span>
<span id="cb39-111"><a href="#cb39-111" aria-hidden="true" tabindex="-1"></a>      deliver_result<span class="op">()</span>;</span>
<span id="cb39-112"><a href="#cb39-112" aria-hidden="true" tabindex="-1"></a>    <span class="op">}</span></span>
<span id="cb39-113"><a href="#cb39-113" aria-hidden="true" tabindex="-1"></a>  <span class="op">}</span></span>
<span id="cb39-114"><a href="#cb39-114" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb39-115"><a href="#cb39-115" aria-hidden="true" tabindex="-1"></a>  <span class="dt">void</span> deliver_result<span class="op">()</span> <span class="kw">noexcept</span> <span class="op">{</span></span>
<span id="cb39-116"><a href="#cb39-116" aria-hidden="true" tabindex="-1"></a>    <span class="cf">try</span> <span class="op">{</span></span>
<span id="cb39-117"><a href="#cb39-117" aria-hidden="true" tabindex="-1"></a>      std<span class="op">::</span>visit<span class="op">([&amp;](</span><span class="kw">auto</span><span class="op">&amp;&amp;</span> resultTuple<span class="op">)</span> <span class="op">{</span></span>
<span id="cb39-118"><a href="#cb39-118" aria-hidden="true" tabindex="-1"></a>        <span class="kw">constexpr</span> <span class="dt">size_t</span> tupleSize <span class="op">=</span></span>
<span id="cb39-119"><a href="#cb39-119" aria-hidden="true" tabindex="-1"></a>          std<span class="op">::</span>tuple_size_v<span class="op">&lt;</span>std<span class="op">::</span>remove_reference_t<span class="op">&lt;</span><span class="kw">decltype</span><span class="op">(</span>resultTuple<span class="op">)&gt;&gt;</span>;</span>
<span id="cb39-120"><a href="#cb39-120" aria-hidden="true" tabindex="-1"></a>        <span class="cf">if</span> <span class="kw">constexpr</span> <span class="op">(</span>tupleSize <span class="op">&gt;</span> <span class="dv">0</span><span class="op">)</span> <span class="op">{</span></span>
<span id="cb39-121"><a href="#cb39-121" aria-hidden="true" tabindex="-1"></a>          std<span class="op">::</span>apply<span class="op">(</span>resultTuple, <span class="op">[&amp;](</span><span class="kw">auto</span> completionFn, <span class="kw">auto</span><span class="op">&amp;&amp;...</span> args<span class="op">)</span> <span class="op">{</span></span>
<span id="cb39-122"><a href="#cb39-122" aria-hidden="true" tabindex="-1"></a>            completionFn<span class="op">(</span>std<span class="op">::</span>move<span class="op">(</span>receiver<span class="op">)</span>, <span class="kw">static_cast</span><span class="op">&lt;</span><span class="kw">decltype</span><span class="op">(</span>args<span class="op">)&gt;(</span>args<span class="op">)...)</span>;</span>
<span id="cb39-123"><a href="#cb39-123" aria-hidden="true" tabindex="-1"></a>          <span class="op">})</span>;</span>
<span id="cb39-124"><a href="#cb39-124" aria-hidden="true" tabindex="-1"></a>        <span class="op">}</span> <span class="cf">else</span> <span class="op">{</span></span>
<span id="cb39-125"><a href="#cb39-125" aria-hidden="true" tabindex="-1"></a>          <span class="co">// Result not initialised (should be unreachable)</span></span>
<span id="cb39-126"><a href="#cb39-126" aria-hidden="true" tabindex="-1"></a>          std<span class="op">::</span>terminate<span class="op">()</span>;</span>
<span id="cb39-127"><a href="#cb39-127" aria-hidden="true" tabindex="-1"></a>        <span class="op">}</span></span>
<span id="cb39-128"><a href="#cb39-128" aria-hidden="true" tabindex="-1"></a>      <span class="op">}</span>, std<span class="op">::</span>move<span class="op">(</span>result<span class="op">))</span>;</span>
<span id="cb39-129"><a href="#cb39-129" aria-hidden="true" tabindex="-1"></a>    <span class="op">}</span> <span class="cf">catch</span> <span class="op">(...)</span> <span class="op">{</span></span>
<span id="cb39-130"><a href="#cb39-130" aria-hidden="true" tabindex="-1"></a>      std<span class="op">::</span>execution<span class="op">::</span>set_error<span class="op">(</span>std<span class="op">::</span>move<span class="op">(</span>receiver<span class="op">)</span>, std<span class="op">::</span>current_exception<span class="op">())</span>;</span>
<span id="cb39-131"><a href="#cb39-131" aria-hidden="true" tabindex="-1"></a>    <span class="op">}</span></span>
<span id="cb39-132"><a href="#cb39-132" aria-hidden="true" tabindex="-1"></a>  <span class="op">}</span></span>
<span id="cb39-133"><a href="#cb39-133" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb39-134"><a href="#cb39-134" aria-hidden="true" tabindex="-1"></a>  <span class="kw">template</span><span class="op">&lt;</span><span class="kw">typename</span><span class="op">...</span> Errors<span class="op">&gt;</span></span>
<span id="cb39-135"><a href="#cb39-135" aria-hidden="true" tabindex="-1"></a>  <span class="kw">struct</span> error_result <span class="op">{</span></span>
<span id="cb39-136"><a href="#cb39-136" aria-hidden="true" tabindex="-1"></a>    <span class="kw">template</span><span class="op">&lt;</span><span class="kw">typename</span><span class="op">...</span> ValueTuples<span class="op">&gt;</span></span>
<span id="cb39-137"><a href="#cb39-137" aria-hidden="true" tabindex="-1"></a>    <span class="kw">using</span> apply <span class="op">=</span> std<span class="op">::</span>variant<span class="op">&lt;</span></span>
<span id="cb39-138"><a href="#cb39-138" aria-hidden="true" tabindex="-1"></a>      std<span class="op">::</span>tuple<span class="op">&lt;&gt;</span>,</span>
<span id="cb39-139"><a href="#cb39-139" aria-hidden="true" tabindex="-1"></a>      std<span class="op">::</span>tuple<span class="op">&lt;</span>std<span class="op">::</span>tag_t<span class="op">&lt;</span>std<span class="op">::</span>execution<span class="op">::</span>set_done<span class="op">&gt;&gt;</span>,</span>
<span id="cb39-140"><a href="#cb39-140" aria-hidden="true" tabindex="-1"></a>      ValueTuples<span class="op">...</span>,</span>
<span id="cb39-141"><a href="#cb39-141" aria-hidden="true" tabindex="-1"></a>      std<span class="op">::</span>tuple<span class="op">&lt;</span>std<span class="op">::</span>tag_t<span class="op">&lt;</span>std<span class="op">::</span>execution<span class="op">::</span>set_error<span class="op">&gt;</span>, std<span class="op">::</span>decay_t<span class="op">&lt;</span>Errors<span class="op">&gt;&gt;...&gt;</span>;</span>
<span id="cb39-142"><a href="#cb39-142" aria-hidden="true" tabindex="-1"></a>    <span class="op">}</span>;</span>
<span id="cb39-143"><a href="#cb39-143" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb39-144"><a href="#cb39-144" aria-hidden="true" tabindex="-1"></a>    <span class="kw">using</span> source_traits_t <span class="op">=</span> std<span class="op">::</span>execution<span class="op">::</span>sender_traits<span class="op">&lt;</span>Source<span class="op">&gt;</span>;</span>
<span id="cb39-145"><a href="#cb39-145" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb39-146"><a href="#cb39-146" aria-hidden="true" tabindex="-1"></a>    <span class="kw">using</span> result_t <span class="op">=</span> </span>
<span id="cb39-147"><a href="#cb39-147" aria-hidden="true" tabindex="-1"></a>      <span class="kw">typename</span> source_traits_t<span class="op">::</span><span class="kw">template</span> value_types<span class="op">&lt;</span></span>
<span id="cb39-148"><a href="#cb39-148" aria-hidden="true" tabindex="-1"></a>        value_result_tuple_t,</span>
<span id="cb39-149"><a href="#cb39-149" aria-hidden="true" tabindex="-1"></a>        <span class="kw">typename</span> source_traits_t<span class="op">::</span><span class="kw">template</span> error_types<span class="op">&lt;</span>error_result<span class="op">&gt;::</span><span class="kw">template</span> apply<span class="op">&gt;</span>;</span>
<span id="cb39-150"><a href="#cb39-150" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb39-151"><a href="#cb39-151" aria-hidden="true" tabindex="-1"></a>    <span class="kw">using</span> parent_stop_token <span class="op">=</span> std<span class="op">::</span>execution<span class="op">::</span>stop_token_type_t<span class="op">&lt;</span>Receiver<span class="op">&gt;</span>;</span>
<span id="cb39-152"><a href="#cb39-152" aria-hidden="true" tabindex="-1"></a>    <span class="kw">using</span> stop_callback <span class="op">=</span></span>
<span id="cb39-153"><a href="#cb39-153" aria-hidden="true" tabindex="-1"></a>      <span class="kw">typename</span> parent_stop_token<span class="op">::</span><span class="kw">template</span> callback_type<span class="op">&lt;</span>forward_stop_request<span class="op">&gt;</span>;</span>
<span id="cb39-154"><a href="#cb39-154" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb39-155"><a href="#cb39-155" aria-hidden="true" tabindex="-1"></a>    Receiver receiver;</span>
<span id="cb39-156"><a href="#cb39-156" aria-hidden="true" tabindex="-1"></a>    std<span class="op">::</span>in_place_stop_source stopSource;</span>
<span id="cb39-157"><a href="#cb39-157" aria-hidden="true" tabindex="-1"></a>    std<span class="op">::</span>atomic<span class="op">&lt;</span>std<span class="op">::</span><span class="dt">uint8_t</span><span class="op">&gt;</span> remaining<span class="op">{</span><span class="dv">2</span><span class="op">}</span>;</span>
<span id="cb39-158"><a href="#cb39-158" aria-hidden="true" tabindex="-1"></a>    std<span class="op">::</span>optional<span class="op">&lt;</span>stop_callback<span class="op">&gt;</span> stopCallback;</span>
<span id="cb39-159"><a href="#cb39-159" aria-hidden="true" tabindex="-1"></a>    result_t result;</span>
<span id="cb39-160"><a href="#cb39-160" aria-hidden="true" tabindex="-1"></a>    std<span class="op">::</span>execution<span class="op">::</span>connect_result_t<span class="op">&lt;</span></span>
<span id="cb39-161"><a href="#cb39-161" aria-hidden="true" tabindex="-1"></a>        Source, _source_receiver<span class="op">&lt;</span>Source, Trigger, Receiver<span class="op">&gt;&gt;</span> sourceOp;</span>
<span id="cb39-162"><a href="#cb39-162" aria-hidden="true" tabindex="-1"></a>    std<span class="op">::</span>execution<span class="op">::</span>connect_result_t<span class="op">&lt;</span></span>
<span id="cb39-163"><a href="#cb39-163" aria-hidden="true" tabindex="-1"></a>        Trigger, _trigger_receiver<span class="op">&lt;</span>Source, Trigger, Receiver<span class="op">&gt;&gt;</span> triggerOp;</span>
<span id="cb39-164"><a href="#cb39-164" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb39-165"><a href="#cb39-165" aria-hidden="true" tabindex="-1"></a><span class="op">}</span>;</span>
<span id="cb39-166"><a href="#cb39-166" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb39-167"><a href="#cb39-167" aria-hidden="true" tabindex="-1"></a><span class="kw">template</span><span class="op">&lt;</span><span class="kw">typename</span> Source, <span class="kw">typename</span> Trigger<span class="op">&gt;</span></span>
<span id="cb39-168"><a href="#cb39-168" aria-hidden="true" tabindex="-1"></a><span class="kw">struct</span> _sender <span class="op">{</span></span>
<span id="cb39-169"><a href="#cb39-169" aria-hidden="true" tabindex="-1"></a>    <span class="kw">template</span><span class="op">&lt;</span><span class="kw">template</span><span class="op">&lt;</span><span class="kw">typename</span><span class="op">...&gt;</span> <span class="kw">class</span> Tuple,</span>
<span id="cb39-170"><a href="#cb39-170" aria-hidden="true" tabindex="-1"></a>             <span class="kw">template</span><span class="op">&lt;</span><span class="kw">typename</span><span class="op">...&gt;</span> <span class="kw">class</span> Variant<span class="op">&gt;</span></span>
<span id="cb39-171"><a href="#cb39-171" aria-hidden="true" tabindex="-1"></a>    <span class="kw">using</span> value_types <span class="op">=</span> <span class="kw">typename</span> Source<span class="op">::</span><span class="kw">template</span> value_types<span class="op">&lt;</span>Tuple, Variant<span class="op">&gt;</span>;</span>
<span id="cb39-172"><a href="#cb39-172" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb39-173"><a href="#cb39-173" aria-hidden="true" tabindex="-1"></a>    <span class="kw">template</span><span class="op">&lt;</span><span class="kw">template</span><span class="op">&lt;</span><span class="kw">typename</span><span class="op">...&gt;</span> <span class="kw">class</span> Variant<span class="op">&gt;</span></span>
<span id="cb39-174"><a href="#cb39-174" aria-hidden="true" tabindex="-1"></a>    <span class="kw">using</span> error_types <span class="op">=</span> <span class="kw">typename</span> Source<span class="op">::</span><span class="kw">template</span> error_types<span class="op">&lt;</span>Variant<span class="op">&gt;</span>;</span>
<span id="cb39-175"><a href="#cb39-175" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb39-176"><a href="#cb39-176" aria-hidden="true" tabindex="-1"></a>    <span class="kw">static</span> <span class="kw">constexpr</span> <span class="dt">bool</span> sends_done <span class="op">=</span> Source<span class="op">::</span>sends_done;</span>
<span id="cb39-177"><a href="#cb39-177" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb39-178"><a href="#cb39-178" aria-hidden="true" tabindex="-1"></a>    <span class="kw">template</span><span class="op">&lt;</span><span class="kw">typename</span> Self, <span class="kw">typename</span> Receiver<span class="op">&gt;</span></span>
<span id="cb39-179"><a href="#cb39-179" aria-hidden="true" tabindex="-1"></a>        <span class="kw">requires</span> std<span class="op">::</span>same_as<span class="op">&lt;</span>std<span class="op">::</span>remove_cvref_t<span class="op">&lt;</span>Self<span class="op">&gt;</span>, _sender<span class="op">&gt;</span> <span class="op">&amp;&amp;</span></span>
<span id="cb39-180"><a href="#cb39-180" aria-hidden="true" tabindex="-1"></a>                 std<span class="op">::</span>execution<span class="op">::</span>receiver<span class="op">&lt;</span>Receiver<span class="op">&gt;</span> <span class="op">&amp;&amp;</span></span>
<span id="cb39-181"><a href="#cb39-181" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb39-182"><a href="#cb39-182" aria-hidden="true" tabindex="-1"></a>    <span class="kw">friend</span> <span class="kw">auto</span> tag_invoke<span class="op">(</span></span>
<span id="cb39-183"><a href="#cb39-183" aria-hidden="true" tabindex="-1"></a>        std<span class="op">::</span>tag_t<span class="op">&lt;</span>std<span class="op">::</span>execution<span class="op">::</span><span class="fu">connect</span><span class="op">&gt;</span>,</span>
<span id="cb39-184"><a href="#cb39-184" aria-hidden="true" tabindex="-1"></a>        Self<span class="op">&amp;&amp;</span> self,</span>
<span id="cb39-185"><a href="#cb39-185" aria-hidden="true" tabindex="-1"></a>        Receiver<span class="op">&amp;&amp;</span> receiver<span class="op">)</span></span>
<span id="cb39-186"><a href="#cb39-186" aria-hidden="true" tabindex="-1"></a>        <span class="op">-&gt;</span> _op<span class="op">&lt;</span>member_t<span class="op">&lt;</span>Self, Source<span class="op">&gt;</span>, member_t<span class="op">&lt;</span>Self, Trigger<span class="op">&gt;</span>, std<span class="op">::</span>remove_cvref_t<span class="op">&lt;</span>Receiver<span class="op">&gt;&gt;</span> <span class="op">{</span></span>
<span id="cb39-187"><a href="#cb39-187" aria-hidden="true" tabindex="-1"></a>        <span class="cf">return</span> _op<span class="op">&lt;</span>member_t<span class="op">&lt;</span>Self, Source<span class="op">&gt;</span>, member_t<span class="op">&lt;</span>Self, Trigger<span class="op">&gt;</span>, std<span class="op">::</span>remove_cvref_T<span class="op">&lt;</span>Receiver<span class="op">&gt;&gt;{</span></span>
<span id="cb39-188"><a href="#cb39-188" aria-hidden="true" tabindex="-1"></a>            <span class="kw">static_cast</span><span class="op">&lt;</span>Self<span class="op">&amp;&amp;&gt;(</span>self<span class="op">).</span>source,</span>
<span id="cb39-189"><a href="#cb39-189" aria-hidden="true" tabindex="-1"></a>            <span class="kw">static_cast</span><span class="op">&lt;</span>Self<span class="op">&amp;&amp;&gt;(</span>self<span class="op">).</span>trigger,</span>
<span id="cb39-190"><a href="#cb39-190" aria-hidden="true" tabindex="-1"></a>            <span class="kw">static_cast</span><span class="op">&lt;</span>Receiver<span class="op">&amp;&amp;&gt;(</span>receiver<span class="op">)</span></span>
<span id="cb39-191"><a href="#cb39-191" aria-hidden="true" tabindex="-1"></a>        <span class="op">}</span>;</span>
<span id="cb39-192"><a href="#cb39-192" aria-hidden="true" tabindex="-1"></a>    <span class="op">}</span></span>
<span id="cb39-193"><a href="#cb39-193" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb39-194"><a href="#cb39-194" aria-hidden="true" tabindex="-1"></a>    Source source;</span>
<span id="cb39-195"><a href="#cb39-195" aria-hidden="true" tabindex="-1"></a>    Trigger trigger;</span>
<span id="cb39-196"><a href="#cb39-196" aria-hidden="true" tabindex="-1"></a><span class="op">}</span>;</span>
<span id="cb39-197"><a href="#cb39-197" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb39-198"><a href="#cb39-198" aria-hidden="true" tabindex="-1"></a><span class="kw">struct</span> _fn <span class="op">{</span></span>
<span id="cb39-199"><a href="#cb39-199" aria-hidden="true" tabindex="-1"></a>    <span class="co">// Dispatch to custom implementation if one provided.</span></span>
<span id="cb39-200"><a href="#cb39-200" aria-hidden="true" tabindex="-1"></a>    <span class="kw">template</span><span class="op">&lt;</span><span class="kw">typename</span> Source, <span class="kw">typename</span> Trigger<span class="op">&gt;</span></span>
<span id="cb39-201"><a href="#cb39-201" aria-hidden="true" tabindex="-1"></a>        <span class="kw">requires</span></span>
<span id="cb39-202"><a href="#cb39-202" aria-hidden="true" tabindex="-1"></a>            std<span class="op">::</span>execution<span class="op">::</span>sender<span class="op">&lt;</span>Source<span class="op">&gt;</span> <span class="op">&amp;&amp;</span></span>
<span id="cb39-203"><a href="#cb39-203" aria-hidden="true" tabindex="-1"></a>            std<span class="op">::</span>execution<span class="op">::</span>sender<span class="op">&lt;</span>Trigger<span class="op">&gt;</span> <span class="op">&amp;&amp;</span></span>
<span id="cb39-204"><a href="#cb39-204" aria-hidden="true" tabindex="-1"></a>            std<span class="op">::</span>tag_invocable<span class="op">&lt;</span>_fn, Source, Trigger<span class="op">&gt;</span></span>
<span id="cb39-205"><a href="#cb39-205" aria-hidden="true" tabindex="-1"></a>    <span class="kw">auto</span> <span class="kw">operator</span><span class="op">()(</span>Source<span class="op">&amp;&amp;</span> source, Trigger<span class="op">&amp;&amp;</span> trigger<span class="op">)</span> <span class="kw">const</span></span>
<span id="cb39-206"><a href="#cb39-206" aria-hidden="true" tabindex="-1"></a>        <span class="kw">noexcept</span><span class="op">(</span>std<span class="op">::</span>is_nothrow_tag_invocable_v<span class="op">&lt;</span>_fn, Source, Trigger<span class="op">&gt;</span></span>
<span id="cb39-207"><a href="#cb39-207" aria-hidden="true" tabindex="-1"></a>        <span class="op">-&gt;</span> std<span class="op">::</span>tag_invoke_result_t<span class="op">&lt;</span>_fn, Source, Trigger<span class="op">&gt;</span> <span class="op">{</span></span>
<span id="cb39-208"><a href="#cb39-208" aria-hidden="true" tabindex="-1"></a>        <span class="cf">return</span> std<span class="op">::</span>tag_invoke<span class="op">(</span>_fn<span class="op">{}</span>, <span class="op">(</span>Source<span class="op">&amp;&amp;)</span>source, <span class="op">(</span>Trigger<span class="op">&amp;&amp;)</span>trigger<span class="op">)</span>;</span>
<span id="cb39-209"><a href="#cb39-209" aria-hidden="true" tabindex="-1"></a>    <span class="op">}</span></span>
<span id="cb39-210"><a href="#cb39-210" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb39-211"><a href="#cb39-211" aria-hidden="true" tabindex="-1"></a>    <span class="co">// Otherwise fall back to default implementation</span></span>
<span id="cb39-212"><a href="#cb39-212" aria-hidden="true" tabindex="-1"></a>    <span class="kw">template</span><span class="op">&lt;</span><span class="kw">typename</span> Source, <span class="kw">typename</span> Trigger<span class="op">&gt;</span></span>
<span id="cb39-213"><a href="#cb39-213" aria-hidden="true" tabindex="-1"></a>        <span class="kw">requires</span></span>
<span id="cb39-214"><a href="#cb39-214" aria-hidden="true" tabindex="-1"></a>            std<span class="op">::</span>execution<span class="op">::</span>sender<span class="op">&lt;</span>Source<span class="op">&gt;</span> <span class="op">&amp;&amp;</span></span>
<span id="cb39-215"><a href="#cb39-215" aria-hidden="true" tabindex="-1"></a>            std<span class="op">::</span>execution<span class="op">::</span>sender<span class="op">&lt;</span>Trigger<span class="op">&gt;</span> <span class="op">&amp;&amp;</span></span>
<span id="cb39-216"><a href="#cb39-216" aria-hidden="true" tabindex="-1"></a>            <span class="op">(!</span>std<span class="op">::</span>tag_invocable<span class="op">&lt;</span>_fn, Source, Trigger<span class="op">&gt;)</span> <span class="op">&amp;&amp;</span></span>
<span id="cb39-217"><a href="#cb39-217" aria-hidden="true" tabindex="-1"></a>            std<span class="op">::</span>constructible_from<span class="op">&lt;</span>std<span class="op">::</span>remove_cvref_t<span class="op">&lt;</span>Source<span class="op">&gt;</span>, Source<span class="op">&gt;</span> <span class="op">&amp;&amp;</span></span>
<span id="cb39-218"><a href="#cb39-218" aria-hidden="true" tabindex="-1"></a>            std<span class="op">::</span>constructible_from<span class="op">&lt;</span>std<span class="op">::</span>remove_cvref_t<span class="op">&lt;</span>Trigger<span class="op">&gt;</span>, Trigger<span class="op">&gt;</span></span>
<span id="cb39-219"><a href="#cb39-219" aria-hidden="true" tabindex="-1"></a>    <span class="kw">auto</span> <span class="kw">operator</span><span class="op">()(</span>Source<span class="op">&amp;&amp;</span> source, Trigger<span class="op">&amp;&amp;</span> trigger<span class="op">)</span> <span class="kw">const</span></span>
<span id="cb39-220"><a href="#cb39-220" aria-hidden="true" tabindex="-1"></a>        <span class="kw">noexcept</span><span class="op">(</span>std<span class="op">::</span>is_nothrow_constructible_v<span class="op">&lt;</span>std<span class="op">::</span>remove_cvref_t<span class="op">&lt;</span>Source<span class="op">&gt;</span>, Source<span class="op">&gt;</span> <span class="op">&amp;&amp;</span></span>
<span id="cb39-221"><a href="#cb39-221" aria-hidden="true" tabindex="-1"></a>                    std<span class="op">::</span>is_nothrow_constructible_v<span class="op">&lt;</span>std<span class="op">::</span>remove_cvref_t<span class="op">&lt;</span>Trigger<span class="op">&gt;</span>, Trigger<span class="op">&gt;)</span></span>
<span id="cb39-222"><a href="#cb39-222" aria-hidden="true" tabindex="-1"></a>        <span class="op">-&gt;</span> _sender<span class="op">&lt;</span>remove_cvref_t<span class="op">&lt;</span>Source<span class="op">&gt;</span>, remove_cvref_t<span class="op">&lt;</span>Trigger<span class="op">&gt;&gt;</span> <span class="op">{</span></span>
<span id="cb39-223"><a href="#cb39-223" aria-hidden="true" tabindex="-1"></a>        <span class="cf">return</span> _sender<span class="op">&lt;</span>remove_cvref_t<span class="op">&lt;</span>Source<span class="op">&gt;</span>, remove_cvref_t<span class="op">&lt;</span>Trigger<span class="op">&gt;&gt;{</span></span>
<span id="cb39-224"><a href="#cb39-224" aria-hidden="true" tabindex="-1"></a>            <span class="op">(</span>Source<span class="op">&amp;&amp;)</span>source,</span>
<span id="cb39-225"><a href="#cb39-225" aria-hidden="true" tabindex="-1"></a>            <span class="op">(</span>Trigger<span class="op">&amp;&amp;)</span>trigger</span>
<span id="cb39-226"><a href="#cb39-226" aria-hidden="true" tabindex="-1"></a>        <span class="op">}</span>;</span>
<span id="cb39-227"><a href="#cb39-227" aria-hidden="true" tabindex="-1"></a>    <span class="op">}</span>         </span>
<span id="cb39-228"><a href="#cb39-228" aria-hidden="true" tabindex="-1"></a><span class="op">}</span>;</span>
<span id="cb39-229"><a href="#cb39-229" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb39-230"><a href="#cb39-230" aria-hidden="true" tabindex="-1"></a><span class="op">}</span> <span class="co">// namespace _stop_when</span></span>
<span id="cb39-231"><a href="#cb39-231" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb39-232"><a href="#cb39-232" aria-hidden="true" tabindex="-1"></a><span class="kw">inline</span> <span class="kw">constexpr</span> _stop_when<span class="op">::</span>_fn stop_when;</span></code></pre></div>
</div>
</div>
</body>
</html>
