<!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="2024-07-16" />
  <title>`finally`, `write_env`, and `unstoppable` Sender
Adaptors</title>
  <style>
      code{white-space: pre-wrap;}
      span.smallcaps{font-variant: small-caps;}
      span.underline{text-decoration: underline;}
      div.column{display: inline-block; vertical-align: top; width: 50%;}
      div.csl-block{margin-left: 1.5em;}
      ul.task-list{list-style: none;}
      pre > code.sourceCode { white-space: pre; position: relative; }
      pre > code.sourceCode > span { display: inline-block; line-height: 1.25; }
      pre > code.sourceCode > span:empty { height: 1.2em; }
      .sourceCode { overflow: visible; }
      code.sourceCode > span { color: inherit; text-decoration: inherit; }
      div.sourceCode { margin: 1em 0; }
      pre.sourceCode { margin: 0; }
      @media screen {
      div.sourceCode { overflow: auto; }
      }
      @media print {
      pre > code.sourceCode { white-space: pre-wrap; }
      pre > code.sourceCode > span { text-indent: -5em; padding-left: 5em; }
      }
      pre.numberSource code
        { counter-reset: source-line 0; }
      pre.numberSource code > span
        { position: relative; left: -4em; counter-increment: source-line; }
      pre.numberSource code > span > a:first-child::before
        { content: counter(source-line);
          position: relative; left: -1em; text-align: right; vertical-align: baseline;
          border: none; display: inline-block;
          -webkit-touch-callout: none; -webkit-user-select: none;
          -khtml-user-select: none; -moz-user-select: none;
          -ms-user-select: none; user-select: none;
          padding: 0 4px; width: 4em;
          color: #aaaaaa;
        }
      pre.numberSource { margin-left: 3em; border-left: 1px solid #aaaaaa;  padding-left: 4px; }
      div.sourceCode
        {  background-color: #f6f8fa; }
      @media screen {
      pre > code.sourceCode > span > a:first-child::before { text-decoration: underline; }
      }
      code span { } /* Normal */
      code span.al { color: #ff0000; } /* Alert */
      code span.an { } /* Annotation */
      code span.at { } /* Attribute */
      code span.bn { color: #9f6807; } /* BaseN */
      code span.bu { color: #9f6807; } /* BuiltIn */
      code span.cf { color: #00607c; } /* ControlFlow */
      code span.ch { color: #9f6807; } /* Char */
      code span.cn { } /* Constant */
      code span.co { color: #008000; font-style: italic; } /* Comment */
      code span.cv { color: #008000; font-style: italic; } /* CommentVar */
      code span.do { color: #008000; } /* Documentation */
      code span.dt { color: #00607c; } /* DataType */
      code span.dv { color: #9f6807; } /* DecVal */
      code span.er { color: #ff0000; font-weight: bold; } /* Error */
      code span.ex { } /* Extension */
      code span.fl { color: #9f6807; } /* Float */
      code span.fu { } /* Function */
      code span.im { } /* Import */
      code span.in { color: #008000; } /* Information */
      code span.kw { color: #00607c; } /* Keyword */
      code span.op { color: #af1915; } /* Operator */
      code span.ot { } /* Other */
      code span.pp { color: #6f4e37; } /* Preprocessor */
      code span.re { } /* RegionMarker */
      code span.sc { color: #9f6807; } /* SpecialChar */
      code span.ss { color: #9f6807; } /* SpecialString */
      code span.st { color: #9f6807; } /* String */
      code span.va { } /* Variable */
      code span.vs { color: #9f6807; } /* VerbatimString */
      code span.wa { color: #008000; font-weight: bold; } /* Warning */
      code.diff {color: #898887}
      code.diff span.va {color: #006e28}
      code.diff span.st {color: #bf0303}
  </style>
  <style type="text/css">
body {
margin: 5em;
font-family: serif;

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

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

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

code.sourceCode > span { display: inline; }
</style>
  <link href="data:image/x-icon;base64,AAABAAIAEBAAAAEAIABoBAAAJgAAACAgAAABACAAqBAAAI4EAAAoAAAAEAAAACAAAAABACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////AIJEAACCRAAAgkQAAIJEAACCRAAAgkQAVoJEAN6CRADegkQAWIJEAACCRAAAgkQAAIJEAACCRAAA////AP///wCCRAAAgkQAAIJEAACCRAAsgkQAvoJEAP+CRAD/gkQA/4JEAP+CRADAgkQALoJEAACCRAAAgkQAAP///wD///8AgkQAAIJEABSCRACSgkQA/IJEAP99PQD/dzMA/3czAP99PQD/gkQA/4JEAPyCRACUgkQAFIJEAAD///8A////AHw+AFiBQwDqgkQA/4BBAP9/PxP/uZd6/9rJtf/bybX/upd7/39AFP+AQQD/gkQA/4FDAOqAQgBc////AP///wDKklv4jlEa/3o7AP+PWC//8+3o///////////////////////z7un/kFox/35AAP+GRwD/mVYA+v///wD///8A0Zpk+NmibP+0d0T/8evj///////+/fv/1sKz/9bCs//9/fr//////+/m2/+NRwL/nloA/5xYAPj///8A////ANKaZPjRmGH/5cKh////////////k149/3UwAP91MQD/lmQ//86rhv+USg3/m1YA/5hSAP+bVgD4////AP///wDSmmT4zpJY/+/bx///////8+TV/8mLT/+TVx//gkIA/5lVAP+VTAD/x6B//7aEVv/JpH7/s39J+P///wD///8A0ppk+M6SWP/u2sf///////Pj1f/Nj1T/2KFs/8mOUv+eWhD/lEsA/8aee/+0glT/x6F7/7J8Rvj///8A////ANKaZPjRmGH/48Cf///////+/v7/2qt//82PVP/OkFX/37KJ/86siv+USg7/mVQA/5hRAP+bVgD4////AP///wDSmmT40ppk/9CVXP/69O////////7+/v/x4M//8d/P//7+/f//////9u7n/6tnJf+XUgD/nFgA+P///wD///8A0ppk+NKaZP/RmWL/1qNy//r07///////////////////////+vXw/9akdP/Wnmn/y5FY/6JfFvj///8A////ANKaZFTSmmTo0ppk/9GYYv/Ql1//5cWm//Hg0P/x4ND/5cWm/9GXYP/RmGH/0ppk/9KaZOjVnmpY////AP///wDSmmQA0ppkEtKaZI7SmmT60ppk/9CWX//OkVb/zpFW/9CWX//SmmT/0ppk/NKaZJDSmmQS0ppkAP///wD///8A0ppkANKaZADSmmQA0ppkKtKaZLrSmmT/0ppk/9KaZP/SmmT/0ppkvNKaZCrSmmQA0ppkANKaZAD///8A////ANKaZADSmmQA0ppkANKaZADSmmQA0ppkUtKaZNzSmmTc0ppkVNKaZADSmmQA0ppkANKaZADSmmQA////AP5/AAD4HwAA4AcAAMADAACAAQAAgAEAAIABAACAAQAAgAEAAIABAACAAQAAgAEAAMADAADgBwAA+B8AAP5/AAAoAAAAIAAAAEAAAAABACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////AP///wCCRAAAgkQAAIJEAACCRAAAgkQAAIJEAACCRAAAgkQAAIJEAACCRAAAgkQAAIJEAAyCRACMgkQA6oJEAOqCRACQgkQAEIJEAACCRAAAgkQAAIJEAACCRAAAgkQAAIJEAACCRAAAgkQAAIJEAACCRAAA////AP///wD///8A////AIJEAACCRAAAgkQAAIJEAACCRAAAgkQAAIJEAACCRAAAgkQAAIJEAACCRABigkQA5oJEAP+CRAD/gkQA/4JEAP+CRADqgkQAZoJEAACCRAAAgkQAAIJEAACCRAAAgkQAAIJEAACCRAAAgkQAAIJEAAD///8A////AP///wD///8AgkQAAIJEAACCRAAAgkQAAIJEAACCRAAAgkQAAIJEAACCRAA4gkQAwoJEAP+CRAD/gkQA/4JEAP+CRAD/gkQA/4JEAP+CRAD/gkQAxIJEADyCRAAAgkQAAIJEAACCRAAAgkQAAIJEAACCRAAAgkQAAP///wD///8A////AP///wCCRAAAgkQAAIJEAACCRAAAgkQAAIJEAACCRAAWgkQAmIJEAP+CRAD/gkQA/4JEAP+CRAD/gkQA/4JEAP+CRAD/gkQA/4JEAP+CRAD/gkQA/4JEAJyCRAAYgkQAAIJEAACCRAAAgkQAAIJEAACCRAAA////AP///wD///8A////AIJEAACCRAAAgkQAAIJEAACCRAAAgkQAdIJEAPCCRAD/gkQA/4JEAP+CRAD/gkQA/4JEAP+CRAD/gkQA/4JEAP+CRAD/gkQA/4JEAP+CRAD/gkQA/4JEAPSCRAB4gkQAAIJEAACCRAAAgkQAAIJEAAD///8A////AP///wD///8AgkQAAIJEAACCRAAAgkQASoJEANKCRAD/gkQA/4JEAP+CRAD/g0YA/39AAP9zLgD/bSQA/2shAP9rIQD/bSQA/3MuAP9/PwD/g0YA/4JEAP+CRAD/gkQA/4JEAP+CRADUgkQAToJEAACCRAAAgkQAAP///wD///8A////AP///wB+PwAAgkUAIoJEAKiCRAD/gkQA/4JEAP+CRAD/hEcA/4BBAP9sIwD/dTAA/5RfKv+viF7/vp56/76ee/+wiF7/lWAr/3YxAP9sIwD/f0AA/4RHAP+CRAD/gkQA/4JEAP+CRAD/gkQArIJEACaBQwAA////AP///wD///8A////AIBCAEBzNAD6f0EA/4NFAP+CRAD/gkQA/4VIAP92MwD/bSUA/6N1Tv/ezsL/////////////////////////////////38/D/6V3Uv9uJgD/dTEA/4VJAP+CRAD/gkQA/4JEAP+BQwD/fUAA/4FDAEj///8A////AP///wD///8AzJRd5qBlKf91NgD/dDUA/4JEAP+FSQD/cy4A/3YyAP/PuKP//////////////////////////////////////////////////////9K7qP94NQD/ciwA/4VJAP+CRAD/fkEA/35BAP+LSwD/mlYA6v///wD///8A////AP///wDdpnL/4qx3/8KJUv+PUhf/cTMA/3AsAP90LgD/4dK+/////////////////////////////////////////////////////////////////+TYxf91MAD/dTIA/31CAP+GRwD/llQA/6FcAP+gWwD8////AP///wD///8A////ANGZY/LSm2X/4ap3/92mcP+wdT3/byQA/8mwj////////////////////////////////////////////////////////////////////////////+LYxv9zLgP/jUoA/59bAP+hXAD/nFgA/5xYAPL///8A////AP///wD///8A0ppk8tKaZP/RmWL/1p9q/9ubXv/XqXj////////////////////////////7+fD/vZyG/6BxS/+gcUr/vJuE//r37f//////////////////////3MOr/5dQBf+dVQD/nVkA/5xYAP+cWAD/nFgA8v///wD///8A////AP///wDSmmTy0ppk/9KaZP/SmWP/yohJ//jo2P//////////////////////4NTG/4JDFf9lGAD/bSQA/20kAP9kGAD/fz8S/+Xb0f//////5NG9/6txN/+LOgD/m1QA/51aAP+cWAD/m1cA/5xYAP+cWADy////AP///wD///8A////ANKaZPLSmmT/0ppk/8+TWf/Unmv//v37//////////////////////+TWRr/VwsA/35AAP+ERgD/g0UA/4JGAP9lHgD/kFga/8KXX/+TRwD/jT4A/49CAP+VTQD/n10A/5xYAP+OQQD/lk4A/55cAPL///8A////AP///wD///8A0ppk8tKaZP/SmmT/y4tO/92yiP//////////////////////8NnE/8eCQP+rcTT/ez0A/3IyAP98PgD/gEMA/5FSAP+USwD/jj8A/5lUAP+JNwD/yqV2/694Mf+HNQD/jkAA/82rf/+laBj/jT4A8v///wD///8A////AP///wDSmmTy0ppk/9KaZP/LiUr/4byY///////////////////////gupX/0I5P/+Wuev/Lklz/l1sj/308AP+QSwD/ol0A/59aAP+aVQD/k0oA/8yoh///////+fXv/6pwO//Lp3v///////Pr4f+oay7y////AP///wD///8A////ANKaZPLSmmT/0ppk/8uJSv/hvJj//////////////////////+G7l//Jhkb/0ppk/96nc//fqXX/x4xO/6dkFP+QSQD/llEA/5xXAP+USgD/yaOA///////38uv/qG05/8ijdv//////8efb/6ZpLPL///8A////AP///wD///8A0ppk8tKaZP/SmmT/zIxO/9yxh///////////////////////7dbA/8iEQf/Sm2X/0Zlj/9ScZv/eqHf/2KJv/7yAQf+XTgD/iToA/5lSAP+JNgD/yKFv/611LP+HNQD/jT8A/8qmeP+kZRT/jT4A8v///wD///8A////AP///wDSmmTy0ppk/9KaZP/Pk1n/1J5q//78+//////////////////+/fv/1aFv/8iEQv/Tm2b/0ppl/9GZY//Wn2z/1pZc/9eldf/Bl2b/kUcA/4w9AP+OQAD/lUwA/59eAP+cWQD/jT8A/5ZOAP+eXADy////AP///wD///8A////ANKaZPLSmmT/0ppk/9KZY//KiEn/8d/P///////////////////////47+f/05tm/8iCP//KiEj/yohJ/8eCP//RmGH//vfy///////n1sP/rXQ7/4k4AP+TTAD/nVoA/5xYAP+cVwD/nFgA/5xYAPL///8A////AP///wD///8A0ppk8tKaZP/SmmT/0ptl/8uLTf/aq37////////////////////////////+/fz/6c2y/961jv/etY7/6Myx//78+v//////////////////////3MWv/5xXD/+ORAD/mFQA/51ZAP+cWAD/nFgA8v///wD///8A////AP///wDSmmTy0ppk/9KaZP/SmmT/0ppk/8mFRP/s1b//////////////////////////////////////////////////////////////////////////////+PD/0JFU/7NzMv+WUQD/kUsA/5tXAP+dWQDy////AP///wD///8A////ANKaZP/SmmT/0ppk/9KaZP/Sm2X/z5NZ/8yMT//z5NX/////////////////////////////////////////////////////////////////9Ofa/8yNUP/UmGH/36p5/8yTWv+qaSD/kksA/5ROAPz///8A////AP///wD///8A0ppk5NKaZP/SmmT/0ppk/9KaZP/TnGf/zY9T/82OUv/t1sD//////////////////////////////////////////////////////+7Yw//OkFX/zI5R/9OcZ//SmmP/26V0/9ymdf/BhUf/ol8R6P///wD///8A////AP///wDSmmQ80ppk9tKaZP/SmmT/0ppk/9KaZP/TnGj/zpFW/8qJSv/dson/8uHS//////////////////////////////////Lj0//etIv/y4lL/86QVf/TnGj/0ppk/9KaZP/RmWP/05xn/9ymdfjUnWdC////AP///wD///8A////ANKaZADSmmQc0ppkotKaZP/SmmT/0ppk/9KaZP/Tm2b/0Zli/8qJSf/NjlH/16Z3/+G8mP/myKr/5siq/+G8mP/Xp3f/zY5S/8qISf/RmGH/05tm/9KaZP/SmmT/0ppk/9KaZP/SmmSm0pljINWdaQD///8A////AP///wD///8A0ppkANKaZADSmmQA0ppkQtKaZMrSmmT/0ppk/9KaZP/SmmT/0ptl/9GYYf/Nj1P/y4lL/8qISP/KiEj/y4lK/82PU//RmGH/0ptl/9KaZP/SmmT/0ppk/9KaZP/SmmTO0ppkRtKaZADSmmQA0ppkAP///wD///8A////AP///wDSmmQA0ppkANKaZADSmmQA0ppkANKaZGzSmmTu0ppk/9KaZP/SmmT/0ppk/9KaZP/SmmT/0ppk/9KaZP/SmmT/0ppk/9KaZP/SmmT/0ppk/9KaZP/SmmTw0ppkcNKaZADSmmQA0ppkANKaZADSmmQA////AP///wD///8A////ANKaZADSmmQA0ppkANKaZADSmmQA0ppkANKaZBLSmmSQ0ppk/9KaZP/SmmT/0ppk/9KaZP/SmmT/0ppk/9KaZP/SmmT/0ppk/9KaZP/SmmT/0ppklNKaZBTSmmQA0ppkANKaZADSmmQA0ppkANKaZAD///8A////AP///wD///8A0ppkANKaZADSmmQA0ppkANKaZADSmmQA0ppkANKaZADSmmQy0ppkutKaZP/SmmT/0ppk/9KaZP/SmmT/0ppk/9KaZP/SmmT/0ppkvtKaZDbSmmQA0ppkANKaZADSmmQA0ppkANKaZADSmmQA0ppkAP///wD///8A////AP///wDSmmQA0ppkANKaZADSmmQA0ppkANKaZADSmmQA0ppkANKaZADSmmQA0ppkXNKaZODSmmT/0ppk/9KaZP/SmmT/0ppk5NKaZGDSmmQA0ppkANKaZADSmmQA0ppkANKaZADSmmQA0ppkANKaZADSmmQA////AP///wD///8A////ANKaZADSmmQA0ppkANKaZADSmmQA0ppkANKaZADSmmQA0ppkANKaZADSmmQA0ppkBtKaZIbSmmTo0ppk6tKaZIrSmmQK0ppkANKaZADSmmQA0ppkANKaZADSmmQA0ppkANKaZADSmmQA0ppkANKaZAD///8A////AP/8P///+B///+AH//+AAf//AAD//AAAP/AAAA/gAAAHwAAAA8AAAAPAAAADwAAAA8AAAAPAAAADwAAAA8AAAAPAAAADwAAAA8AAAAPAAAADwAAAA8AAAAPAAAADwAAAA+AAAAfwAAAP/AAAP/8AAP//gAH//+AH///4H////D//" rel="icon" />
  
  <!--[if lt IE 9]>
    <script src="//cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv-printshiv.min.js"></script>
  <![endif]-->
</head>
<body>
<div class="wrapper">
<header id="title-block-header">
<h1 class="title" style="text-align:center"><code class="sourceCode default">finally</code>,
<code class="sourceCode default">write_env</code>, and
<code class="sourceCode default">unstoppable</code> Sender Adaptors</h1>
<table style="border:none;float:right">
  <tr>
    <td>Document #:</td>
    <td>P3284R1</td>
  </tr>
  <tr>
    <td>Date:</td>
    <td>2024-07-16</td>
  </tr>
  <tr>
    <td style="vertical-align:top">Project:</td>
    <td>Programming Language C++</td>
  </tr>
  <tr>
    <td style="vertical-align:top">Audience:</td>
    <td>
      LEWG Library Evolution<br>
    </td>
  </tr>
  <tr>
    <td style="vertical-align:top">Reply-to:</td>
    <td>
      Eric Niebler<br>&lt;<a href="mailto:eric.niebler@gmail.com" class="email">eric.niebler@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="#introduction" id="toc-introduction"><span class="toc-section-number">1</span> Introduction<span></span></a></li>
<li><a href="#executive-summary" id="toc-executive-summary"><span class="toc-section-number">2</span> Executive
Summary<span></span></a></li>
<li><a href="#description" id="toc-description"><span class="toc-section-number">3</span> Description<span></span></a>
<ul>
<li><a href="#write_env" id="toc-write_env"><span class="toc-section-number">3.1</span>
<code class="sourceCode default">write_env</code><span></span></a>
<ul>
<li><a href="#example-write_env" id="toc-example-write_env"><span class="toc-section-number">3.1.1</span> Example:
<code class="sourceCode default">write_env</code><span></span></a></li>
</ul></li>
<li><a href="#unstoppable" id="toc-unstoppable"><span class="toc-section-number">3.2</span>
<code class="sourceCode default">unstoppable</code><span></span></a></li>
<li><a href="#finally" id="toc-finally"><span class="toc-section-number">3.3</span>
<code class="sourceCode default">finally</code><span></span></a>
<ul>
<li><a href="#example-finally" id="toc-example-finally"><span class="toc-section-number">3.3.1</span> Example:
<code class="sourceCode default">finally</code><span></span></a></li>
</ul></li>
</ul></li>
<li><a href="#discussion" id="toc-discussion"><span class="toc-section-number">4</span> Discussion<span></span></a>
<ul>
<li><a href="#should-finally-apply-unstoppable-by-default-to-its-second-argument" id="toc-should-finally-apply-unstoppable-by-default-to-its-second-argument"><span class="toc-section-number">4.1</span> Should
<code class="sourceCode default">finally</code> apply
<code class="sourceCode default">unstoppable</code> by default to its
second argument?<span></span></a></li>
<li><a href="#is-there-a-different-design-that-better-captures-the-async-raii-intent" id="toc-is-there-a-different-design-that-better-captures-the-async-raii-intent"><span class="toc-section-number">4.2</span> Is there a different design that
better captures the “async RAII” intent?<span></span></a></li>
</ul></li>
<li><a href="#proposed-wording" id="toc-proposed-wording"><span class="toc-section-number">5</span> Proposed
Wording<span></span></a></li>
<li><a href="#bibliography" id="toc-bibliography"><span class="toc-section-number">6</span> References<span></span></a></li>
</ul>
</div>
<h1 data-number="1" id="introduction"><span class="header-section-number">1</span> Introduction<a href="#introduction" class="self-link"></a></h1>
<p>This paper proposes to add three new sender adaptor algorithms to the
<code class="sourceCode default">std::execution</code> namespace,
targetting C++26: <code class="sourceCode default">finally</code>,
<code class="sourceCode default">write_env</code>, and
<code class="sourceCode default">unstoppable</code>. These adaptors were
originally proposed as part of <span class="citation" data-cites="P3175R0">[<a href="#ref-P3175R0" role="doc-biblioref">P3175R0</a>]</span> but have been split out into
their own paper so that the higher priority items in P3175 can advance
more quickly.</p>
<h1 data-number="2" id="executive-summary"><span class="header-section-number">2</span> Executive Summary<a href="#executive-summary" class="self-link"></a></h1>
<p>Below are the specific changes this paper proposes:</p>
<ol type="1">
<li><p>Add a new uncustomizable adaptor
<code class="sourceCode default">write_env</code> for writing values
into the receiver’s execution environment. Use
<code class="sourceCode default">write_env</code> in the implementation
of the <code class="sourceCode default">on</code> algorithm and to
simplify the specification of the
<code class="sourceCode default">let_</code> algorithms.</p></li>
<li><p>Add an uncustomizable
<code class="sourceCode default">unstoppable</code> adaptor that is a
trivial application of
<code class="sourceCode default">write_env</code>: it sets the current
stop token in the receiver’s environment to a
<code class="sourceCode default">never_stop_token</code>.
<code class="sourceCode default">unstoppable</code> is used in the
re-specification of the
<code class="sourceCode default">schedule_from</code>
algorithm.</p></li>
<li><p>Generalize the specification for
<code class="sourceCode default">schedule_from</code> to take two
senders instead of a sender and a scheduler, name it
<code class="sourceCode default">finally</code>, and make it
uncustomizable. Specify the default implementation of
<code class="sourceCode default">schedule_from(sch, snd)</code> as <code class="sourceCode default">finally(snd, unstoppable(schedule(sch)))</code>.</p></li>
</ol>
<h1 data-number="3" id="description"><span class="header-section-number">3</span> Description<a href="#description" class="self-link"></a></h1>
<p><span class="citation" data-cites="P3175R0">[<a href="#ref-P3175R0" role="doc-biblioref">P3175R0</a>]</span> proposed some changes to the
<code class="sourceCode default">std::execution::on</code> algorithm,
the specification of which was made simpler by the addition of some
additional adaptors. Those adaptors were general and useful in their own
right, so P3175R0 suggested they be added to
<code class="sourceCode default">std::execution</code> proper. The
conservative approach was to make them exposition-only, and that is how
things currently stand in the working draft.</p>
<p>The author still feels like those adaptors are worthy of
standardization. This paper proposes adding them.</p>
<p>The adaptors in question are as follows:</p>
<h2 data-number="3.1" id="write_env"><span class="header-section-number">3.1</span>
<code class="sourceCode default">write_env</code><a href="#write_env" class="self-link"></a></h2>
<p>A receiver has an associated “execution environment”, which is an
unstructured, queryable key/value store. It is used to pass implicit
parameters from parent operations to their children. It is occasionally
useful for a sender adaptor to explicitly mutate the key/value store so
that child operations see different values for environment queries. The
<code class="sourceCode default">write_env</code> sender adaptor is used
for that purpose.</p>
<p><code class="sourceCode default">write_env</code> is a customization
point object, although it is not actually customizable. It accepts a
sender <code class="sourceCode default">sndr</code> and an execution
environment <code class="sourceCode default">env</code>, and it returns
a new sender that stores <code class="sourceCode default">sndr</code>
and <code class="sourceCode default">env</code>. When that sender is
connected to a receiver <code class="sourceCode default">rcvr</code>, it
returns the result of connecting
<code class="sourceCode default">sndr</code> with a receiver that adapts
<code class="sourceCode default">rcvr</code>. The environment of that
adapted receiver is the result of joining
<code class="sourceCode default">env</code> with
<code class="sourceCode default">rcvr</code>’s environment. The two
environments are joined such that, when the joined environment is
queried, <code class="sourceCode default">env</code> is queried first,
and if <code class="sourceCode default">env</code> doesn’t have a value
for that query, the result of
<code class="sourceCode default">get_env(rcvr)</code> is queried.</p>
<h3 data-number="3.1.1" id="example-write_env"><span class="header-section-number">3.1.1</span> Example:
<code class="sourceCode default">write_env</code><a href="#example-write_env" class="self-link"></a></h3>
<p>One example of where
<code class="sourceCode default">write_env</code> might be useful is to
specify an allocator to be used by child operations. The code might look
like this:</p>
<div class="sourceCode" id="cb1"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="co">// Turn a query object and a value into a queryable environment</span></span>
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a><span class="co">// (see [@P3325R2]):</span></span>
<span id="cb1-3"><a href="#cb1-3" aria-hidden="true" tabindex="-1"></a><span class="kw">template</span> <span class="op">&lt;</span><span class="kw">class</span> Query, <span class="kw">class</span> Value<span class="op">&gt;</span></span>
<span id="cb1-4"><a href="#cb1-4" aria-hidden="true" tabindex="-1"></a><span class="kw">struct</span> prop <span class="op">{</span></span>
<span id="cb1-5"><a href="#cb1-5" aria-hidden="true" tabindex="-1"></a>  Query query;</span>
<span id="cb1-6"><a href="#cb1-6" aria-hidden="true" tabindex="-1"></a>  Value value;</span>
<span id="cb1-7"><a href="#cb1-7" aria-hidden="true" tabindex="-1"></a>  <span class="kw">decltype</span><span class="op">(</span><span class="kw">auto</span><span class="op">)</span> query<span class="op">(</span>Query<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="op">(</span>value<span class="op">)</span>; <span class="op">}</span></span>
<span id="cb1-8"><a href="#cb1-8" aria-hidden="true" tabindex="-1"></a><span class="op">}</span>;</span>
<span id="cb1-9"><a href="#cb1-9" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-10"><a href="#cb1-10" aria-hidden="true" tabindex="-1"></a><span class="co">// Adapts a sender so that it can use the given allocator:</span></span>
<span id="cb1-11"><a href="#cb1-11" aria-hidden="true" tabindex="-1"></a><span class="kw">struct</span> with_allocator_t <span class="op">{</span></span>
<span id="cb1-12"><a href="#cb1-12" 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>sender Sndr, <span class="kw">class</span> Alloc<span class="op">&gt;</span></span>
<span id="cb1-13"><a href="#cb1-13" aria-hidden="true" tabindex="-1"></a>  <span class="kw">auto</span> <span class="kw">operator</span><span class="op">()(</span>Sndr sndr, Alloc alloc<span class="op">)</span> <span class="kw">const</span> <span class="op">{</span></span>
<span id="cb1-14"><a href="#cb1-14" aria-hidden="true" tabindex="-1"></a>    <span class="cf">return</span> std<span class="op">::</span>execution<span class="op">::</span>write_env<span class="op">(</span>sndr, prop<span class="op">(</span>std<span class="op">::</span>get_allocator, alloc<span class="op">))</span>;</span>
<span id="cb1-15"><a href="#cb1-15" aria-hidden="true" tabindex="-1"></a>  <span class="op">}</span></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 class="kw">template</span> <span class="op">&lt;</span><span class="kw">class</span> Alloc<span class="op">&gt;</span></span>
<span id="cb1-18"><a href="#cb1-18" aria-hidden="true" tabindex="-1"></a>  <span class="kw">auto</span> <span class="kw">operator</span><span class="op">()(</span>Alloc alloc<span class="op">)</span> <span class="kw">const</span> <span class="op">{</span></span>
<span id="cb1-19"><a href="#cb1-19" aria-hidden="true" tabindex="-1"></a>    <span class="cf">return</span> std<span class="op">::</span>execution<span class="op">::</span>write_env<span class="op">(</span>prop<span class="op">(</span>std<span class="op">::</span>get_allocator, alloc<span class="op">))</span>;</span>
<span id="cb1-20"><a href="#cb1-20" aria-hidden="true" tabindex="-1"></a>  <span class="op">}</span></span>
<span id="cb1-21"><a href="#cb1-21" aria-hidden="true" tabindex="-1"></a><span class="op">}</span>;</span>
<span id="cb1-22"><a href="#cb1-22" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-23"><a href="#cb1-23" aria-hidden="true" tabindex="-1"></a><span class="kw">constexpr</span> with_allocator_t with_allocator<span class="op">{}</span>;</span></code></pre></div>
<p>The <code class="sourceCode default">with_allocator</code> adaptor
might be used to parameterize senders produced by a third-party library
as follows:</p>
<div class="sourceCode" id="cb2"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a><span class="kw">namespace</span> ex <span class="op">=</span> std<span class="op">::</span>execution;</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><span class="co">// This returns a sender that does some piece of asynchronous work</span></span>
<span id="cb2-4"><a href="#cb2-4" aria-hidden="true" tabindex="-1"></a><span class="co">// created by a third-party library, but parameterized with a custom</span></span>
<span id="cb2-5"><a href="#cb2-5" aria-hidden="true" tabindex="-1"></a><span class="co">// allocator.</span></span>
<span id="cb2-6"><a href="#cb2-6" aria-hidden="true" tabindex="-1"></a>ex<span class="op">::</span>sender <span class="kw">auto</span> make_async_work_with_alloc<span class="op">()</span> <span class="op">{</span></span>
<span id="cb2-7"><a href="#cb2-7" aria-hidden="true" tabindex="-1"></a>  ex<span class="op">::</span>sender <span class="kw">auto</span> work <span class="op">=</span> third_party<span class="op">::</span>make_async_work<span class="op">()</span>;</span>
<span id="cb2-8"><a href="#cb2-8" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb2-9"><a href="#cb2-9" aria-hidden="true" tabindex="-1"></a>  <span class="cf">return</span> with_allocator<span class="op">(</span>std<span class="op">::</span>move<span class="op">(</span>work<span class="op">)</span>, custom_allocator<span class="op">())</span>;</span>
<span id="cb2-10"><a href="#cb2-10" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
<p>The sender returned by
<code class="sourceCode default">third_party::make_async_work</code>
might query for the allocator and use it to do allocations:</p>
<div class="sourceCode" id="cb3"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a><span class="kw">namespace</span> third_party <span class="op">{</span></span>
<span id="cb3-2"><a href="#cb3-2" aria-hidden="true" tabindex="-1"></a>  <span class="kw">namespace</span> ex <span class="op">=</span> std<span class="op">::</span>execution;</span>
<span id="cb3-3"><a href="#cb3-3" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-4"><a href="#cb3-4" aria-hidden="true" tabindex="-1"></a>  <span class="co">// A function that returns a sender that generates data on a special</span></span>
<span id="cb3-5"><a href="#cb3-5" aria-hidden="true" tabindex="-1"></a>  <span class="co">// execution context, populate a std::vector with it, and then completes</span></span>
<span id="cb3-6"><a href="#cb3-6" aria-hidden="true" tabindex="-1"></a>  <span class="co">// by sending the vector.</span></span>
<span id="cb3-7"><a href="#cb3-7" aria-hidden="true" tabindex="-1"></a>  <span class="kw">constexpr</span> <span class="kw">auto</span> _populate_data_vector <span class="op">=</span> </span>
<span id="cb3-8"><a href="#cb3-8" aria-hidden="true" tabindex="-1"></a>    <span class="op">[]&lt;</span><span class="kw">class</span> Allocator<span class="op">&gt;(</span>Allocator alloc<span class="op">)</span> <span class="op">{</span></span>
<span id="cb3-9"><a href="#cb3-9" aria-hidden="true" tabindex="-1"></a>      <span class="co">// Create an empty vector of ints that uses a specified allocator.</span></span>
<span id="cb3-10"><a href="#cb3-10" aria-hidden="true" tabindex="-1"></a>      <span class="kw">using</span> IntAlloc <span class="op">=</span> std<span class="op">::</span>allocator_traits<span class="op">&lt;</span>Allocator<span class="op">&gt;::</span><span class="kw">template</span> rebind_alloc<span class="op">&lt;</span><span class="dt">int</span><span class="op">&gt;</span>;</span>
<span id="cb3-11"><a href="#cb3-11" aria-hidden="true" tabindex="-1"></a>      <span class="kw">auto</span> data <span class="op">=</span> std<span class="op">::</span>vector<span class="op">&lt;</span><span class="dt">int</span>, IntAlloc<span class="op">&gt;{</span>IntAlloc<span class="op">{</span>std<span class="op">::</span>move<span class="op">(</span>alloc<span class="op">)}}</span>;</span>
<span id="cb3-12"><a href="#cb3-12" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-13"><a href="#cb3-13" aria-hidden="true" tabindex="-1"></a>      <span class="co">// Create some work that generates data and fills in the vector.</span></span>
<span id="cb3-14"><a href="#cb3-14" aria-hidden="true" tabindex="-1"></a>      <span class="kw">auto</span> work <span class="op">=</span> ex<span class="op">::</span>just<span class="op">(</span>std<span class="op">::</span>move<span class="op">(</span>data<span class="op">))</span></span>
<span id="cb3-15"><a href="#cb3-15" aria-hidden="true" tabindex="-1"></a>        <span class="op">|</span> ex<span class="op">::</span>then<span class="op">([](</span><span class="kw">auto</span> data<span class="op">)</span> <span class="op">{</span></span>
<span id="cb3-16"><a href="#cb3-16" aria-hidden="true" tabindex="-1"></a>            <span class="co">// Generate the data and fill in the vector:</span></span>
<span id="cb3-17"><a href="#cb3-17" aria-hidden="true" tabindex="-1"></a>            data<span class="op">.</span>append_range<span class="op">(</span>third_party<span class="op">::</span>make_data<span class="op">())</span></span>
<span id="cb3-18"><a href="#cb3-18" aria-hidden="true" tabindex="-1"></a>            <span class="cf">return</span> data;</span>
<span id="cb3-19"><a href="#cb3-19" aria-hidden="true" tabindex="-1"></a>          <span class="op">})</span>;</span>
<span id="cb3-20"><a href="#cb3-20" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-21"><a href="#cb3-21" aria-hidden="true" tabindex="-1"></a>      <span class="co">// Execute the work on a special third_party execution context:</span></span>
<span id="cb3-22"><a href="#cb3-22" aria-hidden="true" tabindex="-1"></a>      <span class="co">// (This uses the `on` as specified in P3175.)</span></span>
<span id="cb3-23"><a href="#cb3-23" aria-hidden="true" tabindex="-1"></a>      <span class="cf">return</span> ex<span class="op">::</span>on<span class="op">(</span>third_party_scheduler<span class="op">()</span>, std<span class="op">::</span>move<span class="op">(</span>work<span class="op">))</span>;</span>
<span id="cb3-24"><a href="#cb3-24" aria-hidden="true" tabindex="-1"></a>    <span class="op">}</span>;</span>
<span id="cb3-25"><a href="#cb3-25" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-26"><a href="#cb3-26" aria-hidden="true" tabindex="-1"></a>  <span class="co">// A function that returns the sender produced by `_populate_data_vector`,</span></span>
<span id="cb3-27"><a href="#cb3-27" aria-hidden="true" tabindex="-1"></a>  <span class="co">// parameterized by an allocator read out of the receiver&#39;s environment.</span></span>
<span id="cb3-28"><a href="#cb3-28" aria-hidden="true" tabindex="-1"></a>  ex<span class="op">::</span>sender <span class="kw">auto</span> make_async_work<span class="op">()</span> <span class="op">{</span></span>
<span id="cb3-29"><a href="#cb3-29" aria-hidden="true" tabindex="-1"></a>    <span class="cf">return</span> ex<span class="op">::</span>let_value<span class="op">(</span></span>
<span id="cb3-30"><a href="#cb3-30" aria-hidden="true" tabindex="-1"></a>      <span class="co">// This reads the allocator out of the receiver&#39;s execution environment.</span></span>
<span id="cb3-31"><a href="#cb3-31" aria-hidden="true" tabindex="-1"></a>      ex<span class="op">::</span>read_env<span class="op">(</span>std<span class="op">::</span>get_allocator<span class="op">)</span>,</span>
<span id="cb3-32"><a href="#cb3-32" aria-hidden="true" tabindex="-1"></a>      _populate_data_vector</span>
<span id="cb3-33"><a href="#cb3-33" aria-hidden="true" tabindex="-1"></a>    <span class="op">)</span>;</span>
<span id="cb3-34"><a href="#cb3-34" aria-hidden="true" tabindex="-1"></a>  <span class="op">}</span></span>
<span id="cb3-35"><a href="#cb3-35" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
<h2 data-number="3.2" id="unstoppable"><span class="header-section-number">3.2</span>
<code class="sourceCode default">unstoppable</code><a href="#unstoppable" class="self-link"></a></h2>
<p>The <code class="sourceCode default">unstoppable</code> sender
adaptor is a trivial application of
<code class="sourceCode default">write_env</code> that modifies a sender
so that it no longer responds to external stop requests. That can be of
critical importance when the successful completion of a sender is
necessary to ensure program correctness, <em>e.g.</em>, to restore an
invariant.</p>
<p>The <code class="sourceCode default">unstoppable</code> adaptor might
be implemented as follows:</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><span class="kw">inline</span> <span class="kw">constexpr</span> <span class="kw">struct</span> <em>unstoppable-t</em> <span class="op">{</span></span>
<span id="cb4-2"><a href="#cb4-2" aria-hidden="true" tabindex="-1"></a>  <span class="kw">template</span> <span class="op">&lt;</span>sender Sndr<span class="op">&gt;</span></span>
<span id="cb4-3"><a href="#cb4-3" aria-hidden="true" tabindex="-1"></a>  <span class="kw">auto</span> <span class="kw">operator</span><span class="op">()(</span>Sndr sndr<span class="op">)</span> <span class="kw">const</span> <span class="op">{</span></span>
<span id="cb4-4"><a href="#cb4-4" aria-hidden="true" tabindex="-1"></a>    <span class="cf">return</span> write_env<span class="op">(</span>std<span class="op">::</span>move<span class="op">(</span>sndr<span class="op">)</span>, prop<span class="op">(</span>std<span class="op">::</span>get_stop_token, never_stop_token<span class="op">()))</span>;</span>
<span id="cb4-5"><a href="#cb4-5" aria-hidden="true" tabindex="-1"></a>  <span class="op">}</span></span>
<span id="cb4-6"><a href="#cb4-6" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-7"><a href="#cb4-7" aria-hidden="true" tabindex="-1"></a>  <span class="kw">auto</span> <span class="kw">operator</span><span class="op">()()</span> <span class="kw">const</span> <span class="op">{</span></span>
<span id="cb4-8"><a href="#cb4-8" aria-hidden="true" tabindex="-1"></a>    <span class="cf">return</span> write_env<span class="op">(</span>prop<span class="op">(</span>std<span class="op">::</span>get_stop_token, never_stop_token<span class="op">()))</span>;</span>
<span id="cb4-9"><a href="#cb4-9" aria-hidden="true" tabindex="-1"></a>  <span class="op">}</span></span>
<span id="cb4-10"><a href="#cb4-10" aria-hidden="true" tabindex="-1"></a><span class="op">}</span> unstoppable <span class="op">{}</span>;</span></code></pre></div>
<p>The section describing the
<code class="sourceCode default">finally</code> adaptor will give a
motivating example that makes use of
<code class="sourceCode default">unstoppable</code>.</p>
<h2 data-number="3.3" id="finally"><span class="header-section-number">3.3</span>
<code class="sourceCode default">finally</code><a href="#finally" class="self-link"></a></h2>
<p>The C++ language lacks direct support for asynchronous destruction;
that is, there is no way to say, “After this asynchronous operation,
unconditionally run another asynchronous operation, regardless of how
the first one completes.” Without this capability, there is no native
way to have “async RAII”: the pairing the asynchronous acquisition of a
resource with its asynchronous reclaimation.</p>
<p>The <code class="sourceCode default">finally</code> sender adaptor
captures the “async RAII” pattern in the sender domain.
<code class="sourceCode default">finally</code> takes two senders. When
connected and started, the
<code class="sourceCode default">finally</code> sender connects and
starts the first sender. When that sender completes, it saves the
asynchronous result and then connects and starts the second sender. If
the second sender completes successfully, the results from the first
sender are propagated. Otherwise, the results from the second sender are
propagated.</p>
<p>There is a sender in [exec] very much like
<code class="sourceCode default">finally</code> as described above:
<code class="sourceCode default">schedule_from</code>. The only
meaningful difference is that in
<code class="sourceCode default">schedule_from</code>, the “second
sender” is always the result of calling
<code class="sourceCode default">schedule</code> on a scheduler. With
<code class="sourceCode default">finally</code>, the default
implementation of <code class="sourceCode default">schedule_from</code>
is trivial:</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><span class="kw">template</span> <span class="op">&lt;</span>sender Sndr, scheduler Sched<span class="op">&gt;</span></span>
<span id="cb5-2"><a href="#cb5-2" aria-hidden="true" tabindex="-1"></a><span class="kw">auto</span> <em>default-schedule-from-impl</em><span class="op">(</span>Sndr sndr, Sched sched<span class="op">)</span> <span class="op">{</span></span>
<span id="cb5-3"><a href="#cb5-3" aria-hidden="true" tabindex="-1"></a>  <span class="cf">return</span> finally<span class="op">(</span>std<span class="op">::</span>move<span class="op">(</span>sndr<span class="op">)</span>, unstoppable<span class="op">(</span>schedule<span class="op">(</span>sched<span class="op">)))</span>;</span>
<span id="cb5-4"><a href="#cb5-4" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
<p>This paper proposes repurposing the wording of
<code class="sourceCode default">schedule_from</code> to specify
<code class="sourceCode default">finally</code>, and then specifying
<code class="sourceCode default">schedule_from</code> in terms of
<code class="sourceCode default">finally</code> and
<code class="sourceCode default">unstoppable</code>.</p>
<h3 data-number="3.3.1" id="example-finally"><span class="header-section-number">3.3.1</span> Example:
<code class="sourceCode default">finally</code><a href="#example-finally" class="self-link"></a></h3>
<p>In the following example, some asynchronous work must temporarily
break a program invariant. It uses
<code class="sourceCode default">unstoppable</code> and
<code class="sourceCode default">finally</code> to restore the
invariant.</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>  <span class="kw">namespace</span> ex <span class="op">=</span> std<span class="op">::</span>execution;</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>  ex<span class="op">::</span>sender <span class="kw">auto</span> break_invariants<span class="op">(</span><span class="kw">auto</span><span class="op">&amp;...</span> values<span class="op">)</span>;</span>
<span id="cb6-4"><a href="#cb6-4" aria-hidden="true" tabindex="-1"></a>  ex<span class="op">::</span>sender <span class="kw">auto</span> restore_invariants<span class="op">(</span><span class="kw">auto</span><span class="op">&amp;...</span> values<span class="op">)</span>;</span>
<span id="cb6-5"><a href="#cb6-5" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb6-6"><a href="#cb6-6" aria-hidden="true" tabindex="-1"></a>  <span class="co">// This function returns a sender adaptor closure object. When applied to</span></span>
<span id="cb6-7"><a href="#cb6-7" aria-hidden="true" tabindex="-1"></a>  <span class="co">// a sender, it returns a new sender that breaks program invariants,</span></span>
<span id="cb6-8"><a href="#cb6-8" aria-hidden="true" tabindex="-1"></a>  <span class="co">// munges the data, and restores the invariants.</span></span>
<span id="cb6-9"><a href="#cb6-9" aria-hidden="true" tabindex="-1"></a>  <span class="kw">auto</span> safely_munge_data<span class="op">(</span> <span class="op">)</span> <span class="op">{</span></span>
<span id="cb6-10"><a href="#cb6-10" aria-hidden="true" tabindex="-1"></a>    <span class="cf">return</span> ex<span class="op">::</span>let_value<span class="op">(</span> <span class="op">[](</span><span class="kw">auto</span><span class="op">&amp;...</span> values<span class="op">)</span> <span class="op">{</span></span>
<span id="cb6-11"><a href="#cb6-11" aria-hidden="true" tabindex="-1"></a>        <span class="cf">return</span> break_invariants<span class="op">(</span>values<span class="op">...)</span></span>
<span id="cb6-12"><a href="#cb6-12" aria-hidden="true" tabindex="-1"></a>          <span class="op">|</span> ex<span class="op">::</span>then<span class="op">(</span>do_munge<span class="op">)</span> <span class="co">// the invariants will be restored even if `do_munge` throws</span></span>
<span id="cb6-13"><a href="#cb6-13" aria-hidden="true" tabindex="-1"></a>          <span class="op">|</span> ex<span class="op">::</span>finally<span class="op">(</span>ex<span class="op">::</span>unstoppable<span class="op">(</span>restore_invariants<span class="op">(</span>values<span class="op">...)))</span>;</span>
<span id="cb6-14"><a href="#cb6-14" aria-hidden="true" tabindex="-1"></a>    <span class="op">}</span> <span class="op">)</span>;</span>
<span id="cb6-15"><a href="#cb6-15" aria-hidden="true" tabindex="-1"></a>  <span class="op">}</span></span>
<span id="cb6-16"><a href="#cb6-16" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb6-17"><a href="#cb6-17" aria-hidden="true" tabindex="-1"></a>  <span class="kw">auto</span> sndr <span class="op">=</span> <span class="op">...</span>;</span>
<span id="cb6-18"><a href="#cb6-18" aria-hidden="true" tabindex="-1"></a>  scope<span class="op">.</span>spawn<span class="op">(</span> sndr <span class="op">|</span> safely_munge_data<span class="op">()</span> <span class="op">)</span>; <span class="co">// See `counting_scope` from P3149R2</span></span></code></pre></div>
<h1 data-number="4" id="discussion"><span class="header-section-number">4</span> Discussion<a href="#discussion" class="self-link"></a></h1>
<p>There are a number of design considerations for the
<code class="sourceCode default">finally</code> algorithm. The following
questions have been brought up during LEWG design review:</p>
<h2 data-number="4.1" id="should-finally-apply-unstoppable-by-default-to-its-second-argument"><span class="header-section-number">4.1</span> Should
<code class="sourceCode default">finally</code> apply
<code class="sourceCode default">unstoppable</code> by default to its
second argument?<a href="#should-finally-apply-unstoppable-by-default-to-its-second-argument" class="self-link"></a></h2>
<p>The observation was made that, since
<code class="sourceCode default">finally</code> will often be used to do
some cleanup operation or to restore an invariant, that operation should
not respond to external stop requests, so
<code class="sourceCode default">unstoppable</code> should be the
default. It’s a reasonable suggestion. Of course, there would need to be
a way to override the default and allow the cleanup action to be
canceled, and it isn’t clear what the syntax for that would be. Another
adaptor called
<code class="sourceCode default">stoppable_finally</code>?</p>
<p>It is worth noting that
<code class="sourceCode default">unstoppable</code> has uses besides
<code class="sourceCode default">finally</code>, so it arguably should
exist regardless of what the default behavior of
<code class="sourceCode default">finally</code> is. Given that
<code class="sourceCode default">unstoppable</code> should exist anyway,
and that its behavior is pleasantly orthogonal to
<code class="sourceCode default">finally</code>, the authors decided to
keep them separate and let users combine them how they like.</p>
<h2 data-number="4.2" id="is-there-a-different-design-that-better-captures-the-async-raii-intent"><span class="header-section-number">4.2</span> Is there a different design
that better captures the “async RAII” intent?<a href="#is-there-a-different-design-that-better-captures-the-async-raii-intent" class="self-link"></a></h2>
<p>Undoubtedly, the answer is “yes.” There are probably several such
designs. One design that has been explored by Kirk Shoop is the
so-called “async object” (<span class="citation" data-cites="P2849R0">[<a href="#ref-P2849R0" role="doc-biblioref">P2849R0</a>]</span>).</p>
<p>In Kirk’s design, an async object has two basis operations:
<code class="sourceCode default">async_construct</code> and
<code class="sourceCode default">async_destruct</code>, both of which
are asynchronous; that is, they all return senders. When
<code class="sourceCode default">async_construct</code> completes, it
does so with a handle to the object. The handle lets you interact with
the object. Calling
<code class="sourceCode default">async_destruct</code> on the object
ends its lifetime.</p>
<p>A stop source, a run loop, and an async scope like <span class="citation" data-cites="P3149R2">[<a href="#ref-P3149R2" role="doc-biblioref">P3149R2</a>]</span>’s
<code class="sourceCode default">counting_scope</code> can all be given
the async object treatment. Multiple such async resources can be used in
tandem, as in the following example:</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><span class="kw">namespace</span> ex <span class="op">=</span> std<span class="op">::</span>execution;</span>
<span id="cb7-2"><a href="#cb7-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb7-3"><a href="#cb7-3" aria-hidden="true" tabindex="-1"></a><span class="co">// In this example, stop_source_object, run_loop_object, and counting_scope_object</span></span>
<span id="cb7-4"><a href="#cb7-4" aria-hidden="true" tabindex="-1"></a><span class="co">// all satisfy the async_object concept.</span></span>
<span id="cb7-5"><a href="#cb7-5" aria-hidden="true" tabindex="-1"></a>stop_source_object stop;</span>
<span id="cb7-6"><a href="#cb7-6" aria-hidden="true" tabindex="-1"></a>run_loop_object loop;</span>
<span id="cb7-7"><a href="#cb7-7" aria-hidden="true" tabindex="-1"></a>counting_scope_object scope;</span>
<span id="cb7-8"><a href="#cb7-8" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb7-9"><a href="#cb7-9" aria-hidden="true" tabindex="-1"></a><span class="kw">auto</span> thread_fn <span class="op">=</span> <span class="op">[](</span><span class="kw">auto</span> stop, <span class="kw">auto</span> loop, <span class="kw">auto</span> scope<span class="op">)</span> <span class="op">{</span></span>
<span id="cb7-10"><a href="#cb7-10" aria-hidden="true" tabindex="-1"></a>  <span class="co">// `thread_fn` must return a sender that uses the handles passed in.</span></span>
<span id="cb7-11"><a href="#cb7-11" aria-hidden="true" tabindex="-1"></a>  packaged_async_object thread<span class="op">{</span>thread_object<span class="op">{}</span>, <span class="op">[</span>loop<span class="op">]{</span> loop<span class="op">.</span>run<span class="op">()</span>; <span class="op">}}</span>;</span>
<span id="cb7-12"><a href="#cb7-12" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb7-13"><a href="#cb7-13" aria-hidden="true" tabindex="-1"></a>  <span class="kw">auto</span> work_fn <span class="op">=</span> <span class="op">[=](</span><span class="kw">auto</span> <span class="co">/*thread*/</span><span class="op">)</span> <span class="op">{</span></span>
<span id="cb7-14"><a href="#cb7-14" aria-hidden="true" tabindex="-1"></a>    <span class="co">// construct a stop callback that requests top on the run_loop when</span></span>
<span id="cb7-15"><a href="#cb7-15" aria-hidden="true" tabindex="-1"></a>    <span class="co">// stop is requested on the stop_source_object.</span></span>
<span id="cb7-16"><a href="#cb7-16" aria-hidden="true" tabindex="-1"></a>    <span class="kw">auto</span> stop_callback <span class="op">=</span> <span class="op">[]&lt;</span><span class="kw">class</span> Token, <span class="kw">class</span> Fn<span class="op">&gt;(</span>Token tok, Fn fn<span class="op">)</span> <span class="op">{</span></span>
<span id="cb7-17"><a href="#cb7-17" aria-hidden="true" tabindex="-1"></a>      <span class="cf">return</span> stop_callback_of_t<span class="op">&lt;</span>Token, Fn<span class="op">&gt;{</span>tok, fn<span class="op">}</span>;</span>
<span id="cb7-18"><a href="#cb7-18" aria-hidden="true" tabindex="-1"></a>    <span class="op">}(</span>stop<span class="op">.</span>get_token<span class="op">()</span>, <span class="op">[</span>loop<span class="op">]</span> <span class="op">{</span> loop<span class="op">.</span>request_stop<span class="op">()</span>; <span class="op">})</span>;</span>
<span id="cb7-19"><a href="#cb7-19" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb7-20"><a href="#cb7-20" aria-hidden="true" tabindex="-1"></a>    <span class="co">// spawn 1000 tasks on the run_loop in the counting_scope.</span></span>
<span id="cb7-21"><a href="#cb7-21" 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>; i <span class="op">&lt;</span> <span class="dv">1000</span>; <span class="op">++</span>i<span class="op">)</span> <span class="op">{</span></span>
<span id="cb7-22"><a href="#cb7-22" aria-hidden="true" tabindex="-1"></a>      <span class="kw">auto</span> work <span class="op">=</span> ex<span class="op">::</span>then<span class="op">(</span>ex<span class="op">::</span>just<span class="op">()</span>, <span class="op">[=]{</span> do_work<span class="op">(</span>i<span class="op">)</span>;<span class="op">})</span>;</span>
<span id="cb7-23"><a href="#cb7-23" aria-hidden="true" tabindex="-1"></a>      ex<span class="op">::</span>spawn<span class="op">(</span>ex<span class="op">::</span>on<span class="op">(</span>loop<span class="op">.</span>get_scheduler<span class="op">()</span>, std<span class="op">::</span>move<span class="op">(</span>work<span class="op">))</span>, scope<span class="op">)</span>;</span>
<span id="cb7-24"><a href="#cb7-24" aria-hidden="true" tabindex="-1"></a>    <span class="op">}</span></span>
<span id="cb7-25"><a href="#cb7-25" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb7-26"><a href="#cb7-26" aria-hidden="true" tabindex="-1"></a>    <span class="co">// Tell the run_loop to stop when it has finished processing the work:</span></span>
<span id="cb7-27"><a href="#cb7-27" aria-hidden="true" tabindex="-1"></a>    scope_guard guard<span class="op">{[]{</span>loop<span class="op">.</span>request_stop<span class="op">()}}</span>;</span>
<span id="cb7-28"><a href="#cb7-28" aria-hidden="true" tabindex="-1"></a>    <span class="cf">return</span> ex<span class="op">::</span>just<span class="op">()</span>;</span>
<span id="cb7-29"><a href="#cb7-29" aria-hidden="true" tabindex="-1"></a>  <span class="op">}</span>;</span>
<span id="cb7-30"><a href="#cb7-30" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb7-31"><a href="#cb7-31" aria-hidden="true" tabindex="-1"></a>  <span class="cf">return</span> async_using<span class="op">(</span>work_fn, thread<span class="op">)</span>;</span>
<span id="cb7-32"><a href="#cb7-32" aria-hidden="true" tabindex="-1"></a><span class="op">}</span>;</span>
<span id="cb7-33"><a href="#cb7-33" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb7-34"><a href="#cb7-34" aria-hidden="true" tabindex="-1"></a><span class="co">// This sender, when connected and started, will async-construct the</span></span>
<span id="cb7-35"><a href="#cb7-35" aria-hidden="true" tabindex="-1"></a><span class="co">// async objects, passes them as arguments to the thread_fn, start</span></span>
<span id="cb7-36"><a href="#cb7-36" aria-hidden="true" tabindex="-1"></a><span class="co">// the returned sender, and when it completes, async-destruct the</span></span>
<span id="cb7-37"><a href="#cb7-37" aria-hidden="true" tabindex="-1"></a><span class="co">// async objects in the reverse order of their construction:</span></span>
<span id="cb7-38"><a href="#cb7-38" aria-hidden="true" tabindex="-1"></a>ex<span class="op">::</span>sender <span class="kw">auto</span> use <span class="op">=</span> async_using<span class="op">(</span>thread_fn, stop, loop, scope<span class="op">)</span>;</span>
<span id="cb7-39"><a href="#cb7-39" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb7-40"><a href="#cb7-40" aria-hidden="true" tabindex="-1"></a><span class="co">// Launch it all and wait for it to complete:</span></span>
<span id="cb7-41"><a href="#cb7-41" aria-hidden="true" tabindex="-1"></a>std<span class="op">::</span>this_thread<span class="op">::</span>sync_wait<span class="op">(</span>std<span class="op">::</span>move<span class="op">(</span>use<span class="op">))</span>;</span></code></pre></div>
<p>This design nicely captures the “async RAII” pattern. A type modeling
the async object concept is like an async class with an async
constructor and an async destructor. Instead of using
<code class="sourceCode default">finally</code>, a user can implement a
type that satisfies the
<code class="sourceCode default">async_object</code> concept.</p>
<p>Although there are times when it is appropriate to model the
<code class="sourceCode default">async_object</code> concept as
presented in <span class="citation" data-cites="P2849R0">[<a href="#ref-P2849R0" role="doc-biblioref">P2849R0</a>]</span>, doing so
is certainly more work than just using
<code class="sourceCode default">finally</code>. One can think of
<code class="sourceCode default">finally</code> as an <em>ad hoc</em>
form of async RAII. To draw an analogy,
<code class="sourceCode default">finally</code> is to
<code class="sourceCode default">async_object</code> what
<code class="sourceCode default">scope_guard</code> is to custom RAII
wrappers like <code class="sourceCode default">unique_ptr</code>. That
is no diss on <code class="sourceCode default">scope_guard</code>; it
has its place!</p>
<p>So too does <code class="sourceCode default">finally</code> in the
authors’ opinion. It captures a common pattern quite simply, and is not
a far departure from what is in [exec] already. An async object
abstraction is a much heavier lift from a standardization point of view.
Pursuing that design instead of
<code class="sourceCode default">finally</code> risks missing the C++26
boat, leaving users without a standard way to reliably clean up
asynchronous resources.</p>
<p>In the end, the authors expect that we will have both, just as many
codebases make use of both
<code class="sourceCode default">scope_guard</code> and
<code class="sourceCode default">unique_ptr</code>.</p>
<h1 data-number="5" id="proposed-wording"><span class="header-section-number">5</span> Proposed Wording<a href="#proposed-wording" class="self-link"></a></h1>
<p><span class="ednote" style="color: #0000ff">[ Editor&#39;s note: The
wording in this section is based on the current working draft.
]</span></p>
<p><span class="ednote" style="color: #0000ff">[ Editor&#39;s note: Change
[exec.syn] as follows: ]</span></p>
<div class="sourceCode" id="cb8"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb8-1"><a href="#cb8-1" aria-hidden="true" tabindex="-1"></a>  <span class="add" style="color: #006e28"><ins><span><code class="sourceCode default">inline constexpr <em>unspecified</em> write_env{};</code></span></ins></span></span>
<span id="cb8-2"><a href="#cb8-2" aria-hidden="true" tabindex="-1"></a>  <span class="add" style="color: #006e28"><ins><span><code class="sourceCode default">inline constexpr <em>unspecified</em> unstoppable{};</code></span></ins></span></span>
<span id="cb8-3"><a href="#cb8-3" aria-hidden="true" tabindex="-1"></a>  inline constexpr start_on_t start_on{};</span>
<span id="cb8-4"><a href="#cb8-4" aria-hidden="true" tabindex="-1"></a>  inline constexpr continue_on_t continue_on{};</span>
<span id="cb8-5"><a href="#cb8-5" aria-hidden="true" tabindex="-1"></a>  inline constexpr on_t on{};</span>
<span id="cb8-6"><a href="#cb8-6" aria-hidden="true" tabindex="-1"></a>  <span class="add" style="color: #006e28"><ins><span><code class="sourceCode default">inline constexpr <em>unspecified</em> finally{};</code></span></ins></span></span>
<span id="cb8-7"><a href="#cb8-7" aria-hidden="true" tabindex="-1"></a>  inline constexpr schedule_from_t schedule_from{};</span></code></pre></div>
<p><span class="ednote" style="color: #0000ff">[ Editor&#39;s note: Replace
all instances of
“<em><code class="sourceCode default">write-env</code></em>” with
“<code class="sourceCode default">write_env</code>”. After
[exec.adapt.objects], add a new subsection
“<code class="sourceCode default">execution::write_env</code>
[exec.write.env]” and move the specification of the exposition-only
<em><code class="sourceCode default">write-env</code></em> from
[exec.snd.general]/p3.15 into it with the following modifications:
]</span></p>
<p><font size="+1"><span class="marginalizedparent"><a class="marginalized">(34.9.11.?)</a></span></font>
<strong><code class="sourceCode default">execution::write_env</code>
[exec.write.env]</strong></font></p>
<ol type="1">
<li><span class="rm" style="color: #bf0303"><del><em><span><code class="sourceCode default">write-env</code></span></em></del></span><span class="add" style="color: #006e28"><ins><span><code class="sourceCode default">write_env</code></span></ins></span>
is <span class="rm" style="color: #bf0303"><del>an
exposition-only</del></span><span class="add" style="color: #006e28"><ins>a</ins></span> sender adaptor that <span class="add" style="color: #006e28"><ins>accepts a sender and a queryable
object, and that returns a sender that</ins></span>, when connected with
a receiver <code class="sourceCode default">rcvr</code>, connects the
adapted sender with a receiver whose execution environment is the result
of joining the <code class="sourceCode default">queryable</code> <span class="rm" style="color: #bf0303"><del>argument
<span><code class="sourceCode default">env</code></span></del></span><span class="add" style="color: #006e28"><ins>object</ins></span> to the
result of <code class="sourceCode default">get_env(rcvr)</code>.</li>
</ol>
<div class="rm" style="color: #bf0303">

<ol start="2" type="1">
<li><p>Let <i><code class="sourceCode default">write-env-t</code></i> be
an exposition-only empty class type.</p></li>
<li><p><em>Returns:</em> <code class="sourceCode default"><em>make-sender</em>(<em>make-env-t</em>(), std::forward&lt;Env&gt;(env), std::forward&lt;Sndr&gt;(sndr))</code>.</p></li>
</ol>

</div>
<div class="add" style="color: #006e28">

<ol start="2" type="1">
<li><code class="sourceCode default">write_env</code> is a customization
point object. For some subexpressions
<code class="sourceCode default">sndr</code> and
<code class="sourceCode default">env</code>, if
<code class="sourceCode default">decltype((sndr))</code> does not
satisfy <code class="sourceCode default">sender</code> or if
<code class="sourceCode default">decltype((env))</code> does not satisfy
<em><code class="sourceCode default">queryable</code></em>, the
expression <code class="sourceCode default">write_env(sndr, env)</code>
is ill-formed. Otherwise, it is expression-equivalent to
<code><i>make-sender</i>(write_env, env, sndr)</code>.</li>
</ol>

</div>
<ol start="3" type="1">
<li><p><span class="rm" style="color: #bf0303"><del><em>Remarks:</em></del></span> The
exposition-only class template
<em><code class="sourceCode default">impls-for</code></em>
([exec.snd.general]) is specialized for <span class="rm" style="color: #bf0303"><del><em><span><code class="sourceCode default">write-env-t</code></span></em></del></span><span class="add" style="color: #006e28"><ins><span><code class="sourceCode default">write_env</code></span></ins></span>
as follows:</p>
<div class="sourceCode" id="cb9"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb9-1"><a href="#cb9-1" aria-hidden="true" tabindex="-1"></a>template&lt;&gt;</span>
<span id="cb9-2"><a href="#cb9-2" aria-hidden="true" tabindex="-1"></a>struct <em>impls-for</em>&lt;<span class="rm" style="color: #bf0303"><del><span><code class="sourceCode default"><em>write-env-t</em></code></span></del></span><span class="add" style="color: #006e28"><ins><span><code class="sourceCode default"><em>decayed-typeof</em>&lt;write_env&gt;</code></span></ins></span>&gt; : <em>default-impls</em> {</span>
<span id="cb9-3"><a href="#cb9-3" aria-hidden="true" tabindex="-1"></a>  static constexpr auto <em>get-env</em> =</span>
<span id="cb9-4"><a href="#cb9-4" aria-hidden="true" tabindex="-1"></a>    [](auto, const auto&amp; env, const auto&amp; rcvr) noexcept {</span>
<span id="cb9-5"><a href="#cb9-5" aria-hidden="true" tabindex="-1"></a>      return <em>JOIN-ENV</em>(env, get_env(rcvr));</span>
<span id="cb9-6"><a href="#cb9-6" aria-hidden="true" tabindex="-1"></a>    };</span>
<span id="cb9-7"><a href="#cb9-7" aria-hidden="true" tabindex="-1"></a>};</span></code></pre></div></li>
</ol>
<p><span class="ednote" style="color: #0000ff">[ Editor&#39;s note: After
[exec.write.env], add a new subsection
“<code class="sourceCode default">execution::unstoppable</code>
[exec.unstoppable]” as follows: ]</span></p>
<div class="add" style="color: #006e28">
<p><font size="+1"><span class="marginalizedparent"><a class="marginalized">(34.9.11.?)</a></span></font>
<strong><code class="sourceCode default">execution::unstoppable</code>
[exec.unstoppable]</strong></p>
<ol type="1">
<li><p><code class="sourceCode default">unstoppable</code> is a sender
adaptor that connects its inner sender with a receiver that has the
execution environment of the outer receiver but with a
<code class="sourceCode default">never_stop_token</code> as the value of
the <code class="sourceCode default">get_stop_token</code>
query.</p></li>
<li><p>For a subexpression <code class="sourceCode default">sndr</code>,
<code class="sourceCode default">unstoppable(sndr)</code> is expression
equivalent to <code>write_env(sndr, <em>MAKE-ENV</em>(get_stop_token,
never_stop_token{}))</code>.</p>
</blockquote>
</div></li>
</ol>

</div>
<p><span class="ednote" style="color: #0000ff">[ Editor&#39;s note: Change
subsection
“<code class="sourceCode default">execution::schedule_from</code>
[exec.schedule.from]” to
“<code class="sourceCode default">execution::finally</code>
[exec.finally]”, change every instance of
“<code class="sourceCode default">schedule_from</code>” to
“<code class="sourceCode default">finally</code>” and
“<code class="sourceCode default">schedule_from_t</code>” to “<code class="sourceCode default"><em>decayed-typeof</em>&lt;finally&gt;</code>”,
and change the subsection as follows: ]</span></p>
<p><font size="+1"><span class="marginalizedparent"><a class="marginalized">(34.9.11.5)</a></span></font>
<strong><code class="sourceCode default">execution::finally</code>
[exec.finally]</strong></p>
<p><span class="ednote" style="color: #0000ff">[ Editor&#39;s note: Replace
paragraphs 1-3 with the following: ]</span></p>
<div class="add" style="color: #006e28">

<ol type="1">
<li><p><code class="sourceCode default">finally</code> is a sender
adaptor that starts one sender unconditionally after another sender
completes. If the second sender completes successfully, the
<code class="sourceCode default">finally</code> sender completes with
the async results of the first sender. If the second sender completes
with error or stopped, the async results of the first sender are
discarded, and the <code class="sourceCode default">finally</code>
sender completes with the async results of the second sender. <span class="note"><span>[ <em>Note:</em> </span>It is similar in spirit to
the
<code class="sourceCode default">try</code>/<code class="sourceCode default">finally</code>
control structure of some languages.<span> — <em>end note</em>
]</span></span></p></li>
<li><p>The name <code class="sourceCode default">finally</code> denotes
a customization point object. For some subexpressions
<code class="sourceCode default">try_sndr</code> and
<code class="sourceCode default">finally_sndr</code>, if
<code class="sourceCode default">try_sndr</code> or
<code class="sourceCode default">finally_sndr</code> do not satisfy
<code class="sourceCode default">sender</code>, the expression
<code class="sourceCode default">finally(try_sndr, finally_sndr)</code>
is ill-formed.</p></li>
<li><p>Otherwise, the expression
<code class="sourceCode default">finally(try_sndr, finally_sndr)</code>
is expression-equivalent to <code class="sourceCode default"><em>make-sender</em>(finally, {}, try_sndr, finally_sndr)</code>.</p></li>
<li><p>Let <code class="sourceCode default">CS</code> be a
specialization of
<code class="sourceCode default">completion_signatures</code> whose
template parameters are the pack
<code class="sourceCode default">Sigs</code>. Let
<code class="sourceCode default"><em>VALID-FINALLY</em>(CS)</code> be
<code class="sourceCode default">true</code> if and only if there is no
type in <code class="sourceCode default">Sigs</code> of the form
<code class="sourceCode default">set_value_t(Ts...)</code> for which
<code class="sourceCode default">sizeof...(Ts)</code> is greater than
<code class="sourceCode default">0</code>. Let
<code class="sourceCode default">F</code> be
<code class="sourceCode default">decltype((finally_sndr))</code>. If
<code class="sourceCode default">sender_in&lt;F&gt;</code> is
<code class="sourceCode default">true</code> and <code class="sourceCode default"><em>VALID-FINALLY</em>(completion_signatures_of_t&lt;F&gt;)</code>
is <code class="sourceCode default">false</code>, the program is
ill-formed.</p></li>
</ol>

</div>
<ol start="5" type="1">
<li><p>The exposition-only class template
<em><code class="sourceCode default">impls-for</code></em>
([exec.snd.general]) is specialized for
<code class="sourceCode default">finally</code> as follows:</p>
<div class="sourceCode" id="cb10"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb10-1"><a href="#cb10-1" aria-hidden="true" tabindex="-1"></a>namespace std::execution {</span>
<span id="cb10-2"><a href="#cb10-2" aria-hidden="true" tabindex="-1"></a>  template&lt;&gt;</span>
<span id="cb10-3"><a href="#cb10-3" aria-hidden="true" tabindex="-1"></a>  struct <em>impls-for</em>&lt;<em>decayed-typeof</em>&lt;finally&gt;&gt; : <em>default-impls</em> {</span>
<span id="cb10-4"><a href="#cb10-4" aria-hidden="true" tabindex="-1"></a>    static constexpr auto <em>get-attrs</em> = <em>see below</em>;</span>
<span id="cb10-5"><a href="#cb10-5" aria-hidden="true" tabindex="-1"></a>    static constexpr auto <em>get-state</em> = <em>see below</em>;</span>
<span id="cb10-6"><a href="#cb10-6" aria-hidden="true" tabindex="-1"></a>    static constexpr auto <em>complete</em> = <em>see below</em>;</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></code></pre></div>
<ol type="1">
<li>The member <code class="sourceCode default"><em>impls-for</em>&lt;<em>decayed-typeof</em>&lt;finally&gt;&gt;::<em>get-attrs</em></code>
is initialized with a callable object equivalent to the following
lambda:</li>
</ol></li>
</ol>
<div class="rm" style="color: #bf0303">

<blockquote>
<blockquote>
<div class="sourceCode" id="cb11"><pre class="sourceCode default default"><code class="sourceCode default"><span id="cb11-1"><a href="#cb11-1" aria-hidden="true" tabindex="-1"></a>[](const auto&amp; data, const auto&amp; child) noexcept -&gt; decltype(auto) {</span>
<span id="cb11-2"><a href="#cb11-2" aria-hidden="true" tabindex="-1"></a>  return <em>JOIN-ENV</em>(<em>SCHED-ATTRS</em>(data), <em>FWD-ENV</em>(get_env(child)));</span>
<span id="cb11-3"><a href="#cb11-3" aria-hidden="true" tabindex="-1"></a>}</span></code></pre></div>
</blockquote>
</blockquote>

</div>
<div class="add" style="color: #006e28">

<blockquote>
<blockquote>
<div class="sourceCode" id="cb12"><pre class="sourceCode default default"><code class="sourceCode default"><span id="cb12-1"><a href="#cb12-1" aria-hidden="true" tabindex="-1"></a>[](auto, const auto&amp; tsndr, const auto&amp; fsndr) noexcept -&gt; decltype(auto) {</span>
<span id="cb12-2"><a href="#cb12-2" aria-hidden="true" tabindex="-1"></a>  return <em>JOIN-ENV</em>(<em>FWD-ENV</em>(get_env(fsndr)), <em>FWD-ENV</em>(get_env(tsndr)));</span>
<span id="cb12-3"><a href="#cb12-3" aria-hidden="true" tabindex="-1"></a>}</span></code></pre></div>
</blockquote>
</blockquote>

</div>
<blockquote>
<ol start="2" type="1">
<li><p>The member <code class="sourceCode default"><em>impls-for</em>&lt;<em>decayed-typeof</em>&lt;finally&gt;&gt;::<em>get-state</em></code>
is initialized with a callable object equivalent to the following
lambda:</p>
<div class="sourceCode" id="cb13"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb13-1"><a href="#cb13-1" aria-hidden="true" tabindex="-1"></a>[]&lt;class Sndr, class Rcvr&gt;(Sndr&amp;&amp; sndr, Rcvr&amp; rcvr) noexcept(<em>see below</em>)</span>
<span id="cb13-2"><a href="#cb13-2" aria-hidden="true" tabindex="-1"></a>    requires sender_in&lt;<em>child-type</em>&lt;Sndr<span class="add" style="color: #006e28"><ins>, 0</ins></span>&gt;, env_of_t&lt;Rcvr&gt;&gt; <span class="add" style="color: #006e28"><ins>&amp;&amp;</ins></span></span>
<span id="cb13-3"><a href="#cb13-3" aria-hidden="true" tabindex="-1"></a>      <span class="add" style="color: #006e28"><ins><span><code class="sourceCode default">sender_in&lt;<em>child-type</em>&lt;Sndr<span class="add" style="color: #006e28"><ins>, 1</ins></span>&gt;, env_of_t&lt;Rcvr&gt;&gt; &amp;&amp;</code></span></ins></span></span>
<span id="cb13-4"><a href="#cb13-4" aria-hidden="true" tabindex="-1"></a>      <span class="add" style="color: #006e28"><ins><span><code class="sourceCode default"><em>VALID-FINALLY</em>(completion_signatures_of_t&lt;<em>child-type</em>&lt;Sndr<span class="add" style="color: #006e28"><ins>, 1</ins></span>&gt;, env_of_t&lt;Rcvr&gt;&gt;)</code></span></ins></span> {</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>   auto&amp; [_, <span class="rm" style="color: #bf0303"><del><span><code class="sourceCode default">sch, child</code></span></del></span><span class="add" style="color: #006e28"><ins><span><code class="sourceCode default">_, tsndr, fsndr</code></span></ins></span>] = sndr;</span>
<span id="cb13-7"><a href="#cb13-7" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb13-8"><a href="#cb13-8" aria-hidden="true" tabindex="-1"></a>   <span class="rm" style="color: #bf0303"><del><span><code class="sourceCode default">using sched_t = decltype(auto(sch));</code></span></del></span></span>
<span id="cb13-9"><a href="#cb13-9" aria-hidden="true" tabindex="-1"></a>   <span class="add" style="color: #006e28"><ins><span><code class="sourceCode default">using fsndr_t = decltype(std::forward_like&lt;Sndr&gt;(fsndr));</code></span></ins></span></span>
<span id="cb13-10"><a href="#cb13-10" aria-hidden="true" tabindex="-1"></a>   using variant_t = <em>see below</em>;</span>
<span id="cb13-11"><a href="#cb13-11" aria-hidden="true" tabindex="-1"></a>   using receiver_t = <em>see below</em>;</span>
<span id="cb13-12"><a href="#cb13-12" aria-hidden="true" tabindex="-1"></a>   using operation_t = connect_result_t&lt;<span class="rm" style="color: #bf0303"><del><span><code class="sourceCode default">schedule_result_t&lt;sched_t&gt;</code></span></del></span><span class="add" style="color: #006e28"><ins><span><code class="sourceCode default">fsndr_t</code></span></ins></span>, receiver_t&gt;;</span>
<span id="cb13-13"><a href="#cb13-13" aria-hidden="true" tabindex="-1"></a>   constexpr bool nothrow =</span>
<span id="cb13-14"><a href="#cb13-14" aria-hidden="true" tabindex="-1"></a>     noexcept(connect(<span class="rm" style="color: #bf0303"><del><span><code class="sourceCode default">schedule(sch)</code></span></del></span><span class="add" style="color: #006e28"><ins><span><code class="sourceCode default">std::forward_like&lt;Sndr&gt;(fsndr)</code></span></ins></span>, receiver_t{nullptr}));</span>
<span id="cb13-15"><a href="#cb13-15" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb13-16"><a href="#cb13-16" aria-hidden="true" tabindex="-1"></a>   struct <em>state-type</em> {</span>
<span id="cb13-17"><a href="#cb13-17" aria-hidden="true" tabindex="-1"></a>     Rcvr&amp; <em>rcvr</em>;             // exposition only</span>
<span id="cb13-18"><a href="#cb13-18" aria-hidden="true" tabindex="-1"></a>     variant_t <em>async-result</em>; // exposition only</span>
<span id="cb13-19"><a href="#cb13-19" aria-hidden="true" tabindex="-1"></a>     operation_t <em>op-state</em>;   // exposition only</span>
<span id="cb13-20"><a href="#cb13-20" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb13-21"><a href="#cb13-21" aria-hidden="true" tabindex="-1"></a>     explicit <em>state-type</em>(<span class="rm" style="color: #bf0303"><del><span><code class="sourceCode default">sched_t sch</code></span></del></span><span class="add" style="color: #006e28"><ins><span><code class="sourceCode default">fsndr_t fsndr</code></span></ins></span>, Rcvr&amp; rcvr) noexcept(nothrow)</span>
<span id="cb13-22"><a href="#cb13-22" aria-hidden="true" tabindex="-1"></a>       : <em>rcvr</em>(rcvr), <em>op-state</em>(connect(<span class="rm" style="color: #bf0303"><del><span><code class="sourceCode default">schedule(sch)</code></span></del></span><span class="add" style="color: #006e28"><ins><span><code class="sourceCode default">std::forward_like&lt;Sndr&gt;(fsndr)</code></span></ins></span>, receiver_t{this})) {}</span>
<span id="cb13-23"><a href="#cb13-23" aria-hidden="true" tabindex="-1"></a>   };</span>
<span id="cb13-24"><a href="#cb13-24" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb13-25"><a href="#cb13-25" aria-hidden="true" tabindex="-1"></a>   return <em>state-type</em>{<span class="rm" style="color: #bf0303"><del><span><code class="sourceCode default">sch</code></span></del></span><span class="add" style="color: #006e28"><ins><span><code class="sourceCode default">std::forward_like&lt;Sndr&gt;(fsndr)</code></span></ins></span>, rcvr};</span>
<span id="cb13-26"><a href="#cb13-26" aria-hidden="true" tabindex="-1"></a>}</span></code></pre></div>
<ol type="1">
<li><p>Objects of the local class
<em><code class="sourceCode default">state-type</code></em> can be used
to initialize a structured binding.</p></li>
<li><p>Let <code class="sourceCode default">Sigs</code> be a pack of the
arguments to the
<code class="sourceCode default">completion_signatures</code>
specialization named by <code class="sourceCode default">completion_signatures_of_t&lt;<em><code class="sourceCode default">child-type</code></em>&lt;Sndr<span class="add" style="color: #006e28"><ins>, 0</ins></span>&gt;, env_of_t&lt;Rcvr&gt;&gt;</code>.
Let <em><code class="sourceCode default">as-tuple</code></em> be an
alias template that transforms a completion signature
<code class="sourceCode default">Tag(Args...)</code> into the
<code class="sourceCode default">tuple</code> specialization <code class="sourceCode default"><em>decayed-tuple</em>&lt;Tag, Args...&gt;</code>.
Then <code class="sourceCode default">variant_t</code> denotes the type
<code class="sourceCode default">variant&lt;monostate, <em>as-tuple</em>&lt;Sigs&gt;...&gt;</code>,
except with duplicate types removed.</p></li>
<li><p><code class="sourceCode default">receiver_t</code> is an alias
for the following exposition-only class:</p>
<div class="sourceCode" id="cb14"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb14-1"><a href="#cb14-1" aria-hidden="true" tabindex="-1"></a>namespace std::execution {</span>
<span id="cb14-2"><a href="#cb14-2" aria-hidden="true" tabindex="-1"></a>  struct <em>receiver-type</em> {</span>
<span id="cb14-3"><a href="#cb14-3" aria-hidden="true" tabindex="-1"></a>    using receiver_concept = receiver_t;</span>
<span id="cb14-4"><a href="#cb14-4" aria-hidden="true" tabindex="-1"></a>    <em>state-type</em>* <em>state</em>; <em>// exposition only</em></span>
<span id="cb14-5"><a href="#cb14-5" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb14-6"><a href="#cb14-6" aria-hidden="true" tabindex="-1"></a>    void set_value() &amp;&amp; noexcept {</span>
<span id="cb14-7"><a href="#cb14-7" aria-hidden="true" tabindex="-1"></a>      visit(</span>
<span id="cb14-8"><a href="#cb14-8" aria-hidden="true" tabindex="-1"></a>        [this]&lt;class Tuple&gt;(Tuple&amp; result) noexcept -&gt; void {</span>
<span id="cb14-9"><a href="#cb14-9" aria-hidden="true" tabindex="-1"></a>          if constexpr (!same_as&lt;monostate, Tuple&gt;) {</span>
<span id="cb14-10"><a href="#cb14-10" aria-hidden="true" tabindex="-1"></a>            auto&amp; [tag, ...args] = result;</span>
<span id="cb14-11"><a href="#cb14-11" aria-hidden="true" tabindex="-1"></a>            tag(std::move(<em>state</em>-&gt;<em>rcvr</em>), std::move(args)...);</span>
<span id="cb14-12"><a href="#cb14-12" aria-hidden="true" tabindex="-1"></a>          }</span>
<span id="cb14-13"><a href="#cb14-13" aria-hidden="true" tabindex="-1"></a>        },</span>
<span id="cb14-14"><a href="#cb14-14" aria-hidden="true" tabindex="-1"></a>        <em>state</em>-&gt;<em>async-result</em>);</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>
<span id="cb14-17"><a href="#cb14-17" aria-hidden="true" tabindex="-1"></a>    template&lt;class Error&gt;</span>
<span id="cb14-18"><a href="#cb14-18" aria-hidden="true" tabindex="-1"></a>    void set_error(Error&amp;&amp; err) &amp;&amp; noexcept {</span>
<span id="cb14-19"><a href="#cb14-19" aria-hidden="true" tabindex="-1"></a>      execution::set_error(std::move(<em>state</em>-&gt;<em>rcvr</em>), std::forward&lt;Error&gt;(err));</span>
<span id="cb14-20"><a href="#cb14-20" aria-hidden="true" tabindex="-1"></a>    }</span>
<span id="cb14-21"><a href="#cb14-21" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb14-22"><a href="#cb14-22" aria-hidden="true" tabindex="-1"></a>    void set_stopped() &amp;&amp; noexcept {</span>
<span id="cb14-23"><a href="#cb14-23" aria-hidden="true" tabindex="-1"></a>      execution::set_stopped(std::move(<em>state</em>-&gt;<em>rcvr</em>));</span>
<span id="cb14-24"><a href="#cb14-24" aria-hidden="true" tabindex="-1"></a>    }</span>
<span id="cb14-25"><a href="#cb14-25" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb14-26"><a href="#cb14-26" aria-hidden="true" tabindex="-1"></a>    decltype(auto) get_env() const noexcept {</span>
<span id="cb14-27"><a href="#cb14-27" aria-hidden="true" tabindex="-1"></a>      return FWD-ENV(execution::get_env(<em>state</em>-&gt;<em>rcvr</em>));</span>
<span id="cb14-28"><a href="#cb14-28" aria-hidden="true" tabindex="-1"></a>    }</span>
<span id="cb14-29"><a href="#cb14-29" aria-hidden="true" tabindex="-1"></a>  };</span>
<span id="cb14-30"><a href="#cb14-30" aria-hidden="true" tabindex="-1"></a>}</span></code></pre></div></li>
<li><p>The expression in the
<code class="sourceCode default">noexcept</code> clause of the lambda is
<code class="sourceCode default">true</code> if the construction of the
returned <i><code class="sourceCode default">state-type</code></i>
object is not potentially throwing; otherwise,
<code class="sourceCode default">false</code>.</p></li>
</ol></li>
<li><p>The member <code class="sourceCode default"><em>impls-for</em>&lt;<em>decayed-typeof</em>&lt;finally&gt;&gt;::<em>complete</em></code>
is initialized with a callable object equivalent to the following
lambda:</p>
<div class="sourceCode" id="cb15"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb15-1"><a href="#cb15-1" aria-hidden="true" tabindex="-1"></a>[]&lt;class Tag, class... Args&gt;(auto, auto&amp; state, auto&amp; rcvr, Tag, Args&amp;&amp;... args) noexcept -&gt; void {</span>
<span id="cb15-2"><a href="#cb15-2" aria-hidden="true" tabindex="-1"></a>  using result_t = <em>decayed-tuple</em>&lt;Tag, Args...&gt;;</span>
<span id="cb15-3"><a href="#cb15-3" aria-hidden="true" tabindex="-1"></a>  constexpr bool nothrow = is_nothrow_constructible_v&lt;result_t, Tag, Args...&gt;;</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>  <em>TRY-EVAL</em>(std::move(rcvr), [&amp;]() noexcept(nothrow) {</span>
<span id="cb15-6"><a href="#cb15-6" aria-hidden="true" tabindex="-1"></a>    state.<em>async-result</em>.template emplace&lt;result_t&gt;(Tag(), std::forward&lt;Args&gt;(args)...);</span>
<span id="cb15-7"><a href="#cb15-7" aria-hidden="true" tabindex="-1"></a>  }());</span>
<span id="cb15-8"><a href="#cb15-8" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb15-9"><a href="#cb15-9" aria-hidden="true" tabindex="-1"></a>  if (state.<em>async-result</em>.valueless_by_exception())</span>
<span id="cb15-10"><a href="#cb15-10" aria-hidden="true" tabindex="-1"></a>    return;</span>
<span id="cb15-11"><a href="#cb15-11" aria-hidden="true" tabindex="-1"></a>  if (state.<em>async-result</em>.index() == 0)</span>
<span id="cb15-12"><a href="#cb15-12" aria-hidden="true" tabindex="-1"></a>    return;</span>
<span id="cb15-13"><a href="#cb15-13" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb15-14"><a href="#cb15-14" aria-hidden="true" tabindex="-1"></a>  start(state.<em>op-state</em>);</span>
<span id="cb15-15"><a href="#cb15-15" aria-hidden="true" tabindex="-1"></a>};</span></code></pre></div></li>
</ol>
</blockquote>
<p><span class="ednote" style="color: #0000ff">[ Editor&#39;s note: Remove
paragraph 5, which is about the requirements on customizations of the
algorithm; <code class="sourceCode default">finally</code> cannot be
customized. ]</span></p>
<p><span class="ednote" style="color: #0000ff">[ Editor&#39;s note: Insert a
new subsection
“<code class="sourceCode default">execution::schedule_from</code>
[exec.schedule.from]” as follows: ]</span></p>
<p><font size="+1"><span class="marginalizedparent"><a class="marginalized">(34.9.11.?)</a></span></font>
<strong><code class="sourceCode default">execution::schedule_from</code>
[exec.schedule.from]</strong></p>
<p><span class="ednote" style="color: #0000ff">[ Editor&#39;s note: These
three paragraphs are taken unchanged from [exec]. ]</span></p>
<ol type="1">
<li><p><code class="sourceCode default">schedule_from</code> schedules
work dependent on the completion of a sender onto a scheduler’s
associated execution resource. <span class="note"><span>[ <em>Note:</em>
</span><code class="sourceCode default">schedule_from</code> is not
meant to be used in user code; it is used in the implementation of
<code class="sourceCode default">continue_on</code>.<span> — <em>end
note</em> ]</span></span></p></li>
<li><p>The name <code class="sourceCode default">schedule_from</code>
denotes a customization point object. For some subexpressions
<code class="sourceCode default">sch</code> and
<code class="sourceCode default">sndr</code>, let
<code class="sourceCode default">Sch</code> be
<code class="sourceCode default">decltype((sch))</code> and
<code class="sourceCode default">Sndr</code> be
<code class="sourceCode default">decltype((sndr))</code>. If
<code class="sourceCode default">Sch</code> does not satisfy
<code class="sourceCode default">scheduler</code>, or
<code class="sourceCode default">Sndr</code> does not satisfy
<code class="sourceCode default">sender</code>,
<code class="sourceCode default">schedule_from</code> is
ill-formed.</p></li>
<li><p>Otherwise, the expression
<code class="sourceCode default">schedule_from(sch, sndr)</code> is
expression-equivalent to:</p>
<div class="sourceCode" id="cb16"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb16-1"><a href="#cb16-1" aria-hidden="true" tabindex="-1"></a>transform_sender(</span>
<span id="cb16-2"><a href="#cb16-2" aria-hidden="true" tabindex="-1"></a>  <em>query-or-default</em>(get_domain, sch, default_domain()),</span>
<span id="cb16-3"><a href="#cb16-3" aria-hidden="true" tabindex="-1"></a>  <em>make-sender</em>(schedule_from, sch, sndr));</span></code></pre></div>
<p>except that <code class="sourceCode default">sch</code> is evaluated
only once.</p></li>
</ol>
<div class="add" style="color: #006e28">

<ol start="4" type="1">
<li><p>The exposition-only class template
<em><code class="sourceCode default">impls-for</code></em> is
specialized for <code class="sourceCode default">schedule_from_t</code>
as follows:</p>
<div class="sourceCode" id="cb17"><pre class="sourceCode default default"><code class="sourceCode default"><span id="cb17-1"><a href="#cb17-1" aria-hidden="true" tabindex="-1"></a>template&lt;&gt;</span>
<span id="cb17-2"><a href="#cb17-2" aria-hidden="true" tabindex="-1"></a>struct <em>impls-for</em>&lt;schedule_from_t&gt; : <em>default-impls</em> {</span>
<span id="cb17-3"><a href="#cb17-3" aria-hidden="true" tabindex="-1"></a>  static constexpr auto <em>get-attrs</em> =</span>
<span id="cb17-4"><a href="#cb17-4" aria-hidden="true" tabindex="-1"></a>    [](const auto&amp; data, const auto&amp; child) noexcept -&gt; decltype(auto) {</span>
<span id="cb17-5"><a href="#cb17-5" aria-hidden="true" tabindex="-1"></a>      return <em>JOIN-ENV</em>(<em>SCHED-ATTRS</em>(data), <em>FWD-ENV</em>(get_env(child)));</span>
<span id="cb17-6"><a href="#cb17-6" aria-hidden="true" tabindex="-1"></a>    };</span>
<span id="cb17-7"><a href="#cb17-7" aria-hidden="true" tabindex="-1"></a>};</span></code></pre></div></li>
<li><p>Let <code class="sourceCode default">sndr</code> and
<code class="sourceCode default">env</code> be subexpressions and let
<code class="sourceCode default">Sndr</code> be
<code class="sourceCode default">decltype((sndr))</code>. If <code class="sourceCode default"><em>sender-for</em>&lt;Sndr, schedule_from_t&gt;</code>
is <code class="sourceCode default">false</code>, then the expression
<code class="sourceCode default">schedule_from.transform_sender(sndr, env)</code>
is ill-formed; otherwise, it is equal to:</p>
<div class="sourceCode" id="cb18"><pre class="sourceCode default default"><code class="sourceCode default"><span id="cb18-1"><a href="#cb18-1" aria-hidden="true" tabindex="-1"></a>auto&amp;&amp; [tag, sch, child] = sndr;</span>
<span id="cb18-2"><a href="#cb18-2" aria-hidden="true" tabindex="-1"></a>return finally(std::forward_like&lt;Sndr&gt;(child),</span>
<span id="cb18-3"><a href="#cb18-3" aria-hidden="true" tabindex="-1"></a>               unstoppable(schedule(std::forward_like&lt;Sndr&gt;(sch))));</span></code></pre></div>
<p><span class="note"><span>[ <em>Note:</em> </span>This causes the
<code class="sourceCode default">schedule_from(sch, sndr)</code> sender
to become <code class="sourceCode default">finally(sndr,  unstoppable(schedule(sch)))</code>
when it is connected to a receiver with an execution domain that does
not customize
<code class="sourceCode default">schedule_from</code>.<span> — <em>end
note</em> ]</span></span></p></li>
</ol>

</div>
<p><span class="ednote" style="color: #0000ff">[ Editor&#39;s note: The
following paragraph is taken unchanged from [exec]. ]</span></p>
<ol start="6" type="1">
<li>Let <code class="sourceCode default">out_sndr</code> be a
subexpression denoting a sender returned from
<code class="sourceCode default">schedule_from(sch, sndr)</code> or one
equal to such, and let <code class="sourceCode default">OutSndr</code>
be the type
<code class="sourceCode default">decltype((out_sndr))</code>. Let
<code class="sourceCode default">out_rcvr</code> be a subexpression
denoting a receiver that has an environment of type
<code class="sourceCode default">Env</code> such that <code class="sourceCode default">sender_in&lt;OutSndr,     Env&gt;</code> is
<code class="sourceCode default">true</code>. Let
<code class="sourceCode default">op</code> be an lvalue referring to the
operation state that results from connecting
<code class="sourceCode default">out_sndr</code> with
<code class="sourceCode default">out_rcvr</code>. Calling
<code class="sourceCode default">start(op)</code> shall start
<code class="sourceCode default">sndr</code> on the current execution
agent and execute completion operations on
<code class="sourceCode default">out_rcvr</code> on an execution agent
of the execution resource associated with
<code class="sourceCode default">sch</code>. If scheduling onto
<code class="sourceCode default">sch</code> fails, an error completion
on <code class="sourceCode default">out_rcvr</code> shall be executed on
an unspecified execution agent.</li>
</ol>
<p><span class="ednote" style="color: #0000ff">[ Editor&#39;s note: The
following changes to the <code class="sourceCode default">let_*</code>
algorithms are not strictly necessary; they are simplifications made
possible by the addition of the
<code class="sourceCode default">write_env</code> adaptor above.
]</span></p>
<p><span class="ednote" style="color: #0000ff">[ Editor&#39;s note: Remove
[exec.let]p5.1, which defines an exposition-only class
<em><code class="sourceCode default">receiver2</code></em>. ]</span></p>
<p><span class="ednote" style="color: #0000ff">[ Editor&#39;s note: Change
[exec.let]p5.2.2 as follows: ]</span></p>
<ol start="2" type="1">
<li>Given a type <code class="sourceCode default">Tag</code> and a pack
<code class="sourceCode default">Args</code>, let
<em><code class="sourceCode default">as-sndr2</code></em> be an alias
template such that
<code class="sourceCode default"><em>as-sndr2</em>&lt;Tag(Args...)&gt;</code>
denotes the type <code class="sourceCode default"><span class="add" style="color: #006e28"><ins><span><code class="sourceCode default"><em>call-result-t</em>&lt;<em>decayed-typeof</em>&lt;write_env&gt;,</code></span></ins></span> <em>call-result-t</em>&lt;Fn, decay_t&lt;Args&gt;&amp;...&gt;<span class="add" style="color: #006e28"><ins><span><code class="sourceCode default">, Env&gt;</code></span></ins></span></code>.
Then <code class="sourceCode default">ops2_variant_t</code> denotes the
type <code class="sourceCode default">variant&lt;monostate, connect_result_t&lt;<em>as-sndr2</em>&lt;LetSigs&gt;, <span class="rm" style="color: #bf0303"><del><em>receiver2</em>&lt;</del></span>Rcvr<span class="rm" style="color: #bf0303"><del>, Env&gt;</del></span>&gt;...&gt;</code>.</li>
</ol>
<p><span class="ednote" style="color: #0000ff">[ Editor&#39;s note: Change
[exec.let]p5.3 as follows: ]</span></p>
<ol start="3" type="1">
<li><p>The exposition-only function template
<em><code class="sourceCode default">let-bind</code></em> has effects
equivalent to:</p>
<div class="sourceCode" id="cb19"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb19-1"><a href="#cb19-1" aria-hidden="true" tabindex="-1"></a>using args_t = <em><code class="sourceCode default">decayed-tuple</code></em>&lt;Args...&gt;;</span>
<span id="cb19-2"><a href="#cb19-2" aria-hidden="true" tabindex="-1"></a>auto mkop2 = [&amp;] {</span>
<span id="cb19-3"><a href="#cb19-3" aria-hidden="true" tabindex="-1"></a>  return connect(</span>
<span id="cb19-4"><a href="#cb19-4" aria-hidden="true" tabindex="-1"></a>    <span class="add" style="color: #006e28"><ins><span><code class="sourceCode default">write_env(</code></span></ins></span></span>
<span id="cb19-5"><a href="#cb19-5" aria-hidden="true" tabindex="-1"></a>      apply(std::move(state.fn),</span>
<span id="cb19-6"><a href="#cb19-6" aria-hidden="true" tabindex="-1"></a>            state.args.template emplace&lt;args_t&gt;(std::forward&lt;Args&gt;(args)...)),</span>
<span id="cb19-7"><a href="#cb19-7" aria-hidden="true" tabindex="-1"></a>      <span class="add" style="color: #006e28"><ins><span><code class="sourceCode default">std::move(state.env)),</code></span></ins></span></span>
<span id="cb19-8"><a href="#cb19-8" aria-hidden="true" tabindex="-1"></a>    <span class="rm" style="color: #bf0303"><del><span><code class="sourceCode default"><em>receiver2</em>{rcvr, std::move(state.env)}</code></span></del></span><span class="add" style="color: #006e28"><ins><span><code class="sourceCode default">std::move(rcvr)</code></span></ins></span>);</span>
<span id="cb19-9"><a href="#cb19-9" aria-hidden="true" tabindex="-1"></a>};</span>
<span id="cb19-10"><a href="#cb19-10" aria-hidden="true" tabindex="-1"></a>start(state.ops2.template emplace&lt;decltype(mkop2())&gt;(<em><code class="sourceCode default">emplace-from</code></em>{mkop2}));</span></code></pre></div></li>
</ol>
<h1 data-number="6" id="bibliography"><span class="header-section-number">6</span> References<a href="#bibliography" class="self-link"></a></h1>
<div id="refs" class="references csl-bib-body hanging-indent" role="doc-bibliography">
<div id="ref-P2849R0" class="csl-entry" role="doc-biblioentry">
[P2849R0] Kirk Shoop. 2024-05-21. async-object - aka async-RAII objects.
<a href="https://wg21.link/p2849r0"><div class="csl-block">https://wg21.link/p2849r0</div></a>
</div>
<div id="ref-P3149R2" class="csl-entry" role="doc-biblioentry">
[P3149R2] Ian Petersen, Ján Ondrušek; Jessica Wong; Kirk Shoop; Lee
Howes; Lucian Radu Teodorescu; 2024-03-20. async_scope — Creating scopes
for non-sequential concurrency. <a href="https://wg21.link/p3149r2"><div class="csl-block">https://wg21.link/p3149r2</div></a>
</div>
<div id="ref-P3175R0" class="csl-entry" role="doc-biblioentry">
[P3175R0] Eric Niebler. 2024-03-14. Reconsidering the
`std::execution::on` algorithm. <a href="https://wg21.link/p3175r0"><div class="csl-block">https://wg21.link/p3175r0</div></a>
</div>
</div>
</div>
</div>
</body>
</html>
