<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" lang xml:lang>
<head>
  <meta charset="utf-8" />
  <meta name="generator" content="mpark/wg21" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes" />
  <meta name="dcterms.date" content="2025-06-28" />
  <title>Fixing Lazy Sender Algorithm Customization, Again</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>
  <style type="text/css">

code span.co {
color: #666666;
}
</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">Fixing Lazy Sender Algorithm
Customization, Again</h1>
<table style="border:none;float:right">
  <tr>
    <td>Document #:</td>
    <td>P3718R0</td>
  </tr>
  <tr>
    <td>Date:</td>
    <td>2025-06-28</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 Working Group<br>
      LWG Library Working Group<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="#background-continues_on-and-schedule_from" id="toc-background-continues_on-and-schedule_from"><span class="toc-section-number">1</span> Background:
<code class="sourceCode default">continues_on</code> and
<code class="sourceCode default">schedule_from</code>:<span></span></a></li>
<li><a href="#the-problems" id="toc-the-problems"><span class="toc-section-number">2</span> The Problems<span></span></a>
<ul>
<li><a href="#problem-1-a-mix-up" id="toc-problem-1-a-mix-up"><span class="toc-section-number">2.1</span> Problem 1: A
mix-up<span></span></a></li>
<li><a href="#problem-2-a-mis-connect" id="toc-problem-2-a-mis-connect"><span class="toc-section-number">2.2</span> Problem 2: A
mis-<code class="sourceCode default">connect</code><span></span></a></li>
<li><a href="#problem-3-a-muddle" id="toc-problem-3-a-muddle"><span class="toc-section-number">2.3</span> Problem 3: A
muddle<span></span></a></li>
</ul></li>
<li><a href="#back-to-first-principles" id="toc-back-to-first-principles"><span class="toc-section-number">3</span> Back to First
Principles<span></span></a>
<ul>
<li><a href="#special-cases" id="toc-special-cases"><span class="toc-section-number">3.1</span> Special
Cases<span></span></a></li>
<li><a href="#sender-consumers" id="toc-sender-consumers"><span class="toc-section-number">3.2</span> Sender
Consumers<span></span></a></li>
<li><a href="#get_domain_override" id="toc-get_domain_override"><span class="toc-section-number">3.3</span>
<code class="sourceCode default">get_domain_override</code><span></span></a></li>
</ul></li>
<li><a href="#summary-of-proposed-changes" id="toc-summary-of-proposed-changes"><span class="toc-section-number">4</span> Summary of Proposed
Changes<span></span></a></li>
<li><a href="#implementation-experience" id="toc-implementation-experience"><span class="toc-section-number">5</span> Implementation
Experience<span></span></a></li>
<li><a href="#future-directions" id="toc-future-directions"><span class="toc-section-number">6</span> Future
Directions<span></span></a></li>
<li><a href="#proposed-resolution" id="toc-proposed-resolution"><span class="toc-section-number">7</span> Proposed
Resolution<span></span></a></li>
<li><a href="#bibliography" id="toc-bibliography"><span class="toc-section-number">8</span> References<span></span></a></li>
</ul>
</div>
<h1 data-number="1" id="background-continues_on-and-schedule_from"><span class="header-section-number">1</span> Background:
<code class="sourceCode default">continues_on</code> and
<code class="sourceCode default">schedule_from</code>:<a href="#background-continues_on-and-schedule_from" class="self-link"></a></h1>
<p><code class="sourceCode default">std::execution</code> has two
customizable algorithms for transfering execution from one context to
another: <code class="sourceCode default">continues_on</code> and
<code class="sourceCode default">schedule_from</code>. The reason for
having two is due to the fact there are two execution contexts in play:
the context we’re transitioning <em>from</em> and the one we’re
transitioning <em>to</em>.</p>
<p>A generic execution framework cannot know how to transition between
arbitrary contexts; that is an NxM problem. Instead,
<code class="sourceCode default">std::execution</code> provides a way
for schedulers to separately customize how to transition to and from a
standard thread of execution (ToE); i.e.,
<code class="sourceCode default">std::thread</code> or
<code class="sourceCode default">main</code>. Transitions between
unrelated contexts is accomplised with a hop through a ToE. We
accomplish this by providing two customization points: one for
specifying any special sauce needed to transfer <em>from</em> a standard
ToE, and another for the transfer back.</p>
<p>The <code class="sourceCode default">schedule_from</code> algorithm
looks for customizations based on the domain of the destination, and the
<code class="sourceCode default">continues_on</code> algorithm
dispatches based on the domain of the source. A “domain” is a tag type
associated with an execution context that is used to find algorithm
customizations for that context. The
<code class="sourceCode default">continues_on</code> algorithm is
required to lower to the result of a call to
<code class="sourceCode default">schedule_from</code>. In this way,
every context transition gets all the special sauce it needs to get from
one arbitrary context to another.</p>
<p>We can see this in the definitions of the
<code class="sourceCode default">continues_on</code> and
<code class="sourceCode default">schedule_from</code> customizations
points:</p>
<table>
<colgroup>
<col style="width: 30%" />
<col style="width: 70%" />
</colgroup>
<thead>
<tr class="header">
<th>
<div style="text-align:center">
<p><strong>Algorithm</strong></p>
</div>
</th>
<th>
<div style="text-align:center">
<p><strong>Returns</strong></p>
</div>
</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td>
<pre><code class="sourceCode default">continues_on(sndr, sched)</code></pre>
</td>
<td>
<pre><code class="sourceCode default">transform_sender(<em><code class="sourceCode default">get-domain-early</code></em>(sndr),
                 <em><code class="sourceCode default">make-sender</code></em>(continues_on, sched, sndr))</code></pre>
</td>
</tr>
<tr class="even">
<td>
<pre><code class="sourceCode default">schedule_from(sched, sndr)</code></pre>
</td>
<td>
<pre><code class="sourceCode default">transform_sender(<em><code class="sourceCode default">query-or-default</code></em>(get_domain, sched, default_domain{}),
                 <em><code class="sourceCode default">make-sender</code></em>(schedule_from, sched, sndr))</code></pre>
</td>
</tr>
</tbody>
</table>
<p>By asking for the predecessor sender’s domain,
<code class="sourceCode default">continues_on</code> uses the domain of
the source to find its customization. And by asking for the scheduler’s
domain, <code class="sourceCode default">schedule_from</code> uses the
domain of the destination.</p>
<p>The final piece is the transformation, within the
<code class="sourceCode default">connect</code> customization point, of
the <code class="sourceCode default">continues_on</code> sender to the
<code class="sourceCode default">schedule_from</code> sender, which is
done with the <code class="sourceCode default">continues_on.transform_sender(Sndr, Env)</code>
member function (see <span>33.9.12.4
<a href="https://wg21.link/exec.continues.on#5">[exec.continues.on#5]</a></span>).</p>
<hr />
<h1 data-number="2" id="the-problems"><span class="header-section-number">2</span> The Problems<a href="#the-problems" class="self-link"></a></h1>
<h2 data-number="2.1" id="problem-1-a-mix-up"><span class="header-section-number">2.1</span> Problem 1: A mix-up<a href="#problem-1-a-mix-up" class="self-link"></a></h2>
<p>When <code class="sourceCode default">connect</code>-time
customization was added to
<code class="sourceCode default">std::execution</code> in <span class="citation" data-cites="P2999R3">[<a href="#ref-P2999R3" role="doc-biblioref">P2999R3</a>]</span>, the logic of
<code class="sourceCode default">continues_on</code>/<code class="sourceCode default">schedule_from</code>
customization accidentally got reversed: The exposition-only
<em><code class="sourceCode default">get-domain-late</code></em>
function, which is called from
<code class="sourceCode default">connect</code>, determines the domain
used to find a sender transform function. It says:</p>
<blockquote>
<blockquote>
<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="kw">template</span><span class="op">&lt;</span><span class="kw">class</span> Sndr, <span class="kw">class</span> Env<span class="op">&gt;</span></span>
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a>  <span class="kw">constexpr</span> <span class="kw">auto</span> <em>get-domain-late</em><span class="op">(</span><span class="kw">const</span> Sndr<span class="op">&amp;</span> sndr, <span class="kw">const</span> Env<span class="op">&amp;</span> env<span class="op">)</span> <span class="kw">noexcept</span>;</span></code></pre></div>
</blockquote>
<ol start="14" type="1">
<li><p>Effects: Equivalent to:</p>
<ol type="1">
<li><p>If <code class="sourceCode default"><em>sender-for</em>&lt;Sndr, continues_on_t&gt;</code>
is <code class="sourceCode default">true</code>, then</p>
<blockquote>
<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="cf">return</span> Domain<span class="op">()</span>;</span></code></pre></div>
</blockquote>
<p>where <code class="sourceCode default">Domain</code> is the type of
the following expression:</p>
<blockquote>
<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="op">[]</span> <span class="op">{</span></span>
<span id="cb3-2"><a href="#cb3-2" aria-hidden="true" tabindex="-1"></a>  <span class="kw">auto</span> <span class="op">[</span>_, sch, _<span class="op">]</span> <span class="op">=</span> sndr;</span>
<span id="cb3-3"><a href="#cb3-3" aria-hidden="true" tabindex="-1"></a>  <span class="cf">return</span> <em>query-or-default</em><span class="op">(</span>get_domain, sch, default_domain<span class="op">())</span>;</span>
<span id="cb3-4"><a href="#cb3-4" aria-hidden="true" tabindex="-1"></a><span class="op">}()</span>;</span></code></pre></div>
</blockquote>
<p>[<em>Note 1</em>: The
<code class="sourceCode default">continues_on</code> algorithm works in
tandem with <code class="sourceCode default">schedule_from</code>
([exec.schedule.from]) to give scheduler authors a way to customize both
how to transition onto
(<code class="sourceCode default">continues_on</code>) and off of
(<code class="sourceCode default">schedule_from</code>) a given
execution context. Thus,
<code class="sourceCode default">continues_on</code> ignores the domain
of the predecessor and uses the domain of the destination scheduler to
select a customization, a property that is unique to
<code class="sourceCode default">continues_on</code>. That is why it is
given special treatment here. — <em>end note</em>]</p></li>
<li><p>Otherwise,</p>
<blockquote>
<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="cf">return</span> Domain<span class="op">()</span>;</span></code></pre></div>
</blockquote>
<p>where <code class="sourceCode default">Domain</code> is the first of
the following expressions that is well-formed and whose type is not
<code class="sourceCode default">void</code>:</p>
<ul>
<li><code class="sourceCode default">get_domain(get_env(sndr))</code></li>
<li><code class="sourceCode default"><em><code class="sourceCode default">completion-domain</code></em>&lt;void&gt;(sndr)</code></li>
<li><code class="sourceCode default">get_domain(env)</code></li>
<li><code class="sourceCode default">get_domain(get_scheduler(env))</code></li>
<li><code class="sourceCode default">default_domain()</code></li>
</ul></li>
</ol></li>
</ol>
</blockquote>
<p>Paragraph 14.1 above gets the roles of
<code class="sourceCode default">continues_on</code> and
<code class="sourceCode default">schedule_from</code> mixed up. They
should be reversed.</p>
<hr />
<h2 data-number="2.2" id="problem-2-a-mis-connect"><span class="header-section-number">2.2</span> Problem 2: A
mis-<code class="sourceCode default">connect</code><a href="#problem-2-a-mis-connect" class="self-link"></a></h2>
<p>All of the adaptor algorithm CPOs use the domain of the
predecessor(s) to find customizations. For example,
<code class="sourceCode default">then(sndr, fn)</code> returns <code class="sourceCode default">transform_sender(<em><code class="sourceCode default">get-domain-early</code></em>(sndr), <em><code class="sourceCode default">make-sender</code></em>(then, fn, sndr))</code>;
i.e., the domain is pulled from
<code class="sourceCode default">sndr</code>. A sender that advertizes a
domain is making an assertion about where it will complete. Where the
predecessor completes is where the current sender’s continuation will
execute.</p>
<p>If we look at the <code class="sourceCode default">connect</code>
customization point at how a late customization is found, we see that
before it does anything else, it transforms the input sender as
follows:</p>
<blockquote>
<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>transform_sender<span class="op">(</span><span class="kw">decltype</span><span class="op">(</span><em><code class="sourceCode default">get-domain-late</code></em><span class="op">(</span>sndr, get_env<span class="op">(</span>rcvr<span class="op">))){}</span>, sndr, get_env<span class="op">(</span>rcvr<span class="op">))</span></span></code></pre></div>
</blockquote>
<p>We can see that when passed a
<code class="sourceCode default">then</code> sender, we ask the
<code class="sourceCode default">then</code> sender for its domain (and
use the domain of the receiver’s env as a fallback). That means that for
<code class="sourceCode default">then</code> senders,
<code class="sourceCode default">connect</code> dispatches to a
customization based on the domain of the
<code class="sourceCode default">then</code> sender itself. That is
different from early customization, which used the domain of the
predecessor. The inconsistency is unintentional.</p>
<p>For <code class="sourceCode default">then</code> and most other
adaptors, it doesn’t make any difference. The
<code class="sourceCode default">then</code> sender completes wherever
its predecessor completes, so the domain of
<code class="sourceCode default">then</code> is the same as the domain
for the predecessor. That is not the case for all algorithms, though.
For <code class="sourceCode default">continues_on</code>, the domain on
which it completes can be different from the domain on which its
predecessor completes.</p>
<p>In short, for <code class="sourceCode default">continues_on</code>
and friends, <code class="sourceCode default">connect</code> is using
the wrong domain to dispatch to a customization.</p>
<hr />
<h2 data-number="2.3" id="problem-3-a-muddle"><span class="header-section-number">2.3</span> Problem 3: A muddle<a href="#problem-3-a-muddle" class="self-link"></a></h2>
<p>The <code class="sourceCode default">connect</code> customization
point uses
<em><code class="sourceCode default">get-domain-late</code></em> to
determine the domain to use when applying a sender transformation. Quite
apart from mixing up
<code class="sourceCode default">schedule_from</code> and
<code class="sourceCode default">continues_on</code>,
<em><code class="sourceCode default">get-domain-late</code></em>
incorrectly gives precedence to the sender’s domain over that of the
receiver. The (flawed) reasoning was that a sender starts where its
predecessor completes, which makes intuitive sense when reading a sender
chain:</p>
<blockquote>
<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>sender <span class="kw">auto</span> sndr <span class="op">=</span> just<span class="op">()</span> <span class="op">|</span> continues_on<span class="op">(</span>sch<span class="op">)</span> <span class="op">|</span> then<span class="op">([]</span> <span class="op">{</span> puts<span class="op">(</span><span class="st">&quot;hello world&quot;</span><span class="op">)</span>; <span class="op">})</span>;</span></code></pre></div>
</blockquote>
<p>Reading the above code, one might naturally infer that the
<code class="sourceCode default">then</code> sender will start on the
execution context associated with
<code class="sourceCode default">sch</code>.</p>
<p>The trouble is: that’s not true.</p>
<p>Senders nest and so too do their receivers and operation states.
After <code class="sourceCode default">sndr</code> is connected to a
receiver, calling <code class="sourceCode default">start</code> on the
resulting operation state is actually calling
<code class="sourceCode default">start</code> on the
<code class="sourceCode default">then</code> sender’s operation state!
The actual order of events is:</p>
<ol type="1">
<li><code class="sourceCode default">start</code> is called on the
<code class="sourceCode default">then</code> sender’s operation
state,</li>
<li>… which calls <code class="sourceCode default">start</code> on the
<code class="sourceCode default">continues_on</code> operation
state,</li>
<li>… which calls <code class="sourceCode default">start</code> on the
<code class="sourceCode default">just</code> operation state,</li>
<li>… which calls <code class="sourceCode default">set_value</code> on
the <code class="sourceCode default">continues_on</code> receiver,</li>
<li>… which connects and starts a
<code class="sourceCode default">schedule(sch)</code> sender,</li>
<li>… which causes <code class="sourceCode default">set_value</code> to
be called on <code class="sourceCode default">then</code>’s receiver
from the execution context of
<code class="sourceCode default">sch</code>,</li>
<li>… which finally calls
<code class="sourceCode default">set_value</code> on the receiver used
to connect <code class="sourceCode default">sndr</code>.</li>
</ol>
<p>If we want to dispatch based on where a sender will start, we should
not be asking the sender. A sender can only know where it will complete.
The receiver knows where it will start. The receiver is an extension of
the parent sender. The parent sender starts the child, and so it can
pass information to the child about where
<code class="sourceCode default">start</code> is being called from. It
does so via its receiver’s environment.</p>
<p>Therefore,
<em><code class="sourceCode default">get-domain-late</code></em> is
wrong to give precedence to the sender’s domain.</p>
<hr />
<h1 data-number="3" id="back-to-first-principles"><span class="header-section-number">3</span> Back to First Principles<a href="#back-to-first-principles" class="self-link"></a></h1>
<p>The <code class="sourceCode default">get_domain</code> query actually
has two meanings depending on what is being queried:</p>
<ul>
<li>When querying a sender’s attributes,
<code class="sourceCode default">get_domain</code> tells where the
sender will <em>complete</em>.</li>
<li>When querying a receiver’s environment,
<code class="sourceCode default">get_domain</code> tells where the
sender will <em>start</em>.</li>
</ul>
<p>What’s more, this information propagates in different directions.
Information about where senders complete is passed from left-to-right
(in pipeline order) while the senders are being constructed, whereas
information about where senders start is passed right-to-left while the
senders are being connected.</p>
<p>Both bits of information – where a sender will start and where it
will complete – can usefully contribute to the selection of an
implementation for a sender and its successor.</p>
<ol type="1">
<li>As we build a sender up, we can use information about where the
predecessor will complete – known only by the predecessor – to control
how the sender is constructed.</li>
<li>As we are connecting a sender and a receiver, we can use information
about where the sender will start – known only by the receiver – to
select an algorithm implementation.</li>
</ol>
<p>This is a clean and orderly separation of concerns.
<em><code class="sourceCode default">get-domain-early</code></em>
returns the sender’s domain and
<em><code class="sourceCode default">get-domain-late</code></em> returns
the receiver’s domain.</p>
<p>So are we done? Well, no.</p>
<h2 data-number="3.1" id="special-cases"><span class="header-section-number">3.1</span> Special Cases<a href="#special-cases" class="self-link"></a></h2>
<p>We still want <code class="sourceCode default">schedule_from</code>
and <code class="sourceCode default">continues_on</code> to have special
rules so that scheduler authors can properly orchestrate the transitions
from one context to another.
<code class="sourceCode default">schedule_from(sch, sndr)</code> should
use <code class="sourceCode default">sch</code> to find a customization,
and <code class="sourceCode default">continues_on(sndr, sch)</code>
should use <code class="sourceCode default">sndr</code> to find
customizations, both when building the senders and when connecting
them.</p>
<p>The <code class="sourceCode default">schedule_from</code>
customization point does not use
<em><code class="sourceCode default">get-domain-early</code></em>; it
only looks at <code class="sourceCode default">sch</code> when looking
for a sender transform, so that part is fine. But when connecting a
<code class="sourceCode default">schedule_from</code> sender, if we are
only looking at the receiver’s domain, then we won’t be using the domain
of the scheduler as we should.</p>
<p>The <code class="sourceCode default">continues_on</code> algorithm
also needs something different.
<em><code class="sourceCode default">get-domain-early</code></em> does
the right thing by returning the domain of the predecessor, but again if
we only use the receiver’s domain in
<code class="sourceCode default">connect</code>, we won’t be using the
predecessor’s domain as we should.</p>
<p>The special nature of these two algorithms begs for special handling
at <code class="sourceCode default">connect</code> time. One solution
would be to special-case them in
<em><code class="sourceCode default">get-domain-late</code></em>. But
there is another case of interest that suggests a more general
solution.</p>
<h2 data-number="3.2" id="sender-consumers"><span class="header-section-number">3.2</span> Sender Consumers<a href="#sender-consumers" class="self-link"></a></h2>
<p>Consider the following code, which schedules some work on a GPU
scheduler and then waits for it to complete:</p>
<blockquote>
<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> se <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>gpu_context gpu; <span class="co">// non-standard</span></span>
<span id="cb7-3"><a href="#cb7-3" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb7-4"><a href="#cb7-4" aria-hidden="true" tabindex="-1"></a>se<span class="op">::</span>sender <span class="kw">auto</span> sndr <span class="op">=</span> se<span class="op">::</span>schedule<span class="op">(</span>gpu<span class="op">.</span>get_scheduler<span class="op">())</span> <span class="op">|</span> se<span class="op">::</span>then<span class="op">([]{</span> <span class="cf">return</span> <span class="dv">42</span>; <span class="op">})</span>;</span>
<span id="cb7-5"><a href="#cb7-5" aria-hidden="true" tabindex="-1"></a><span class="kw">auto</span> <span class="op">[</span>result<span class="op">]</span>        <span class="op">=</span> se<span class="op">::</span>sync_wait<span class="op">(</span>std<span class="op">::</span>move<span class="op">(</span>sndr<span class="op">))</span>;</span></code></pre></div>
</blockquote>
<p>Waiting for GPU work to complete requires GPU-specific primitives.
How then should <code class="sourceCode default">sync_wait</code> find
such a custom implementation? The sender knows that it will complete on
the GPU, so perhaps <code class="sourceCode default">sync_wait</code>
should use
<code class="sourceCode default"><em><code class="sourceCode default">get-domain-early</code></em>(sndr)</code>
to find a customization.</p>
<p>But <code class="sourceCode default">sync_wait</code> knows the
environment of the receiver it will use to connect the sender. It stands
to reason that <code class="sourceCode default">sync_wait</code> should
use <code class="sourceCode default"><em><code class="sourceCode default">=</code></em>(sndr, <em><code class="sourceCode default">sync-wait-env</code></em>{...})</code>
to determine the domain to use. This becomes more obvious when we
consider a possible overload of
<code class="sourceCode default">sync_wait</code> that accepts an
environment as a second parameter. Certainly then, when the user has
given <code class="sourceCode default">sync_wait</code> an environment,
it should use it to find a customization.</p>
<p>The trouble is that if
<code class="sourceCode default">sync_wait</code> uses
<em><code class="sourceCode default">get-domain-late</code></em> to find
a customization, and if
<em><code class="sourceCode default">get-domain-late</code></em> only
asks the environment for the domain (with special-cases for
<code class="sourceCode default">schedule_from</code> and
<code class="sourceCode default">continues_on</code>), then it will not
find the custom GPU implementation necessary.</p>
<p>We have a carve-out in
<em><code class="sourceCode default">get-domain-late</code></em> for
<code class="sourceCode default">schedule_from</code> and
<code class="sourceCode default">continues_on</code> senders. It seems
we <em>also</em> need a carve-out for GPU senders … but that’s absurd!
If a GPU domain need a carve-out, then other domains will surely need a
carve-out too. We need a generic solution.</p>
<h2 data-number="3.3" id="get_domain_override"><span class="header-section-number">3.3</span>
<code class="sourceCode default">get_domain_override</code><a href="#get_domain_override" class="self-link"></a></h2>
<p>Senders need to have a way to override the domain of the receiver.
With such a mechanism, we can replace the special-case handling of
<code class="sourceCode default">schedule_from</code> and
<code class="sourceCode default">continues_on</code> with the generic
solution. The
<em><code class="sourceCode default">get-domain-late</code></em> helper
would first ask the sender if it has a “late-domain override”. If so,
that is the domain returned. Otherwise, it queries the receiver’s
environment as per usual.</p>
<p>All we need is one new sender attribute query, tentatively called
<code class="sourceCode default">get_domain_override</code>. The
<code class="sourceCode default">continues_on</code> and
<code class="sourceCode default">schedule_from</code> senders would
define this attribute,
<code class="sourceCode default">continues_on</code> to return the
domain of the predecessor and
<code class="sourceCode default">schedule_from</code> to return the
domain of the scheduler. And for the GPU sender case, the GPU domain can
have an early transform that wraps all senders so that they too define
that attribute.</p>
<h1 data-number="4" id="summary-of-proposed-changes"><span class="header-section-number">4</span> Summary of Proposed Changes<a href="#summary-of-proposed-changes" class="self-link"></a></h1>
<ol type="1">
<li><p>Add a non-forwarding
<code class="sourceCode default">get_domain_override</code> query with
no default implementation.</p></li>
<li><p>Give meaning to the
<code class="sourceCode default">get_scheduler</code> query by requiring
that an operation be started on an execution agent associated with the
scheduler from the environment of the receiver used to create the
operation.</p></li>
<li><p>Tweak the definitions of
<em><code class="sourceCode default">SCHED-ATTRS</code></em> and
<em><code class="sourceCode default">SCHED-ENV</code></em> to avoid
forwaring the <code class="sourceCode default">get_domain</code>
query.</p></li>
<li><p>Simplify the definition of the exposition-only
<em><code class="sourceCode default">completion-domain</code></em>
helper, which no longer needs a configurable default.</p></li>
<li><p>Specify that <code class="sourceCode default">get_domain_override(get_env(schedule_from(sch, sndr)))</code>
returns the domain of
<code class="sourceCode default">sch</code>.</p></li>
<li><p>Specify that <code class="sourceCode default">get_domain_override(get_env(continues_on(sndr, sch)))</code>
returns the domain of <code class="sourceCode default">sndr</code> (if
it has one).</p></li>
<li><p>Specify that <code class="sourceCode default">get_domain_override(get_env(starts_on(sch, sndr)))</code>
returns the domain of
<code class="sourceCode default">sch</code>.</p></li>
<li><p>The expression
<em><code class="sourceCode default">get-domain-late</code></em><code class="sourceCode default">(sndr, env, def)</code>
should be equivalent to:</p>
<ol type="1">
<li><code class="sourceCode default">get_domain_override(get_env(sndr))</code> if
that expression is well-formed.</li>
<li>Otherwise, <code class="sourceCode default">get_domain(env)</code>
if that expression is well-formed.</li>
<li>Otherwise,
<code class="sourceCode default">get_domain(get_scheduler(env))</code>
if that expression is well-formed.</li>
<li>Otherwise, <code class="sourceCode default">def</code>.</li>
</ol></li>
<li><p>Specify that <code class="sourceCode default">sync_wait</code>
and <code class="sourceCode default">sync_wait_with_variant</code> use
<code class="sourceCode default"><em><code class="sourceCode default">get-domain-late</code></em>(sndr, <em><code class="sourceCode default">sync-wait-env</code></em>{}, <em><code class="sourceCode default">get-domain-early</code></em>(sndr))</code>
when looking for a customization.</p></li>
</ol>
<h1 data-number="5" id="implementation-experience"><span class="header-section-number">5</span> Implementation Experience<a href="#implementation-experience" class="self-link"></a></h1>
<p>The design presented here is the result of a project to reimplement
the GPU scheduler for NVIDIA’s <a href="https://github.com/NVIDIA/cccl">CCCL</a> library. The old GPU
scheduler, which is currently still being used by <a href="https://github.com/NVIDIA/stdexec">stdexec</a>, uses early
customization exclusively. This requires that every algorithm is
reimplemented from scratch for the GPU, resulting in a large amount of
code duplication. Employing late customization would result in more
accurate dispatch and facilitate more code reuse.</p>
<p>With <code class="sourceCode default">std::execution</code>’s current
customization scheme, it was impossible for
<code class="sourceCode default">connect</code> to find the GPU
customization for the
<code class="sourceCode default">continues_on</code> algorithm. Pulling
on that thread revealed the other problems discussed in <span id="the-problems">Section 2</span>. Solving the problems first required
a deeper understanding of the separate roles senders and receivers play
in selecting a domain. That deeper understanding informed the design
proposed in this paper.</p>
<p>The newly redesigned GPU scheduler, which uses this proposed design,
can be found in <a href="https://github.com/NVIDIA/cccl/pull/4579">this
pull request</a> for the CCCL repository on GitHub, and <a href="https://github.com/NVIDIA/stdexec/pull/1542">this other pull
request</a> implements this proposed design for stdexec, the reference
implementation.</p>
<h1 data-number="6" id="future-directions"><span class="header-section-number">6</span> Future Directions<a href="#future-directions" class="self-link"></a></h1>
<p>This paper revealed a need for a
<code class="sourceCode default">sync_wait</code> overload that accepts
an environment in addition to a sender, like:</p>
<blockquote>
<div class="sourceCode" id="cb8"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb8-1"><a href="#cb8-1" aria-hidden="true" tabindex="-1"></a><span class="kw">template</span> <span class="op">&lt;</span>sender Sndr, queryable Env<span class="op">&gt;</span></span>
<span id="cb8-2"><a href="#cb8-2" aria-hidden="true" tabindex="-1"></a><span class="kw">auto</span> sync_wait<span class="op">(</span>Sndr<span class="op">&amp;&amp;</span> sndr, Env<span class="op">&amp;&amp;</span> env<span class="op">)</span>;</span></code></pre></div>
</blockquote>
<p>With such an overload, the user could specify a scheduler
corresponding to the current execution context (maybe
<code class="sourceCode default">sync_wait</code> is being called from
the GPU!), which would in turn determine what
<code class="sourceCode default">sync_wait</code> implementation gets
selected.</p>
<p>The <code class="sourceCode default">env</code> parameter would also
give callers a way to parameterize the
<code class="sourceCode default">sync_wait</code> algorithm with an
allocator, or a stop token, or perhaps even a different delegation
scheduler.</p>
<p>A separate paper will propose such an overload.</p>
<h1 data-number="7" id="proposed-resolution"><span class="header-section-number">7</span> Proposed Resolution<a href="#proposed-resolution" class="self-link"></a></h1>
<p><span class="ednote" style="color: #0000ff">[ Editor&#39;s note: To
[execution.syn], add the following: ]</span></p>
<blockquote>
<blockquote>
<div class="sourceCode" id="cb9"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb9-1"><a href="#cb9-1" aria-hidden="true" tabindex="-1"></a><span style="color:blue;font-style:italic">… as before …</span></span>
<span id="cb9-2"><a href="#cb9-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb9-3"><a href="#cb9-3" aria-hidden="true" tabindex="-1"></a><span class="kw">namespace</span> std<span class="op">::</span>execution <span class="op">{</span></span>
<span id="cb9-4"><a href="#cb9-4" aria-hidden="true" tabindex="-1"></a>  <span class="co">// [exec.queries], queries</span></span>
<span id="cb9-5"><a href="#cb9-5" aria-hidden="true" tabindex="-1"></a>  <span class="kw">struct</span> get_domain_t <span class="op">{</span> <em><code class="sourceCode default">unspecified</code></em> <span class="op">}</span>;</span>
<span id="cb9-6"><a href="#cb9-6" aria-hidden="true" tabindex="-1"></a>  <span class="add" style="color: #006e28"><ins><span><code class="sourceCode default">struct get_domain_override_t { <em><code class="sourceCode default">unspecified</code></em> };</code></span></ins></span></span>
<span id="cb9-7"><a href="#cb9-7" aria-hidden="true" tabindex="-1"></a>  <span class="kw">struct</span> get_scheduler_t <span class="op">{</span> <em><code class="sourceCode default">unspecified</code></em> <span class="op">}</span>;</span>
<span id="cb9-8"><a href="#cb9-8" aria-hidden="true" tabindex="-1"></a>  <span class="kw">struct</span> get_delegation_scheduler_t <span class="op">{</span> <em><code class="sourceCode default">unspecified</code></em> <span class="op">}</span>;</span>
<span id="cb9-9"><a href="#cb9-9" aria-hidden="true" tabindex="-1"></a>  <span class="kw">struct</span> get_forward_progress_guarantee_t <span class="op">{</span> <em><code class="sourceCode default">unspecified</code></em> <span class="op">}</span>;</span>
<span id="cb9-10"><a href="#cb9-10" aria-hidden="true" tabindex="-1"></a>  <span class="kw">template</span><span class="op">&lt;</span><span class="kw">class</span> CPO<span class="op">&gt;</span></span>
<span id="cb9-11"><a href="#cb9-11" aria-hidden="true" tabindex="-1"></a>    <span class="kw">struct</span> get_completion_scheduler_t <span class="op">{</span> <em><code class="sourceCode default">unspecified</code></em> <span class="op">}</span>;</span>
<span id="cb9-12"><a href="#cb9-12" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb9-13"><a href="#cb9-13" aria-hidden="true" tabindex="-1"></a>  <span class="kw">inline</span> <span class="kw">constexpr</span> get_domain_t get_domain<span class="op">{}</span>;</span>
<span id="cb9-14"><a href="#cb9-14" aria-hidden="true" tabindex="-1"></a>  <span class="add" style="color: #006e28"><ins><span><code class="sourceCode default">inline constexpr get_domain_override_t get_domain_override{};</code></span></ins></span></span>
<span id="cb9-15"><a href="#cb9-15" aria-hidden="true" tabindex="-1"></a>  <span class="kw">inline</span> <span class="kw">constexpr</span> get_scheduler_t get_scheduler<span class="op">{}</span>;</span>
<span id="cb9-16"><a href="#cb9-16" aria-hidden="true" tabindex="-1"></a>  <span class="kw">inline</span> <span class="kw">constexpr</span> get_delegation_scheduler_t get_delegation_scheduler<span class="op">{}</span>;</span>
<span id="cb9-17"><a href="#cb9-17" aria-hidden="true" tabindex="-1"></a>  <span class="kw">enum</span> <span class="kw">class</span> forward_progress_guarantee;</span>
<span id="cb9-18"><a href="#cb9-18" aria-hidden="true" tabindex="-1"></a>  <span class="kw">inline</span> <span class="kw">constexpr</span> get_forward_progress_guarantee_t get_forward_progress_guarantee<span class="op">{}</span>;</span>
<span id="cb9-19"><a href="#cb9-19" aria-hidden="true" tabindex="-1"></a>  <span class="kw">template</span><span class="op">&lt;</span><span class="kw">class</span> CPO<span class="op">&gt;</span></span>
<span id="cb9-20"><a href="#cb9-20" aria-hidden="true" tabindex="-1"></a>    <span class="kw">constexpr</span> get_completion_scheduler_t<span class="op">&lt;</span>CPO<span class="op">&gt;</span> get_completion_scheduler<span class="op">{}</span>;</span>
<span id="cb9-21"><a href="#cb9-21" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb9-22"><a href="#cb9-22" aria-hidden="true" tabindex="-1"></a><span style="color:blue;font-style:italic">… as before …</span></span></code></pre></div>
</blockquote>
</blockquote>
<p><span class="ednote" style="color: #0000ff">[ Editor&#39;s note: After
<span>33.5.5
<a href="https://wg21.link/exec.get.domain">[exec.get.domain]</a></span>
add a new subsection [exec.get.domain.override] as follows: ]</span></p>
<div class="add" style="color: #006e28">

<blockquote>
<p><strong>[33.5.?]
<code class="sourceCode default">execution​::get_domain_override</code>
[exec.get.domain.override]</strong></p>
<p><span class="marginalizedparent"><a class="marginalized">1</a></span>
<code class="sourceCode default">get_domain_override</code> asks a
queryable object for the domain tag to use in
<code class="sourceCode default">connect</code> and
<code class="sourceCode default">get_completion_signatures</code> to
find a sender transformation.</p>
<p><span class="marginalizedparent"><a class="marginalized">2</a></span>
The name <code class="sourceCode default">get_domain_override</code>
denotes a query object. For a subexpression
<code class="sourceCode default">env</code>,
<code class="sourceCode default">get_domain_override(env)</code> is
expression-equivalent to <code class="sourceCode default"><em><code class="sourceCode default">MANDATE-NOTHROW</code></em>(<em><code class="sourceCode default">AS-CONST</code></em>(env).query(get_domain_override))</code>.</p>
</blockquote>

</div>
<p><span class="ednote" style="color: #0000ff">[ Editor&#39;s note: Change
<span>33.5.6
<a href="https://wg21.link/exec.get.scheduler">[exec.get.scheduler]</a></span>
as follows: ]</span></p>
<blockquote>
<p><span class="marginalizedparent"><a class="marginalized">1</a></span>
<code class="sourceCode default">get_scheduler</code> asks a queryable
object for its associated scheduler.</p>
<p><span class="marginalizedparent"><a class="marginalized">2</a></span>
The name <code class="sourceCode default">get_scheduler</code> denotes a
query object. For a subexpression
<code class="sourceCode default">env</code>,
<code class="sourceCode default">get_scheduler(env)</code> is
expression-equivalent to <code class="sourceCode default"><em><code class="sourceCode default">MANDATE-NOTHROW</code></em>(<em><code class="sourceCode default">AS-CONST</code></em>(env).query(get_scheduler))</code>.</p>
<p><em>Mandates</em>: If the expression above is well-formed, its type
satisfies <code class="sourceCode default">scheduler</code>.</p>
<p><span class="marginalizedparent"><a class="marginalized">3</a></span>
<code class="sourceCode default">forwarding_query(execution​::​get_scheduler)</code>
is a core constant expression and has value
<code class="sourceCode default">true</code>.</p>
<div class="add" style="color: #006e28">
<p><span class="marginalizedparent"><a class="marginalized">?</a></span>
Given subexpressions <code class="sourceCode default">sndr</code> and
<code class="sourceCode default">rcvr</code> such that <code class="sourceCode default">sender_to&lt;decltype((sndr)), decltype((rcvr))&gt;</code>
is <code class="sourceCode default">true</code> and the expression
<code class="sourceCode default">get_scheduler(get_env(rcvr))</code> is
well-formed, an operation state that is the result of calling
<code class="sourceCode default">connect(sndr, rcvr)</code> shall, if it
is started, be started on an execution agent associated with the
scheduler
<code class="sourceCode default">get_scheduler(get_env(rcvr))</code>.</p>
</div>
</blockquote>
<p><span class="ednote" style="color: #0000ff">[ Editor&#39;s note: Change
<span>33.9.2
<a href="https://wg21.link/exec.snd.expos#6">[exec.snd.expos#6]</a></span>
as follows: ]</span></p>
<blockquote>
<p><span class="marginalizedparent"><a class="marginalized">6</a></span>
For a scheduler <code class="sourceCode default">sch</code> <span class="add" style="color: #006e28"><ins>and queryable object
<span><code class="sourceCode default">obj</code></span></ins></span>,
<code class="sourceCode default"><em><code class="sourceCode default">SCHED-ATTRS</code></em>(sch<span class="add" style="color: #006e28"><ins><span><code class="sourceCode default">, obj</code></span></ins></span>)</code> is
an expression <code class="sourceCode default">o1</code> whose type
satisfies <em><code class="sourceCode default">queryable</code></em>
such that<span class="add" style="color: #006e28"><ins>:</ins></span>
<span class="ednote" style="color: #0000ff">[ Editor&#39;s note: reformatted
as a list. ]</span></p>
<ul>
<li><p><span class="marginalizedparent"><a class="marginalized">(6.1)</a></span>
<code>o1.query(get_completion_scheduler&lt;<span class="rm" style="color: #bf0303"><del>Tag</del></span> <span class="add" style="color: #006e28"><ins>set_value_t</ins></span>&gt;)</code> is an
expression with the same type and value as
<code class="sourceCode default">sch</code><span class="rm" style="color: #bf0303"><del>where
<span><code class="sourceCode default">Tag</code></span> is one of
<span><code class="sourceCode default">set_value_t</code></span> or
<span><code class="sourceCode default">set_stopped_t</code></span></del></span>,
<span class="rm" style="color: #bf0303"><del>and such
that</del></span></p></li>
<li><p><span class="marginalizedparent"><a class="marginalized">(6.2)</a></span>
<span class="add" style="color: #006e28"><ins><span><code class="sourceCode default">o1.query(get_completion_scheduler&lt;Tag&gt;)</code></span>
is ill-formed for
<span><code class="sourceCode default">Tag</code></span> other than
<span><code class="sourceCode default">set_value_t</code></span>,</ins></span></p></li>
<li><p><span class="marginalizedparent"><a class="marginalized">(6.3)</a></span>
<code class="sourceCode default">o1.query(get_domain)</code> is
expression-equivalent to
<code class="sourceCode default">sch.query(get_domain)</code><span class="rm" style="color: #bf0303"><del>.</del></span> <span class="add" style="color: #006e28"><ins>if that expression is well-formed, and
<span><code class="sourceCode default">default_domain()</code></span>
otherwise, and</ins></span></p></li>
<li><p><span class="marginalizedparent"><a class="marginalized">(6.4)</a></span>
<span class="add" style="color: #006e28"><ins>For a pack of
subexpressions <span><code class="sourceCode default">as</code></span>
and query object <span><code class="sourceCode default">Q</code></span>
such that
<span><code class="sourceCode default">forwarding_query(Q)</code></span>
is <span><code class="sourceCode default">true</code></span>,
<span><code class="sourceCode default">o1.query(Q, as...)</code></span>
is expression-equivalent to
<span><code class="sourceCode default">obj.query(Q, as...)</code></span></ins></span>.</p></li>
</ul>
<p><span class="add" style="color: #006e28"><ins><span><code class="sourceCode default"><em><code class="sourceCode default">SCHED-ATTRS</code></em>(sch)</code></span>
is expression-equivalent to
<span><code class="sourceCode default"><em><code class="sourceCode default">SCHED-ATTRS</code></em>(sch, env{})</code></span>.</ins></span></p>
<p><span class="marginalizedparent"><a class="marginalized">?</a></span>
<code><em>SCHED-ENV</em>(sch<span class="add" style="color: #006e28"><ins>, obj</ins></span>)</code> is an expression
<code class="sourceCode default">o2</code> whose type satisfies
<em><code class="sourceCode default">queryable</code></em> such
that<span class="add" style="color: #006e28"><ins>:</ins></span> <span class="ednote" style="color: #0000ff">[ Editor&#39;s note: reformatted as a
list. ]</span></p>
<ul>
<li><p><span class="marginalizedparent"><a class="marginalized">(?.1)</a></span>
<code class="sourceCode default">o2.query(get_scheduler)</code> is a
prvalue with the same type and value as
<code class="sourceCode default">sch</code>, <span class="rm" style="color: #bf0303"><del>and such that</del></span></p></li>
<li><p><span class="marginalizedparent"><a class="marginalized">(?.2)</a></span>
<code class="sourceCode default">o2.query(get_domain)</code> is
expression-equivalent to
<code class="sourceCode default">sch.query(get_domain)</code><span class="rm" style="color: #bf0303"><del>.</del></span> <span class="add" style="color: #006e28"><ins>if that expression is well-formed, and
<span><code class="sourceCode default">default_domain()</code></span>
otherwise, and</ins></span></p></li>
<li><p><span class="marginalizedparent"><a class="marginalized">(?.3)</a></span>
<span class="add" style="color: #006e28"><ins>For a pack of
subexpressions <span><code class="sourceCode default">as</code></span>
and query object <span><code class="sourceCode default">Q</code></span>
such that
<span><code class="sourceCode default">forwarding_query(Q)</code></span>
is <span><code class="sourceCode default">true</code></span>,
<span><code class="sourceCode default">o2.query(Q, as...)</code></span>
is expression-equivalent to
<span><code class="sourceCode default">obj.query(Q, as...)</code></span></ins></span>.</p></li>
</ul>
<p><span class="add" style="color: #006e28"><ins><span><code class="sourceCode default"><em><code class="sourceCode default">SCHED-ENV</code></em>(sch)</code></span>
is expression-equivalent to
<span><code class="sourceCode default"><em><code class="sourceCode default">SCHED-ENV</code></em>(sch, env{})</code></span>.</ins></span></p>
</blockquote>
<p><span class="ednote" style="color: #0000ff">[ Editor&#39;s note: Change
<span>33.9.2
<a href="https://wg21.link/exec.snd.expos#8">[exec.snd.expos#8]</a></span>
and <span>33.9.2
<a href="https://wg21.link/exec.snd.expos#9">[exec.snd.expos#9]</a></span>
as follows: ]</span></p>
<blockquote>
<blockquote>
<div class="sourceCode" id="cb10"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb10-1"><a href="#cb10-1" aria-hidden="true" tabindex="-1"></a><span class="kw">template</span><span class="op">&lt;</span><span class="rm" style="color: #bf0303"><del><span><code class="sourceCode default">class Default = default_domain,</code></span></del></span> <span class="kw">class</span> Sndr<span class="op">&gt;</span></span>
<span id="cb10-2"><a href="#cb10-2" aria-hidden="true" tabindex="-1"></a>  <span class="kw">constexpr</span> <span class="kw">auto</span> <em><code class="sourceCode default">completion-domain</code></em><span class="op">(</span><span class="kw">const</span> Sndr<span class="op">&amp;</span> sndr<span class="op">)</span> <span class="kw">noexcept</span>;</span></code></pre></div>
</blockquote>
<p><span class="marginalizedparent"><a class="marginalized">8</a></span>
<code class="sourceCode default"><em><code class="sourceCode default">COMPL-DOMAIN</code></em>(T)</code>
is the type of the expression <code class="sourceCode default">get_domain(get_completion_scheduler&lt;T&gt;(get_env(sndr)))</code>.</p>
<p><span class="marginalizedparent"><a class="marginalized">9</a></span>
<em>Effects</em>: If all of the types
<code class="sourceCode default"><em><code class="sourceCode default">COMPL-DOMAIN</code></em>(set_value_t)</code>,
<code class="sourceCode default"><em><code class="sourceCode default">COMPL-DOMAIN</code></em>(set_error_t)</code>,
and <code class="sourceCode default"><em><code class="sourceCode default">COMPL-DOMAIN</code></em>(set_stopped_t)</code>
are ill-formed, <code class="sourceCode default"><em><code class="sourceCode default">completion-domain</code></em><span class="rm" style="color: #bf0303"><del><span><code class="sourceCode default">&lt;Default&gt;</code></span></del></span>(sndr)</code>
is a default-constructed prvalue of type <span class="rm" style="color: #bf0303"><del><span><code class="sourceCode default">Default</code></span></del></span><span class="add" style="color: #006e28"><ins><span><code class="sourceCode default">default_domain</code></span></ins></span>.
Otherwise, if they all share a common type (<span>21.3.8.7
<a href="https://wg21.link/meta.trans.other">[meta.trans.other]</a></span>)
(ignoring those types that are ill-formed), then <code class="sourceCode default"><em><code class="sourceCode default">completion-domain</code></em><span class="rm" style="color: #bf0303"><del><span><code class="sourceCode default">&lt;Default&gt;</code></span></del></span>(sndr)</code>
is a default-constructed prvalue of that type. Otherwise, <code class="sourceCode default"><em><code class="sourceCode default">completion-domain</code></em><span class="rm" style="color: #bf0303"><del><span><code class="sourceCode default">&lt;Default&gt;</code></span></del></span>(sndr)</code>
is ill-formed.</p>
</blockquote>
<p><span class="ednote" style="color: #0000ff">[ Editor&#39;s note: Change
<span>33.9.2
<a href="https://wg21.link/exec.snd.expos#14">[exec.snd.expos#14]</a></span>
as follows: ]</span></p>
<blockquote>
<blockquote>
<div class="sourceCode" id="cb11"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb11-1"><a href="#cb11-1" aria-hidden="true" tabindex="-1"></a><span class="kw">template</span><span class="op">&lt;</span><span class="kw">class</span> Sndr, <span class="kw">class</span> Env<span class="add" style="color: #006e28"><ins><span><code class="sourceCode default">, class Default = default_domain</code></span></ins></span><span class="op">&gt;</span></span>
<span id="cb11-2"><a href="#cb11-2" aria-hidden="true" tabindex="-1"></a>  <span class="kw">constexpr</span> <span class="kw">auto</span> <em><code class="sourceCode default">get-domain-late</code></em><span class="op">(</span><span class="kw">const</span> Sndr<span class="op">&amp;</span> sndr, <span class="kw">const</span> Env<span class="op">&amp;</span> env<span class="add" style="color: #006e28"><ins><span><code class="sourceCode default">, Default = {}</code></span></ins></span><span class="op">)</span> <span class="kw">noexcept</span>;</span></code></pre></div>
</blockquote>
<ol start="14" type="1">
<li><p>Effects: Equivalent to:</p>
<div class="rm" style="color: #bf0303">

<ol type="1">
<li><p><span class="rm" style="color: #bf0303"><del>If <span><code class="sourceCode default"><em>sender-for</em>&lt;Sndr, continues_on_t&gt;</code></span>
is <span><code class="sourceCode default">true</code></span>,
then</del></span></p>
<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><span class="rm" style="color: #bf0303"><del>return
Domain();</del></span></span></code></pre></div>
</blockquote>
<p><span class="rm" style="color: #bf0303"><del>where
<span><code class="sourceCode default">Domain</code></span> is the type
of the following expression:</del></span></p>
<blockquote>
<div class="sourceCode" id="cb13"><pre class="sourceCode default default"><code class="sourceCode default"><span id="cb13-1"><a href="#cb13-1" aria-hidden="true" tabindex="-1"></a><span class="rm" style="color: #bf0303"><del><span><code class="sourceCode default">[] {</code></span></del></span></span>
<span id="cb13-2"><a href="#cb13-2" aria-hidden="true" tabindex="-1"></a>  <span class="rm" style="color: #bf0303"><del><span><code class="sourceCode default">auto [_, sch, _] = sndr;</code></span></del></span></span>
<span id="cb13-3"><a href="#cb13-3" aria-hidden="true" tabindex="-1"></a>  <span class="rm" style="color: #bf0303"><del><span><code class="sourceCode default">return <em><code class="sourceCode default">query-or-default</code></em>(get_domain, sch, default_domain());</code></span></del></span></span>
<span id="cb13-4"><a href="#cb13-4" aria-hidden="true" tabindex="-1"></a><span class="rm" style="color: #bf0303"><del><span><code class="sourceCode default">}();</code></span></del></span></span></code></pre></div>
</blockquote>
<p><span class="rm" style="color: #bf0303"><del>[<em>Note 1</em>: The
<span><code class="sourceCode default">continues_on</code></span>
algorithm works in tandem with
<span><code class="sourceCode default">schedule_from</code></span>
([exec.schedule.from]) to give scheduler authors a way to customize both
how to transition onto
(<span><code class="sourceCode default">continues_on</code></span>) and
off of
(<span><code class="sourceCode default">schedule_from</code></span>) a
given execution context. Thus,
<span><code class="sourceCode default">continues_on</code></span>
ignores the domain of the predecessor and uses the domain of the
destination scheduler to select a customization, a property that is
unique to
<span><code class="sourceCode default">continues_on</code></span>. That
is why it is given special treatment here. — <em>end
note</em>]</del></span></p></li>
<li><p><span class="rm" style="color: #bf0303"><del>Otherwise,</del></span></p></li>
</ol>

</div>
<blockquote>
<div class="sourceCode" id="cb14"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb14-1"><a href="#cb14-1" aria-hidden="true" tabindex="-1"></a><span class="cf">return</span> Domain<span class="op">()</span>;</span></code></pre></div>
</blockquote>
<p>where <code class="sourceCode default">Domain</code> is the <span class="add" style="color: #006e28"><ins>type of the</ins></span> first
of the following expressions that is well-formed<span class="rm" style="color: #bf0303"><del>and whose type is not
<span><code class="sourceCode default">void</code></span></del></span>:</p>
<ul>
<li><code class="sourceCode default">get_domain<span class="add" style="color: #006e28"><ins><span><code class="sourceCode default">_override</code></span></ins></span>(get_env(sndr))</code></li>
<li><span class="rm" style="color: #bf0303"><del><span><code class="sourceCode default"><em><code class="sourceCode default">completion-domain</code></em>&lt;void&gt;(sndr)</code></span></del></span></li>
<li><code class="sourceCode default">get_domain(env)</code></li>
<li><code class="sourceCode default">get_domain(get_scheduler(env))</code></li>
<li><span class="rm" style="color: #bf0303"><del><span><code class="sourceCode default">default_domain()</code></span></del></span><span class="add" style="color: #006e28"><ins><span><code class="sourceCode default">Default()</code></span></ins></span></li>
</ul></li>
</ol>
</blockquote>
<p><span class="ednote" style="color: #0000ff">[ Editor&#39;s note: Insert a
new paragraph after <span>33.9.12.3
<a href="https://wg21.link/exec.starts.on#3">[exec.starts.on#3]</a></span>
as follows: ]</span></p>
<div class="add" style="color: #006e28">

<blockquote>
<p><span class="marginalizedparent"><a class="marginalized">?</a></span>
The exposition-only class template
<em><code class="sourceCode default">impls-for</code></em> is
specialized for <code class="sourceCode default">starts_on_t</code> as
follows:</p>
<blockquote>
<div class="sourceCode" id="cb15"><pre class="sourceCode default cpp"><code class="sourceCode default"><span id="cb15-1"><a href="#cb15-1" aria-hidden="true" tabindex="-1"></a>namespace std::execution {</span>
<span id="cb15-2"><a href="#cb15-2" aria-hidden="true" tabindex="-1"></a>  template&lt;&gt;</span>
<span id="cb15-3"><a href="#cb15-3" aria-hidden="true" tabindex="-1"></a>  struct <em><code class="sourceCode default">impls-for</code></em>&lt;starts_on_t&gt; : <em><code class="sourceCode default">default-impls</code></em> {</span>
<span id="cb15-4"><a href="#cb15-4" aria-hidden="true" tabindex="-1"></a>    static constexpr auto <em><code class="sourceCode default">get-attrs</code></em> =</span>
<span id="cb15-5"><a href="#cb15-5" aria-hidden="true" tabindex="-1"></a>      [](const auto&amp; sch, const auto&amp; child) noexcept -&gt; decltype(auto) {</span>
<span id="cb15-6"><a href="#cb15-6" aria-hidden="true" tabindex="-1"></a>        auto <em><code class="sourceCode default">sch-domain</code></em> = <em><code class="sourceCode default">query-with-default</code></em>(get_domain, sch, default_domain());</span>
<span id="cb15-7"><a href="#cb15-7" aria-hidden="true" tabindex="-1"></a>        return <em><code class="sourceCode default">JOIN-ENV</code></em>(<em><code class="sourceCode default">MAKE-ENV</code></em>(get_domain_override, <em><code class="sourceCode default">sch-domain</code></em>), <em><code class="sourceCode default">FWD-ENV</code></em>(get_env(child)));</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>  };</span>
<span id="cb15-10"><a href="#cb15-10" aria-hidden="true" tabindex="-1"></a>}</span></code></pre></div>
</blockquote>
</blockquote>

</div>
<p><span class="ednote" style="color: #0000ff">[ Editor&#39;s note: Change
<span>33.9.12.4
<a href="https://wg21.link/exec.continues.on#4">[exec.continues.on#4]</a></span>
as follows: ]</span></p>
<blockquote>
<p><span class="marginalizedparent"><a class="marginalized">4</a></span>
The exposition-only class template
<em><code class="sourceCode default">impls-for</code></em> is
specialized for <code class="sourceCode default">continues_on_t</code>
as follows:</p>
<blockquote>
<div class="sourceCode" id="cb16"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb16-1"><a href="#cb16-1" aria-hidden="true" tabindex="-1"></a><span class="kw">namespace</span> std<span class="op">::</span>execution <span class="op">{</span></span>
<span id="cb16-2"><a href="#cb16-2" aria-hidden="true" tabindex="-1"></a>  <span class="kw">template</span><span class="op">&lt;&gt;</span></span>
<span id="cb16-3"><a href="#cb16-3" aria-hidden="true" tabindex="-1"></a>  <span class="kw">struct</span> <em><code class="sourceCode default">impls-for</code></em><span class="op">&lt;</span>continues_on_t<span class="op">&gt;</span> <span class="op">:</span> <em><code class="sourceCode default">default-impls</code></em> <span class="op">{</span></span>
<span id="cb16-4"><a href="#cb16-4" aria-hidden="true" tabindex="-1"></a>    <span class="kw">static</span> <span class="kw">constexpr</span> <span class="kw">auto</span> <em><code class="sourceCode default">get-attrs</code></em> <span class="op">=</span></span>
<span id="cb16-5"><a href="#cb16-5" aria-hidden="true" tabindex="-1"></a>      <span class="op">[](</span><span class="kw">const</span> <span class="kw">auto</span><span class="op">&amp;</span> data, <span class="kw">const</span> <span class="kw">auto</span><span class="op">&amp;</span> child<span class="op">)</span> <span class="kw">noexcept</span> <span class="op">-&gt;</span> <span class="kw">decltype</span><span class="op">(</span><span class="kw">auto</span><span class="op">)</span> <span class="op">{</span></span>
<span id="cb16-6"><a href="#cb16-6" aria-hidden="true" tabindex="-1"></a>        <span class="rm" style="color: #bf0303"><del><span><code class="sourceCode default">return <em><code class="sourceCode default">JOIN-ENV</code></em>(<em><code class="sourceCode default">SCHED-ATTRS</code></em>(data), <em><code class="sourceCode default">FWD-ENV</code></em>(get_env(child)));</code></span></del></span></span>
<span id="cb16-7"><a href="#cb16-7" aria-hidden="true" tabindex="-1"></a>        <span class="add" style="color: #006e28"><ins><span><code class="sourceCode default">return <em><code class="sourceCode default">JOIN-ENV</code></em>(E, <em><code class="sourceCode default">SCHED-ATTRS</code></em>(data, get_env(child)));</code></span></ins></span></span>
<span id="cb16-8"><a href="#cb16-8" aria-hidden="true" tabindex="-1"></a>      <span class="op">}</span>;</span>
<span id="cb16-9"><a href="#cb16-9" aria-hidden="true" tabindex="-1"></a>  <span class="op">}</span>;</span>
<span id="cb16-10"><a href="#cb16-10" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
</blockquote>
<p><span class="add" style="color: #006e28"><ins>where
<span><code class="sourceCode default">E</code></span> is a queryable
object such that
<span><code class="sourceCode default">E.query(get_domain_override)</code></span>
is expression-equivalent to
<span><code class="sourceCode default">get_domain(get_env(child))</code></span>
if that expression is well-formed; otherwise, <span><code class="sourceCode default">get_domain(get_completion_scheduler&lt;set_value_t&gt;(get_env(child)))</code></span>
if that expression is well-formed; otherwise,
<span><code class="sourceCode default">E.query(get_domain_override)</code></span>
is ill-formed.</ins></span></p>
</blockquote>
<p><span class="ednote" style="color: #0000ff">[ Editor&#39;s note: Change
<span>33.9.12.5
<a href="https://wg21.link/exec.schedule.from#5">[exec.schedule.from#5]</a></span>
as follows: ]</span></p>
<blockquote>
<p><span class="marginalizedparent"><a class="marginalized">5</a></span>
The member <code class="sourceCode default"><em><code class="sourceCode default">impls-for</code></em>&lt;schedule_from_t&gt;​::<em><code class="sourceCode default">​get-attrs</code></em></code>
is initialized with a callable object equivalent to the following
lambda:</p>
<blockquote>
<div class="sourceCode" id="cb17"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb17-1"><a href="#cb17-1" aria-hidden="true" tabindex="-1"></a><span class="op">[](</span><span class="kw">const</span> <span class="kw">auto</span><span class="op">&amp;</span> data, <span class="kw">const</span> <span class="kw">auto</span><span class="op">&amp;</span> child<span class="op">)</span> <span class="kw">noexcept</span> <span class="op">-&gt;</span> <span class="kw">decltype</span><span class="op">(</span><span class="kw">auto</span><span class="op">)</span> <span class="op">{</span></span>
<span id="cb17-2"><a href="#cb17-2" aria-hidden="true" tabindex="-1"></a>  <span class="rm" style="color: #bf0303"><del><span><code class="sourceCode default">return <em><code class="sourceCode default">JOIN-ENV</code></em>(<em><code class="sourceCode default">SCHED-ATTRS</code></em>(data), <em><code class="sourceCode default">FWD-ENV</code></em>(get_env(child)));</code></span></del></span></span>
<span id="cb17-3"><a href="#cb17-3" aria-hidden="true" tabindex="-1"></a>  <span class="add" style="color: #006e28"><ins><span><code class="sourceCode default">return <em><code class="sourceCode default">JOIN-ENV</code></em>(E, <em><code class="sourceCode default">SCHED-ATTRS</code></em>(data, get_env(child)));</code></span></ins></span></span>
<span id="cb17-4"><a href="#cb17-4" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
</blockquote>
<p><span class="add" style="color: #006e28"><ins>where
<span><code class="sourceCode default">E</code></span> is a queryable
object such that
<span><code class="sourceCode default">E.query(get_domain_override)</code></span>
is expression-equivalent to <span><code class="sourceCode default"><em><code class="sourceCode default">query-with-default</code></em>(get_domain, data, default_domain())</code></span>.</ins></span></p>
</blockquote>
<p><span class="ednote" style="color: #0000ff">[ Editor&#39;s note: Change
<span>33.9.12.6
<a href="https://wg21.link/exec.on#7">[exec.on#7]</a></span>, as
follows: ]</span></p>
<blockquote>
<p><span class="marginalizedparent"><a class="marginalized">7</a></span>
The expression
<code class="sourceCode default">on.transform_env(out_sndr, env)</code>
has effects equivalent to:</p>
<blockquote>
<div class="sourceCode" id="cb18"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb18-1"><a href="#cb18-1" aria-hidden="true" tabindex="-1"></a><span class="kw">auto</span><span class="op">&amp;&amp;</span> <span class="op">[</span>_, data, _<span class="op">]</span> <span class="op">=</span> out_sndr;</span>
<span id="cb18-2"><a href="#cb18-2" aria-hidden="true" tabindex="-1"></a><span class="cf">if</span> <span class="kw">constexpr</span> <span class="op">(</span>scheduler<span class="op">&lt;</span><span class="kw">decltype</span><span class="op">(</span>data<span class="op">)&gt;)</span> <span class="op">{</span></span>
<span id="cb18-3"><a href="#cb18-3" aria-hidden="true" tabindex="-1"></a>  <span class="rm" style="color: #bf0303"><del><span><code class="sourceCode default">return <em><code class="sourceCode default">JOIN-ENV</code></em>(<em><code class="sourceCode default">SCHED-ENV</code></em>(std::forward_like&lt;OutSndr&gt;(data)), <em><code class="sourceCode default">FWD-ENV</code></em>(std::forward&lt;Env&gt;(env)));</code></span></del></span></span>
<span id="cb18-4"><a href="#cb18-4" aria-hidden="true" tabindex="-1"></a>  <span class="add" style="color: #006e28"><ins><span><code class="sourceCode default">return <em><code class="sourceCode default">SCHED-ENV</code></em>(std::forward_like&lt;OutSndr&gt;(data), std::forward&lt;Env&gt;(env));</code></span></ins></span></span>
<span id="cb18-5"><a href="#cb18-5" aria-hidden="true" tabindex="-1"></a><span class="op">}</span> <span class="cf">else</span> <span class="op">{</span></span>
<span id="cb18-6"><a href="#cb18-6" aria-hidden="true" tabindex="-1"></a>  <span class="cf">return</span> std<span class="op">::</span>forward<span class="op">&lt;</span>Env<span class="op">&gt;(</span>env<span class="op">)</span>;</span>
<span id="cb18-7"><a href="#cb18-7" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
</blockquote>
</blockquote>
<p><span class="ednote" style="color: #0000ff">[ Editor&#39;s note: After
<span>33.9.12.8
<a href="https://wg21.link/exec.let#4">[exec.let#4]</a></span>, insert
two new paragraphs: ]</span></p>
<blockquote>
<p><span class="marginalizedparent"><a class="marginalized">4</a></span>
Otherwise, the expression
<code class="sourceCode default"><em><code class="sourceCode default">let-cpo</code></em>(sndr, f)</code>
is expression-equivalent to:</p>
<blockquote>
<div class="sourceCode" id="cb19"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb19-1"><a href="#cb19-1" aria-hidden="true" tabindex="-1"></a>transform_sender<span class="op">(</span><em><code class="sourceCode default">get-domain-early</code></em><span class="op">(</span>sndr<span class="op">)</span>, <em><code class="sourceCode default">make-sender</code></em><span class="op">(</span><em><code class="sourceCode default">let-cpo</code></em>, f, sndr<span class="op">))</span></span></code></pre></div>
</blockquote>
<p>except that <code class="sourceCode default">sndr</code> is evaluated
only once.</p>
<div class="add" style="color: #006e28">
<p><span class="marginalizedparent"><a class="marginalized">?</a></span>
Given a type <code class="sourceCode default">C</code> of the form <code class="sourceCode default">completion_signatures&lt;Sigs...&gt;</code>,
let
<code class="sourceCode default"><em><code class="sourceCode default">SELECT-SIGS</code></em>(C)</code>
be a pack of those types in <code class="sourceCode default">Sigs</code>
with a return type of <code class="sourceCode default"><em><code class="sourceCode default">decayed-typeof</code></em>&lt;<em><code class="sourceCode default">set-cpo</code></em>&gt;</code>.</p>
<p><span class="marginalizedparent"><a class="marginalized">?</a></span>
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><code class="sourceCode default">as-sndr2</code></em>&lt;Tag(Args...)&gt;</code>
denotes the type <code class="sourceCode default"><em><code class="sourceCode default">call-result-t</code></em>&lt;F,    decay_t&lt;Args&gt;&amp;...&gt;</code>,
and let <em><code class="sourceCode default">as-tuple</code></em> be an
alias template such that <code class="sourceCode default"><em><code class="sourceCode default">as-tuple</code></em>&lt;Tag(Args...)&gt;</code>
denotes the type <code class="sourceCode default"><em><code class="sourceCode default">decayed-tuple</code></em>&lt;Args...&gt;</code>.</p>
</div>
<p><span class="marginalizedparent"><a class="marginalized">5</a></span>
The exposition-only class template impls-for (<span>33.9.1
<a href="https://wg21.link/exec.snd.general">[exec.snd.general]</a></span>)
is specialized for
<em><code class="sourceCode default">let-cpo</code></em> as follows:</p>
<blockquote>
<div class="sourceCode" id="cb20"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb20-1"><a href="#cb20-1" aria-hidden="true" tabindex="-1"></a><span class="kw">namespace</span> std<span class="op">::</span>execution <span class="op">{</span></span>
<span id="cb20-2"><a href="#cb20-2" aria-hidden="true" tabindex="-1"></a>  <span class="kw">template</span><span class="op">&lt;</span><span class="kw">class</span> State, <span class="kw">class</span> Rcvr, <span class="kw">class</span><span class="op">...</span> Args<span class="op">&gt;</span></span>
<span id="cb20-3"><a href="#cb20-3" aria-hidden="true" tabindex="-1"></a>  <span class="dt">void</span> <em><code class="sourceCode default">let-bind</code></em><span class="op">(</span>State<span class="op">&amp;</span> state, Rcvr<span class="op">&amp;</span> rcvr, Args<span class="op">&amp;&amp;...</span> args<span class="op">)</span>;      <span class="co">// exposition only</span></span>
<span id="cb20-4"><a href="#cb20-4" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb20-5"><a href="#cb20-5" aria-hidden="true" tabindex="-1"></a>  <span class="kw">template</span><span class="op">&lt;&gt;</span></span>
<span id="cb20-6"><a href="#cb20-6" aria-hidden="true" tabindex="-1"></a>  <span class="kw">struct</span> <em><code class="sourceCode default">impls-for</code></em><span class="op">&lt;</span><em><code class="sourceCode default">decayed-typeof</code></em><span class="op">&lt;</span><em><code class="sourceCode default">let-cpo</code></em><span class="op">&gt;&gt;</span> <span class="op">:</span> <em><code class="sourceCode default">default-impls</code></em> <span class="op">{</span></span>
<span id="cb20-7"><a href="#cb20-7" aria-hidden="true" tabindex="-1"></a>    <span class="add" style="color: #006e28"><ins><span><code class="sourceCode default">static constexpr auto <em><code class="sourceCode default">get-attrs</code></em> = <em>see below</em>;</code></span></ins></span></span>
<span id="cb20-8"><a href="#cb20-8" aria-hidden="true" tabindex="-1"></a>    <span class="kw">static</span> <span class="kw">constexpr</span> <span class="kw">auto</span> <em><code class="sourceCode default">get-state</code></em> <span class="op">=</span> <em>see below</em>;</span>
<span id="cb20-9"><a href="#cb20-9" aria-hidden="true" tabindex="-1"></a>    <span class="kw">static</span> <span class="kw">constexpr</span> <span class="kw">auto</span> <em><code class="sourceCode default">complete</code></em> <span class="op">=</span> <em>see below</em>;</span>
<span id="cb20-10"><a href="#cb20-10" aria-hidden="true" tabindex="-1"></a>  <span class="op">}</span>;</span>
<span id="cb20-11"><a href="#cb20-11" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
</blockquote>
<div class="add" style="color: #006e28">
<p><span class="marginalizedparent"><a class="marginalized">?</a></span>
The member <code class="sourceCode default"><em><code class="sourceCode default">impls-for</code></em>&lt;<em><code class="sourceCode default">decayed-typeof</code></em>&lt;<em><code class="sourceCode default">let-cpo</code></em>&gt;&gt;​::<em><code class="sourceCode default">​get-attrs</code></em></code>
is initialized with a callable object equivalent to the following
lambda:</p>
<blockquote>
<div class="sourceCode" id="cb21"><pre class="sourceCode default cpp"><code class="sourceCode default"><span id="cb21-1"><a href="#cb21-1" aria-hidden="true" tabindex="-1"></a>[]&lt;class Fn, class Child&gt;(const Fn&amp; data, const Child&amp; child) noexcept -&gt; decltype(auto) {</span>
<span id="cb21-2"><a href="#cb21-2" aria-hidden="true" tabindex="-1"></a>  return <em><code class="sourceCode default">JOIN-ENV</code></em>(E, <em><code class="sourceCode default">FWD-ENV</code></em>(get_env(child)));</span>
<span id="cb21-3"><a href="#cb21-3" aria-hidden="true" tabindex="-1"></a>}</span></code></pre></div>
</blockquote>
<p>where <code class="sourceCode default">E</code> is a queryable object
equivalent to</p>
<blockquote>
<div class="sourceCode" id="cb22"><pre class="sourceCode default cpp"><code class="sourceCode default"><span id="cb22-1"><a href="#cb22-1" aria-hidden="true" tabindex="-1"></a><em><code class="sourceCode default">MAKE-ENV</code></em>(get_domain, common_type_t&lt;<em><code class="sourceCode default">early-domain-of-t</code></em>&lt;<em><code class="sourceCode default">as-sndr2</code></em>&lt;<em><code class="sourceCode default">SELECT-SIGS</code></em>(C)&gt;&gt;...&gt;{})</span></code></pre></div>
</blockquote>
<p>if that expression is well-formed, where
<code class="sourceCode default">C</code> is <code class="sourceCode default">completion_signatures_of_t&lt;Child&gt;</code>
and <code class="sourceCode default"><em><code class="sourceCode default">early-domain-of-t</code></em>&lt;Sndr&gt;</code>
denotes the type <code class="sourceCode default">decltype(<em><code class="sourceCode default">get-domain-early</code></em>(declval&lt;Sndr&gt;()))</code>.
Otherwise, <code class="sourceCode default">E</code> is equivalent to
<code class="sourceCode default">env{}</code>.</p>
</div>
</blockquote>
<p><span class="ednote" style="color: #0000ff">[ Editor&#39;s note: Change
<span>33.9.12.8
<a href="https://wg21.link/exec.let#6">[exec.let#6]</a></span> as
follows: ]</span></p>
<blockquote>
<p><span class="marginalizedparent"><a class="marginalized">6</a></span>
Let <em><code class="sourceCode default">receiver2</code></em> denote
the following exposition-only class template:</p>
<div class="sourceCode" id="cb23"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb23-1"><a href="#cb23-1" aria-hidden="true" tabindex="-1"></a><span class="kw">namespace</span> std<span class="op">::</span>execution <span class="op">{</span></span>
<span id="cb23-2"><a href="#cb23-2" aria-hidden="true" tabindex="-1"></a>  <span class="kw">template</span><span class="op">&lt;</span><span class="kw">class</span> Rcvr, <span class="kw">class</span> Env<span class="op">&gt;</span></span>
<span id="cb23-3"><a href="#cb23-3" aria-hidden="true" tabindex="-1"></a>  <span class="kw">struct</span> receiver2 <span class="op">{</span></span>
<span id="cb23-4"><a href="#cb23-4" aria-hidden="true" tabindex="-1"></a>    <span style="color:blue;font-style:italic">… as before …</span></span>
<span id="cb23-5"><a href="#cb23-5" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb23-6"><a href="#cb23-6" aria-hidden="true" tabindex="-1"></a>    Rcvr<span class="op">&amp;</span> <em><code class="sourceCode default">rcvr</code></em>;                      <span class="co">// exposition only</span></span>
<span id="cb23-7"><a href="#cb23-7" aria-hidden="true" tabindex="-1"></a>    Env <em><code class="sourceCode default">env</code></em>;                         <span class="co">// exposition only</span></span>
<span id="cb23-8"><a href="#cb23-8" aria-hidden="true" tabindex="-1"></a>  <span class="op">}</span>;</span>
<span id="cb23-9"><a href="#cb23-9" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
<p>Invocation of the function
<code class="sourceCode default"><em><code class="sourceCode default">receiver2​</code></em>::​get_env</code>
returns an object <code class="sourceCode default">e</code> such
that</p>
<ul>
<li><p><span class="marginalizedparent"><a class="marginalized">(6.1)</a></span>
<code class="sourceCode default">decltype(e)</code> models
<code class="sourceCode default">queryable</code> and</p></li>
<li><p><span class="marginalizedparent"><a class="marginalized">(6.2)</a></span>
given a query object <code class="sourceCode default">q</code>, the
expression <code class="sourceCode default">e.query(q)</code> is
expression-equivalent to<span class="add" style="color: #006e28"><ins>:</ins></span> <span class="ednote" style="color: #0000ff">[ Editor&#39;s note: Reformated as a list
]</span></p>
<ul>
<li><span class="marginalizedparent"><a class="marginalized">(6.2.1)</a></span>
<code class="sourceCode default">env.query(q)</code> if that expression
is valid<span class="rm" style="color: #bf0303"><del>,</del></span><span class="add" style="color: #006e28"><ins>.</ins></span></li>
<li><span class="marginalizedparent"><a class="marginalized">(6.2.2)</a></span>
<span class="rm" style="color: #bf0303"><del>o</del></span><span class="add" style="color: #006e28"><ins>O</ins></span>therwise<span class="add" style="color: #006e28"><ins>,</ins></span> <span class="rm" style="color: #bf0303"><del><span><code class="sourceCode default">e.query(q)</code></span>
is expression-equivalent to</del></span>
<code class="sourceCode default">get_env(rcvr).query(q)</code> <span class="add" style="color: #006e28"><ins>if that expression is valid and
the decayed type of
<span><code class="sourceCode default">q</code></span> is neither
<span><code class="sourceCode default">get_scheduler_t</code></span> nor
<span><code class="sourceCode default">get_domain_t</code></span></ins></span>.</li>
<li><span class="marginalizedparent"><a class="marginalized">(6.2.3)</a></span>
<span class="add" style="color: #006e28"><ins>Otherwise,
<span><code class="sourceCode default">e.query(q)</code></span> is
ill-formed.</ins></span></li>
</ul></li>
</ul>
</blockquote>
<p><span class="ednote" style="color: #0000ff">[ Editor&#39;s note: Replace
<span>33.9.12.8
<a href="https://wg21.link/exec.let#8">[exec.let#8]</a></span> and
<span>33.9.12.8
<a href="https://wg21.link/exec.let#9">[exec.let#9]</a></span> as shown
below: ]</span></p>
<blockquote>
<div class="rm" style="color: #bf0303">
<p><span class="marginalizedparent"><a class="marginalized">8</a></span>
<span class="rm" style="color: #bf0303"><del>Let
<span><code class="sourceCode default">Sigs</code></span> be a pack of
the arguments to the
<span><code class="sourceCode default">completion_signatures</code></span>
specialization named by <span><code class="sourceCode default">completion_signatures_of_t&lt;<em><code class="sourceCode default">child-type</code></em>&lt;Sndr&gt;,    env_of_t&lt;Rcvr&gt;&gt;</code></span>.
Let <span><code class="sourceCode default">LetSigs</code></span> be a
pack of those types in
<span><code class="sourceCode default">Sigs</code></span> with a return
type of <span><code class="sourceCode default"><em><code class="sourceCode default">decayed-typeof</code></em>&lt;<em><code class="sourceCode default">set-cpo</code></em>&gt;</code></span>.
Let
<em><span><code class="sourceCode default">as-tuple</code></span></em>
be an alias template such that <span><code class="sourceCode default"><em><code class="sourceCode default">as-tuple</code></em>&lt;Tag(Args...)&gt;</code></span>
denotes the type <span><code class="sourceCode default"><em><code class="sourceCode default">decayed-tuple</code></em>&lt;Args...&gt;</code></span>.
Then <span><code class="sourceCode default">args_variant_t</code></span>
denotes the type <span><code class="sourceCode default">variant&lt;monostate,    <em><code class="sourceCode default">as-tuple</code></em>&lt;LetSigs&gt;...&gt;</code></span>
except with duplicate types removed.</del></span></p>
<p><span class="marginalizedparent"><a class="marginalized">9</a></span>
<span class="rm" style="color: #bf0303"><del>Given a type
<span><code class="sourceCode default">Tag</code></span> and a pack
<span><code class="sourceCode default">Args</code></span>, let
<em><span><code class="sourceCode default">as-sndr2</code></span></em>
be an alias template such that <span><code class="sourceCode default"><em><code class="sourceCode default">as-sndr2</code></em>&lt;Tag(Args...)&gt;</code></span>
denotes the type <span><code class="sourceCode default"><em><code class="sourceCode default">call-result-t</code></em>&lt;Fn, decay_t&lt;Args&gt;&amp;...&gt;</code></span>.
Then <span><code class="sourceCode default">ops2_variant_t</code></span>
denotes the type <span><code class="sourceCode default">variant&lt;monostate, connect_result_t&lt;<em><code class="sourceCode default">as-sndr2</code></em>&lt;LetSigs&gt;, receiver2&lt;Rcvr, Env&gt;&gt;...&gt;</code></span>
except with duplicate types removed.</del></span></p>
</div>
<div class="add" style="color: #006e28">
<p><span class="marginalizedparent"><a class="marginalized">?</a></span>
Let <code class="sourceCode default">C</code> be the type named by <code class="sourceCode default">completion_signatures_of_t&lt;<em><code class="sourceCode default">child-type</code></em>&lt;Sndr&gt;, env_of_t&lt;Rcvr&gt;&gt;</code>.
Then <code class="sourceCode default">args_variant_t</code> denotes the
type <code class="sourceCode default">variant&lt;monostate, <em><code class="sourceCode default">as-tuple</code></em>&lt;<em><code class="sourceCode default">SELECT-SIGS</code></em>(C)&gt;...&gt;</code>
except with duplicate types removed, and
<code class="sourceCode default">ops2_variant_t</code> denotes the type
<code class="sourceCode default">variant&lt;monostate, connect_result_t&lt;<em><code class="sourceCode default">as-sndr2</code></em>&lt;<em><code class="sourceCode default">SELECT-SIGS</code></em>(C)&gt;, receiver2&lt;Rcvr, Env&gt;&gt;...&gt;</code>
except with duplicate types removed.</p>
</div>
</blockquote>
<p><span class="ednote" style="color: #0000ff">[ Editor&#39;s note: Change
<span>33.9.13.1
<a href="https://wg21.link/exec.sync.wait#4">[exec.sync.wait#4]</a></span>
as follows: ]</span></p>
<blockquote>
<p><span class="marginalizedparent"><a class="marginalized">4</a></span>
The name <code class="sourceCode default">this_thread​::​sync_wait</code>
denotes a customization point object. For a subexpression
<code class="sourceCode default">sndr</code>, let
<code class="sourceCode default">Sndr</code> be
<code class="sourceCode default">decltype((sndr))</code>. If <code class="sourceCode default">sender_in&lt;Sndr, <em><code class="sourceCode default">sync-wait-env</code></em>&gt;</code>
is <code class="sourceCode default">false</code>, the expression
<code class="sourceCode default">this_thread​::​sync_wait(sndr)</code> is
ill-formed. Otherwise, it is expression-equivalent to the following,
except that <code class="sourceCode default">sndr</code> is evaluated
only once:</p>
<blockquote>
<div class="sourceCode" id="cb24"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb24-1"><a href="#cb24-1" aria-hidden="true" tabindex="-1"></a>apply_sender<span class="op">(</span><em><code class="sourceCode default">get-domain-</code></em><span class="rm" style="color: #bf0303"><del><em><span><code class="sourceCode default">early</code></span></em></del></span><span class="add" style="color: #006e28"><ins><em><span><code class="sourceCode default">late</code></span></em></ins></span><span class="op">(</span>sndr<span class="add" style="color: #006e28"><ins><span><code class="sourceCode default">, <em><code class="sourceCode default">sync-wait-env</code></em>{}, <em><code class="sourceCode default">get-domain-early</code></em>(sndr)</code></span></ins></span><span class="op">)</span>, sync_wait, sndr<span class="op">)</span></span></code></pre></div>
</blockquote>
<p><em>Mandates</em>:</p>
<ul>
<li><p><span class="marginalizedparent"><a class="marginalized">(4.1)</a></span> The
type <code class="sourceCode default"><em><code class="sourceCode default">sync-wait-result-type</code></em>&lt;Sndr&gt;</code>
is well-formed.</p></li>
<li><p><span class="marginalizedparent"><a class="marginalized">(4.2)</a></span>
<code class="sourceCode default">same_as&lt;decltype(<em><code class="sourceCode default">e</code></em>), <em><code class="sourceCode default">sync-wait-result-type</code></em>&lt;Sndr&gt;&gt;</code>
is <code class="sourceCode default">true</code>, where
<em><code class="sourceCode default">e</code></em> is the
<code class="sourceCode default">apply_sender</code> expression
above.</p></li>
</ul>
</blockquote>
<p><span class="ednote" style="color: #0000ff">[ Editor&#39;s note: Change
<span>33.9.13.2
<a href="https://wg21.link/exec.sync.wait.var#1">[exec.sync.wait.var#1]</a></span>
as follows: ]</span></p>
<blockquote>
<p><span class="marginalizedparent"><a class="marginalized">1</a></span>
The name <code class="sourceCode default">this_thread​::​sync_wait_with_variant</code>
denotes a customization point object. For a subexpression
<code class="sourceCode default">sndr</code>, let
<code class="sourceCode default">Sndr</code> be
<code class="sourceCode default">decltype(into_variant(sndr))</code>. If
<code class="sourceCode default">sender_in&lt;Sndr, <em><code class="sourceCode default">sync-wait-env</code></em>&gt;</code>
is <code class="sourceCode default">false</code>, the expression <code class="sourceCode default">this_thread​::​sync_wait_with_variant(sndr)</code>
is ill-formed. Otherwise, it is expression-equivalent to the following,
except that <code class="sourceCode default">sndr</code> is evaluated
only once:</p>
<blockquote>
<div class="sourceCode" id="cb25"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb25-1"><a href="#cb25-1" aria-hidden="true" tabindex="-1"></a>apply_sender<span class="op">(</span><em><code class="sourceCode default">get-domain-</code></em><span class="rm" style="color: #bf0303"><del><em><span><code class="sourceCode default">early</code></span></em></del></span><span class="add" style="color: #006e28"><ins><em><span><code class="sourceCode default">late</code></span></em></ins></span><span class="op">(</span>sndr<span class="add" style="color: #006e28"><ins><span><code class="sourceCode default">, <em><code class="sourceCode default">sync-wait-env</code></em>{}, <em><code class="sourceCode default">get-domain-early</code></em>(sndr)</code></span></ins></span><span class="op">)</span>,</span>
<span id="cb25-2"><a href="#cb25-2" aria-hidden="true" tabindex="-1"></a>             sync_wait_with_variant, sndr<span class="op">)</span></span></code></pre></div>
</blockquote>
<p><em>Mandates</em>:</p>
<ul>
<li><p><span class="marginalizedparent"><a class="marginalized">(1.1)</a></span> The
type <code class="sourceCode default"><em><code class="sourceCode default">sync-wait-with-variant-result-type</code></em>&lt;Sndr&gt;</code>
is well-formed.</p></li>
<li><p><span class="marginalizedparent"><a class="marginalized">(1.2)</a></span>
<code class="sourceCode default">same_as&lt;decltype(<em><code class="sourceCode default">e</code></em>), <em><code class="sourceCode default">sync-wait-with-variant-result-type</code></em>&lt;Sndr&gt;&gt;</code>
is <code class="sourceCode default">true</code>, where
<em><code class="sourceCode default">e</code></em> is the
<code class="sourceCode default">apply_sender</code> expression
above.</p></li>
</ul>
</blockquote>
<h1 data-number="8" id="bibliography"><span class="header-section-number">8</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-P2999R3" class="csl-entry" role="doc-biblioentry">
[P2999R3] Eric Niebler. 2023-12-13. Sender Algorithm Customization. <a href="https://wg21.link/p2999r3"><div class="csl-block">https://wg21.link/p2999r3</div></a>
</div>
</div>
</div>
</div>
</body>
</html>
