From Jason Turner

[exec]

Large diff (252.9 KB) - rendering may be slow on some devices

Diff to HTML by rtfpessoa

Files changed (1) hide show
  1. tmp/tmpq1bcp5oi/{from.md → to.md} +7087 -0
tmp/tmpq1bcp5oi/{from.md → to.md} RENAMED
@@ -0,0 +1,7087 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Execution control library <a id="exec">[[exec]]</a>
2
+
3
+ ## General <a id="exec.general">[[exec.general]]</a>
4
+
5
+ This Clause describes components supporting execution of function
6
+ objects [[function.objects]].
7
+
8
+ The following subclauses describe the requirements, concepts, and
9
+ components for execution control primitives as summarized in
10
+ [[exec.summary]].
11
+
12
+ **Table: Execution control library summary** <a id="exec.summary">[exec.summary]</a>
13
+
14
+ | Subclause | | Header |
15
+ | ---------------- | ---------------- | ------------- |
16
+ | [[exec.sched]] | Schedulers | `<execution>` |
17
+ | [[exec.recv]] | Receivers | |
18
+ | [[exec.opstate]] | Operation states | |
19
+ | [[exec.snd]] | Senders | |
20
+
21
+
22
+ [[exec.pos]] shows the types of customization point objects
23
+ [[customization.point.object]] used in the execution control library.
24
+
25
+ **Table: Types of customization point objects in the execution control library** <a id="exec.pos">[exec.pos]</a>
26
+
27
+ | Customization point object type | Purpose | Examples |
28
+ | ------------------------------- | ------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
29
+ | core | provide core execution functionality, and connection between core components | e.g., `connect`, `start` |
30
+ | completion functions | called by senders to announce the completion of the work (success, error, or cancellation) | `set_value`, `set_error`, `set_stopped` |
31
+ | senders | allow the specialization of the provided sender algorithms | sender factories (e.g., `schedule`, `just`, `read_env`); sender adaptors (e.g., `continues_on`, `then`, `let_value`); sender consumers (e.g., `sync_wait`) |
32
+ | queries | allow querying different properties of objects | general queries (e.g., `get_allocator`, `get_stop_token`); environment queries (e.g., `get_scheduler`, `get_delegation_scheduler`); scheduler queries (e.g., `get_forward_progress_guarantee`); sender attribute queries (e.g., `get_completion_scheduler`) |
33
+
34
+
35
+ This clause makes use of the following exposition-only entities.
36
+
37
+ For a subexpression `expr`, let `MANDATE-NOTHROW(expr)` be
38
+ expression-equivalent to `expr`.
39
+
40
+ *Mandates:* `noexcept(expr)` is `true`.
41
+
42
+ ``` cpp
43
+ namespace std {
44
+ template<class T>
45
+ concept movable-value = // exposition only
46
+ move_constructible<decay_t<T>> &&
47
+ constructible_from<decay_t<T>, T> &&
48
+ (!is_array_v<remove_reference_t<T>>);
49
+ }
50
+ ```
51
+
52
+ For function types `F1` and `F2` denoting `R1(Args1...)` and
53
+ `R2(Args2...)`, respectively, `MATCHING-SIG(F1, F2)` is `true` if and
54
+ only if `same_as<R1(Args1&&...), R2(Args2&&...)>` is `true`.
55
+
56
+ For a subexpression `err`, let `Err` be `decltype((err))` and let
57
+ `AS-EXCEPT-PTR(err)` be:
58
+
59
+ - `err` if `decay_t<Err>` denotes the type `exception_ptr`.
60
+ *Preconditions:* `!err` is `false`.
61
+ - Otherwise, `make_exception_ptr(system_error(err))` if `decay_t<Err>`
62
+ denotes the type `error_code`.
63
+ - Otherwise, `make_exception_ptr(err)`.
64
+
65
+ For a subexpression `expr`, let `AS-CONST(expr)` be
66
+ expression-equivalent to
67
+
68
+ ``` cpp
69
+ [](const auto& x) noexcept -> const auto& { return x; }(expr)
70
+ ```
71
+
72
+ ## Queries and queryables <a id="exec.queryable">[[exec.queryable]]</a>
73
+
74
+ ### General <a id="exec.queryable.general">[[exec.queryable.general]]</a>
75
+
76
+ A *queryable object* is a read-only collection of key/value pair where
77
+ each key is a customization point object known as a *query object*. A
78
+ *query* is an invocation of a query object with a queryable object as
79
+ its first argument and a (possibly empty) set of additional arguments. A
80
+ query imposes syntactic and semantic requirements on its invocations.
81
+
82
+ Let `q` be a query object, let `args` be a (possibly empty) pack of
83
+ subexpressions, let `env` be a subexpression that refers to a queryable
84
+ object `o` of type `O`, and let `cenv` be a subexpression referring to
85
+ `o` such that `decltype((cenv))` is `const O&`. The expression
86
+ `q(env, args...)` is equal to [[concepts.equality]] the expression
87
+ `q(cenv, args...)`.
88
+
89
+ The type of a query expression cannot be `void`.
90
+
91
+ The expression `q(env, args...)` is equality-preserving
92
+ [[concepts.equality]] and does not modify the query object or the
93
+ arguments.
94
+
95
+ If the expression `env.query(q, args...)` is well-formed, then it is
96
+ expression-equivalent to `q(env, args...)`.
97
+
98
+ Unless otherwise specified, the result of a query is valid as long as
99
+ the queryable object is valid.
100
+
101
+ ### `queryable` concept <a id="exec.queryable.concept">[[exec.queryable.concept]]</a>
102
+
103
+ ``` cpp
104
+ namespace std {
105
+ template<class T>
106
+ concept queryable = destructible<T>; // exposition only
107
+ }
108
+ ```
109
+
110
+ The exposition-only `queryable` concept specifies the constraints on the
111
+ types of queryable objects.
112
+
113
+ Let `env` be an object of type `Env`. The type `Env` models `queryable`
114
+ if for each callable object `q` and a pack of subexpressions `args`, if
115
+ `requires { q(env, args...) }` is `true` then `q(env, args...)` meets
116
+ any semantic requirements imposed by `q`.
117
+
118
+ ## Asynchronous operations <a id="exec.async.ops">[[exec.async.ops]]</a>
119
+
120
+ An *execution resource* is a program entity that manages a (possibly
121
+ dynamic) set of execution agents [[thread.req.lockable.general]], which
122
+ it uses to execute parallel work on behalf of callers.
123
+
124
+ [*Example 1*: The currently active thread, a system-provided thread
125
+ pool, and uses of an API associated with an external hardware
126
+ accelerator are all examples of execution resources. — *end example*]
127
+
128
+ Execution resources execute asynchronous operations. An execution
129
+ resource is either valid or invalid.
130
+
131
+ An *asynchronous operation* is a distinct unit of program execution that
132
+
133
+ - is explicitly created;
134
+ - can be explicitly started once at most;
135
+ - once started, eventually completes exactly once with a (possibly
136
+ empty) set of result datums and in exactly one of three
137
+ *dispositions*: success, failure, or cancellation;
138
+ - A successful completion, also known as a *value completion*, can
139
+ have an arbitrary number of result datums.
140
+ - A failure completion, also known as an *error completion*, has a
141
+ single result datum.
142
+ - A cancellation completion, also known as a *stopped completion*, has
143
+ no result datum.
144
+
145
+ An asynchronous operation’s *async result* is its disposition and its
146
+ (possibly empty) set of result datums.
147
+ - can complete on a different execution resource than the execution
148
+ resource on which it started; and
149
+ - can create and start other asynchronous operations called
150
+ *child operations*. A child operation is an asynchronous operation
151
+ that is created by the parent operation and, if started, completes
152
+ before the parent operation completes. A *parent operation* is the
153
+ asynchronous operation that created a particular child operation.
154
+
155
+ [*Note 1*: An asynchronous operation can execute synchronously; that
156
+ is, it can complete during the execution of its start operation on the
157
+ thread of execution that started it. — *end note*]
158
+
159
+ An asynchronous operation has associated state known as its
160
+ *operation state*.
161
+
162
+ An asynchronous operation has an associated environment. An
163
+ *environment* is a queryable object [[exec.queryable]] representing the
164
+ execution-time properties of the operation’s caller. The caller of an
165
+ asynchronous operation is its parent operation or the function that
166
+ created it.
167
+
168
+ An asynchronous operation has an associated receiver. A *receiver* is an
169
+ aggregation of three handlers for the three asynchronous completion
170
+ dispositions:
171
+
172
+ - a value completion handler for a value completion,
173
+ - an error completion handler for an error completion, and
174
+ - a stopped completion handler for a stopped completion.
175
+
176
+ A receiver has an associated environment. An asynchronous operation’s
177
+ operation state owns the operation’s receiver. The environment of an
178
+ asynchronous operation is equal to its receiver’s environment.
179
+
180
+ For each completion disposition, there is a *completion function*. A
181
+ completion function is a customization point object
182
+ [[customization.point.object]] that accepts an asynchronous operation’s
183
+ receiver as the first argument and the result datums of the asynchronous
184
+ operation as additional arguments. The value completion function invokes
185
+ the receiver’s value completion handler with the value result datums;
186
+ likewise for the error completion function and the stopped completion
187
+ function. A completion function has an associated type known as its
188
+ *completion tag* that is the unqualified type of the completion
189
+ function. A valid invocation of a completion function is called a
190
+ *completion operation*.
191
+
192
+ The *lifetime of an asynchronous operation*, also known as the
193
+ operation’s *async lifetime*, begins when its start operation begins
194
+ executing and ends when its completion operation begins executing. If
195
+ the lifetime of an asynchronous operation’s associated operation state
196
+ ends before the lifetime of the asynchronous operation, the behavior is
197
+ undefined. After an asynchronous operation executes a completion
198
+ operation, its associated operation state is invalid. Accessing any part
199
+ of an invalid operation state is undefined behavior.
200
+
201
+ An asynchronous operation shall not execute a completion operation
202
+ before its start operation has begun executing. After its start
203
+ operation has begun executing, exactly one completion operation shall
204
+ execute. The lifetime of an asynchronous operation’s operation state can
205
+ end during the execution of the completion operation.
206
+
207
+ A *sender* is a factory for one or more asynchronous operations.
208
+ *Connecting* a sender and a receiver creates an asynchronous operation.
209
+ The asynchronous operation’s associated receiver is equal to the
210
+ receiver used to create it, and its associated environment is equal to
211
+ the environment associated with the receiver used to create it. The
212
+ lifetime of an asynchronous operation’s associated operation state does
213
+ not depend on the lifetimes of either the sender or the receiver from
214
+ which it was created. A sender is started when it is connected to a
215
+ receiver and the resulting asynchronous operation is started. A sender’s
216
+ async result is the async result of the asynchronous operation created
217
+ by connecting it to a receiver. A sender sends its results by way of the
218
+ asynchronous operation(s) it produces, and a receiver receives those
219
+ results. A sender is either valid or invalid; it becomes invalid when
220
+ its parent sender (see below) becomes invalid.
221
+
222
+ A *scheduler* is an abstraction of an execution resource with a uniform,
223
+ generic interface for scheduling work onto that resource. It is a
224
+ factory for senders whose asynchronous operations execute value
225
+ completion operations on an execution agent belonging to the scheduler’s
226
+ associated execution resource. A *schedule-expression* obtains such a
227
+ sender from a scheduler. A *schedule sender* is the result of a schedule
228
+ expression. On success, an asynchronous operation produced by a schedule
229
+ sender executes a value completion operation with an empty set of result
230
+ datums. Multiple schedulers can refer to the same execution resource. A
231
+ scheduler can be valid or invalid. A scheduler becomes invalid when the
232
+ execution resource to which it refers becomes invalid, as do any
233
+ schedule senders obtained from the scheduler, and any operation states
234
+ obtained from those senders.
235
+
236
+ An asynchronous operation has one or more associated completion
237
+ schedulers for each of its possible dispositions. A *completion
238
+ scheduler* is a scheduler whose associated execution resource is used to
239
+ execute a completion operation for an asynchronous operation. A value
240
+ completion scheduler is a scheduler on which an asynchronous operation’s
241
+ value completion operation can execute. Likewise for error completion
242
+ schedulers and stopped completion schedulers.
243
+
244
+ A sender has an associated queryable object [[exec.queryable]] known as
245
+ its *attributes* that describes various characteristics of the sender
246
+ and of the asynchronous operation(s) it produces. For each disposition,
247
+ there is a query object for reading the associated completion scheduler
248
+ from a sender’s attributes; i.e., a value completion scheduler query
249
+ object for reading a sender’s value completion scheduler, etc. If a
250
+ completion scheduler query is well-formed, the returned completion
251
+ scheduler is unique for that disposition for any asynchronous operation
252
+ the sender creates. A schedule sender is required to have a value
253
+ completion scheduler attribute whose value is equal to the scheduler
254
+ that produced the schedule sender.
255
+
256
+ A *completion signature* is a function type that describes a completion
257
+ operation. An asynchronous operation has a finite set of possible
258
+ completion signatures corresponding to the completion operations that
259
+ the asynchronous operation potentially evaluates [[basic.def.odr]]. For
260
+ a completion function `set`, receiver `rcvr`, and pack of arguments
261
+ `args`, let `c` be the completion operation `set(rcvr, args...)`, and
262
+ let `F` be the function type `decltype(auto(set))(decltype((args))...)`.
263
+ A completion signature `Sig` is associated with `c` if and only if
264
+ `MATCHING-SIG(Sig, F)` is `true` [[exec.general]]. Together, a sender
265
+ type and an environment type `Env` determine the set of completion
266
+ signatures of an asynchronous operation that results from connecting the
267
+ sender with a receiver that has an environment of type `Env`. The type
268
+ of the receiver does not affect an asynchronous operation’s completion
269
+ signatures, only the type of the receiver’s environment. A
270
+ *non-dependent sender* is a sender type whose completion signatures are
271
+ knowable independent of an execution environment.
272
+
273
+ A sender algorithm is a function that takes and/or returns a sender.
274
+ There are three categories of sender algorithms:
275
+
276
+ - A *sender factory* is a function that takes non-senders as arguments
277
+ and that returns a sender.
278
+ - A *sender adaptor* is a function that constructs and returns a parent
279
+ sender from a set of one or more child senders and a (possibly empty)
280
+ set of additional arguments. An asynchronous operation created by a
281
+ parent sender is a parent operation to the child operations created by
282
+ the child senders.
283
+ - A *sender consumer* is a function that takes one or more senders and a
284
+ (possibly empty) set of additional arguments, and whose return type is
285
+ not the type of a sender.
286
+
287
+ ## Header `<execution>` synopsis <a id="execution.syn">[[execution.syn]]</a>
288
+
289
+ ``` cpp
290
+ namespace std {
291
+ // [execpol.type], execution policy type trait
292
+ template<class T> struct is_execution_policy; // freestanding
293
+ template<class T> constexpr bool is_execution_policy_v = // freestanding
294
+ is_execution_policy<T>::value;
295
+ }
296
+
297
+ namespace std::execution {
298
+ // [execpol.seq], sequenced execution policy
299
+ class sequenced_policy;
300
+
301
+ // [execpol.par], parallel execution policy
302
+ class parallel_policy;
303
+
304
+ // [execpol.parunseq], parallel and unsequenced execution policy
305
+ class parallel_unsequenced_policy;
306
+
307
+ // [execpol.unseq], unsequenced execution policy
308
+ class unsequenced_policy;
309
+
310
+ // [execpol.objects], execution policy objects
311
+ inline constexpr sequenced_policy seq{ unspecified };
312
+ inline constexpr parallel_policy par{ unspecified };
313
+ inline constexpr parallel_unsequenced_policy par_unseq{ unspecified };
314
+ inline constexpr unsequenced_policy unseq{ unspecified };
315
+ }
316
+
317
+ namespace std {
318
+ // [exec.general], helper concepts
319
+ template<class T>
320
+ concept exposition onlyconceptnc{movable-value} = see belownc; // exposition only
321
+
322
+ template<class From, class To>
323
+ concept decays-to = same_as<decay_t<From>, To>; // exposition only
324
+
325
+ template<class T>
326
+ concept class-type = exposition onlyconceptnc{decays-to}<T, T> && is_class_v<T>; // exposition only
327
+
328
+ // [exec.queryable], queryable objects
329
+ template<class T>
330
+ concept exposition onlyconceptnc{queryable} = see belownc; // exposition only
331
+
332
+ // [exec.queries], queries
333
+ struct forwarding_query_t { unspecified };
334
+ struct get_allocator_t { unspecified };
335
+ struct get_stop_token_t { unspecified };
336
+
337
+ inline constexpr forwarding_query_t forwarding_query{};
338
+ inline constexpr get_allocator_t get_allocator{};
339
+ inline constexpr get_stop_token_t get_stop_token{};
340
+
341
+ template<class T>
342
+ using stop_token_of_t = remove_cvref_t<decltype(get_stop_token(declval<T>()))>;
343
+
344
+ template<class T>
345
+ concept forwarding-query = forwarding_query(T{}); // exposition only
346
+ }
347
+
348
+ namespace std::execution {
349
+ // [exec.queries], queries
350
+ struct get_domain_t { unspecified };
351
+ struct get_scheduler_t { unspecified };
352
+ struct get_delegation_scheduler_t { unspecified };
353
+ struct get_forward_progress_guarantee_t { unspecified };
354
+ template<class CPO>
355
+ struct get_completion_scheduler_t { unspecified };
356
+ struct get_await_completion_adaptor_t { unspecified };
357
+
358
+ inline constexpr get_domain_t get_domain{};
359
+ inline constexpr get_scheduler_t get_scheduler{};
360
+ inline constexpr get_delegation_scheduler_t get_delegation_scheduler{};
361
+ enum class forward_progress_guarantee;
362
+ inline constexpr get_forward_progress_guarantee_t get_forward_progress_guarantee{};
363
+ template<class CPO>
364
+ constexpr get_completion_scheduler_t<CPO> get_completion_scheduler{};
365
+ inline constexpr get_await_completion_adaptor_t get_await_completion_adaptor{};
366
+
367
+ struct get_env_t { unspecified };
368
+ inline constexpr get_env_t get_env{};
369
+
370
+ template<class T>
371
+ using env_of_t = decltype(get_env(declval<T>()));
372
+
373
+ // [exec.domain.default], execution domains
374
+ struct default_domain;
375
+
376
+ // [exec.sched], schedulers
377
+ struct scheduler_t {};
378
+
379
+ template<class Sch>
380
+ concept scheduler = see below;
381
+
382
+ // [exec.recv], receivers
383
+ struct receiver_t {};
384
+
385
+ template<class Rcvr>
386
+ concept receiver = see below;
387
+
388
+ template<class Rcvr, class Completions>
389
+ concept receiver_of = see below;
390
+
391
+ struct set_value_t { unspecified };
392
+ struct set_error_t { unspecified };
393
+ struct set_stopped_t { unspecified };
394
+
395
+ inline constexpr set_value_t set_value{};
396
+ inline constexpr set_error_t set_error{};
397
+ inline constexpr set_stopped_t set_stopped{};
398
+
399
+ // [exec.opstate], operation states
400
+ struct operation_state_t {};
401
+
402
+ template<class O>
403
+ concept operation_state = see below;
404
+
405
+ struct start_t;
406
+ inline constexpr start_t start{};
407
+
408
+ // [exec.snd], senders
409
+ struct sender_t {};
410
+
411
+ template<class Sndr>
412
+ inline constexpr bool enable_sender = see below;
413
+
414
+ template<class Sndr>
415
+ concept sender = see below;
416
+
417
+ template<class Sndr, class... Env>
418
+ concept sender_in = see below;
419
+
420
+ template<class Sndr>
421
+ concept dependent_sender = see below;
422
+
423
+ template<class Sndr, class Rcvr>
424
+ concept sender_to = see below;
425
+
426
+ template<class... Ts>
427
+ struct type-list; // exposition only
428
+
429
+ template<class... Ts>
430
+ using decayed-tuple = tuple<decay_t<Ts>...>; // exposition only
431
+
432
+ template<class... Ts>
433
+ using variant-or-empty = see belownc; // exposition only
434
+
435
+ template<class Sndr, class Env = env<>,
436
+ template<class...> class Tuple = decayed-tuple,
437
+ template<class...> class Variant = variant-or-empty>
438
+ requires sender_in<Sndr, Env>
439
+ using value_types_of_t = see below;
440
+
441
+ template<class Sndr, class Env = env<>,
442
+ template<class...> class Variant = variant-or-empty>
443
+ requires sender_in<Sndr, Env>
444
+ using error_types_of_t = see below;
445
+
446
+ template<class Sndr, class Env = env<>>
447
+ requires sender_in<Sndr, Env>
448
+ constexpr bool sends_stopped = see below;
449
+
450
+ template<class Sndr, class... Env>
451
+ using single-sender-value-type = see belownc; // exposition only
452
+
453
+ template<class Sndr, class... Env>
454
+ concept single-sender = see below; // exposition only
455
+
456
+ template<sender Sndr>
457
+ using tag_of_t = see below;
458
+
459
+ // [exec.snd.transform], sender transformations
460
+ template<class Domain, sender Sndr, queryable... Env>
461
+ requires (sizeof...(Env) <= 1)
462
+ constexpr sender decltype(auto) transform_sender(
463
+ Domain dom, Sndr&& sndr, const Env&... env) noexcept(see below);
464
+
465
+ // [exec.snd.transform.env], environment transformations
466
+ template<class Domain, sender Sndr, queryable Env>
467
+ constexpr queryable decltype(auto) transform_env(
468
+ Domain dom, Sndr&& sndr, Env&& env) noexcept;
469
+
470
+ // [exec.snd.apply], sender algorithm application
471
+ template<class Domain, class Tag, sender Sndr, class... Args>
472
+ constexpr decltype(auto) apply_sender(
473
+ Domain dom, Tag, Sndr&& sndr, Args&&... args) noexcept(see below);
474
+
475
+ // [exec.getcomplsigs], get completion signatures
476
+ template<class Sndr, class... Env>
477
+ consteval auto get_completion_signatures() -> valid-completion-signatures auto;
478
+
479
+ template<class Sndr, class... Env>
480
+ requires sender_in<Sndr, Env...>
481
+ using completion_signatures_of_t = decltype(get_completion_signatures<Sndr, Env...>());
482
+
483
+ // [exec.connect], the connect sender algorithm
484
+ struct connect_t;
485
+ inline constexpr connect_t connect{};
486
+
487
+ template<class Sndr, class Rcvr>
488
+ using connect_result_t =
489
+ decltype(connect(declval<Sndr>(), declval<Rcvr>()));
490
+
491
+ // [exec.factories], sender factories
492
+ struct just_t { unspecified };
493
+ struct just_error_t { unspecified };
494
+ struct just_stopped_t { unspecified };
495
+ struct schedule_t { unspecified };
496
+
497
+ inline constexpr just_t just{};
498
+ inline constexpr just_error_t just_error{};
499
+ inline constexpr just_stopped_t just_stopped{};
500
+ inline constexpr schedule_t schedule{};
501
+ inline constexpr unspecified read_env{};
502
+
503
+ template<scheduler Sch>
504
+ using schedule_result_t = decltype(schedule(declval<Sch>()));
505
+
506
+ // [exec.adapt], sender adaptors
507
+ template<class-type D>
508
+ struct sender_adaptor_closure { };
509
+
510
+ struct starts_on_t { unspecified };
511
+ struct continues_on_t { unspecified };
512
+ struct on_t { unspecified };
513
+ struct schedule_from_t { unspecified };
514
+ struct then_t { unspecified };
515
+ struct upon_error_t { unspecified };
516
+ struct upon_stopped_t { unspecified };
517
+ struct let_value_t { unspecified };
518
+ struct let_error_t { unspecified };
519
+ struct let_stopped_t { unspecified };
520
+ struct bulk_t { unspecified };
521
+ struct bulk_chunked_t { unspecified };
522
+ struct bulk_unchunked_t { unspecified };
523
+ struct when_all_t { unspecified };
524
+ struct when_all_with_variant_t { unspecified };
525
+ struct into_variant_t { unspecified };
526
+ struct stopped_as_optional_t { unspecified };
527
+ struct stopped_as_error_t { unspecified };
528
+ struct associate_t { unspecified };
529
+ struct spawn_future_t { unspecified };
530
+
531
+ inline constexpr unspecified write_env{};
532
+ inline constexpr unspecified unstoppable{};
533
+ inline constexpr starts_on_t starts_on{};
534
+ inline constexpr continues_on_t continues_on{};
535
+ inline constexpr on_t on{};
536
+ inline constexpr schedule_from_t schedule_from{};
537
+ inline constexpr then_t then{};
538
+ inline constexpr upon_error_t upon_error{};
539
+ inline constexpr upon_stopped_t upon_stopped{};
540
+ inline constexpr let_value_t let_value{};
541
+ inline constexpr let_error_t let_error{};
542
+ inline constexpr let_stopped_t let_stopped{};
543
+ inline constexpr bulk_t bulk{};
544
+ inline constexpr bulk_chunked_t bulk_chunked{};
545
+ inline constexpr bulk_unchunked_t bulk_unchunked{};
546
+ inline constexpr when_all_t when_all{};
547
+ inline constexpr when_all_with_variant_t when_all_with_variant{};
548
+ inline constexpr into_variant_t into_variant{};
549
+ inline constexpr stopped_as_optional_t stopped_as_optional{};
550
+ inline constexpr stopped_as_error_t stopped_as_error{};
551
+ inline constexpr associate_t associate{};
552
+ inline constexpr spawn_future_t spawn_future{};
553
+ }
554
+
555
+ namespace std::this_thread {
556
+ // [exec.consumers], consumers
557
+ struct sync_wait_t { unspecified };
558
+ struct sync_wait_with_variant_t { unspecified };
559
+
560
+ inline constexpr sync_wait_t sync_wait{};
561
+ inline constexpr sync_wait_with_variant_t sync_wait_with_variant{};
562
+ }
563
+
564
+ namespace std::execution {
565
+ // [exec.consumers], consumers
566
+ struct spawn_t { unspecified };
567
+ inline constexpr spawn_t spawn{};
568
+
569
+ // [exec.cmplsig], completion signatures
570
+ template<class Fn>
571
+ concept exposition onlyconceptnc{completion-signature} = see belownc; // exposition only
572
+
573
+ template<completion-signature... Fns>
574
+ struct completion_signatures;
575
+
576
+ template<class Sigs>
577
+ concept exposition onlyconceptnc{valid-completion-signatures} = see belownc; // exposition only
578
+
579
+ struct dependent_sender_error : exception {};
580
+
581
+ // [exec.prop], class template prop
582
+ template<class QueryTag, class ValueType>
583
+ struct prop;
584
+
585
+ // [exec.env], class template env
586
+ template<queryable... Envs>
587
+ struct env;
588
+
589
+ // [exec.run.loop], run_loop
590
+ class run_loop;
591
+
592
+ // [exec.as.awaitable], coroutine utility as_awaitable
593
+ struct as_awaitable_t { unspecified };
594
+ inline constexpr as_awaitable_t as_awaitable{};
595
+
596
+ // [exec.with.awaitable.senders], coroutine utility with_awaitable_senders
597
+ template<class-type Promise>
598
+ struct with_awaitable_senders;
599
+
600
+ // [exec.affine.on], coroutine utility affine_on
601
+ struct affine_on_t { unspecified };
602
+ inline constexpr affine_on_t affine_on{};
603
+
604
+ // [exec.inline.scheduler], inline scheduler
605
+ class inline_scheduler;
606
+
607
+ // [exec.task.scheduler], task scheduler
608
+ class task_scheduler;
609
+
610
+ template<class E>
611
+ struct with_error {
612
+ using type = remove_cvref_t<E>;
613
+ type error;
614
+ };
615
+ template<class E>
616
+ with_error(E) -> with_error<E>;
617
+
618
+ template<scheduler Sch>
619
+ struct change_coroutine_scheduler {
620
+ using type = remove_cvref_t<Sch>;
621
+ type scheduler;
622
+ };
623
+ template<scheduler Sch>
624
+ change_coroutine_scheduler(Sch) -> change_coroutine_scheduler<Sch>;
625
+
626
+ // [exec.task], class template task
627
+ template<class T, class Environment>
628
+ class task;
629
+
630
+ // [exec.scope.concepts], scope concepts
631
+ template<class Token>
632
+ concept scope_token = see below;
633
+
634
+ // [exec.scope.simple.counting], simple counting scope
635
+ class simple_counting_scope;
636
+
637
+ // [exec.scope.counting], counting scope
638
+ class counting_scope;
639
+
640
+ // [exec.par.scheduler], parallel scheduler
641
+ class parallel_scheduler;
642
+ parallel_scheduler get_parallel_scheduler();
643
+
644
+ // [exec.sysctxrepl], namespace system_context_replaceability
645
+ namespace system_context_replaceability@ {
646
+ struct receiver_proxy;
647
+ struct bulk_item_receiver_proxy;
648
+ struct parallel_scheduler_backend;
649
+
650
+ shared_ptr<parallel_scheduler_backend> query_parallel_scheduler_backend();
651
+ }
652
+ }
653
+ ```
654
+
655
+ The exposition-only type `variant-or-empty<Ts...>` is defined as
656
+ follows:
657
+
658
+ - If `sizeof...(Ts)` is greater than zero, `variant-or-empty<Ts...>`
659
+ denotes `variant<Us...>` where `Us...` is the pack `decay_t<Ts>...`
660
+ with duplicate types removed.
661
+ - Otherwise, `variant-or-empty<Ts...>` denotes the exposition-only class
662
+ type:
663
+ ``` cpp
664
+ namespace std::execution {
665
+ struct empty-variant { // exposition only
666
+ empty-variant() = delete;
667
+ };
668
+ }
669
+ ```
670
+
671
+ For type `Sndr` and pack of types `Env`, let `CS` be
672
+ `completion_signatures_of_t<Sndr, Env...>`. Then
673
+ `single-sender-value-type<Sndr, Env...>` is ill-formed if `CS` is
674
+ ill-formed or if `sizeof...(Env) > 1` is `true`; otherwise, it is an
675
+ alias for:
676
+
677
+ - `gather-signatures<set_value_t, CS, decay_t, type_identity_t>` if that
678
+ type is well-formed,
679
+ - Otherwise, `void` if
680
+ `gather-signatures<set_value_t, CS, tuple, variant>` is
681
+ `variant<tuple<>>` or `variant<>`,
682
+ - Otherwise,
683
+ `gather-signatures<set_value_t, CS, decayed-tuple, type_identity_t>`
684
+ if that type is well-formed,
685
+ - Otherwise, `single-sender-value-type<Sndr, Env...>` is ill-formed.
686
+
687
+ The exposition-only concept `single-sender` is defined as follows:
688
+
689
+ ``` cpp
690
+ namespace std::execution {
691
+ template<class Sndr, class... Env>
692
+ concept single-sender = sender_in<Sndr, Env...> &&
693
+ requires {
694
+ typename single-sender-value-type<Sndr, Env...>;
695
+ };
696
+ }
697
+ ```
698
+
699
+ A type satisfies and models the exposition-only concept
700
+ *valid-completion-signatures* if it is a specialization of the
701
+ `completion_signatures` class template.
702
+
703
+ ## Queries <a id="exec.queries">[[exec.queries]]</a>
704
+
705
+ ### `forwarding_query` <a id="exec.fwd.env">[[exec.fwd.env]]</a>
706
+
707
+ `forwarding_query` asks a query object whether it should be forwarded
708
+ through queryable adaptors.
709
+
710
+ The name `forwarding_query` denotes a query object. For some query
711
+ object `q` of type `Q`, `forwarding_query(q)` is expression-equivalent
712
+ to:
713
+
714
+ - `MANDATE-NOTHROW(q.query(forwarding_query))` if that expression is
715
+ well-formed. *Mandates:* The expression above has type `bool` and is a
716
+ core constant expression if `q` is a core constant expression.
717
+ - Otherwise, `true` if `derived_from<Q, forwarding_query_t>` is `true`.
718
+ - Otherwise, `false`.
719
+
720
+ ### `get_allocator` <a id="exec.get.allocator">[[exec.get.allocator]]</a>
721
+
722
+ `get_allocator` asks a queryable object for its associated allocator.
723
+
724
+ The name `get_allocator` denotes a query object. For a subexpression
725
+ `env`, `get_allocator(env)` is expression-equivalent to
726
+ `MANDATE-NOTHROW(AS-CONST(env).query(get_allocator))`.
727
+
728
+ *Mandates:* If the expression above is well-formed, its type satisfies
729
+ `simple-allocator` [[allocator.requirements.general]].
730
+
731
+ `forwarding_query(get_allocator)` is a core constant expression and has
732
+ value `true`.
733
+
734
+ ### `get_stop_token` <a id="exec.get.stop.token">[[exec.get.stop.token]]</a>
735
+
736
+ `get_stop_token` asks a queryable object for an associated stop token.
737
+
738
+ The name `get_stop_token` denotes a query object. For a subexpression
739
+ `env`, `get_stop_token(env)` is expression-equivalent to:
740
+
741
+ - `MANDATE-NOTHROW(AS-CONST(env).query(get_stop_token))` if that
742
+ expression is well-formed. *Mandates:* The type of the expression
743
+ above satisfies `stoppable_token`.
744
+ - Otherwise, `never_stop_token{}`.
745
+
746
+ `forwarding_query(get_stop_token)` is a core constant expression and has
747
+ value `true`.
748
+
749
+ ### `execution::get_env` <a id="exec.get.env">[[exec.get.env]]</a>
750
+
751
+ `execution::get_env` is a customization point object. For a
752
+ subexpression `o`, `execution::get_env(o)` is expression-equivalent to:
753
+
754
+ - `MANDATE-NOTHROW(AS-CONST(o).get_env())` if that expression is
755
+ well-formed. *Mandates:* The type of the expression above satisfies
756
+ `queryable` [[exec.queryable]].
757
+ - Otherwise, `env<>{}`.
758
+
759
+ The value of `get_env(o)` shall be valid while `o` is valid.
760
+
761
+ [*Note 1*: When passed a sender object, `get_env` returns the sender’s
762
+ associated attributes. When passed a receiver, `get_env` returns the
763
+ receiver’s associated execution environment. — *end note*]
764
+
765
+ ### `execution::get_domain` <a id="exec.get.domain">[[exec.get.domain]]</a>
766
+
767
+ `get_domain` asks a queryable object for its associated execution domain
768
+ tag.
769
+
770
+ The name `get_domain` denotes a query object. For a subexpression `env`,
771
+ `get_domain(env)` is expression-equivalent to
772
+ `MANDATE-NOTHROW(AS-CONST(env).query(get_domain))`.
773
+
774
+ `forwarding_query(execution::get_domain)` is a core constant expression
775
+ and has value `true`.
776
+
777
+ ### `execution::get_scheduler` <a id="exec.get.scheduler">[[exec.get.scheduler]]</a>
778
+
779
+ `get_scheduler` asks a queryable object for its associated scheduler.
780
+
781
+ The name `get_scheduler` denotes a query object. For a subexpression
782
+ `env`, `get_scheduler(env)` is expression-equivalent to
783
+ `MANDATE-NOTHROW(AS-CONST(env).query(get_scheduler))`.
784
+
785
+ *Mandates:* If the expression above is well-formed, its type satisfies
786
+ `scheduler`.
787
+
788
+ `forwarding_query(execution::get_scheduler)` is a core constant
789
+ expression and has value `true`.
790
+
791
+ ### `execution::get_delegation_scheduler` <a id="exec.get.delegation.scheduler">[[exec.get.delegation.scheduler]]</a>
792
+
793
+ `get_delegation_scheduler` asks a queryable object for a scheduler that
794
+ can be used to delegate work to for the purpose of forward progress
795
+ delegation [[intro.progress]].
796
+
797
+ The name `get_delegation_scheduler` denotes a query object. For a
798
+ subexpression `env`, `get_delegation_scheduler(env)` is
799
+ expression-equivalent to
800
+ `MANDATE-NOTHROW(AS-CONST(env).query(get_delegation_scheduler))`.
801
+
802
+ *Mandates:* If the expression above is well-formed, its type satisfies
803
+ `scheduler`.
804
+
805
+ `forwarding_query(execution::get_delegation_scheduler)` is a core
806
+ constant expression and has value `true`.
807
+
808
+ ### `execution::get_forward_progress_guarantee` <a id="exec.get.fwd.progress">[[exec.get.fwd.progress]]</a>
809
+
810
+ ``` cpp
811
+ namespace std::execution {
812
+ enum class forward_progress_guarantee {
813
+ concurrent,
814
+ parallel,
815
+ weakly_parallel
816
+ };
817
+ }
818
+ ```
819
+
820
+ `get_forward_progress_guarantee` asks a scheduler about the forward
821
+ progress guarantee of execution agents created by that scheduler’s
822
+ associated execution resource [[intro.progress]].
823
+
824
+ The name `get_forward_progress_guarantee` denotes a query object. For a
825
+ subexpression `sch`, let `Sch` be `decltype((sch))`. If `Sch` does not
826
+ satisfy `scheduler`, `get_forward_progress_guarantee` is ill-formed.
827
+ Otherwise, `get_forward_progress_guarantee(sch)` is
828
+ expression-equivalent to:
829
+
830
+ - `MANDATE-NOTHROW(AS-CONST(sch).query(get_forward_progress_guarantee))`,
831
+ if that expression is well-formed. *Mandates:* The type of the
832
+ expression above is `forward_progress_guarantee`.
833
+ - Otherwise, `forward_progress_guarantee::weakly_parallel`.
834
+
835
+ If `get_forward_progress_guarantee(sch)` for some scheduler `sch`
836
+ returns `forward_progress_guarantee::concurrent`, all execution agents
837
+ created by that scheduler’s associated execution resource shall provide
838
+ the concurrent forward progress guarantee. If it returns
839
+ `forward_progress_guarantee::parallel`, all such execution agents shall
840
+ provide at least the parallel forward progress guarantee.
841
+
842
+ ### `execution::get_completion_scheduler` <a id="exec.get.compl.sched">[[exec.get.compl.sched]]</a>
843
+
844
+ `get_completion_scheduler<completion-tag>` obtains the completion
845
+ scheduler associated with a completion tag from a sender’s attributes.
846
+
847
+ The name `get_completion_scheduler` denotes a query object template. For
848
+ a subexpression `q`, the expression
849
+ `get_completion_scheduler<completion-tag>(q)` is ill-formed if
850
+ *`completion-tag`* is not one of `set_value_t`, `set_error_t`, or
851
+ `set_stopped_t`. Otherwise,
852
+ `get_completion_scheduler<completion-tag>(q)` is expression-equivalent
853
+ to
854
+
855
+ ``` cpp
856
+ MANDATE-NOTHROW(AS-CONST(q).query(get_completion_scheduler<completion-tag>))
857
+ ```
858
+
859
+ *Mandates:* If the expression above is well-formed, its type satisfies
860
+ `scheduler`.
861
+
862
+ Let *`completion-fn`* be a completion function [[exec.async.ops]]; let
863
+ *`completion-tag`* be the associated completion tag of
864
+ *`completion-fn`*; let `args` be a pack of subexpressions; and let
865
+ `sndr` be a subexpression such that `sender<decltype((sndr))>` is `true`
866
+ and `get_completion_scheduler<completion-tag>(get_env(sndr))` is
867
+ well-formed and denotes a scheduler `sch`. If an asynchronous operation
868
+ created by connecting `sndr` with a receiver `rcvr` causes the
869
+ evaluation of `completion-fn(rcvr, args...)`, the behavior is undefined
870
+ unless the evaluation happens on an execution agent that belongs to
871
+ `sch`’s associated execution resource.
872
+
873
+ The expression
874
+ `forwarding_query(get_completion_scheduler<completion-tag>)` is a core
875
+ constant expression and has value `true`.
876
+
877
+ ### `execution::get_await_completion_adaptor` <a id="exec.get.await.adapt">[[exec.get.await.adapt]]</a>
878
+
879
+ `get_await_completion_adaptor` asks a queryable object for its
880
+ associated awaitable completion adaptor.
881
+
882
+ The name `get_await_completion_adaptor` denotes a query object. For a
883
+ subexpression `env`,
884
+
885
+ ``` cpp
886
+ get_await_completion_adaptor(env)
887
+ ```
888
+
889
+ is expression-equivalent to
890
+
891
+ ``` cpp
892
+ MANDATE-NOTHROW(AS-CONST(env).query(get_await_completion_adaptor))
893
+ ```
894
+
895
+ `forwarding_query(execution::get_await_completion_adaptor)`
896
+
897
+ is a core constant expression and has value `true`.
898
+
899
+ ## Schedulers <a id="exec.sched">[[exec.sched]]</a>
900
+
901
+ The `scheduler` concept defines the requirements of a scheduler type
902
+ [[exec.async.ops]]. `schedule` is a customization point object that
903
+ accepts a scheduler. A valid invocation of `schedule` is a
904
+ schedule-expression.
905
+
906
+ ``` cpp
907
+ namespace std::execution {
908
+ template<class Sch>
909
+ concept scheduler =
910
+ derived_from<typename remove_cvref_t<Sch>::scheduler_concept, scheduler_t> &&
911
+ queryable<Sch> &&
912
+ requires(Sch&& sch) {
913
+ { schedule(std::forward<Sch>(sch)) } -> sender;
914
+ { auto(get_completion_scheduler<set_value_t>(
915
+ get_env(schedule(std::forward<Sch>(sch))))) }
916
+ -> same_as<remove_cvref_t<Sch>>;
917
+ } &&
918
+ equality_comparable<remove_cvref_t<Sch>> &&
919
+ copyable<remove_cvref_t<Sch>>;
920
+ }
921
+ ```
922
+
923
+ Let `Sch` be the type of a scheduler and let `Env` be the type of an
924
+ execution environment for which `sender_in<schedule_result_t<Sch>, Env>`
925
+ is satisfied. Then `sender-in-of<schedule_result_t<Sch>, Env>` shall be
926
+ modeled.
927
+
928
+ No operation required by `copyable<remove_cvref_t<Sch>>` and
929
+ `equality_comparable<remove_cvref_t<Sch>>` shall exit via an exception.
930
+ None of these operations, nor a scheduler type’s `schedule` function,
931
+ shall introduce data races as a result of potentially concurrent
932
+ [[intro.races]] invocations of those operations from different threads.
933
+
934
+ For any two values `sch1` and `sch2` of some scheduler type `Sch`,
935
+ `sch1 == sch2` shall return `true` only if both `sch1` and `sch2` share
936
+ the same associated execution resource.
937
+
938
+ For a given scheduler expression `sch`, the expression
939
+ `get_completion_scheduler<set_value_t>(get_env(schedule(sch)))` shall
940
+ compare equal to `sch`.
941
+
942
+ For a given scheduler expression `sch`, if the expression
943
+ `get_domain(sch)` is well-formed, then the expression
944
+ `get_domain(get_env(schedule(sch)))` is also well-formed and has the
945
+ same type.
946
+
947
+ A scheduler type’s destructor shall not block pending completion of any
948
+ receivers connected to the sender objects returned from `schedule`.
949
+
950
+ [*Note 1*: The ability to wait for completion of submitted function
951
+ objects can be provided by the associated execution resource of the
952
+ scheduler. — *end note*]
953
+
954
+ ## Receivers <a id="exec.recv">[[exec.recv]]</a>
955
+
956
+ ### Receiver concepts <a id="exec.recv.concepts">[[exec.recv.concepts]]</a>
957
+
958
+ A receiver represents the continuation of an asynchronous operation. The
959
+ `receiver` concept defines the requirements for a receiver type
960
+ [[exec.async.ops]]. The `receiver_of` concept defines the requirements
961
+ for a receiver type that is usable as the first argument of a set of
962
+ completion operations corresponding to a set of completion signatures.
963
+ The `get_env` customization point object is used to access a receiver’s
964
+ associated environment.
965
+
966
+ ``` cpp
967
+ namespace std::execution {
968
+ template<class Rcvr>
969
+ concept receiver =
970
+ derived_from<typename remove_cvref_t<Rcvr>::receiver_concept, receiver_t> &&
971
+ requires(const remove_cvref_t<Rcvr>& rcvr) {
972
+ { get_env(rcvr) } -> queryable;
973
+ } &&
974
+ move_constructible<remove_cvref_t<Rcvr>> && // rvalues are movable, and
975
+ constructible_from<remove_cvref_t<Rcvr>, Rcvr>; // lvalues are copyable
976
+
977
+ template<class Signature, class Rcvr>
978
+ concept valid-completion-for = // exposition only
979
+ requires (Signature* sig) {
980
+ []<class Tag, class... Args>(Tag(*)(Args...))
981
+ requires callable<Tag, remove_cvref_t<Rcvr>, Args...>
982
+ {}(sig);
983
+ };
984
+
985
+ template<class Rcvr, class Completions>
986
+ concept has-completions = // exposition only
987
+ requires (Completions* completions) {
988
+ []<valid-completion-for<Rcvr>...Sigs>(completion_signatures<Sigs...>*)
989
+ {}(completions);
990
+ };
991
+
992
+ template<class Rcvr, class Completions>
993
+ concept receiver_of =
994
+ receiver<Rcvr> && has-completions<Rcvr, Completions>;
995
+ }
996
+ ```
997
+
998
+ Class types that are marked `final` do not model the `receiver` concept.
999
+
1000
+ Let `rcvr` be a receiver and let `op_state` be an operation state
1001
+ associated with an asynchronous operation created by connecting `rcvr`
1002
+ with a sender. Let `token` be a stop token equal to
1003
+ `get_stop_token(get_env(rcvr))`. `token` shall remain valid for the
1004
+ duration of the asynchronous operation’s lifetime [[exec.async.ops]].
1005
+
1006
+ [*Note 1*: This means that, unless it knows about further guarantees
1007
+ provided by the type of `rcvr`, the implementation of `op_state` cannot
1008
+ use `token` after it executes a completion operation. This also implies
1009
+ that any stop callbacks registered on token must be destroyed before the
1010
+ invocation of the completion operation. — *end note*]
1011
+
1012
+ ### `execution::set_value` <a id="exec.set.value">[[exec.set.value]]</a>
1013
+
1014
+ `set_value` is a value completion function [[exec.async.ops]]. Its
1015
+ associated completion tag is `set_value_t`. The expression
1016
+ `set_value(rcvr, vs...)` for a subexpression `rcvr` and pack of
1017
+ subexpressions `vs` is ill-formed if `rcvr` is an lvalue or an rvalue of
1018
+ const type. Otherwise, it is expression-equivalent to
1019
+ `MANDATE-NOTHROW(rcvr.set_value(vs...))`.
1020
+
1021
+ ### `execution::set_error` <a id="exec.set.error">[[exec.set.error]]</a>
1022
+
1023
+ `set_error` is an error completion function [[exec.async.ops]]. Its
1024
+ associated completion tag is `set_error_t`. The expression
1025
+ `set_error(rcvr, err)` for some subexpressions `rcvr` and `err` is
1026
+ ill-formed if `rcvr` is an lvalue or an rvalue of const type. Otherwise,
1027
+ it is expression-equivalent to `MANDATE-NOTHROW(rcvr.set_error(err))`.
1028
+
1029
+ ### `execution::set_stopped` <a id="exec.set.stopped">[[exec.set.stopped]]</a>
1030
+
1031
+ `set_stopped` is a stopped completion function [[exec.async.ops]]. Its
1032
+ associated completion tag is `set_stopped_t`. The expression
1033
+ `set_stopped(rcvr)` for a subexpression `rcvr` is ill-formed if `rcvr`
1034
+ is an lvalue or an rvalue of const type. Otherwise, it is
1035
+ expression-equivalent to `MANDATE-NOTHROW(rcvr.set_stopped())`.
1036
+
1037
+ ## Operation states <a id="exec.opstate">[[exec.opstate]]</a>
1038
+
1039
+ ### General <a id="exec.opstate.general">[[exec.opstate.general]]</a>
1040
+
1041
+ The `operation_state` concept defines the requirements of an operation
1042
+ state type [[exec.async.ops]].
1043
+
1044
+ ``` cpp
1045
+ namespace std::execution {
1046
+ template<class O>
1047
+ concept operation_state =
1048
+ derived_from<typename O::operation_state_concept, operation_state_t> &&
1049
+ requires (O& o) {
1050
+ start(o);
1051
+ };
1052
+ }
1053
+ ```
1054
+
1055
+ If an `operation_state` object is destroyed during the lifetime of its
1056
+ asynchronous operation [[exec.async.ops]], the behavior is undefined.
1057
+
1058
+ [*Note 1*: The `operation_state` concept does not impose requirements
1059
+ on any operations other than destruction and `start`, including copy and
1060
+ move operations. Invoking any such operation on an object whose type
1061
+ models `operation_state` can lead to undefined behavior. — *end note*]
1062
+
1063
+ The program is ill-formed if it performs a copy or move construction or
1064
+ assignment operation on an operation state object created by connecting
1065
+ a library-provided sender.
1066
+
1067
+ ### `execution::start` <a id="exec.opstate.start">[[exec.opstate.start]]</a>
1068
+
1069
+ The name `start` denotes a customization point object that starts
1070
+ [[exec.async.ops]] the asynchronous operation associated with the
1071
+ operation state object. For a subexpression `op`, the expression
1072
+ `start(op)` is ill-formed if `op` is an rvalue. Otherwise, it is
1073
+ expression-equivalent to `MANDATE-NOTHROW(op.start())`.
1074
+
1075
+ If `op.start()` does not start [[exec.async.ops]] the asynchronous
1076
+ operation associated with the operation state `op`, the behavior of
1077
+ calling `start(op)` is undefined.
1078
+
1079
+ ## Senders <a id="exec.snd">[[exec.snd]]</a>
1080
+
1081
+ ### General <a id="exec.snd.general">[[exec.snd.general]]</a>
1082
+
1083
+ Subclauses [[exec.factories]] and [[exec.adapt]] define customizable
1084
+ algorithms that return senders. Each algorithm has a default
1085
+ implementation. Let `sndr` be the result of an invocation of such an
1086
+ algorithm or an object equal to the result [[concepts.equality]], and
1087
+ let `Sndr` be `decltype((sndr))`. Let `rcvr` be a receiver of type
1088
+ `Rcvr` with associated environment `env` of type `Env` such that
1089
+ `sender_to<Sndr, Rcvr>` is `true`. For the default implementation of the
1090
+ algorithm that produced `sndr`, connecting `sndr` to `rcvr` and starting
1091
+ the resulting operation state [[exec.async.ops]] necessarily results in
1092
+ the potential evaluation [[basic.def.odr]] of a set of completion
1093
+ operations whose first argument is a subexpression equal to `rcvr`. Let
1094
+ `Sigs` be a pack of completion signatures corresponding to this set of
1095
+ completion operations, and let `CS` be the type of the expression
1096
+ `get_completion_signatures<Sndr, Env>()`. Then `CS` is a specialization
1097
+ of the class template `completion_signatures` [[exec.cmplsig]], the set
1098
+ of whose template arguments is `Sigs`. If none of the types in `Sigs`
1099
+ are dependent on the type `Env`, then the expression
1100
+ `get_completion_signatures<Sndr>()` is well-formed and its type is `CS`.
1101
+ If a user-provided implementation of the algorithm that produced `sndr`
1102
+ is selected instead of the default:
1103
+
1104
+ - Any completion signature that is in the set of types denoted by
1105
+ `completion_signatures_of_t<Sndr, Env>` and that is not part of `Sigs`
1106
+ shall correspond to error or stopped completion operations, unless
1107
+ otherwise specified.
1108
+ - If none of the types in `Sigs` are dependent on the type `Env`, then
1109
+ `completion_signatures_of_t<Sndr>` and
1110
+ `completion_signatures_of_t<Sndr, Env>` shall denote the same type.
1111
+
1112
+ ### Exposition-only entities <a id="exec.snd.expos">[[exec.snd.expos]]</a>
1113
+
1114
+ Subclause [[exec.snd]] makes use of the following exposition-only
1115
+ entities.
1116
+
1117
+ For a queryable object `env`, `FWD-ENV(env)` is an expression whose type
1118
+ satisfies `queryable` such that for a query object `q` and a pack of
1119
+ subexpressions `as`, the expression `FWD-ENV(env).query(q, as...)` is
1120
+ ill-formed if `forwarding_query(q)` is `false`; otherwise, it is
1121
+ expression-equivalent to `env.query(q, as...)`. The type
1122
+ `FWD-ENV-T(Env)` is `decltype(FWD-ENV(declval<Env>()))`.
1123
+
1124
+ For a query object `q` and a subexpression `v`, `MAKE-ENV(q, v)` is an
1125
+ expression `env` whose type satisfies `queryable` such that the result
1126
+ of `env.query(q)` has a value equal to `v` [[concepts.equality]]. Unless
1127
+ otherwise stated, the object to which `env.query(q)` refers remains
1128
+ valid while `env` remains valid.
1129
+
1130
+ For two queryable objects `env1` and `env2`, a query object `q`, and a
1131
+ pack of subexpressions `as`, `JOIN-ENV(env1, env2)` is an expression
1132
+ `env3` whose type satisfies `queryable` such that `env3.query(q, as...)`
1133
+ is expression-equivalent to:
1134
+
1135
+ - `env1.query(q, as...)` if that expression is well-formed,
1136
+ - otherwise, `env2.query(q, as...)` if that expression is well-formed,
1137
+ - otherwise, `env3.query(q, as...)` is ill-formed.
1138
+
1139
+ The results of *`FWD-ENV`*, *`MAKE-ENV`*, and *`JOIN-ENV`* can be
1140
+ context-dependent; i.e., they can evaluate to expressions with different
1141
+ types and value categories in different contexts for the same arguments.
1142
+
1143
+ For a scheduler `sch`, `SCHED-ATTRS(sch)` is an expression `o1` whose
1144
+ type satisfies `queryable` such that
1145
+ `o1.query(get_completion_scheduler<Tag>)` is an expression with the same
1146
+ type and value as `sch` where `Tag` is one of `set_value_t` or
1147
+ `set_stopped_t`, and such that `o1.query(get_domain)` is
1148
+ expression-equivalent to `sch.query(get_domain)`. `SCHED-ENV(sch)` is an
1149
+ expression `o2` whose type satisfies `queryable` such that
1150
+ `o2.query(get_scheduler)` is a prvalue with the same type and value as
1151
+ `sch`, and such that `o2.query(get_domain)` is expression-equivalent to
1152
+ `sch.query(get_domain)`.
1153
+
1154
+ For two subexpressions `rcvr` and `expr`, `SET-VALUE(rcvr, expr)` is
1155
+ expression-equivalent to `(expr, set_value(std::move(rcvr)))` if the
1156
+ type of `expr` is `void`; otherwise, `set_value(std::move(rcvr), expr)`.
1157
+ `TRY-EVAL(rcvr, expr)` is equivalent to:
1158
+
1159
+ ``` cpp
1160
+ try {
1161
+ expr;
1162
+ } catch(...) {
1163
+ set_error(std::move(rcvr), current_exception());
1164
+ }
1165
+ ```
1166
+
1167
+ if `expr` is potentially-throwing; otherwise, `expr`.
1168
+ `TRY-SET-VALUE(rcvr, expr)` is
1169
+
1170
+ ``` cpp
1171
+ TRY-EVAL(rcvr, SET-VALUE(rcvr, expr))
1172
+ ```
1173
+
1174
+ except that `rcvr` is evaluated only once.
1175
+
1176
+ ``` cpp
1177
+ template<class Default = default_domain, class Sndr>
1178
+ constexpr auto completion-domain(const Sndr& sndr) noexcept;
1179
+ ```
1180
+
1181
+ *`COMPL-DOMAIN`*`(T)` is the type of the expression
1182
+ `get_domain(get_completion_scheduler<T>(get_env(sndr)))`.
1183
+
1184
+ *Effects:* If all of the types *`COMPL-DOMAIN`*`(set_value_t)`,
1185
+ *`COMPL-DOMAIN`*`(set_error_t)`, and *`COMPL-DOMAIN`*`(set_stopped_t)`
1186
+ are ill-formed, `completion-domain<Default>(sndr)` is a
1187
+ default-constructed prvalue of type `Default`. Otherwise, if they all
1188
+ share a common type [[meta.trans.other]] (ignoring those types that are
1189
+ ill-formed), then *`completion-domain`*`<Default>(sndr)` is a
1190
+ default-constructed prvalue of that type. Otherwise,
1191
+ *`completion-domain`*`<Default>(sndr)` is ill-formed.
1192
+
1193
+ ``` cpp
1194
+ template<class Tag, class Env, class Default>
1195
+ constexpr decltype(auto) query-with-default(
1196
+ Tag, const Env& env, Default&& value) noexcept(see below);
1197
+ ```
1198
+
1199
+ Let `e` be the expression `Tag()(env)` if that expression is
1200
+ well-formed; otherwise, it is
1201
+ `static_cast<Default>(std::forward<Default>(value))`.
1202
+
1203
+ *Returns:* `e`.
1204
+
1205
+ *Remarks:* The expression in the noexcept clause is `noexcept(e)`.
1206
+
1207
+ ``` cpp
1208
+ template<class Sndr>
1209
+ constexpr auto get-domain-early(const Sndr& sndr) noexcept;
1210
+ ```
1211
+
1212
+ *Effects:* Equivalent to:
1213
+
1214
+ ``` cpp
1215
+ return Domain();
1216
+ ```
1217
+
1218
+ where `Domain` is the decayed type of the first of the following
1219
+ expressions that is well-formed:
1220
+
1221
+ - `get_domain(get_env(sndr))`
1222
+ - *`completion-domain`*`(sndr)`
1223
+ - `default_domain()`
1224
+
1225
+ ``` cpp
1226
+ template<class Sndr, class Env>
1227
+ constexpr auto get-domain-late(const Sndr& sndr, const Env& env) noexcept;
1228
+ ```
1229
+
1230
+ *Effects:* Equivalent to:
1231
+
1232
+ - If `sender-for<Sndr, continues_on_t>` is `true`, then
1233
+ ``` cpp
1234
+ return Domain();
1235
+ ```
1236
+
1237
+ where `Domain` is the type of the following expression:
1238
+ ``` cpp
1239
+ [] {
1240
+ auto [_, sch, _] = sndr;
1241
+ return query-with-default(get_domain, sch, default_domain());
1242
+ }();
1243
+ ```
1244
+
1245
+ \[*Note 1*: The `continues_on` algorithm works in tandem with
1246
+ `schedule_from`[[exec.schedule.from]] to give scheduler authors a
1247
+ way to customize both how to transition onto (`continues_on`) and off
1248
+ of (`schedule_from`) a given execution context. Thus, `continues_on`
1249
+ ignores the domain of the predecessor and uses the domain of the
1250
+ destination scheduler to select a customization, a property that is
1251
+ unique to `continues_on`. That is why it is given special treatment
1252
+ here. — *end note*]
1253
+ - Otherwise,
1254
+ ``` cpp
1255
+ return Domain();
1256
+ ```
1257
+
1258
+ where `Domain` is the first of the following expressions that is
1259
+ well-formed and whose type is not `void`:
1260
+ - `get_domain(get_env(sndr))`
1261
+ - *`completion-domain`*`<void>(sndr)`
1262
+ - `get_domain(env)`
1263
+ - `get_domain(get_scheduler(env))`
1264
+ - `default_domain()`
1265
+
1266
+ ``` cpp
1267
+ template<callable Fun>
1268
+ requires is_nothrow_move_constructible_v<Fun>
1269
+ struct emplace-from {
1270
+ Fun fun; // exposition only
1271
+ using type = call-result-t<Fun>;
1272
+
1273
+ constexpr operator type() && noexcept(nothrow-callable<Fun>) {
1274
+ return std::move(fun)();
1275
+ }
1276
+
1277
+ constexpr type operator()() && noexcept(nothrow-callable<Fun>) {
1278
+ return std::move(fun)();
1279
+ }
1280
+ };
1281
+ ```
1282
+
1283
+ [*Note 2*: *`emplace-from`* is used to emplace non-movable types into
1284
+ `tuple`, `optional`, `variant`, and similar types. — *end note*]
1285
+
1286
+ ``` cpp
1287
+ struct on-stop-request {
1288
+ inplace_stop_source& stop-src; // exposition only
1289
+ void operator()() noexcept { stop-src.request_stop(); }
1290
+ };
1291
+ ```
1292
+
1293
+ ``` cpp
1294
+ template<class T_0, class T_1, …, class T_n>
1295
+ struct product-type { // exposition only
1296
+ T_0 t_0; // exposition only
1297
+ T_1 t_1; // exposition only
1298
+
1299
+ T_n t_n; // exposition only
1300
+
1301
+ template<size_t I, class Self>
1302
+ constexpr decltype(auto) get(this Self&& self) noexcept; // exposition only
1303
+
1304
+ template<class Self, class Fn>
1305
+ constexpr decltype(auto) apply(this Self&& self, Fn&& fn) // exposition only
1306
+ noexcept(see below);
1307
+ };
1308
+ ```
1309
+
1310
+ [*Note 3*: *`product-type`* is presented here in pseudo-code form for
1311
+ the sake of exposition. It can be approximated in standard C++ with a
1312
+ tuple-like implementation that takes care to keep the type an aggregate
1313
+ that can be used as the initializer of a structured binding
1314
+ declaration. — *end note*]
1315
+
1316
+ [*Note 4*: An expression of type *`product-type`* is usable as the
1317
+ initializer of a structured binding declaration
1318
+ [[dcl.struct.bind]]. — *end note*]
1319
+
1320
+ ``` cpp
1321
+ template<size_t I, class Self>
1322
+ constexpr decltype(auto) get(this Self&& self) noexcept;
1323
+ ```
1324
+
1325
+ *Effects:* Equivalent to:
1326
+
1327
+ ``` cpp
1328
+ auto& [...ts] = self;
1329
+ return std::forward_like<Self>(ts...[I]);
1330
+ ```
1331
+
1332
+ ``` cpp
1333
+ template<class Self, class Fn>
1334
+ constexpr decltype(auto) apply(this Self&& self, Fn&& fn) noexcept(see below);
1335
+ ```
1336
+
1337
+ *Constraints:* The expression in the `return` statement below is
1338
+ well-formed.
1339
+
1340
+ *Effects:* Equivalent to:
1341
+
1342
+ ``` cpp
1343
+ auto& [...ts] = self;
1344
+ return std::forward<Fn>(fn)(std::forward_like<Self>(ts)...);
1345
+ ```
1346
+
1347
+ *Remarks:* The expression in the `noexcept` clause is `true` if the
1348
+ `return` statement above is not potentially throwing; otherwise,
1349
+ `false`.
1350
+
1351
+ Let `valid-specialization` be the following concept:
1352
+
1353
+ ``` cpp
1354
+ namespace std::execution {
1355
+ template<template<class...> class T, class... Args>
1356
+ concept valid-specialization = // exposition only
1357
+ requires { typename T<Args...>; };
1358
+ }
1359
+ ```
1360
+
1361
+ ``` cpp
1362
+ template<class Tag, class Data = see below, class... Child>
1363
+ constexpr auto make-sender(Tag tag, Data&& data, Child&&... child);
1364
+ ```
1365
+
1366
+ *Mandates:* The following expressions are `true`:
1367
+
1368
+ - `semiregular<Tag>`
1369
+ - `movable-value<Data>`
1370
+ - `(sender<Child> && ...)`
1371
+ - `dependent_sender<Sndr> || sender_in<Sndr>`, where `Sndr` is
1372
+ *`basic-sender`*`<Tag, Data,Child...>` as defined below. *Recommended
1373
+ practice:* If evaluation of `sender_in<Sndr>` results in an uncaught
1374
+ exception from the evaluation of `get_completion_signatures<Sndr>()`,
1375
+ the implementation should include information about that exception in
1376
+ the resulting diagnostic.
1377
+
1378
+ *Returns:* A prvalue of type
1379
+ *`basic-sender`*`<Tag, decay_t<Data>, decay_t<Child>...>` that has been
1380
+ direct-list-initialized with the forwarded arguments, where
1381
+ *basic-sender* is the following exposition-only class template except as
1382
+ noted below.
1383
+
1384
+ *Remarks:* The default template argument for the `Data` template
1385
+ parameter denotes an unspecified empty trivially copyable class type
1386
+ that models `semiregular`.
1387
+
1388
+ ``` cpp
1389
+ namespace std::execution {
1390
+ template<class Tag>
1391
+ concept completion-tag = // exposition only
1392
+ same_as<Tag, set_value_t> || same_as<Tag, set_error_t> || same_as<Tag, set_stopped_t>;
1393
+
1394
+ struct default-impls { // exposition only
1395
+ static constexpr auto get-attrs = see belownc; // exposition only
1396
+ static constexpr auto get-env = see belownc; // exposition only
1397
+ static constexpr auto get-state = see belownc; // exposition only
1398
+ static constexpr auto start = see belownc; // exposition only
1399
+ static constexpr auto complete = see belownc; // exposition only
1400
+
1401
+ template<class Sndr, class... Env>
1402
+ static consteval void check-types(); // exposition only
1403
+ };
1404
+
1405
+ template<class Tag>
1406
+ struct impls-for : default-impls {}; // exposition only
1407
+
1408
+ template<class Sndr, class Rcvr> // exposition only
1409
+ using state-type = decay_t<call-result-t<
1410
+ decltype(impls-for<tag_of_t<Sndr>>::get-state), Sndr, Rcvr&>>;
1411
+
1412
+ template<class Index, class Sndr, class Rcvr> // exposition only
1413
+ using env-type = call-result-t<
1414
+ decltype(impls-for<tag_of_t<Sndr>>::get-env), Index,
1415
+ state-type<Sndr, Rcvr>&, const Rcvr&>;
1416
+
1417
+ template<class Sndr>
1418
+ using data-type = decltype(declval<Sndr>().template get<1>()); // exposition only
1419
+
1420
+ template<class Sndr, size_t I = 0>
1421
+ using child-type = decltype(declval<Sndr>().template get<I+2>()); // exposition only
1422
+
1423
+ template<class Sndr>
1424
+ using indices-for = remove_reference_t<Sndr>::indices-for; // exposition only
1425
+
1426
+ template<class Sndr, class Rcvr>
1427
+ struct basic-state { // exposition only
1428
+ basic-state(Sndr&& sndr, Rcvr&& rcvr) noexcept(see below)
1429
+ : rcvr(std::move(rcvr))
1430
+ , state(impls-for<tag_of_t<Sndr>>::get-state(std::forward<Sndr>(sndr), rcvr)) { }
1431
+
1432
+ Rcvr rcvr; // exposition only
1433
+ state-type<Sndr, Rcvr> state; // exposition only
1434
+ };
1435
+
1436
+ template<class Sndr, class Rcvr, class Index>
1437
+ requires valid-specialization<env-type, Index, Sndr, Rcvr>
1438
+ struct basic-receiver { // exposition only
1439
+ using receiver_concept = receiver_t;
1440
+
1441
+ using tag-t = tag_of_t<Sndr>; // exposition only
1442
+ using state-t = state-type<Sndr, Rcvr>; // exposition only
1443
+ static constexpr const auto& complete = impls-for<tag-t>::complete; // exposition only
1444
+
1445
+ template<class... Args>
1446
+ requires callable<decltype(complete), Index, state-t&, Rcvr&, set_value_t, Args...>
1447
+ void set_value(Args&&... args) && noexcept {
1448
+ complete(Index(), op->state, op->rcvr, set_value_t(), std::forward<Args>(args)...);
1449
+ }
1450
+
1451
+ template<class Error>
1452
+ requires callable<decltype(complete), Index, state-t&, Rcvr&, set_error_t, Error>
1453
+ void set_error(Error&& err) && noexcept {
1454
+ complete(Index(), op->state, op->rcvr, set_error_t(), std::forward<Error>(err));
1455
+ }
1456
+
1457
+ void set_stopped() && noexcept
1458
+ requires callable<decltype(complete), Index, state-t&, Rcvr&, set_stopped_t> {
1459
+ complete(Index(), op->state, op->rcvr, set_stopped_t());
1460
+ }
1461
+
1462
+ auto get_env() const noexcept -> env-type<Index, Sndr, Rcvr> {
1463
+ return impls-for<tag-t>::get-env(Index(), op->state, op->rcvr);
1464
+ }
1465
+
1466
+ basic-state<Sndr, Rcvr>* op; // exposition only
1467
+ };
1468
+
1469
+ constexpr auto connect-all = see belownc; // exposition only
1470
+
1471
+ template<class Sndr, class Rcvr>
1472
+ using connect-all-result = call-result-t< // exposition only
1473
+ decltype(connect-all), basic-state<Sndr, Rcvr>*, Sndr, indices-for<Sndr>>;
1474
+
1475
+ template<class Sndr, class Rcvr>
1476
+ requires valid-specialization<state-type, Sndr, Rcvr> &&
1477
+ valid-specialization<connect-all-result, Sndr, Rcvr>
1478
+ struct basic-operation : basic-state<Sndr, Rcvr> { // exposition only
1479
+ using operation_state_concept = operation_state_t;
1480
+ using tag-t = tag_of_t<Sndr>; // exposition only
1481
+
1482
+ connect-all-result<Sndr, Rcvr> inner-ops; // exposition only
1483
+
1484
+ basic-operation(Sndr&& sndr, Rcvr&& rcvr) noexcept(see belownc) // exposition only
1485
+ : basic-state<Sndr, Rcvr>(std::forward<Sndr>(sndr), std::move(rcvr)),
1486
+ inner-ops(connect-all(this, std::forward<Sndr>(sndr), indices-for<Sndr>()))
1487
+ {}
1488
+
1489
+ void start() & noexcept {
1490
+ auto& [...ops] = inner-ops;
1491
+ impls-for<tag-t>::start(this->state, this->rcvr, ops...);
1492
+ }
1493
+ };
1494
+
1495
+ template<class Tag, class Data, class... Child>
1496
+ struct basic-sender : product-type<Tag, Data, Child...> { // exposition only
1497
+ using sender_concept = sender_t;
1498
+ using indices-for = index_sequence_for<Child...>; // exposition only
1499
+
1500
+ decltype(auto) get_env() const noexcept {
1501
+ auto& [_, data, ...child] = *this;
1502
+ return impls-for<Tag>::get-attrs(data, child...);
1503
+ }
1504
+
1505
+ template<decays-to<basic-sender> Self, receiver Rcvr>
1506
+ auto connect(this Self&& self, Rcvr rcvr) noexcept(see below)
1507
+ -> basic-operation<Self, Rcvr> {
1508
+ return {std::forward<Self>(self), std::move(rcvr)};
1509
+ }
1510
+
1511
+ template<decays-to<basic-sender> Self, class... Env>
1512
+ static constexpr auto get_completion_signatures();
1513
+ };
1514
+ }
1515
+ ```
1516
+
1517
+ It is unspecified whether a specialization of *`basic-sender`* is an
1518
+ aggregate.
1519
+
1520
+ An expression of type *`basic-sender`* is usable as the initializer of a
1521
+ structured binding declaration [[dcl.struct.bind]].
1522
+
1523
+ The expression in the `noexcept` clause of the constructor of
1524
+ *`basic-state`* is
1525
+
1526
+ ``` cpp
1527
+ is_nothrow_move_constructible_v<Rcvr> &&
1528
+ nothrow-callable<decltype(impls-for<tag_of_t<Sndr>>::get-state), Sndr, Rcvr&> &&
1529
+ (same_as<state-type<Sndr, Rcvr>, get-state-result> ||
1530
+ is_nothrow_constructible_v<state-type<Sndr, Rcvr>, get-state-result>)
1531
+ ```
1532
+
1533
+ where *`get-state-result`* is
1534
+
1535
+ ``` cpp
1536
+ call-result-t<decltype(impls-for<tag_of_t<Sndr>>::get-state), Sndr, Rcvr&>.
1537
+ ```
1538
+
1539
+ The object *`connect-all`* is initialized with a callable object
1540
+ equivalent to the following lambda:
1541
+
1542
+ ``` cpp
1543
+ []<class Sndr, class Rcvr, size_t... Is>(
1544
+ basic-state<Sndr, Rcvr>* op, Sndr&& sndr, index_sequence<Is...>) noexcept(see below)
1545
+ -> decltype(auto) {
1546
+ auto& [_, data, ...child] = sndr;
1547
+ return product-type{connect(
1548
+ std::forward_like<Sndr>(child),
1549
+ basic-receiver<Sndr, Rcvr, integral_constant<size_t, Is>>{op})...};
1550
+ }
1551
+ ```
1552
+
1553
+ *Constraints:* The expression in the `return` statement is well-formed.
1554
+
1555
+ *Remarks:* The expression in the `noexcept` clause is `true` if the
1556
+ `return` statement is not potentially throwing; otherwise, `false`.
1557
+
1558
+ The expression in the `noexcept` clause of the constructor of
1559
+ *`basic-operation`* is:
1560
+
1561
+ ``` cpp
1562
+ is_nothrow_constructible_v<basic-state<Self, Rcvr>, Self, Rcvr> &&
1563
+ noexcept(connect-all(this, std::forward<Sndr>(sndr), indices-for<Sndr>()))
1564
+ ```
1565
+
1566
+ The expression in the `noexcept` clause of the `connect` member function
1567
+ of *`basic-sender`* is:
1568
+
1569
+ ``` cpp
1570
+ is_nothrow_constructible_v<basic-operation<Self, Rcvr>, Self, Rcvr>
1571
+ ```
1572
+
1573
+ The member `default-impls::get-attrs` is initialized with a callable
1574
+ object equivalent to the following lambda:
1575
+
1576
+ ``` cpp
1577
+ [](const auto&, const auto&... child) noexcept -> decltype(auto) {
1578
+ if constexpr (sizeof...(child) == 1)
1579
+ return (FWD-ENV(get_env(child)), ...);
1580
+ else
1581
+ return env<>();
1582
+ }
1583
+ ```
1584
+
1585
+ The member `default-impls::get-env` is initialized with a callable
1586
+ object equivalent to the following lambda:
1587
+
1588
+ ``` cpp
1589
+ [](auto, auto&, const auto& rcvr) noexcept -> decltype(auto) {
1590
+ return FWD-ENV(get_env(rcvr));
1591
+ }
1592
+ ```
1593
+
1594
+ The member `default-impls::get-state` is initialized with a callable
1595
+ object equivalent to the following lambda:
1596
+
1597
+ ``` cpp
1598
+ []<class Sndr, class Rcvr>(Sndr&& sndr, Rcvr& rcvr) noexcept -> decltype(auto) {
1599
+ auto& [_, data, ...child] = sndr;
1600
+ return allocator-aware-forward(std::forward_like<Sndr>(data), rcvr);
1601
+ }
1602
+ ```
1603
+
1604
+ The member `default-impls::start` is initialized with a callable object
1605
+ equivalent to the following lambda:
1606
+
1607
+ ``` cpp
1608
+ [](auto&, auto&, auto&... ops) noexcept -> void {
1609
+ (execution::start(ops), ...);
1610
+ }
1611
+ ```
1612
+
1613
+ The member `default-impls::complete` is initialized with a callable
1614
+ object equivalent to the following lambda:
1615
+
1616
+ ``` cpp
1617
+ []<class Index, class Rcvr, class Tag, class... Args>(
1618
+ Index, auto& state, Rcvr& rcvr, Tag, Args&&... args) noexcept
1619
+ -> void requires callable<Tag, Rcvr, Args...> {
1620
+ static_assert(Index::value == 0);
1621
+ Tag()(std::move(rcvr), std::forward<Args>(args)...);
1622
+ }
1623
+ ```
1624
+
1625
+ ``` cpp
1626
+ template<class Sndr, class... Env>
1627
+ static consteval void default-impls::check-types();
1628
+ ```
1629
+
1630
+ Let `Is` be the pack of integral template arguments of the
1631
+ `integer_sequence` specialization denoted by *`indices-for`*`<Sndr>`.
1632
+
1633
+ *Effects:* Equivalent to:
1634
+
1635
+ ``` cpp
1636
+ (get_completion_signatures<child-type<Sndr, Is>, FWD-ENV-T(Env)...>(), ...);
1637
+ ```
1638
+
1639
+ [*Note 1*:
1640
+
1641
+ For any types `T` and `S`, and pack `E`, let `e` be the expression
1642
+ *`impls-for`*`<T>::`*`check-types`*`<S, E...>()`. Then exactly one of
1643
+ the following is `true`:
1644
+
1645
+ - `e` is ill-formed, or
1646
+ - the evaluation of `e` exits with an exception, or
1647
+ - `e` is a core constant expression.
1648
+
1649
+ When `e` is a core constant expression, the pack `S, E...` uniquely
1650
+ determines a set of completion signatures.
1651
+
1652
+ — *end note*]
1653
+
1654
+ ``` cpp
1655
+ template<class Tag, class Data, class... Child>
1656
+ template<class Sndr, class... Env>
1657
+ constexpr auto basic-sender<Tag, Data, Child...>::get_completion_signatures();
1658
+ ```
1659
+
1660
+ Let `Rcvr` be the type of a receiver whose environment has type `E`,
1661
+ where `E` is the first type in the list `Env..., env<>`. Let
1662
+ *`CHECK-TYPES`*`()` be the expression
1663
+ *`impls-for`*`<Tag>::template `*`check-types`*`<Sndr, E>()`, and let
1664
+ `CS` be a type determined as follows:
1665
+
1666
+ - If *`CHECK-TYPES`*`()` is a core constant expression, let `op` be an
1667
+ lvalue subexpression whose type is `connect_result_t<Sndr, Rcvr>`.
1668
+ Then `CS` is the specialization of `completion_signatures` the set of
1669
+ whose template arguments correspond to the set of completion
1670
+ operations that are potentially evaluated [[basic.def.odr]] as a
1671
+ result of evaluating `op.start()`.
1672
+ - Otherwise, `CS` is `completion_signatures<>`.
1673
+
1674
+ *Constraints:* *`CHECK-TYPES`*`()` is a well-formed expression.
1675
+
1676
+ *Effects:* Equivalent to:
1677
+
1678
+ ``` cpp
1679
+ CHECK-TYPES();
1680
+ return CS();
1681
+ ```
1682
+
1683
+ ``` cpp
1684
+ template<class... Fns>
1685
+ struct overload-set : Fns... {
1686
+ using Fns::operator()...;
1687
+ };
1688
+ ```
1689
+
1690
+ ``` cpp
1691
+ struct not-a-sender {
1692
+ using sender_concept = sender_t;
1693
+
1694
+ template<class Sndr>
1695
+ static consteval auto get_completion_signatures() -> completion_signatures<> {
1696
+ throw unspecified-exception();
1697
+ }
1698
+ };
1699
+ ```
1700
+
1701
+ where `unspecified-exception` is a type derived from `exception`.
1702
+
1703
+ ``` cpp
1704
+ constexpr void decay-copyable-result-datums(auto cs) {
1705
+ cs.for-each([]<class Tag, class... Ts>(Tag(*)(Ts...)) {
1706
+ if constexpr (!(is_constructible_v<decay_t<Ts>, Ts> &&...))
1707
+ throw unspecified-exception();
1708
+ });
1709
+ }
1710
+ ```
1711
+
1712
+ where `unspecified-exception` is a type derived from `exception`.
1713
+
1714
+ ``` cpp
1715
+ template<class T, class Context>
1716
+ decltype(auto) allocator-aware-forward(T&& obj, Context&& context); // exposition only
1717
+ ```
1718
+
1719
+ *allocator-aware-forward* is an exposition-only function template used
1720
+ to either create a new object of type `remove_cvref_t<T>` from `obj` or
1721
+ forward `obj` depending on whether an allocator is available. If the
1722
+ environment associated with `context` provides an allocator (i.e., the
1723
+ expression `get_allocator(get_env(context))` is valid), let *alloc* be
1724
+ the result of this expression and let `P` be `remove_cvref_t<T>`.
1725
+
1726
+ *Returns:*
1727
+
1728
+ - If *alloc* is not defined, returns `std::forward<T>(obj)`,
1729
+ - otherwise if `P` is a specialization of *product-type*, returns an
1730
+ object of type `P` whose elements are initialized using
1731
+ ``` cpp
1732
+ make_obj_using_allocator<decltype(e)>(std::forward_like<T>(e), alloc)
1733
+ ```
1734
+
1735
+ where `e` is the corresponding element of `obj`,
1736
+ - otherwise, returns
1737
+ `make_obj_using_allocator<P>(std::forward<T>(obj), `*`alloc`*`)`.
1738
+
1739
+ ### Sender concepts <a id="exec.snd.concepts">[[exec.snd.concepts]]</a>
1740
+
1741
+ The `sender` concept defines the requirements for a sender type
1742
+ [[exec.async.ops]]. The `sender_in` concept defines the requirements for
1743
+ a sender type that can create asynchronous operations given an
1744
+ associated environment type. The `sender_to` concept defines the
1745
+ requirements for a sender type that can connect with a specific receiver
1746
+ type. The `get_env` customization point object is used to access a
1747
+ sender’s associated attributes. The connect customization point object
1748
+ is used to connect [[exec.async.ops]] a sender and a receiver to produce
1749
+ an operation state.
1750
+
1751
+ ``` cpp
1752
+ namespace std::execution {
1753
+ template<auto>
1754
+ concept is-constant = true; // exposition only
1755
+
1756
+ template<class Sndr>
1757
+ concept is-sender = // exposition only
1758
+ derived_from<typename Sndr::sender_concept, sender_t>;
1759
+
1760
+ template<class Sndr>
1761
+ concept enable-sender = // exposition only
1762
+ is-sender<Sndr> ||
1763
+ is-awaitable<Sndr, env-promise<env<>>>; // [exec.awaitable]
1764
+
1765
+ template<class Sndr>
1766
+ inline constexpr bool enable_sender = enable-sender<Sndr>;
1767
+
1768
+ template<class Sndr>
1769
+ consteval bool is-dependent-sender-helper() try { // exposition only
1770
+ get_completion_signatures<Sndr>();
1771
+ return false;
1772
+ } catch (dependent_sender_error&) {
1773
+ return true;
1774
+ }
1775
+
1776
+ template<class Sndr>
1777
+ concept sender =
1778
+ enable_sender<remove_cvref_t<Sndr>> &&
1779
+ requires (const remove_cvref_t<Sndr>& sndr) {
1780
+ { get_env(sndr) } -> queryable;
1781
+ } &&
1782
+ move_constructible<remove_cvref_t<Sndr>> &&
1783
+ constructible_from<remove_cvref_t<Sndr>, Sndr>;
1784
+
1785
+ template<class Sndr, class... Env>
1786
+ concept sender_in =
1787
+ sender<Sndr> &&
1788
+ (sizeof...(Env) <= 1) &&
1789
+ (queryable<Env> &&...) &&
1790
+ is-constant<get_completion_signatures<Sndr, Env...>()>;
1791
+
1792
+ template<class Sndr>
1793
+ concept dependent_sender =
1794
+ sender<Sndr> && bool_constant<is-dependent-sender-helper<Sndr>()>::value;
1795
+
1796
+ template<class Sndr, class Rcvr>
1797
+ concept sender_to =
1798
+ sender_in<Sndr, env_of_t<Rcvr>> &&
1799
+ receiver_of<Rcvr, completion_signatures_of_t<Sndr, env_of_t<Rcvr>>> &&
1800
+ requires (Sndr&& sndr, Rcvr&& rcvr) {
1801
+ connect(std::forward<Sndr>(sndr), std::forward<Rcvr>(rcvr));
1802
+ };
1803
+ }
1804
+ ```
1805
+
1806
+ For a type `Sndr`, if `sender<Sndr>` is `true` and
1807
+ `dependent_sender<Sndr>` is `false`, then `Sndr` is a non-dependent
1808
+ sender [[exec.async.ops]].
1809
+
1810
+ Given a subexpression `sndr`, let `Sndr` be `decltype((sndr))` and let
1811
+ `rcvr` be a receiver with an associated environment whose type is `Env`.
1812
+ A completion operation is a *permissible completion* for `Sndr` and
1813
+ `Env` if its completion signature appears in the argument list of the
1814
+ specialization of `completion_signatures` denoted by
1815
+ `completion_signatures_of_t<Sndr, Env>`. `Sndr` and `Env` model
1816
+ `sender_in<Sndr, Env>` if all the completion operations that are
1817
+ potentially evaluated by connecting `sndr` to `rcvr` and starting the
1818
+ resulting operation state are permissible completions for `Sndr` and
1819
+ `Env`.
1820
+
1821
+ *Remarks:* Pursuant to [[namespace.std]], users may specialize
1822
+ `enable_sender` to `true` for cv-unqualified program-defined types that
1823
+ model `sender`, and `false` for types that do not. Such specializations
1824
+ shall be usable in constant expressions [[expr.const]] and have type
1825
+ `const bool`.
1826
+
1827
+ The exposition-only concepts `sender-of` and `sender-in-of` define the
1828
+ requirements for a sender type that completes with a given unique set of
1829
+ value result types.
1830
+
1831
+ ``` cpp
1832
+ namespace std::execution {
1833
+ template<class... As>
1834
+ using value-signature = set_value_t(As...); // exposition only
1835
+
1836
+ template<class Sndr, class SetValue, class... Env>
1837
+ concept sender-in-of-impl = // exposition only
1838
+ sender_in<Sndr, Env...> &&
1839
+ MATCHING-SIG(SetValue, // see [exec.general]
1840
+ gather-signatures<set_value_t, // see [exec.cmplsig]
1841
+ completion_signatures_of_t<Sndr, Env...>,
1842
+ value-signature,
1843
+ type_identity_t>);
1844
+
1845
+ template<class Sndr, class Env, class... Values>
1846
+ concept sender-in-of = // exposition only
1847
+ sender-in-of-impl<Sndr, set_value_t(Values...), Env>;
1848
+
1849
+ template<class Sndr, class... Values>
1850
+ concept sender-of = // exposition only
1851
+ sender-in-of-impl<Sndr, set_value_t(Values...)>;
1852
+ }
1853
+ ```
1854
+
1855
+ Let `sndr` be an expression such that `decltype((sndr))` is `Sndr`. The
1856
+ type `tag_of_t<Sndr>` is as follows:
1857
+
1858
+ - If the declaration
1859
+ ``` cpp
1860
+ auto&& [tag, data, ...children] = sndr;
1861
+ ```
1862
+
1863
+ would be well-formed, `tag_of_t<Sndr>` is an alias for
1864
+ `decltype(auto(tag))`.
1865
+ - Otherwise, `tag_of_t<Sndr>` is ill-formed.
1866
+
1867
+ Let `sender-for` be an exposition-only concept defined as follows:
1868
+
1869
+ ``` cpp
1870
+ namespace std::execution {
1871
+ template<class Sndr, class Tag>
1872
+ concept sender-for =
1873
+ sender<Sndr> &&
1874
+ same_as<tag_of_t<Sndr>, Tag>;
1875
+ }
1876
+ ```
1877
+
1878
+ For a type `T`, `SET-VALUE-SIG(T)` denotes the type `set_value_t()` if
1879
+ `T` is cv `void`; otherwise, it denotes the type `set_value_t(T)`.
1880
+
1881
+ Library-provided sender types
1882
+
1883
+ - always expose an overload of a member `connect` that accepts an rvalue
1884
+ sender and
1885
+ - only expose an overload of a member `connect` that accepts an lvalue
1886
+ sender if they model `copy_constructible`.
1887
+
1888
+ ### Awaitable helpers <a id="exec.awaitable">[[exec.awaitable]]</a>
1889
+
1890
+ The sender concepts recognize awaitables as senders. For [[exec]], an
1891
+ *awaitable* is an expression that would be well-formed as the operand of
1892
+ a `co_await` expression within a given context.
1893
+
1894
+ For a subexpression `c`, let `GET-AWAITER(c, p)` be
1895
+ expression-equivalent to the series of transformations and conversions
1896
+ applied to `c` as the operand of an *await-expression* in a coroutine,
1897
+ resulting in lvalue `e` as described by [[expr.await]], where `p` is an
1898
+ lvalue referring to the coroutine’s promise, which has type `Promise`.
1899
+
1900
+ [*Note 1*: This includes the invocation of the promise type’s
1901
+ `await_transform` member if any, the invocation of the
1902
+ `operator co_await` picked by overload resolution if any, and any
1903
+ necessary implicit conversions and materializations. — *end note*]
1904
+
1905
+ Let `GET-AWAITER(c)` be expression-equivalent to `GET-AWAITER(c, q)`
1906
+ where `q` is an lvalue of an unspecified empty class type `none-such`
1907
+ that lacks an `await_transform` member, and where
1908
+ `coroutine_handle<none-such>` behaves as `coroutine_handle<void>`.
1909
+
1910
+ Let `is-awaitable` be the following exposition-only concept:
1911
+
1912
+ ``` cpp
1913
+ namespace std {
1914
+ template<class T>
1915
+ concept await-suspend-result = see below; // exposition only
1916
+
1917
+ template<class A, class... Promise>
1918
+ concept is-awaiter = // exposition only
1919
+ requires (A& a, coroutine_handle<Promise...> h) {
1920
+ a.await_ready() ? 1 : 0;
1921
+ { a.await_suspend(h) } -> await-suspend-result;
1922
+ a.await_resume();
1923
+ };
1924
+
1925
+ template<class C, class... Promise>
1926
+ concept is-awaitable = // exposition only
1927
+ requires (C (*fc)() noexcept, Promise&... p) {
1928
+ { GET-AWAITER(fc(), p...) } -> is-awaiter<Promise...>;
1929
+ };
1930
+ }
1931
+ ```
1932
+
1933
+ `\defexposconcept{await-suspend-result}<T>` is `true` if and only if one
1934
+ of the following is `true`:
1935
+
1936
+ - `T` is `void`, or
1937
+ - `T` is `bool`, or
1938
+ - `T` is a specialization of `coroutine_handle`.
1939
+
1940
+ For a subexpression `c` such that `decltype((c))` is type `C`, and an
1941
+ lvalue `p` of type `Promise`, `await-result- type<C, Promise>` denotes
1942
+ the type `decltype(GET-AWAITER(c, p).await_resume())` and
1943
+ `await-result-type<C>` denotes the type
1944
+ `decltype(GET-AWAITER(c).await_resume())`.
1945
+
1946
+ Let *`with-await-transform`* be the exposition-only class template:
1947
+
1948
+ ``` cpp
1949
+ namespace std::execution {
1950
+ template<class T, class Promise>
1951
+ concept has-as-awaitable = // exposition only
1952
+ requires (T&& t, Promise& p) {
1953
+ { std::forward<T>(t).as_awaitable(p) } -> is-awaitable<Promise&>;
1954
+ };
1955
+
1956
+ template<class Derived>
1957
+ struct with-await-transform { // exposition only
1958
+ template<class T>
1959
+ T&& await_transform(T&& value) noexcept {
1960
+ return std::forward<T>(value);
1961
+ }
1962
+
1963
+ template<has-as-awaitable<Derived> T>
1964
+ auto await_transform(T&& value)
1965
+ noexcept(noexcept(std::forward<T>(value).as_awaitable(declval<Derived&>())))
1966
+ -> decltype(std::forward<T>(value).as_awaitable(declval<Derived&>())) {
1967
+ return std::forward<T>(value).as_awaitable(static_cast<Derived&>(*this));
1968
+ }
1969
+ };
1970
+ }
1971
+ ```
1972
+
1973
+ Let *`env-promise`* be the exposition-only class template:
1974
+
1975
+ ``` cpp
1976
+ namespace std::execution {
1977
+ template<class Env>
1978
+ struct env-promise : with-await-transform<env-promise<Env>> { // exposition only
1979
+ unspecified get_return_object() noexcept;
1980
+ unspecified initial_suspend() noexcept;
1981
+ unspecified final_suspend() noexcept;
1982
+ void unhandled_exception() noexcept;
1983
+ void return_void() noexcept;
1984
+ coroutine_handle<> unhandled_stopped() noexcept;
1985
+
1986
+ const Env& get_env() const noexcept;
1987
+ };
1988
+ }
1989
+ ```
1990
+
1991
+ [*Note 2*: Specializations of *`env-promise`* are used only for the
1992
+ purpose of type computation; its members need not be
1993
+ defined. — *end note*]
1994
+
1995
+ ### `execution::default_domain` <a id="exec.domain.default">[[exec.domain.default]]</a>
1996
+
1997
+ ``` cpp
1998
+ namespace std::execution {
1999
+ struct default_domain {
2000
+ template<sender Sndr, queryable... Env>
2001
+ requires (sizeof...(Env) <= 1)
2002
+ static constexpr sender decltype(auto) transform_sender(Sndr&& sndr, const Env&... env)
2003
+ noexcept(see below);
2004
+
2005
+ template<sender Sndr, queryable Env>
2006
+ static constexpr queryable decltype(auto) transform_env(Sndr&& sndr, Env&& env) noexcept;
2007
+
2008
+ template<class Tag, sender Sndr, class... Args>
2009
+ static constexpr decltype(auto) apply_sender(Tag, Sndr&& sndr, Args&&... args)
2010
+ noexcept(see below);
2011
+ };
2012
+ }
2013
+ ```
2014
+
2015
+ ``` cpp
2016
+ template<sender Sndr, queryable... Env>
2017
+ requires (sizeof...(Env) <= 1)
2018
+ constexpr sender decltype(auto) transform_sender(Sndr&& sndr, const Env&... env)
2019
+ noexcept(see below);
2020
+ ```
2021
+
2022
+ Let `e` be the expression
2023
+
2024
+ ``` cpp
2025
+ tag_of_t<Sndr>().transform_sender(std::forward<Sndr>(sndr), env...)
2026
+ ```
2027
+
2028
+ if that expression is well-formed; otherwise,
2029
+ `std::forward<Sndr>(sndr)`.
2030
+
2031
+ *Returns:* `e`.
2032
+
2033
+ *Remarks:* The exception specification is equivalent to `noexcept(e)`.
2034
+
2035
+ ``` cpp
2036
+ template<sender Sndr, queryable Env>
2037
+ constexpr queryable decltype(auto) transform_env(Sndr&& sndr, Env&& env) noexcept;
2038
+ ```
2039
+
2040
+ Let `e` be the expression
2041
+
2042
+ ``` cpp
2043
+ tag_of_t<Sndr>().transform_env(std::forward<Sndr>(sndr), std::forward<Env>(env))
2044
+ ```
2045
+
2046
+ if that expression is well-formed; otherwise,
2047
+ *`FWD-ENV`*`(std::forward<Env>(env))`.
2048
+
2049
+ *Mandates:* `noexcept(e)` is `true`.
2050
+
2051
+ *Returns:* `e`.
2052
+
2053
+ ``` cpp
2054
+ template<class Tag, sender Sndr, class... Args>
2055
+ constexpr decltype(auto) apply_sender(Tag, Sndr&& sndr, Args&&... args)
2056
+ noexcept(see below);
2057
+ ```
2058
+
2059
+ Let `e` be the expression
2060
+
2061
+ ``` cpp
2062
+ Tag().apply_sender(std::forward<Sndr>(sndr), std::forward<Args>(args)...)
2063
+ ```
2064
+
2065
+ *Constraints:* `e` is a well-formed expression.
2066
+
2067
+ *Returns:* `e`.
2068
+
2069
+ *Remarks:* The exception specification is equivalent to `noexcept(e)`.
2070
+
2071
+ ### `execution::transform_sender` <a id="exec.snd.transform">[[exec.snd.transform]]</a>
2072
+
2073
+ ``` cpp
2074
+ namespace std::execution {
2075
+ template<class Domain, sender Sndr, queryable... Env>
2076
+ requires (sizeof...(Env) <= 1)
2077
+ constexpr sender decltype(auto) transform_sender(Domain dom, Sndr&& sndr, const Env&... env)
2078
+ noexcept(see below);
2079
+ }
2080
+ ```
2081
+
2082
+ Let *transformed-sndr* be the expression
2083
+
2084
+ ``` cpp
2085
+ dom.transform_sender(std::forward<Sndr>(sndr), env...)
2086
+ ```
2087
+
2088
+ if that expression is well-formed; otherwise,
2089
+
2090
+ ``` cpp
2091
+ default_domain().transform_sender(std::forward<Sndr>(sndr), env...)
2092
+ ```
2093
+
2094
+ Let *final-sndr* be the expression *transformed-sndr* if
2095
+ *transformed-sndr* and *sndr* have the same type ignoring cv-qualifiers;
2096
+ otherwise, it is the expression
2097
+ `transform_sender(dom, `*`transformed-sndr`*`, env...)`.
2098
+
2099
+ *Returns:* *final-sndr*.
2100
+
2101
+ *Remarks:* The exception specification is equivalent to
2102
+ `noexcept(`*`final-sndr`*`)`.
2103
+
2104
+ ### `execution::transform_env` <a id="exec.snd.transform.env">[[exec.snd.transform.env]]</a>
2105
+
2106
+ ``` cpp
2107
+ namespace std::execution {
2108
+ template<class Domain, sender Sndr, queryable Env>
2109
+ constexpr queryable decltype(auto) transform_env(Domain dom, Sndr&& sndr, Env&& env) noexcept;
2110
+ }
2111
+ ```
2112
+
2113
+ Let `e` be the expression
2114
+
2115
+ ``` cpp
2116
+ dom.transform_env(std::forward<Sndr>(sndr), std::forward<Env>(env))
2117
+ ```
2118
+
2119
+ if that expression is well-formed; otherwise,
2120
+
2121
+ ``` cpp
2122
+ default_domain().transform_env(std::forward<Sndr>(sndr), std::forward<Env>(env))
2123
+ ```
2124
+
2125
+ *Mandates:* `noexcept(e)` is `true`.
2126
+
2127
+ *Returns:* `e`.
2128
+
2129
+ ### `execution::apply_sender` <a id="exec.snd.apply">[[exec.snd.apply]]</a>
2130
+
2131
+ ``` cpp
2132
+ namespace std::execution {
2133
+ template<class Domain, class Tag, sender Sndr, class... Args>
2134
+ constexpr decltype(auto) apply_sender(Domain dom, Tag, Sndr&& sndr, Args&&... args)
2135
+ noexcept(see below);
2136
+ }
2137
+ ```
2138
+
2139
+ Let e be the expression
2140
+
2141
+ ``` cpp
2142
+ dom.apply_sender(Tag(), std::forward<Sndr>(sndr), std::forward<Args>(args)...)
2143
+ ```
2144
+
2145
+ if that expression is well-formed; otherwise,
2146
+
2147
+ ``` cpp
2148
+ default_domain().apply_sender(Tag(), std::forward<Sndr>(sndr), std::forward<Args>(args)...)
2149
+ ```
2150
+
2151
+ *Constraints:* The expression e is well-formed.
2152
+
2153
+ *Returns:* e.
2154
+
2155
+ *Remarks:* The exception specification is equivalent to `noexcept(`e`)`.
2156
+
2157
+ ### `execution::get_completion_signatures` <a id="exec.getcomplsigs">[[exec.getcomplsigs]]</a>
2158
+
2159
+ ``` cpp
2160
+ template<class Sndr, class... Env>
2161
+ consteval auto get_completion_signatures() -> valid-completion-signatures auto;
2162
+ ```
2163
+
2164
+ Let except be an rvalue subexpression of an unspecified class type
2165
+ Except such that `<`Except`> && derived_from<`Except`, exception>` is
2166
+ `true`. Let *`CHECKED-COMPLSIGS`*`(`e`)` be e if e is a core constant
2167
+ expression whose type satisfies `valid-completion-signatures`;
2168
+ otherwise, it is the following expression:
2169
+
2170
+ ``` cpp
2171
+ (e, throw except, completion_signatures())
2172
+ ```
2173
+
2174
+ Let *`get-complsigs`*`<Sndr, Env...>()` be expression-equivalent to
2175
+ `remove_reference_t<Sndr>::template get_completion_signatures<Sndr, Env...>()`.
2176
+ Let `NewSndr` be `Sndr` if `sizeof...(Env) == 0` is `true`; otherwise,
2177
+ `decltype(`s`)` where s is the following expression:
2178
+
2179
+ ``` cpp
2180
+ transform_sender(
2181
+ get-domain-late(declval<Sndr>(), declval<Env>()...),
2182
+ declval<Sndr>(),
2183
+ declval<Env>()...)
2184
+ ```
2185
+
2186
+ *Constraints:* `sizeof...(Env) <= 1` is `true`.
2187
+
2188
+ *Effects:* Equivalent to: `return `e`;` where e is expression-equivalent
2189
+ to the following:
2190
+
2191
+ - *`CHECKED-COMPLSIGS`*`(`*`get-complsigs`*`<NewSndr, Env...>())` if
2192
+ *`get-complsigs`*`<NewSndr, Env...>()` is a well-formed expression.
2193
+ - Otherwise, *`CHECKED-COMPLSIGS`*`(`*`get-complsigs`*`<NewSndr>())` if
2194
+ *`get-complsigs`*`<NewSndr>()` is a well-formed expression.
2195
+ - Otherwise,
2196
+ ``` cpp
2197
+ completion_signatures<
2198
+ SET-VALUE-SIG(await-result-type<NewSndr, env-promise<Env>...>), // [exec.snd.concepts]
2199
+ set_error_t(exception_ptr),
2200
+ set_stopped_t()>
2201
+ ```
2202
+
2203
+ if `is-awaitable<NewSndr, `*`env-promise`*`<Env>...>` is `true`.
2204
+ - Otherwise,
2205
+ `(throw `*`dependent-sender-error`*`(), completion_signatures())` if
2206
+ `sizeof...(Env) == 0` is `true`, where *`dependent-sender-error`* is
2207
+ `dependent_sender_error` or an unspecified type derived publicly and
2208
+ unambiguously from `dependent_sender_error`.
2209
+ - Otherwise, `(throw `except`, completion_signatures())`.
2210
+
2211
+ Given a type `Env`, if `completion_signatures_of_t<Sndr>` and
2212
+ `completion_signatures_of_t<Sndr, Env>` are both well-formed, they shall
2213
+ denote the same type.
2214
+
2215
+ Let `rcvr` be an rvalue whose type `Rcvr` models `receiver`, and let
2216
+ `Sndr` be the type of a sender such that
2217
+ `sender_in<Sndr, env_of_t<Rcvr>>` is `true`. Let `Sigs...` be the
2218
+ template arguments of the `completion_signatures` specialization named
2219
+ by `completion_signatures_of_t<Sndr, env_of_t<Rcvr>>`. Let `CSO` be a
2220
+ completion function. If sender `Sndr` or its operation state cause the
2221
+ expression `CSO(rcvr, args...)` to be potentially evaluated
2222
+ [[basic.def.odr]] then there shall be a signature `Sig` in `Sigs...`
2223
+ such that
2224
+
2225
+ ``` cpp
2226
+ MATCHING-SIG(decayed-typeof<CSO>(decltype(args)...), Sig)
2227
+ ```
2228
+
2229
+ is `true` [[exec.general]].
2230
+
2231
+ ### `execution::connect` <a id="exec.connect">[[exec.connect]]</a>
2232
+
2233
+ `connect` connects [[exec.async.ops]] a sender with a receiver.
2234
+
2235
+ The name `connect` denotes a customization point object. For
2236
+ subexpressions `sndr` and `rcvr`, let `Sndr` be `decltype((sndr))` and
2237
+ `Rcvr` be `decltype((rcvr))`, let `new_sndr` be the expression
2238
+
2239
+ ``` cpp
2240
+ transform_sender(decltype(get-domain-late(sndr, get_env(rcvr))){}, sndr, get_env(rcvr))
2241
+ ```
2242
+
2243
+ and let `DS` and `DR` be `decay_t<decltype((new_sndr))>` and
2244
+ `decay_t<Rcvr>`, respectively.
2245
+
2246
+ Let *`connect-awaitable-promise`* be the following exposition-only
2247
+ class:
2248
+
2249
+ ``` cpp
2250
+ namespace std::execution {
2251
+ struct connect-awaitable-promise : with-await-transform<connect-awaitable-promise> {
2252
+
2253
+ connect-awaitable-promise(DS&, DR& rcvr) noexcept : rcvr(rcvr) {}
2254
+
2255
+ suspend_always initial_suspend() noexcept { return {}; }
2256
+ [[noreturn]] suspend_always final_suspend() noexcept { terminate(); }
2257
+ [[noreturn]] void unhandled_exception() noexcept { terminate(); }
2258
+ [[noreturn]] void return_void() noexcept { terminate(); }
2259
+
2260
+ coroutine_handle<> unhandled_stopped() noexcept {
2261
+ set_stopped(std::move(rcvr));
2262
+ return noop_coroutine();
2263
+ }
2264
+
2265
+ operation-state-task get_return_object() noexcept {
2266
+ return operation-state-task{
2267
+ coroutine_handle<connect-awaitable-promise>::from_promise(*this)};
2268
+ }
2269
+
2270
+ env_of_t<DR> get_env() const noexcept {
2271
+ return execution::get_env(rcvr);
2272
+ }
2273
+
2274
+ private:
2275
+ DR& rcvr; // exposition only
2276
+ };
2277
+ }
2278
+ ```
2279
+
2280
+ Let *`operation-state-task`* be the following exposition-only class:
2281
+
2282
+ ``` cpp
2283
+ namespace std::execution {
2284
+ struct operation-state-task { // exposition only
2285
+ using operation_state_concept = operation_state_t;
2286
+ using promise_type = connect-awaitable-promise;
2287
+
2288
+ explicit operation-state-task(coroutine_handle<> h) noexcept : coro(h) {}
2289
+ operation-state-task(operation-state-task&&) = delete;
2290
+ ~operation-state-task() { coro.destroy(); }
2291
+
2292
+ void start() & noexcept {
2293
+ coro.resume();
2294
+ }
2295
+
2296
+ private:
2297
+ coroutine_handle<> coro; // exposition only
2298
+ };
2299
+ }
2300
+ ```
2301
+
2302
+ Let `V` name the type
2303
+ `await-result-type<DS, connect-awaitable-promise>`, let `Sigs` name the
2304
+ type
2305
+
2306
+ ``` cpp
2307
+ completion_signatures<
2308
+ SET-VALUE-SIG(V), // see~[exec.snd.concepts]
2309
+ set_error_t(exception_ptr),
2310
+ set_stopped_t()>
2311
+ ```
2312
+
2313
+ and let *`connect-awaitable`* be an exposition-only coroutine defined as
2314
+ follows:
2315
+
2316
+ ``` cpp
2317
+ namespace std::execution {
2318
+ template<class Fun, class... Ts>
2319
+ auto suspend-complete(Fun fun, Ts&&... as) noexcept { // exposition only
2320
+ auto fn = [&, fun]() noexcept { fun(std::forward<Ts>(as)...); };
2321
+
2322
+ struct awaiter {
2323
+ decltype(fn) fn; // exposition only
2324
+
2325
+ static constexpr bool await_ready() noexcept { return false; }
2326
+ void await_suspend(coroutine_handle<>) noexcept { fn(); }
2327
+ [[noreturn]] void await_resume() noexcept { unreachable(); }
2328
+ };
2329
+ return awaiter{fn};
2330
+ }
2331
+
2332
+ operation-state-task connect-awaitable(DS sndr, DR rcvr) requires receiver_of<DR, Sigs> {
2333
+ exception_ptr ep;
2334
+ try {
2335
+ if constexpr (same_as<V, void>) {
2336
+ co_await std::move(sndr);
2337
+ co_await suspend-complete(set_value, std::move(rcvr));
2338
+ } else {
2339
+ co_await suspend-complete(set_value, std::move(rcvr), co_await std::move(sndr));
2340
+ }
2341
+ } catch(...) {
2342
+ ep = current_exception();
2343
+ }
2344
+ co_await suspend-complete(set_error, std::move(rcvr), std::move(ep));
2345
+ }
2346
+ }
2347
+ ```
2348
+
2349
+ The expression `connect(sndr, rcvr)` is expression-equivalent to:
2350
+
2351
+ - `new_sndr.connect(rcvr)` if that expression is well-formed.
2352
+ *Mandates:* The type of the expression above satisfies
2353
+ `operation_state`.
2354
+ - Otherwise, `connect-awaitable(new_sndr, rcvr)`.
2355
+
2356
+ Except that `rcvr` is evaluated only once.
2357
+
2358
+ *Mandates:* The following are `true`:
2359
+
2360
+ - `\texttt{sender_in}<Sndr, env_of_t<Rcvr>>`
2361
+ - `\texttt{receiver_of}<Rcvr, completion_signatures_of_t<Sndr, env_of_t<Rcvr>>>`
2362
+
2363
+ ### Sender factories <a id="exec.factories">[[exec.factories]]</a>
2364
+
2365
+ #### `execution::schedule` <a id="exec.schedule">[[exec.schedule]]</a>
2366
+
2367
+ `schedule` obtains a schedule sender [[exec.async.ops]] from a
2368
+ scheduler.
2369
+
2370
+ The name `schedule` denotes a customization point object. For a
2371
+ subexpression `sch`, the expression `schedule(sch)` is
2372
+ expression-equivalent to `sch.schedule()`.
2373
+
2374
+ *Mandates:* The type of `sch.schedule()` satisfies `sender`.
2375
+
2376
+ If the expression
2377
+
2378
+ ``` cpp
2379
+ get_completion_scheduler<set_value_t>(get_env(sch.schedule())) == sch
2380
+ ```
2381
+
2382
+ is ill-formed or evaluates to `false`, the behavior of calling
2383
+ `schedule(sch)` is undefined.
2384
+
2385
+ #### `execution::just`, `execution::just_error`, `execution::just_stopped` <a id="exec.just">[[exec.just]]</a>
2386
+
2387
+ `just`, `just_error`, and `just_stopped` are sender factories whose
2388
+ asynchronous operations complete synchronously in their start operation
2389
+ with a value completion operation, an error completion operation, or a
2390
+ stopped completion operation, respectively.
2391
+
2392
+ The names `just`, `just_error`, and `just_stopped` denote customization
2393
+ point objects. Let *`just-cpo`* be one of `just`, `just_error`, or
2394
+ `just_stopped`. For a pack of subexpressions `ts`, let `Ts` be the pack
2395
+ of types `decltype((ts))`. The expression `just-cpo(ts...)` is
2396
+ ill-formed if
2397
+
2398
+ - `(movable-value<Ts> &&...)` is `false`, or
2399
+ - *`just-cpo`* is `just_error` and `sizeof...(ts) == 1` is `false`, or
2400
+ - *`just-cpo`* is `just_stopped` and `sizeof...(ts) == 0` is `false`.
2401
+
2402
+ Otherwise, it is expression-equivalent to
2403
+ `make-sender(just-cpo, product-type{ts...})`.
2404
+
2405
+ For `just`, `just_error`, and `just_stopped`, let *`set-cpo`* be
2406
+ `set_value`, `set_error`, and `set_stopped`, respectively. The
2407
+ exposition-only class template *`impls-for`* [[exec.snd.expos]] is
2408
+ specialized for *`just-cpo`* as follows:
2409
+
2410
+ ``` cpp
2411
+ namespace std::execution {
2412
+ template<>
2413
+ struct impls-for<decayed-typeof<just-cpo>> : default-impls {
2414
+ static constexpr auto start =
2415
+ [](auto& state, auto& rcvr) noexcept -> void {
2416
+ auto& [...ts] = state;
2417
+ set-cpo(std::move(rcvr), std::move(ts)...);
2418
+ };
2419
+ };
2420
+ }
2421
+ ```
2422
+
2423
+ #### `execution::read_env` <a id="exec.read.env">[[exec.read.env]]</a>
2424
+
2425
+ `read_env` is a sender factory for a sender whose asynchronous operation
2426
+ completes synchronously in its start operation with a value completion
2427
+ result equal to a value read from the receiver’s associated environment.
2428
+
2429
+ `read_env` is a customization point object. For some query object `q`,
2430
+ the expression `read_env(q)` is expression-equivalent to
2431
+ `make-sender(read_env, q)`.
2432
+
2433
+ The exposition-only class template *`impls-for`* [[exec.snd.expos]] is
2434
+ specialized for `read_env` as follows:
2435
+
2436
+ ``` cpp
2437
+ namespace std::execution {
2438
+ template<>
2439
+ struct impls-for<decayed-typeof<read_env>> : default-impls {
2440
+ static constexpr auto start =
2441
+ [](auto query, auto& rcvr) noexcept -> void {
2442
+ TRY-SET-VALUE(rcvr, query(get_env(rcvr)));
2443
+ };
2444
+ };
2445
+
2446
+ template<class Sndr, class Env>
2447
+ static consteval void check-types();
2448
+ }
2449
+ ```
2450
+
2451
+ ``` cpp
2452
+ template<class Sndr, class Env>
2453
+ static consteval void check-types();
2454
+ ```
2455
+
2456
+ Let `Q` be `decay_t<`*`data-type`*`<Sndr>>`.
2457
+
2458
+ *Throws:* An exception of an unspecified type derived from `exception`
2459
+ if the expression `Q()(env)` is ill-formed or has type `void`, where
2460
+ `env` is an lvalue subexpression whose type is `Env`.
2461
+
2462
+ ### Sender adaptors <a id="exec.adapt">[[exec.adapt]]</a>
2463
+
2464
+ #### General <a id="exec.adapt.general">[[exec.adapt.general]]</a>
2465
+
2466
+ Subclause [[exec.adapt]] specifies a set of sender adaptors.
2467
+
2468
+ The bitwise inclusive operator is overloaded for the purpose of creating
2469
+ sender chains. The adaptors also support function call syntax with
2470
+ equivalent semantics.
2471
+
2472
+ Unless otherwise specified:
2473
+
2474
+ - A sender adaptor is prohibited from causing observable effects, apart
2475
+ from moving and copying its arguments, before the returned sender is
2476
+ connected with a receiver using `connect`, and `start` is called on
2477
+ the resulting operation state.
2478
+ - A parent sender [[exec.async.ops]] with a single child sender `sndr`
2479
+ has an associated attribute object equal to `FWD-ENV(get_env(sndr))`
2480
+ [[exec.fwd.env]].
2481
+ - A parent sender with more than one child sender has an associated
2482
+ attributes object equal to `env<>{}`.
2483
+ - When a parent sender is connected to a receiver `rcvr`, any receiver
2484
+ used to connect a child sender has an associated environment equal to
2485
+ `FWD-ENV(get_env(rcvr))`.
2486
+ - An adaptor whose child senders are all non-dependent
2487
+ [[exec.async.ops]] is itself non-dependent.
2488
+ - These requirements apply to any function that is selected by the
2489
+ implementation of the sender adaptor.
2490
+ - *Recommended practice:* Implementations should use the completion
2491
+ signatures of the adaptors to communicate type errors to users and to
2492
+ propagate any such type errors from child senders.
2493
+
2494
+ If a sender returned from a sender adaptor specified in [[exec.adapt]]
2495
+ is specified to include `set_error_t(Err)` among its set of completion
2496
+ signatures where `decay_t<Err>` denotes the type `exception_ptr`, but
2497
+ the implementation does not potentially evaluate an error completion
2498
+ operation with an `exception_ptr` argument, the implementation is
2499
+ allowed to omit the `exception_ptr` error completion signature from the
2500
+ set.
2501
+
2502
+ #### Closure objects <a id="exec.adapt.obj">[[exec.adapt.obj]]</a>
2503
+
2504
+ A *pipeable sender adaptor closure object* is a function object that
2505
+ accepts one or more `sender` arguments and returns a `sender`. For a
2506
+ pipeable sender adaptor closure object `c` and an expression `sndr` such
2507
+ that `decltype((sndr))` models `sender`, the following expressions are
2508
+ equivalent and yield a `sender`:
2509
+
2510
+ ``` cpp
2511
+ c(sndr)
2512
+ sndr | c
2513
+ ```
2514
+
2515
+ Given an additional pipeable sender adaptor closure object `d`, the
2516
+ expression `c | d` produces another pipeable sender adaptor closure
2517
+ object `e`:
2518
+
2519
+ `e` is a perfect forwarding call wrapper [[func.require]] with the
2520
+ following properties:
2521
+
2522
+ - Its target object is an object `d2` of type `decltype(auto(d))`
2523
+ direct-non-list-initialized with `d`.
2524
+ - It has one bound argument entity, an object `c2` of type
2525
+ `decltype(auto(c))` direct-non-list-initialized with `c`.
2526
+ - Its call pattern is `d2(c2(arg))`, where arg is the argument used in a
2527
+ function call expression of `e`.
2528
+
2529
+ The expression `c | d` is well-formed if and only if the initializations
2530
+ of the state entities [[func.def]] of `e` are all well-formed.
2531
+
2532
+ An object `t` of type `T` is a pipeable sender adaptor closure object if
2533
+ `T` models `derived_from<sender_adaptor_closure<T>>`, `T` has no other
2534
+ base classes of type `sender_adaptor_closure<U>` for any other type `U`,
2535
+ and `T` does not satisfy `sender`.
2536
+
2537
+ The template parameter `D` for `sender_adaptor_closure` can be an
2538
+ incomplete type. Before any expression of type cv `D` appears as an
2539
+ operand to the `|` operator, `D` shall be complete and model
2540
+ `derived_from<sender_adaptor_closure<D>>`. The behavior of an expression
2541
+ involving an object of type cv `D` as an operand to the `|` operator is
2542
+ undefined if overload resolution selects a program-defined `operator|`
2543
+ function.
2544
+
2545
+ A *pipeable sender adaptor object* is a customization point object that
2546
+ accepts a `sender` as its first argument and returns a `sender`. If a
2547
+ pipeable sender adaptor object accepts only one argument, then it is a
2548
+ pipeable sender adaptor closure object.
2549
+
2550
+ If a pipeable sender adaptor object adaptor accepts more than one
2551
+ argument, then let `sndr` be an expression such that `decltype((sndr))`
2552
+ models `sender`, let `args...` be arguments such that
2553
+ `adaptor(sndr, args...)` is a well-formed expression as specified below,
2554
+ and let `BoundArgs` be a pack that denotes `decltype(auto(args))...`.
2555
+ The expression `adaptor(args...)` produces a pipeable sender adaptor
2556
+ closure object `f` that is a perfect forwarding call wrapper with the
2557
+ following properties:
2558
+
2559
+ - Its target object is a copy of adaptor.
2560
+ - Its bound argument entities `bound_args` consist of objects of types
2561
+ `BoundArgs...` direct-non-list-initialized with
2562
+ `std::forward<decltype((args))>(args)...`, respectively.
2563
+ - Its call pattern is `adaptor(rcvr, bound_args...)`, where `rcvr` is
2564
+ the argument used in a function call expression of `f`.
2565
+
2566
+ The expression `adaptor(args...)` is well-formed if and only if the
2567
+ initializations of the bound argument entities of the result, as
2568
+ specified above, are all well-formed.
2569
+
2570
+ #### `execution::write_env` <a id="exec.write.env">[[exec.write.env]]</a>
2571
+
2572
+ `write_env` is a sender adaptor that accepts a sender and a queryable
2573
+ object, and that returns a sender that, when connected with a receiver
2574
+ `rcvr`, connects the adapted sender with a receiver whose execution
2575
+ environment is the result of joining the `queryable` object to the
2576
+ result of `get_env(rcvr)`.
2577
+
2578
+ `write_env` is a customization point object. For some subexpressions
2579
+ `sndr` and `env`, if `decltype((sndr))` does not satisfy `sender` or if
2580
+ `decltype((env))` does not satisfy `queryable`, the expression
2581
+ `write_env(sndr, env)` is ill-formed. Otherwise, it is
2582
+ expression-equivalent to `make-sender(write_env, env, sndr)`.
2583
+
2584
+ Let *`write-env-t`* denote the type `decltype(auto(write_env))`. The
2585
+ exposition-only class template *`impls-for`* [[exec.snd.expos]] is
2586
+ specialized for *`write-env-t`* as follows:
2587
+
2588
+ ``` cpp
2589
+ template<>
2590
+ struct impls-for<write-env-t> : default-impls {
2591
+ static constexpr auto join-env(const auto& state, const auto& env) noexcept {
2592
+ return see below;
2593
+ }
2594
+
2595
+ static constexpr auto get-env =
2596
+ [](auto, const auto& state, const auto& rcvr) noexcept {
2597
+ return join-env(state, FWD-ENV(get_env(rcvr)));
2598
+ };
2599
+
2600
+ template<class Sndr, class... Env>
2601
+ static consteval void check-types();
2602
+ };
2603
+ ```
2604
+
2605
+ Invocation of `impls-for<write-env-t>::join-env` returns an object `e`
2606
+ such that
2607
+
2608
+ - `decltype(e)` models `queryable` and
2609
+ - given a query object `q`, the expression `e.query(q)` is
2610
+ expression-equivalent to `state.query(q)` if that expression is valid,
2611
+ otherwise, `e.query(q)` is expression-equivalent to `env.query(q)`.
2612
+
2613
+ For a type `Sndr` and a pack of types `Env`, let `State` be
2614
+ `data-type<Sndr>` and let `JoinEnv` be the pack
2615
+ `decltype(join-env(declval<State>(), FWD-ENV(declval<Env>())))`. Then
2616
+ `impls-for<write-env-{t}>::check-types<Sndr, Env...>()` is
2617
+ expression-equivalent to
2618
+ `get_completion_signatures<child-{type}<Sndr>, JoinEnv...>()`.
2619
+
2620
+ #### `execution::unstoppable` <a id="exec.unstoppable">[[exec.unstoppable]]</a>
2621
+
2622
+ `unstoppable` is a sender adaptor that connects its inner sender with a
2623
+ receiver that has the execution environment of the outer receiver but
2624
+ with an object of type `never_stop_token` as the result of the
2625
+ `get_stop_token query`.
2626
+
2627
+ For a subexpression `sndr`, `unstoppable(sndr)` is expression-equivalent
2628
+ to `write_env(sndr, prop(get_stop_token, never_stop_token{}))`.
2629
+
2630
+ #### `execution::starts_on` <a id="exec.starts.on">[[exec.starts.on]]</a>
2631
+
2632
+ `starts_on` adapts an input sender into a sender that will start on an
2633
+ execution agent belonging to a particular scheduler’s associated
2634
+ execution resource.
2635
+
2636
+ The name `starts_on` denotes a customization point object. For
2637
+ subexpressions `sch` and `sndr`, if `decltype(( sch))` does not satisfy
2638
+ `scheduler`, or `decltype((sndr))` does not satisfy `sender`,
2639
+ `starts_on(sch, sndr)` is ill-formed.
2640
+
2641
+ Otherwise, the expression `starts_on(sch, sndr)` is
2642
+ expression-equivalent to:
2643
+
2644
+ ``` cpp
2645
+ transform_sender(
2646
+ query-with-default(get_domain, sch, default_domain()),
2647
+ make-sender(starts_on, sch, sndr))
2648
+ ```
2649
+
2650
+ except that `sch` is evaluated only once.
2651
+
2652
+ Let `out_sndr` and `env` be subexpressions such that `OutSndr` is
2653
+ `decltype((out_sndr))`. If `sender-for<OutSndr, starts_on_t>` is
2654
+ `false`, then the expressions `starts_on.transform_env(out_sndr, env)`
2655
+ and `starts_on.transform_sender(out_sndr, env)` are ill-formed;
2656
+ otherwise
2657
+
2658
+ - `starts_on.transform_env(out_sndr, env)` is equivalent to:
2659
+ ``` cpp
2660
+ auto&& [_, sch, _] = out_sndr;
2661
+ return JOIN-ENV(SCHED-ENV(sch), FWD-ENV(env));
2662
+ ```
2663
+ - `starts_on.transform_sender(out_sndr, env)` is equivalent to:
2664
+ ``` cpp
2665
+ auto&& [_, sch, sndr] = out_sndr;
2666
+ return let_value(
2667
+ schedule(sch),
2668
+ [sndr = std::forward_like<OutSndr>(sndr)]() mutable
2669
+ noexcept(is_nothrow_move_constructible_v<decay_t<OutSndr>>) {
2670
+ return std::move(sndr);
2671
+ });
2672
+ ```
2673
+
2674
+ Let `out_sndr` be a subexpression denoting a sender returned from
2675
+ `starts_on(sch, sndr)` or one equal to such, and let `OutSndr` be the
2676
+ type `decltype((out_sndr))`. Let `out_rcvr` be a subexpression denoting
2677
+ a receiver that has an environment of type `Env` such that
2678
+ `sender_in<OutSndr, Env>` is `true`. Let `op` be an lvalue referring to
2679
+ the operation state that results from connecting `out_sndr` with
2680
+ `out_rcvr`. Calling `start(op)` shall start `sndr` on an execution agent
2681
+ of the associated execution resource of `sch`. If scheduling onto `sch`
2682
+ fails, an error completion on `out_rcvr` shall be executed on an
2683
+ unspecified execution agent.
2684
+
2685
+ #### `execution::continues_on` <a id="exec.continues.on">[[exec.continues.on]]</a>
2686
+
2687
+ `continues_on` adapts a sender into one that completes on the specified
2688
+ scheduler.
2689
+
2690
+ The name `continues_on` denotes a pipeable sender adaptor object. For
2691
+ subexpressions `sch` and `sndr`, if `decltype((sch))` does not satisfy
2692
+ `scheduler`, or `decltype((sndr))` does not satisfy `sender`,
2693
+ `continues_on(sndr, sch)` is ill-formed.
2694
+
2695
+ Otherwise, the expression `continues_on(sndr, sch)` is
2696
+ expression-equivalent to:
2697
+
2698
+ ``` cpp
2699
+ transform_sender(get-domain-early(sndr), make-sender(continues_on, sch, sndr))
2700
+ ```
2701
+
2702
+ except that `sndr` is evaluated only once.
2703
+
2704
+ The exposition-only class template *`impls-for`* [[exec.snd.expos]] is
2705
+ specialized for `continues_on_t` as follows:
2706
+
2707
+ ``` cpp
2708
+ namespace std::execution {
2709
+ template<>
2710
+ struct impls-for<continues_on_t> : default-impls {
2711
+ static constexpr auto get-attrs =
2712
+ [](const auto& data, const auto& child) noexcept -> decltype(auto) {
2713
+ return JOIN-ENV(SCHED-ATTRS(data), FWD-ENV(get_env(child)));
2714
+ };
2715
+ };
2716
+ }
2717
+ ```
2718
+
2719
+ Let `sndr` and `env` be subexpressions such that `Sndr` is
2720
+ `decltype((sndr))`. If `sender-for<Sndr, continues_on_t>` is `false`,
2721
+ then the expression `continues_on.transform_sender(sndr, env)` is
2722
+ ill-formed; otherwise, it is equal to:
2723
+
2724
+ ``` cpp
2725
+ auto [_, data, child] = sndr;
2726
+ return schedule_from(std::move(data), std::move(child));
2727
+ ```
2728
+
2729
+ [*Note 1*: This causes the `continues_on(sndr, sch)` sender to become
2730
+ `schedule_from(sch, sndr)` when it is connected with a receiver whose
2731
+ execution domain does not customize `continues_on`. — *end note*]
2732
+
2733
+ Let `out_sndr` be a subexpression denoting a sender returned from
2734
+ `continues_on(sndr, sch)` or one equal to such, and let `OutSndr` be the
2735
+ type `decltype((out_sndr))`. Let `out_rcvr` be a subexpression denoting
2736
+ a receiver that has an environment of type `Env` such that
2737
+ `sender_in<OutSndr, Env>` is `true`. Let `op` be an lvalue referring to
2738
+ the operation state that results from connecting `out_sndr` with
2739
+ `out_rcvr`. Calling `start(op)` shall start `sndr` on the current
2740
+ execution agent and execute completion operations on `out_rcvr` on an
2741
+ execution agent of the execution resource associated with `sch`. If
2742
+ scheduling onto `sch` fails, an error completion on `out_rcvr` shall be
2743
+ executed on an unspecified execution agent.
2744
+
2745
+ #### `execution::schedule_from` <a id="exec.schedule.from">[[exec.schedule.from]]</a>
2746
+
2747
+ `schedule_from` schedules work dependent on the completion of a sender
2748
+ onto a scheduler’s associated execution resource.
2749
+
2750
+ [*Note 1*: `schedule_from` is not meant to be used in user code; it is
2751
+ used in the implementation of `continues_on`. — *end note*]
2752
+
2753
+ The name `schedule_from` denotes a customization point object. For some
2754
+ subexpressions `sch` and `sndr`, let `Sch` be `decltype((sch))` and
2755
+ `Sndr` be `decltype((sndr))`. If `Sch` does not satisfy `scheduler`, or
2756
+ `Sndr` does not satisfy `sender`, `schedule_from(sch, sndr)` is
2757
+ ill-formed.
2758
+
2759
+ Otherwise, the expression `schedule_from(sch, sndr)` is
2760
+ expression-equivalent to:
2761
+
2762
+ ``` cpp
2763
+ transform_sender(
2764
+ query-with-default(get_domain, sch, default_domain()),
2765
+ make-sender(schedule_from, sch, sndr))
2766
+ ```
2767
+
2768
+ except that `sch` is evaluated only once.
2769
+
2770
+ The exposition-only class template *`impls-for`* [[exec.snd.expos]] is
2771
+ specialized for `schedule_from_t` as follows:
2772
+
2773
+ ``` cpp
2774
+ namespace std::execution {
2775
+ template<>
2776
+ struct impls-for<schedule_from_t> : default-impls {
2777
+ static constexpr auto get-attrs = see below;
2778
+ static constexpr auto get-state = see below;
2779
+ static constexpr auto complete = see below;
2780
+
2781
+ template<class Sndr, class... Env>
2782
+ static consteval void check-types();
2783
+ };
2784
+ }
2785
+ ```
2786
+
2787
+ The member `impls-for<schedule_from_t>::get-attrs` is initialized with a
2788
+ callable object equivalent to the following lambda:
2789
+
2790
+ ``` cpp
2791
+ [](const auto& data, const auto& child) noexcept -> decltype(auto) {
2792
+ return JOIN-ENV(SCHED-ATTRS(data), FWD-ENV(get_env(child)));
2793
+ }
2794
+ ```
2795
+
2796
+ The member `impls-for<schedule_from_t>::get-state` is initialized with a
2797
+ callable object equivalent to the following lambda:
2798
+
2799
+ ``` cpp
2800
+ []<class Sndr, class Rcvr>(Sndr&& sndr, Rcvr& rcvr) noexcept(see below)
2801
+ requires sender_in<child-type<Sndr>, FWD-ENV-T(env_of_t<Rcvr>)> {
2802
+
2803
+ auto& [_, sch, child] = sndr;
2804
+
2805
+ using sched_t = decltype(auto(sch));
2806
+ using variant_t = see below;
2807
+ using receiver_t = see below;
2808
+ using operation_t = connect_result_t<schedule_result_t<sched_t>, receiver_t>;
2809
+ constexpr bool nothrow = noexcept(connect(schedule(sch), receiver_t{nullptr}));
2810
+
2811
+ struct state-type {
2812
+ Rcvr& rcvr; // exposition only
2813
+ variant_t async-result; // exposition only
2814
+ operation_t op-state; // exposition only
2815
+
2816
+ explicit state-type(sched_t sch, Rcvr& rcvr) noexcept(nothrow)
2817
+ : rcvr(rcvr), op-state(connect(schedule(sch), receiver_t{this})) {}
2818
+ };
2819
+
2820
+ return state-type{sch, rcvr};
2821
+ }
2822
+ ```
2823
+
2824
+ ``` cpp
2825
+ template<class Sndr, class... Env>
2826
+ static consteval void check-types();
2827
+ ```
2828
+
2829
+ *Effects:* Equivalent to:
2830
+
2831
+ ``` cpp
2832
+ get_completion_signatures<schedule_result_t<data-type<Sndr>>, FWD-ENV-T(Env)...>();
2833
+ auto cs = get_completion_signatures<child-type<Sndr>, FWD-ENV-T(Env)...>();
2834
+ decay-copyable-result-datums(cs); // see [exec.snd.expos]
2835
+ ```
2836
+
2837
+ Objects of the local class *`state-type`* can be used to initialize a
2838
+ structured binding.
2839
+
2840
+ Let `Sigs` be a pack of the arguments to the `completion_signatures`
2841
+ specialization named by
2842
+ `completion_signatures_of_t<child-type<Sndr>, FWD-ENV-T(env_of_t<Rcvr>)>`.
2843
+ Let *`as-tuple`* be an alias template such that `as-tuple<Tag(Args...)>`
2844
+ denotes the type `decayed-tuple<Tag, Args...>`, and let
2845
+ *`is-nothrow-decay-copy-sig`* be a variable template such that
2846
+ `auto(is-nothrow-decay-copy-sig<Tag(Args...{})>)` is a constant
2847
+ expression of type `bool` and equal to
2848
+ `(is_nothrow_constructible_v<decay_t<Args>, Args> && ...)`. Let
2849
+ *`error-completion`* be a pack consisting of the type
2850
+ `set_error_t(exception_ptr)` if
2851
+ `(is-nothrow-decay-copy-sig<Sigs> &&...)` is `false`, and an empty pack
2852
+ otherwise. Then `variant_t` denotes the type
2853
+ `variant<monostate, as-tuple<Sigs>..., error-completion...>`, except
2854
+ with duplicate types removed.
2855
+
2856
+ `receiver_t` is an alias for the following exposition-only class:
2857
+
2858
+ ``` cpp
2859
+ namespace std::execution {
2860
+ struct receiver-type {
2861
+ using receiver_concept = receiver_t;
2862
+ state-type* state; // exposition only
2863
+
2864
+ void set_value() && noexcept {
2865
+ visit(
2866
+ [this]<class Tuple>(Tuple& result) noexcept -> void {
2867
+ if constexpr (!same_as<monostate, Tuple>) {
2868
+ auto& [tag, ...args] = result;
2869
+ tag(std::move(state->rcvr), std::move(args)...);
2870
+ }
2871
+ },
2872
+ state->async-result);
2873
+ }
2874
+
2875
+ template<class Error>
2876
+ void set_error(Error&& err) && noexcept {
2877
+ execution::set_error(std::move(state->rcvr), std::forward<Error>(err));
2878
+ }
2879
+
2880
+ void set_stopped() && noexcept {
2881
+ execution::set_stopped(std::move(state->rcvr));
2882
+ }
2883
+
2884
+ decltype(auto) get_env() const noexcept {
2885
+ return FWD-ENV(execution::get_env(state->rcvr));
2886
+ }
2887
+ };
2888
+ }
2889
+ ```
2890
+
2891
+ The expression in the `noexcept` clause of the lambda is `true` if the
2892
+ construction of the returned *`state-type`* object is not potentially
2893
+ throwing; otherwise, `false`.
2894
+
2895
+ The member `impls-for<schedule_from_t>::complete` is initialized with a
2896
+ callable object equivalent to the following lambda:
2897
+
2898
+ ``` cpp
2899
+ []<class Tag, class... Args>(auto, auto& state, auto& rcvr, Tag, Args&&... args) noexcept
2900
+ -> void {
2901
+ using result_t = decayed-tuple<Tag, Args...>;
2902
+ constexpr bool nothrow = (is_nothrow_constructible_v<decay_t<Args>, Args> && ...);
2903
+
2904
+ try {
2905
+ state.async-result.template emplace<result_t>(Tag(), std::forward<Args>(args)...);
2906
+ } catch (...) {
2907
+ if constexpr (!nothrow)
2908
+ state.async-result.template emplace<tuple<set_error_t,
2909
+ exception_ptr>>(set_error, current_exception());
2910
+ }
2911
+ start(state.op-state);
2912
+ };
2913
+ ```
2914
+
2915
+ Let `out_sndr` be a subexpression denoting a sender returned from
2916
+ `schedule_from(sch, sndr)` or one equal to such, and let `OutSndr` be
2917
+ the type `decltype((out_sndr))`. Let `out_rcvr` be a subexpression
2918
+ denoting a receiver that has an environment of type `Env` such that
2919
+ `sender_in<OutSndr, Env>` is `true`. Let `op` be an lvalue referring to
2920
+ the operation state that results from connecting `out_sndr` with
2921
+ `out_rcvr`. Calling `start(op)` shall start `sndr` on the current
2922
+ execution agent and execute completion operations on `out_rcvr` on an
2923
+ execution agent of the execution resource associated with `sch`. If
2924
+ scheduling onto `sch` fails, an error completion on `out_rcvr` shall be
2925
+ executed on an unspecified execution agent.
2926
+
2927
+ #### `execution::on` <a id="exec.on">[[exec.on]]</a>
2928
+
2929
+ The `on` sender adaptor has two forms:
2930
+
2931
+ - `on(sch, sndr)`, which starts a sender `sndr` on an execution agent
2932
+ belonging to a scheduler `sch`’s associated execution resource and
2933
+ that, upon `sndr`’s completion, transfers execution back to the
2934
+ execution resource on which the `on` sender was started.
2935
+ - `on(sndr, sch, closure)`, which upon completion of a sender `sndr`,
2936
+ transfers execution to an execution agent belonging to a scheduler
2937
+ `sch`’s associated execution resource, then executes a sender adaptor
2938
+ closure `closure` with the async results of the sender, and that then
2939
+ transfers execution back to the execution resource on which `sndr`
2940
+ completed.
2941
+
2942
+ The name `on` denotes a pipeable sender adaptor object. For
2943
+ subexpressions `sch` and `sndr`, `on(sch, sndr)` is ill-formed if any of
2944
+ the following is `true`:
2945
+
2946
+ - `decltype((sch))` does not satisfy `scheduler`, or
2947
+ - `decltype((sndr))` does not satisfy `sender` and `sndr` is not a
2948
+ pipeable sender adaptor closure object [[exec.adapt.obj]], or
2949
+ - `decltype((sndr))` satisfies `sender` and `sndr `is also a pipeable
2950
+ sender adaptor closure object.
2951
+
2952
+ Otherwise, if `decltype((sndr))` satisfies `sender`, the expression
2953
+ `on(sch, sndr)` is expression-equivalent to:
2954
+
2955
+ ``` cpp
2956
+ transform_sender(
2957
+ query-with-default(get_domain, sch, default_domain()),
2958
+ make-sender(on, sch, sndr))
2959
+ ```
2960
+
2961
+ except that `sch` is evaluated only once.
2962
+
2963
+ For subexpressions `sndr`, `sch`, and `closure`, if
2964
+
2965
+ - `decltype((sch))` does not satisfy `scheduler`, or
2966
+ - `decltype((sndr))` does not satisfy `sender`, or
2967
+ - `closure` is not a pipeable sender adaptor closure object
2968
+ [[exec.adapt.obj]],
2969
+
2970
+ the expression `on(sndr, sch, closure)` is ill-formed; otherwise, it is
2971
+ expression-equivalent to:
2972
+
2973
+ ``` cpp
2974
+ transform_sender(
2975
+ get-domain-early(sndr),
2976
+ make-sender(on, product-type{sch, closure}, sndr))
2977
+ ```
2978
+
2979
+ except that `sndr` is evaluated only once.
2980
+
2981
+ Let `out_sndr` and `env` be subexpressions, let `OutSndr` be
2982
+ `decltype((out_sndr))`, and let `Env` be `decltype((env))`. If
2983
+ `sender-for<OutSndr, on_t>` is `false`, then the expressions
2984
+ `on.transform_env(out_sndr, env)` and
2985
+ `on.transform_sender(out_sndr, env)` are ill-formed.
2986
+
2987
+ Otherwise: Let *`not-a-scheduler`* be an unspecified empty class type.
2988
+
2989
+ The expression `on.transform_env(out_sndr, env)` has effects equivalent
2990
+ to:
2991
+
2992
+ ``` cpp
2993
+ auto&& [_, data, _] = out_sndr;
2994
+ if constexpr (scheduler<decltype(data)>) {
2995
+ return JOIN-ENV(SCHED-ENV(std::forward_like<OutSndr>(data)), FWD-ENV(std::forward<Env>(env)));
2996
+ } else {
2997
+ return std::forward<Env>(env);
2998
+ }
2999
+ ```
3000
+
3001
+ The expression `on.transform_sender(out_sndr, env)` has effects
3002
+ equivalent to:
3003
+
3004
+ ``` cpp
3005
+ auto&& [_, data, child] = out_sndr;
3006
+ if constexpr (scheduler<decltype(data)>) {
3007
+ auto orig_sch =
3008
+ query-with-default(get_scheduler, env, not-a-scheduler());
3009
+
3010
+ if constexpr (same_as<decltype(orig_sch), not-a-scheduler>) {
3011
+ return not-a-sender{};
3012
+ } else {
3013
+ return continues_on(
3014
+ starts_on(std::forward_like<OutSndr>(data), std::forward_like<OutSndr>(child)),
3015
+ std::move(orig_sch));
3016
+ }
3017
+ } else {
3018
+ auto& [sch, closure] = data;
3019
+ auto orig_sch = query-with-default(
3020
+ get_completion_scheduler<set_value_t>,
3021
+ get_env(child),
3022
+ query-with-default(get_scheduler, env, not-a-scheduler()));
3023
+
3024
+ if constexpr (same_as<decltype(orig_sch), not-a-scheduler>) {
3025
+ return not-a-sender{};
3026
+ } else {
3027
+ return write_env(
3028
+ continues_on(
3029
+ std::forward_like<OutSndr>(closure)(
3030
+ continues_on(
3031
+ write_env(std::forward_like<OutSndr>(child), SCHED-ENV(orig_sch)),
3032
+ sch)),
3033
+ orig_sch),
3034
+ SCHED-ENV(sch));
3035
+ }
3036
+ }
3037
+ ```
3038
+
3039
+ Let `out_sndr` be a subexpression denoting a sender returned from
3040
+ `on(sch, sndr)` or one equal to such, and let `OutSndr` be the type
3041
+ `decltype((out_sndr))`. Let `out_rcvr` be a subexpression denoting a
3042
+ receiver that has an environment of type `Env` such that
3043
+ `sender_in<OutSndr, Env>` is `true`. Let `op` be an lvalue referring to
3044
+ the operation state that results from connecting `out_sndr` with
3045
+ `out_rcvr`. Calling `start(op)` shall
3046
+
3047
+ - remember the current scheduler, `get_scheduler(get_env(rcvr))`;
3048
+ - start `sndr` on an execution agent belonging to `sch`’s associated
3049
+ execution resource;
3050
+ - upon `sndr`’s completion, transfer execution back to the execution
3051
+ resource associated with the scheduler remembered in step 1; and
3052
+ - forward `sndr`’s async result to `out_rcvr`.
3053
+
3054
+ If any scheduling operation fails, an error completion on `out_rcvr`
3055
+ shall be executed on an unspecified execution agent.
3056
+
3057
+ Let `out_sndr` be a subexpression denoting a sender returned from
3058
+ `on(sndr, sch, closure)` or one equal to such, and let `OutSndr` be the
3059
+ type `decltype((out_sndr))`. Let `out_rcvr` be a subexpression denoting
3060
+ a receiver that has an environment of type `Env` such that
3061
+ `sender_in<OutSndr, Env>` is `true`. Let `op` be an lvalue referring to
3062
+ the operation state that results from connecting `out_sndr` with
3063
+ `out_rcvr`. Calling `start(op)` shall
3064
+
3065
+ - remember the current scheduler, which is the first of the following
3066
+ expressions that is well-formed:
3067
+ - `get_completion_scheduler<set_value_t>(get_env(sndr))`
3068
+ - `get_scheduler(get_env(rcvr))`;
3069
+ - start `sndr` on the current execution agent;
3070
+ - upon `sndr`’s completion, transfer execution to an agent owned by
3071
+ `sch`’s associated execution resource;
3072
+ - forward `sndr`’s async result as if by connecting and starting a
3073
+ sender `closure(S)`, where `S` is a sender that completes
3074
+ synchronously with `sndr`’s async result; and
3075
+ - upon completion of the operation started in the previous step,
3076
+ transfer execution back to the execution resource associated with the
3077
+ scheduler remembered in step 1 and forward the operation’s async
3078
+ result to `out_rcvr`.
3079
+
3080
+ If any scheduling operation fails, an error completion on `out_rcvr`
3081
+ shall be executed on an unspecified execution agent.
3082
+
3083
+ #### `execution::then`, `execution::upon_error`, `execution::upon_stopped` <a id="exec.then">[[exec.then]]</a>
3084
+
3085
+ `then` attaches an invocable as a continuation for an input sender’s
3086
+ value completion operation. `upon_error` and `upon_stopped` do the same
3087
+ for the error and stopped completion operations, respectively, sending
3088
+ the result of the invocable as a value completion.
3089
+
3090
+ The names `then`, `upon_error`, and `upon_stopped` denote pipeable
3091
+ sender adaptor objects. Let the expression *`then-cpo`* be one of
3092
+ `then`, `upon_error`, or `upon_stopped`. For subexpressions `sndr` and
3093
+ `f`, if `decltype((sndr))` does not satisfy `sender`, or `decltype((f))`
3094
+ does not satisfy `movable-value`, `then-cpo(sndr, f) `is ill-formed.
3095
+
3096
+ Otherwise, the expression `then-cpo(sndr, f)` is expression-equivalent
3097
+ to:
3098
+
3099
+ ``` cpp
3100
+ transform_sender(get-domain-early(sndr), make-sender(then-cpo, f, sndr))
3101
+ ```
3102
+
3103
+ except that `sndr` is evaluated only once.
3104
+
3105
+ For `then`, `upon_error`, and `upon_stopped`, let *`set-cpo`* be
3106
+ `set_value`, `set_error`, and `set_stopped`, respectively. The
3107
+ exposition-only class template *`impls-for`* [[exec.snd.expos]] is
3108
+ specialized for *`then-cpo`* as follows:
3109
+
3110
+ ``` cpp
3111
+ namespace std::execution {
3112
+ template<>
3113
+ struct impls-for<decayed-typeof<then-cpo>> : default-impls {
3114
+ static constexpr auto complete =
3115
+ []<class Tag, class... Args>
3116
+ (auto, auto& fn, auto& rcvr, Tag, Args&&... args) noexcept -> void {
3117
+ if constexpr (same_as<Tag, decayed-typeof<set-cpo>>) {
3118
+ TRY-SET-VALUE(rcvr,
3119
+ invoke(std::move(fn), std::forward<Args>(args)...));
3120
+ } else {
3121
+ Tag()(std::move(rcvr), std::forward<Args>(args)...);
3122
+ }
3123
+ };
3124
+
3125
+ template<class Sndr, class... Env>
3126
+ static consteval void check-types();
3127
+ };
3128
+ }
3129
+ ```
3130
+
3131
+ ``` cpp
3132
+ template<class Sndr, class... Env>
3133
+ static consteval void check-types();
3134
+ ```
3135
+
3136
+ *Effects:* Equivalent to:
3137
+
3138
+ ``` cpp
3139
+ auto cs = get_completion_signatures<child-type<Sndr>, FWD-ENV-T(Env)...>();
3140
+ auto fn = []<class... Ts>(set_value_t(*)(Ts...)) {
3141
+ if constexpr (!invocable<remove_cvref_t<data-type<Sndr>>, Ts...>)
3142
+ throw unspecified-exception();
3143
+ };
3144
+ cs.for-each(overload-set{fn, [](auto){}});
3145
+ ```
3146
+
3147
+ where *`unspecified-exception`* is a type derived from `exception`.
3148
+
3149
+ The expression `then-cpo(sndr, f)` has undefined behavior unless it
3150
+ returns a sender `out_sndr` that
3151
+
3152
+ - invokes `f` or a copy of such with the value, error, or stopped result
3153
+ datums of `sndr` for `then`, `upon_error`, and `upon_stopped`,
3154
+ respectively, using the result value of `f` as `out_sndr`’s value
3155
+ completion, and
3156
+ - forwards all other completion operations unchanged.
3157
+
3158
+ #### `execution::let_value`, `execution::let_error`, `execution::let_stopped` <a id="exec.let">[[exec.let]]</a>
3159
+
3160
+ `let_value`, `let_error`, and `let_stopped` transform a sender’s value,
3161
+ error, and stopped completions, respectively, into a new child
3162
+ asynchronous operation by passing the sender’s result datums to a
3163
+ user-specified callable, which returns a new sender that is connected
3164
+ and started.
3165
+
3166
+ For `let_value`, `let_error`, and `let_stopped`, let *`set-cpo`* be
3167
+ `set_value`, `set_error`, and `set_stopped`, respectively. Let the
3168
+ expression *`let-cpo`* be one of `let_value`, `let_error`, or
3169
+ `let_stopped`. For a subexpression `sndr`, let `let-env(sndr)` be
3170
+ expression-equivalent to the first well-formed expression below:
3171
+
3172
+ - `\exposid{SCHED-ENV}(get_completion_scheduler<\exposid{decayed-typeof}<\exposid{set-cpo}>>(get_env(sndr)))`
3173
+ - `\exposid{MAKE-ENV}(get_domain, get_domain(get_env(sndr)))`
3174
+ - `(void(sndr), env<>{})`
3175
+
3176
+ The names `let_value`, `let_error`, and `let_stopped` denote pipeable
3177
+ sender adaptor objects. For subexpressions `sndr` and `f`, let `F` be
3178
+ the decayed type of `f`. If `decltype((sndr))` does not satisfy `sender`
3179
+ or if `decltype((f))` does not satisfy `movable-value`, the expression
3180
+ `let-cpo(sndr, f)` is ill-formed. If `F` does not satisfy `invocable`,
3181
+ the expression `let_stopped(sndr, f)` is ill-formed.
3182
+
3183
+ Otherwise, the expression `let-cpo(sndr, f)` is expression-equivalent
3184
+ to:
3185
+
3186
+ ``` cpp
3187
+ transform_sender(get-domain-early(sndr), make-sender(let-cpo, f, sndr))
3188
+ ```
3189
+
3190
+ except that `sndr` is evaluated only once.
3191
+
3192
+ The exposition-only class template *`impls-for`* [[exec.snd.expos]] is
3193
+ specialized for *`let-cpo`* as follows:
3194
+
3195
+ ``` cpp
3196
+ namespace std::execution {
3197
+ template<class State, class Rcvr, class... Args>
3198
+ void let-bind(State& state, Rcvr& rcvr, Args&&... args); // exposition only
3199
+
3200
+ template<>
3201
+ struct impls-for<decayed-typeof<let-cpo>> : default-impls {
3202
+ static constexpr auto get-state = see below;
3203
+ static constexpr auto complete = see below;
3204
+
3205
+ template<class Sndr, class... Env>
3206
+ static consteval void check-types();
3207
+ };
3208
+ }
3209
+ ```
3210
+
3211
+ Let *`receiver2`* denote the following exposition-only class template:
3212
+
3213
+ ``` cpp
3214
+ namespace std::execution {
3215
+ template<class Rcvr, class Env>
3216
+ struct receiver2 {
3217
+ using receiver_concept = receiver_t;
3218
+
3219
+ template<class... Args>
3220
+ void set_value(Args&&... args) && noexcept {
3221
+ execution::set_value(std::move(rcvr), std::forward<Args>(args)...);
3222
+ }
3223
+
3224
+ template<class Error>
3225
+ void set_error(Error&& err) && noexcept {
3226
+ execution::set_error(std::move(rcvr), std::forward<Error>(err));
3227
+ }
3228
+
3229
+ void set_stopped() && noexcept {
3230
+ execution::set_stopped(std::move(rcvr));
3231
+ }
3232
+
3233
+ decltype(auto) get_env() const noexcept {
3234
+ return see below;
3235
+ }
3236
+
3237
+ Rcvr& rcvr; // exposition only
3238
+ Env env; // exposition only
3239
+ };
3240
+ }
3241
+ ```
3242
+
3243
+ Invocation of the function `receiver2::get_env` returns an object `e`
3244
+ such that
3245
+
3246
+ - `decltype(e)` models `queryable` and
3247
+ - given a query object `q`, the expression `e.query(q)` is
3248
+ expression-equivalent to `env.query(q)` if that expression is valid;
3249
+ otherwise, if the type of `q` satisfies `forwarding-query`,
3250
+ `e.query(q)` is expression-equivalent to `get_env(rcvr).query(q)`;
3251
+ otherwise, `e.query(q)` is ill-formed.
3252
+
3253
+ ``` cpp
3254
+ template<class Sndr, class... Env>
3255
+ static consteval void check-types();
3256
+ ```
3257
+
3258
+ *Effects:* Equivalent to:
3259
+
3260
+ ``` cpp
3261
+ using LetFn = remove_cvref_t<data-type<Sndr>>;
3262
+ auto cs = get_completion_signatures<child-type<Sndr>, FWD-ENV-T(Env)...>();
3263
+ auto fn = []<class... Ts>(decayed-typeof<set-cpo>(*)(Ts...)) {
3264
+ if constexpr (!is-valid-let-sender) // see below
3265
+ throw unspecified-exception();
3266
+ };
3267
+ cs.for-each(overload-set(fn, [](auto){}));
3268
+ ```
3269
+
3270
+ where *`unspecified-exception`* is a type derived from `exception`, and
3271
+ where *`is-valid-let-sender`* is `true` if and only if all of the
3272
+ following are `true`:
3273
+
3274
+ - `(constructible_from<decay_t<Ts>, Ts> &&...)`
3275
+ - `invocable<LetFn, decay_t<Ts>&...>`
3276
+ - `sender<invoke_result_t<LetFn, decay_t<Ts>&...>>`
3277
+ - `sizeof...(Env) == 0 || sender_in<invoke_result_t<LetFn, decay_t<Ts>&...>, `*`env-t`*`...>`
3278
+
3279
+ where *`env-t`* is the pack
3280
+ `decltype(`*`let-cpo`*`.transform_env(declval<Sndr>(), declval<Env>()))`.
3281
+
3282
+ `\exposid{impls-for}<\exposid{decayed-typeof}<\exposid{let-cpo}>>::\exposid{get-state}`
3283
+
3284
+ is initialized with a callable object equivalent to the following:
3285
+
3286
+ ``` cpp
3287
+ []<class Sndr, class Rcvr>(Sndr&& sndr, Rcvr& rcvr) requires see below {
3288
+ auto& [_, fn, child] = sndr;
3289
+ using fn_t = decay_t<decltype(fn)>;
3290
+ using env_t = decltype(let-env(child));
3291
+ using args_variant_t = see below;
3292
+ using ops2_variant_t = see below;
3293
+
3294
+ struct state-type {
3295
+ fn_t fn; // exposition only
3296
+ env_t env; // exposition only
3297
+ args_variant_t args; // exposition only
3298
+ ops2_variant_t ops2; // exposition only
3299
+ };
3300
+ return state-type{allocator-aware-forward(std::forward_like<Sndr>(fn), rcvr),
3301
+ let-env(child), {}, {}};
3302
+ }
3303
+ ```
3304
+
3305
+ Let `Sigs` be a pack of the arguments to the `completion_signatures`
3306
+ specialization named by
3307
+ `completion_signatures_of_t<child-type<Sndr>, FWD-ENV-T(env_of_t<Rcvr>)>`.
3308
+ Let `LetSigs` be a pack of those types in `Sigs` with a return type of
3309
+ `decayed-typeof<set-cpo>`. Let *`as-tuple`* be an alias template such
3310
+ that `as-tuple<Tag(Args...)>` denotes the type `decayed-tuple<Args...>`.
3311
+ Then `args_variant_t` denotes the type
3312
+ `variant<monostate, as-tuple<LetSigs>...>` except with duplicate types
3313
+ removed.
3314
+
3315
+ Given a type `Tag` and a pack `Args`, let *`as-sndr2`* be an alias
3316
+ template such that `as-sndr2<Tag(Args...)>` denotes the type
3317
+ `call-result-t<F, decay_t<Args>&...>`. Then `ops2_variant_t` denotes the
3318
+ type
3319
+
3320
+ ``` cpp
3321
+ variant<monostate, connect_result_t<as-sndr2<LetSigs>, receiver2<Rcvr, env_t>>...>
3322
+ ```
3323
+
3324
+ except with duplicate types removed.
3325
+
3326
+ The *requires-clause* constraining the above lambda is satisfied if and
3327
+ only if the types `args_variant_t` and `ops2_variant_t` are well-formed.
3328
+
3329
+ The exposition-only function template *`let-bind`* has effects
3330
+ equivalent to:
3331
+
3332
+ ``` cpp
3333
+ using args_t = decayed-tuple<Args...>;
3334
+ auto mkop2 = [&] {
3335
+ return connect(
3336
+ apply(std::move(state.fn),
3337
+ state.args.template emplace<args_t>(std::forward<Args>(args)...)),
3338
+ receiver2{rcvr, std::move(state.env)});
3339
+ };
3340
+ start(state.ops2.template emplace<decltype(mkop2())>(emplace-from{mkop2}));
3341
+ ```
3342
+
3343
+ `\exposid{impls-for}<\exposid{decayed-typeof}<\exposid{let-cpo}>>::\exposid{complete}`
3344
+
3345
+ is initialized with a callable object equivalent to the following:
3346
+
3347
+ ``` cpp
3348
+ []<class Tag, class... Args>
3349
+ (auto, auto& state, auto& rcvr, Tag, Args&&... args) noexcept -> void {
3350
+ if constexpr (same_as<Tag, decayed-typeof<set-cpo>>) {
3351
+ TRY-EVAL(rcvr, let-bind(state, rcvr, std::forward<Args>(args)...));
3352
+ } else {
3353
+ Tag()(std::move(rcvr), std::forward<Args>(args)...);
3354
+ }
3355
+ }
3356
+ ```
3357
+
3358
+ Let `sndr` and `env` be subexpressions, and let `Sndr` be
3359
+ `decltype((sndr))`. If `sender-for<Sndr, decayed-typeof<let-cpo>>` is
3360
+ `false`, then the expression `let-cpo.transform_env(sndr, env)` is
3361
+ ill-formed. Otherwise, it is equal to:
3362
+
3363
+ ``` cpp
3364
+ auto& [_, _, child] = sndr;
3365
+ return JOIN-ENV(let-env(child), FWD-ENV(env));
3366
+ ```
3367
+
3368
+ Let the subexpression `out_sndr` denote the result of the invocation
3369
+ `let-cpo(sndr, f)` or an object equal to such, and let the subexpression
3370
+ `rcvr` denote a receiver such that the expression
3371
+ `connect(out_sndr, rcvr)` is well-formed. The expression
3372
+ `connect(out_sndr, rcvr)` has undefined behavior unless it creates an
3373
+ asynchronous operation [[exec.async.ops]] that, when started:
3374
+
3375
+ - invokes `f` when *`set-cpo`* is called with `sndr`’s result datums,
3376
+ - makes its completion dependent on the completion of a sender returned
3377
+ by `f`, and
3378
+ - propagates the other completion operations sent by `sndr`.
3379
+
3380
+ #### `execution::bulk`, `execution::bulk_chunked`, and `execution::bulk_unchunked` <a id="exec.bulk">[[exec.bulk]]</a>
3381
+
3382
+ `bulk`, `bulk_chunked`, and `bulk_unchunked` run a task repeatedly for
3383
+ every index in an index space.
3384
+
3385
+ The names `bulk`, `bulk_chunked`, and `bulk_unchunked` denote pipeable
3386
+ sender adaptor objects. Let `bulk-algo` be either `bulk`,
3387
+ `bulk_chunked`, or `bulk_unchunked`. For subexpressions `sndr`,
3388
+ `policy`, `shape`, and `f`, let `Policy` be
3389
+ `remove_cvref_t<decltype(policy)>`, `Shape` be `decltype(auto(shape))`,
3390
+ and `Func` be `decay_t<decltype((f))>`. If
3391
+
3392
+ - `decltype((sndr))` does not satisfy `sender`, or
3393
+ - `is_execution_policy_v<Policy>` is `false`, or
3394
+ - `Shape` does not satisfy `integral`, or
3395
+ - `Func` does not model `copy_constructible`,
3396
+
3397
+ `bulk-algo(sndr, policy, shape, f)` is ill-formed.
3398
+
3399
+ Otherwise, the expression `bulk-algo(sndr, policy, shape, f)` is
3400
+ expression-equivalent to:
3401
+
3402
+ ``` cpp
3403
+ transform_sender(get-domain-early(sndr), make-sender(
3404
+ bulk-algo, product-type<see below, Shape, Func>{policy, shape, f}, sndr))
3405
+ ```
3406
+
3407
+ except that `sndr` is evaluated only once. The first template argument
3408
+ of *`product-type`* is `Policy` if `Policy` models `copy_constructible`,
3409
+ and `const Policy&` otherwise.
3410
+
3411
+ Let `sndr` and `env` be subexpressions such that `Sndr` is
3412
+ `decltype((sndr))`. If `sender-for<Sndr, bulk_t>` is `false`, then the
3413
+ expression `bulk.transform_sender(sndr, env)` is ill-formed; otherwise,
3414
+ it is equivalent to:
3415
+
3416
+ ``` cpp
3417
+ auto [_, data, child] = sndr;
3418
+ auto& [policy, shape, f] = data;
3419
+ auto new_f = [func = std::move(f)](Shape begin, Shape end, auto&&... vs)
3420
+ noexcept(noexcept(f(begin, vs...))) {
3421
+ while (begin != end) func(begin++, vs...);
3422
+ }
3423
+ return bulk_chunked(std::move(child), policy, shape, std::move(new_f));
3424
+ ```
3425
+
3426
+ [*Note 1*: This causes the `bulk(sndr, policy, shape, f)` sender to be
3427
+ expressed in terms of `bulk_chunked(sndr, policy, shape, f)` when it is
3428
+ connected to a receiver whose execution domain does not customize
3429
+ `bulk`. — *end note*]
3430
+
3431
+ The exposition-only class template *`impls-for`* [[exec.snd.expos]] is
3432
+ specialized for `bulk_chunked_t` as follows:
3433
+
3434
+ ``` cpp
3435
+ namespace std::execution {
3436
+ template<>
3437
+ struct impls-for<bulk_chunked_t> : default-impls {
3438
+ static constexpr auto complete = see below;
3439
+
3440
+ template<class Sndr, class... Env>
3441
+ static consteval void check-types();
3442
+ };
3443
+ }
3444
+ ```
3445
+
3446
+ The member `impls-for<bulk_chunked_t>::complete` is initialized with a
3447
+ callable object equivalent to the following lambda:
3448
+
3449
+ ``` cpp
3450
+ []<class Index, class State, class Rcvr, class Tag, class... Args>
3451
+ (Index, State& state, Rcvr& rcvr, Tag, Args&&... args) noexcept
3452
+ -> void requires see below {
3453
+ if constexpr (same_as<Tag, set_value_t>) {
3454
+ auto& [policy, shape, f] = state;
3455
+ constexpr bool nothrow = noexcept(f(auto(shape), auto(shape), args...));
3456
+ TRY-EVAL(rcvr, [&]() noexcept(nothrow) {
3457
+ f(static_cast<decltype(auto(shape))>(0), auto(shape), args...);
3458
+ Tag()(std::move(rcvr), std::forward<Args>(args)...);
3459
+ }());
3460
+ } else {
3461
+ Tag()(std::move(rcvr), std::forward<Args>(args)...);
3462
+ }
3463
+ }
3464
+ ```
3465
+
3466
+ The expression in the *requires-clause* of the lambda above is `true` if
3467
+ and only if `Tag` denotes a type other than `set_value_t` or if the
3468
+ expression `f(auto(shape), auto(shape), args...)` is well-formed.
3469
+
3470
+ The exposition-only class template *`impls-for`* [[exec.snd.expos]] is
3471
+ specialized for `bulk_unchunked_t` as follows:
3472
+
3473
+ ``` cpp
3474
+ namespace std::execution {
3475
+ template<>
3476
+ struct impls-for<bulk_unchunked_t> : default-impls {
3477
+ static constexpr auto complete = see below;
3478
+ };
3479
+ }
3480
+ ```
3481
+
3482
+ The member `impls-for<bulk_unchunked_t>::complete` is initialized with a
3483
+ callable object equivalent to the following lambda:
3484
+
3485
+ ``` cpp
3486
+ []<class Index, class State, class Rcvr, class Tag, class... Args>
3487
+ (Index, State& state, Rcvr& rcvr, Tag, Args&&... args) noexcept
3488
+ -> void requires see below {
3489
+ if constexpr (same_as<Tag, set_value_t>) {
3490
+ auto& [policy, shape, f] = state;
3491
+ constexpr bool nothrow = noexcept(f(auto(shape), args...));
3492
+ TRY-EVAL(rcvr, [&]() noexcept(nothrow) {
3493
+ for (decltype(auto(shape)) i = 0; i < shape; ++i) {
3494
+ f(auto(i), args...);
3495
+ }
3496
+ Tag()(std::move(rcvr), std::forward<Args>(args)...);
3497
+ }());
3498
+ } else {
3499
+ Tag()(std::move(rcvr), std::forward<Args>(args)...);
3500
+ }
3501
+ }
3502
+ ```
3503
+
3504
+ The expression in the *requires-clause* of the lambda above is `true` if
3505
+ and only if `Tag` denotes a type other than `set_value_t` or if the
3506
+ expression `f(auto(shape), args...)` is well-formed.
3507
+
3508
+ ``` cpp
3509
+ template<class Sndr, class... Env>
3510
+ static consteval void check-types();
3511
+ ```
3512
+
3513
+ *Effects:* Equivalent to:
3514
+
3515
+ ``` cpp
3516
+ auto cs = get_completion_signatures<child-type<Sndr>, FWD-ENV-T(Env)...>();
3517
+ auto fn = []<class... Ts>(set_value_t(*)(Ts...)) {
3518
+ if constexpr (!invocable<remove_cvref_t<data-type<Sndr>>, Ts&...>)
3519
+ throw unspecified-exception();
3520
+ };
3521
+ cs.for-each(overload-set(fn, [](auto){}));
3522
+ ```
3523
+
3524
+ where *`unspecified-exception`* is a type derived from `exception`.
3525
+
3526
+ Let the subexpression `out_sndr` denote the result of the invocation
3527
+ `bulk-algo(sndr, policy, shape, f)` or an object equal to such, and let
3528
+ the subexpression `rcvr` denote a receiver such that the expression
3529
+ `connect(out_sndr, rcvr)` is well-formed. The expression
3530
+ `connect(out_sndr, rcvr)` has undefined behavior unless it creates an
3531
+ asynchronous operation [[exec.async.ops]] that, when started:
3532
+
3533
+ - If `sndr` has a successful completion, where `args` is a pack of
3534
+ lvalue subexpressions referring to the value completion result datums
3535
+ of `sndr`, or decayed copies of those values if they model
3536
+ `copy_constructible`, then:
3537
+ - If `out_sndr` also completes successfully, then:
3538
+ - for `bulk`, invokes `f(i, args...)` for every i of type `Shape`
3539
+ from `0` to `shape`;
3540
+ - for `bulk_unchunked`, invokes `f(i, args...)` for every i of type
3541
+ `Shape` from `0` to `shape`; *Recommended practice:* The
3542
+ underlying scheduler should execute each iteration on a distinct
3543
+ execution agent.
3544
+ - for `bulk_chunked`, invokes `f(b, e, args...)` zero or more times
3545
+ with pairs of b and e of type `Shape` in range \[`0`, `shape`\],
3546
+ such that b < e and for every i of type `Shape` from `0` to
3547
+ `shape`, there is exactly one invocation with a pair b and e, such
3548
+ that i is in the range \[b, e).
3549
+ - If `out_sndr` completes with `set_error(rcvr, eptr)`, then the
3550
+ asynchronous operation may invoke a subset of the invocations of `f`
3551
+ before the error completion handler is called, and `eptr` is an
3552
+ `exception_ptr` containing either:
3553
+ - an exception thrown by an invocation of `f`, or
3554
+ - a `bad_alloc` exception if the implementation fails to allocate
3555
+ required resources, or
3556
+ - an exception derived from `runtime_error`.
3557
+ - If `out_sndr` completes with `set_stopped(rcvr)`, then the
3558
+ asynchronous operation may invoke a subset of the invocations of `f`
3559
+ before the stopped completion handler.
3560
+ - If `sndr` does not complete with `set_value`, then the completion is
3561
+ forwarded to `recv`.
3562
+ - For `bulk-algo`, the parameter `policy` describes the manner in which
3563
+ the execution of the asynchronous operations corresponding to these
3564
+ algorithms may be parallelized and the manner in which they apply `f`.
3565
+ Permissions and requirements on parallel algorithm element access
3566
+ functions [[algorithms.parallel.exec]] apply to `f`.
3567
+
3568
+ [*Note 2*: The asynchronous operation corresponding to
3569
+ `bulk-algo(sndr, policy, shape, f)` can complete with `set_stopped` if
3570
+ cancellation is requested or ignore cancellation
3571
+ requests. — *end note*]
3572
+
3573
+ #### `execution::when_all` <a id="exec.when.all">[[exec.when.all]]</a>
3574
+
3575
+ `when_all` and `when_all_with_variant` both adapt multiple input senders
3576
+ into a sender that completes when all input senders have completed.
3577
+ `when_all` only accepts senders with a single value completion signature
3578
+ and on success concatenates all the input senders’ value result datums
3579
+ into its own value completion operation.
3580
+ `when_all_with_variant(sndrs...)` is semantically equivalent to
3581
+ w`hen_all(into_variant(sndrs)...)`, where `sndrs` is a pack of
3582
+ subexpressions whose types model `sender`.
3583
+
3584
+ The names `when_all` and `when_all_with_variant` denote customization
3585
+ point objects. Let `sndrs` be a pack of subexpressions, let `Sndrs` be a
3586
+ pack of the types `decltype((sndrs))...`, and let `CD` be the type
3587
+ `common_type_t<decltype(get-domain-early(sndrs))...>`. Let `CD2` be `CD`
3588
+ if `CD` is well-formed, and `default_domain` otherwise. The expressions
3589
+ `when_all(sndrs...)` and `when_all_with_variant(sndrs...)` are
3590
+ ill-formed if any of the following is `true`:
3591
+
3592
+ - `sizeof...(sndrs)` is `0`, or
3593
+ - `(sender<Sndrs> && ...)` is `false`.
3594
+
3595
+ The expression `when_all(sndrs...)` is expression-equivalent to:
3596
+
3597
+ ``` cpp
3598
+ transform_sender(CD2(), make-sender(when_all, {}, sndrs...))
3599
+ ```
3600
+
3601
+ The exposition-only class template *`impls-for`* [[exec.snd.expos]] is
3602
+ specialized for `when_all_t` as follows:
3603
+
3604
+ ``` cpp
3605
+ namespace std::execution {
3606
+ template<>
3607
+ struct impls-for<when_all_t> : default-impls {
3608
+ static constexpr auto get-attrs = see below;
3609
+ static constexpr auto get-env = see below;
3610
+ static constexpr auto get-state = see below;
3611
+ static constexpr auto start = see below;
3612
+ static constexpr auto complete = see below;
3613
+
3614
+ template<class Sndr, class... Env>
3615
+ static consteval void check-types();
3616
+ };
3617
+ }
3618
+ ```
3619
+
3620
+ Let *`make-when-all-env`* be the following exposition-only function
3621
+ template:
3622
+
3623
+ ``` cpp
3624
+ template<class Env>
3625
+ constexpr auto make-when-all-env(inplace_stop_source& stop_src, // exposition only
3626
+ Env&& env) noexcept {
3627
+ return see below;
3628
+ }
3629
+ ```
3630
+
3631
+ Returns an object `e` such that
3632
+
3633
+ - `decltype(e)` models `queryable`, and
3634
+ - `e.query(get_stop_token)` is expression-equivalent to
3635
+ `state.stop-src.get_token()`, and
3636
+ - given a query object `q` with type other than cv `get_stop_token_t`
3637
+ and whose type satisfies *`forwarding-query`*, `e.query(q)` is
3638
+ expression-equivalent to `get_env(rcvr).query(q)`.
3639
+
3640
+ Let `when-all-env` be an alias template such that `when-all-env<Env>`
3641
+ denotes the type
3642
+ `decltype(make-{when-all-env}(declval<inplace_stop_source&>(), declval<Env>()))`.
3643
+
3644
+ ``` cpp
3645
+ template<class Sndr, class... Env>
3646
+ static consteval void check-types();
3647
+ ```
3648
+
3649
+ Let `Is` be the pack of integral template arguments of the
3650
+ `integer_sequence` specialization denoted by *`indices-for`*`<Sndr>`.
3651
+
3652
+ *Effects:* Equivalent to:
3653
+
3654
+ ``` cpp
3655
+ auto fn = []<class Child>() {
3656
+ auto cs = get_completion_signatures<Child, when-all-env<Env>...>();
3657
+ if constexpr (cs.count-of(set_value) >= 2)
3658
+ throw unspecified-exception();
3659
+ decay-copyable-result-datums(cs); // see [exec.snd.expos]
3660
+ };
3661
+ (fn.template operator()<child-type<Sndr, Is>>(), ...);
3662
+ ```
3663
+
3664
+ where *`unspecified-exception`* is a type derived from `exception`.
3665
+
3666
+ *Throws:* Any exception thrown as a result of evaluating the *Effects*,
3667
+ or an exception of an unspecified type derived from `exception` when
3668
+ `CD` is ill-formed.
3669
+
3670
+ The member `impls-for<when_all_t>::get-attrs` is initialized with a
3671
+ callable object equivalent to the following lambda expression:
3672
+
3673
+ ``` cpp
3674
+ [](auto&&, auto&&... child) noexcept {
3675
+ if constexpr (same_as<CD, default_domain>) {
3676
+ return env<>();
3677
+ } else {
3678
+ return MAKE-ENV(get_domain, CD());
3679
+ }
3680
+ }
3681
+ ```
3682
+
3683
+ The member `impls-for<when_all_t>::get-env` is initialized with a
3684
+ callable object equivalent to the following lambda expression:
3685
+
3686
+ ``` cpp
3687
+ []<class State, class Rcvr>(auto&&, State& state, const Receiver& rcvr) noexcept {
3688
+ return make-when-all-env(state.stop-src, get_env(rcvr));
3689
+ }
3690
+ ```
3691
+
3692
+ The member `impls-for<when_all_t>::get-state` is initialized with a
3693
+ callable object equivalent to the following lambda expression:
3694
+
3695
+ ``` cpp
3696
+ []<class Sndr, class Rcvr>(Sndr&& sndr, Rcvr& rcvr) noexcept(noexcept(e)) -> decltype(e) {
3697
+ return e;
3698
+ }
3699
+ ```
3700
+
3701
+ where e is the expression
3702
+
3703
+ ``` cpp
3704
+ std::forward<Sndr>(sndr).apply(make-state<Rcvr>())
3705
+ ```
3706
+
3707
+ and where *`make-state`* is the following exposition-only class
3708
+ template:
3709
+
3710
+ ``` cpp
3711
+ enum class disposition { started, error, stopped }; // exposition only
3712
+
3713
+ template<class Rcvr>
3714
+ struct make-state {
3715
+ template<class... Sndrs>
3716
+ auto operator()(auto, auto, Sndrs&&... sndrs) const {
3717
+ using values_tuple = see below;
3718
+ using errors_variant = see below;
3719
+ using stop_callback = stop_callback_for_t<stop_token_of_t<env_of_t<Rcvr>>, on-stop-request>;
3720
+
3721
+ struct state-type {
3722
+ void arrive(Rcvr& rcvr) noexcept { // exposition only
3723
+ if (0 == --count) {
3724
+ complete(rcvr);
3725
+ }
3726
+ }
3727
+
3728
+ void complete(Rcvr& rcvr) noexcept; // exposition only
3729
+
3730
+ atomic<size_t> count{sizeof...(sndrs)}; // exposition only
3731
+ inplace_stop_source stop_src{}; // exposition only
3732
+ atomic<disposition> disp{disposition::started}; // exposition only
3733
+ errors_variant errors{}; // exposition only
3734
+ values_tuple values{}; // exposition only
3735
+ optional<stop_callback> on_stop{nullopt}; // exposition only
3736
+ };
3737
+
3738
+ return state-type{};
3739
+ }
3740
+ };
3741
+ ```
3742
+
3743
+ Let *`copy-fail`* be `exception_ptr` if decay-copying any of the child
3744
+ senders’ result datums can potentially throw; otherwise, `none-such`,
3745
+ where `none-such` is an unspecified empty class type.
3746
+
3747
+ The alias `values_tuple` denotes the type
3748
+
3749
+ ``` cpp
3750
+ tuple<value_types_of_t<Sndrs, FWD-ENV-T(env_of_t<Rcvr>), decayed-tuple, optional>...>
3751
+ ```
3752
+
3753
+ if that type is well-formed; otherwise, `tuple<>`.
3754
+
3755
+ The alias `errors_variant` denotes the type
3756
+ `variant<none-such, copy-fail, Es...>` with duplicate types removed,
3757
+ where `Es` is the pack of the decayed types of all the child senders’
3758
+ possible error result datums.
3759
+
3760
+ The member `void state-type::complete(Rcvr& rcvr) noexcept` behaves as
3761
+ follows:
3762
+
3763
+ - If `disp` is equal to `disposition::started`, evaluates:
3764
+ ``` cpp
3765
+ auto tie = []<class... T>(tuple<T...>& t) noexcept { return tuple<T&...>(t); };
3766
+ auto set = [&](auto&... t) noexcept { set_value(std::move(rcvr), std::move(t)...); };
3767
+
3768
+ on_stop.reset();
3769
+ apply(
3770
+ [&](auto&... opts) noexcept {
3771
+ apply(set, tuple_cat(tie(*opts)...));
3772
+ },
3773
+ values);
3774
+ ```
3775
+ - Otherwise, if `disp` is equal to `disposition::error`, evaluates:
3776
+ ``` cpp
3777
+ on_stop.reset();
3778
+ visit(
3779
+ [&]<class Error>(Error& error) noexcept {
3780
+ if constexpr (!same_as<Error, none-such>) {
3781
+ set_error(std::move(rcvr), std::move(error));
3782
+ }
3783
+ },
3784
+ errors);
3785
+ ```
3786
+ - Otherwise, evaluates:
3787
+ ``` cpp
3788
+ on_stop.reset();
3789
+ set_stopped(std::move(rcvr));
3790
+ ```
3791
+
3792
+ The member `impls-for<when_all_t>::start` is initialized with a callable
3793
+ object equivalent to the following lambda expression:
3794
+
3795
+ ``` cpp
3796
+ []<class State, class Rcvr, class... Ops>(
3797
+ State& state, Rcvr& rcvr, Ops&... ops) noexcept -> void {
3798
+ state.on_stop.emplace(
3799
+ get_stop_token(get_env(rcvr)),
3800
+ on-stop-request{state.stop_src});
3801
+ if (state.stop_src.stop_requested()) {
3802
+ state.on_stop.reset();
3803
+ set_stopped(std::move(rcvr));
3804
+ } else {
3805
+ (start(ops), ...);
3806
+ }
3807
+ }
3808
+ ```
3809
+
3810
+ The member `impls-for<when_all_t>::complete` is initialized with a
3811
+ callable object equivalent to the following lambda expression:
3812
+
3813
+ ``` cpp
3814
+ []<class Index, class State, class Rcvr, class Set, class... Args>(
3815
+ this auto& complete, Index, State& state, Rcvr& rcvr, Set, Args&&... args) noexcept -> void {
3816
+ if constexpr (same_as<Set, set_error_t>) {
3817
+ if (disposition::error != state.disp.exchange(disposition::error)) {
3818
+ state.stop_src.request_stop();
3819
+ TRY-EMPLACE-ERROR(state.errors, std::forward<Args>(args)...);
3820
+ }
3821
+ } else if constexpr (same_as<Set, set_stopped_t>) {
3822
+ auto expected = disposition::started;
3823
+ if (state.disp.compare_exchange_strong(expected, disposition::stopped)) {
3824
+ state.stop_src.request_stop();
3825
+ }
3826
+ } else if constexpr (!same_as<decltype(State::values), tuple<>>) {
3827
+ if (state.disp == disposition::started) {
3828
+ auto& opt = get<Index::value>(state.values);
3829
+ TRY-EMPLACE-VALUE(complete, opt, std::forward<Args>(args)...);
3830
+ }
3831
+ }
3832
+ state.arrive(rcvr);
3833
+ }
3834
+ ```
3835
+
3836
+ where `TRY-EMPLACE-ERROR(v, e)`, for subexpressions `v` and `e`, is
3837
+ equivalent to:
3838
+
3839
+ ``` cpp
3840
+ try {
3841
+ v.template emplace<decltype(auto(e))>(e);
3842
+ } catch (...) {
3843
+ v.template emplace<exception_ptr>(current_exception());
3844
+ }
3845
+ ```
3846
+
3847
+ if the expression `decltype(auto(e))(e)` is potentially throwing;
3848
+ otherwise, `v.template emplace<decltype(auto(e))>(e)`; and where
3849
+ `TRY-EMPLACE-VALUE(c, o, as...)`, for subexpressions `c`, `o`, and pack
3850
+ of subexpressions `as`, is equivalent to:
3851
+
3852
+ ``` cpp
3853
+ try {
3854
+ o.emplace(as...);
3855
+ } catch (...) {
3856
+ c(Index(), state, rcvr, set_error, current_exception());
3857
+ return;
3858
+ }
3859
+ ```
3860
+
3861
+ if the expression `decayed-tuple<decltype(as)...>{as...}` is potentially
3862
+ throwing; otherwise, `o.emplace(as...)`.
3863
+
3864
+ The expression `when_all_with_variant(sndrs...)` is
3865
+ expression-equivalent to:
3866
+
3867
+ ``` cpp
3868
+ transform_sender(CD2(), make-sender(when_all_with_variant, {}, sndrs...));
3869
+ ```
3870
+
3871
+ Given subexpressions `sndr` and `env`, if
3872
+ `sender-for<decltype((sndr)), when_all_with_variant_t>` is `false`, then
3873
+ the expression `when_all_with_variant.transform_sender(sndr, env)` is
3874
+ ill-formed; otherwise, it is equivalent to:
3875
+
3876
+ ``` cpp
3877
+ auto&& [_, _, ...child] = sndr;
3878
+ return when_all(into_variant(std::forward_like<decltype((sndr))>(child))...);
3879
+ ```
3880
+
3881
+ [*Note 1*: This causes the `when_all_with_variant(sndrs...)` sender to
3882
+ become `when_all(into_variant(sndrs)...)` when it is connected with a
3883
+ receiver whose execution domain does not customize
3884
+ `when_all_with_variant`. — *end note*]
3885
+
3886
+ #### `execution::into_variant` <a id="exec.into.variant">[[exec.into.variant]]</a>
3887
+
3888
+ `into_variant` adapts a sender with multiple value completion signatures
3889
+ into a sender with just one value completion signature consisting of a
3890
+ `variant` of `tuple`s.
3891
+
3892
+ The name `into_variant` denotes a pipeable sender adaptor object. For a
3893
+ subexpression `sndr`, let `Sndr` be `decltype((sndr))`. If `Sndr` does
3894
+ not satisfy `sender`, `into_variant(sndr)` is ill-formed.
3895
+
3896
+ Otherwise, the expression `into_variant(sndr)` is expression-equivalent
3897
+ to:
3898
+
3899
+ ``` cpp
3900
+ transform_sender(get-domain-early(sndr), make-sender(into_variant, {}, sndr))
3901
+ ```
3902
+
3903
+ except that `sndr` is only evaluated once.
3904
+
3905
+ The exposition-only class template *`impls-for`* [[exec.snd.expos]] is
3906
+ specialized for `into_variant` as follows:
3907
+
3908
+ ``` cpp
3909
+ namespace std::execution {
3910
+ template<>
3911
+ struct impls-for<into_variant_t> : default-impls {
3912
+ static constexpr auto get-state = see below;
3913
+ static constexpr auto complete = see below;
3914
+
3915
+ template<class Sndr, class... Env>
3916
+ static consteval void check-types() {
3917
+ auto cs = get_completion_signatures<child-type<Sndr>, FWD-ENV-T(Env)...>();
3918
+ decay-copyable-result-datums(cs); // see [exec.snd.expos]
3919
+ }
3920
+ };
3921
+ }
3922
+ ```
3923
+
3924
+ The member `impls-for<into_variant_t>::get-state` is initialized with a
3925
+ callable object equivalent to the following lambda:
3926
+
3927
+ ``` cpp
3928
+ []<class Sndr, class Rcvr>(Sndr&& sndr, Rcvr& rcvr) noexcept
3929
+ -> type_identity<value_types_of_t<child-type<Sndr>, FWD-ENV-T(env_of_t<Rcvr>)>> {
3930
+ return {};
3931
+ }
3932
+ ```
3933
+
3934
+ The member `impls-for<into_variant_t>::complete` is initialized with a
3935
+ callable object equivalent to the following lambda:
3936
+
3937
+ ``` cpp
3938
+ []<class State, class Rcvr, class Tag, class... Args>(
3939
+ auto, State, Rcvr& rcvr, Tag, Args&&... args) noexcept -> void {
3940
+ if constexpr (same_as<Tag, set_value_t>) {
3941
+ using variant_type = State::type;
3942
+ TRY-SET-VALUE(rcvr, variant_type(decayed-tuple<Args...>{std::forward<Args>(args)...}));
3943
+ } else {
3944
+ Tag()(std::move(rcvr), std::forward<Args>(args)...);
3945
+ }
3946
+ }
3947
+ ```
3948
+
3949
+ #### `execution::stopped_as_optional` <a id="exec.stopped.opt">[[exec.stopped.opt]]</a>
3950
+
3951
+ `stopped_as_optional` maps a sender’s stopped completion operation into
3952
+ a value completion operation as a disengaged `optional`. The sender’s
3953
+ value completion operation is also converted into an `optional`. The
3954
+ result is a sender that never completes with stopped, reporting
3955
+ cancellation by completing with a disengaged `optional`.
3956
+
3957
+ The name `stopped_as_optional` denotes a pipeable sender adaptor object.
3958
+ For a subexpression `sndr`, let `Sndr` be `decltype((sndr))`. The
3959
+ expression `stopped_as_optional(sndr)` is expression-equivalent to:
3960
+
3961
+ ``` cpp
3962
+ transform_sender(get-domain-early(sndr), make-sender(stopped_as_optional, {}, sndr))
3963
+ ```
3964
+
3965
+ except that `sndr` is only evaluated once.
3966
+
3967
+ The exposition-only class template *`impls-for`* [[exec.snd.expos]] is
3968
+ specialized for `stopped_as_optional_t` as follows:
3969
+
3970
+ ``` cpp
3971
+ namespace std::execution {
3972
+ template<>
3973
+ struct impls-for<stopped_as_optional_t> : default-impls {
3974
+ template<class Sndr, class... Env>
3975
+ static consteval void check-types() {
3976
+ default-impls::check-types<Sndr, Env...>();
3977
+ if constexpr (!requires {
3978
+ requires (!same_as<void, single-sender-value-type<child-type<Sndr>,
3979
+ FWD-ENV-T(Env)...>>); })
3980
+ throw unspecified-exception();
3981
+ }
3982
+ };
3983
+ }
3984
+ ```
3985
+
3986
+ where `unspecified-exception` is a type derived from `exception`.
3987
+
3988
+ Let `sndr` and `env` be subexpressions such that `Sndr` is
3989
+ `decltype((sndr))` and `Env` is `decltype((env))`. If
3990
+ `sender-for<Sndr, stopped_as_optional_t>` is `false` then the expression
3991
+ `stopped_as_optional.transform_sender(sndr, env)` is ill-formed;
3992
+ otherwise, if `sender_in<child-type<Sndr>, FWD-ENV-T(Env)>` is `false`,
3993
+ the expression `stopped_as_optional.transform_sender(sndr, env)` is
3994
+ equivalent to `not-a-sender()`; otherwise, it is equivalent to:
3995
+
3996
+ ``` cpp
3997
+ auto&& [_, _, child] = sndr;
3998
+ using V = single-sender-value-type<child-type<Sndr>, FWD-ENV-T(Env)>;
3999
+ return let_stopped(
4000
+ then(std::forward_like<Sndr>(child),
4001
+ []<class... Ts>(Ts&&... ts) noexcept(is_nothrow_constructible_v<V, Ts...>) {
4002
+ return optional<V>(in_place, std::forward<Ts>(ts)...);
4003
+ }),
4004
+ []() noexcept { return just(optional<V>()); });
4005
+ ```
4006
+
4007
+ #### `execution::stopped_as_error` <a id="exec.stopped.err">[[exec.stopped.err]]</a>
4008
+
4009
+ `stopped_as_error` maps an input sender’s stopped completion operation
4010
+ into an error completion operation as a custom error type. The result is
4011
+ a sender that never completes with stopped, reporting cancellation by
4012
+ completing with an error.
4013
+
4014
+ The name `stopped_as_error` denotes a pipeable sender adaptor object.
4015
+ For some subexpressions `sndr` and `err`, let `Sndr` be
4016
+ `decltype((sndr))` and let `Err` be `decltype((err))`. If the type
4017
+ `Sndr` does not satisfy `sender` or if the type `Err` does not satisfy
4018
+ `movable-value`, `stopped_as_error(sndr, err)` is ill-formed. Otherwise,
4019
+ the expression `stopped_as_error(sndr, err)` is expression-equivalent
4020
+ to:
4021
+
4022
+ ``` cpp
4023
+ transform_sender(get-domain-early(sndr), make-sender(stopped_as_error, err, sndr))
4024
+ ```
4025
+
4026
+ except that `sndr` is only evaluated once.
4027
+
4028
+ Let `sndr` and `env` be subexpressions such that `Sndr` is
4029
+ `decltype((sndr))` and `Env` is `decltype((env))`. If
4030
+ `sender-for<Sndr, stopped_as_error_t>` is `false`, then the expression
4031
+ `stopped_as_error.transform_sender(sndr, env)` is ill-formed; otherwise,
4032
+ it is equivalent to:
4033
+
4034
+ ``` cpp
4035
+ auto&& [_, err, child] = sndr;
4036
+ using E = decltype(auto(err));
4037
+ return let_stopped(
4038
+ std::forward_like<Sndr>(child),
4039
+ [err = std::forward_like<Sndr>(err)]() mutable noexcept(is_nothrow_move_constructible_v<E>) {
4040
+ return just_error(std::move(err));
4041
+ });
4042
+ ```
4043
+
4044
+ #### `execution::associate` <a id="exec.associate">[[exec.associate]]</a>
4045
+
4046
+ `associate` tries to associate a sender with an async scope such that
4047
+ the scope can track the lifetime of any asynchronous operations created
4048
+ with the sender.
4049
+
4050
+ Let *`associate-data`* be the following exposition-only class template:
4051
+
4052
+ ``` cpp
4053
+ namespace std::execution {
4054
+ template<scope_token Token, sender Sender>
4055
+ struct associate-data { // exposition only
4056
+ using wrap-sender = // exposition only
4057
+ remove_cvref_t<decltype(declval<Token&>().wrap(declval<Sender>()))>;
4058
+
4059
+ explicit associate-data(Token t, Sender&& s)
4060
+ : sndr(t.wrap(std::forward<Sender>(s))),
4061
+ token(t) {
4062
+ if (!token.try_associate())
4063
+ sndr.reset();
4064
+ }
4065
+
4066
+ associate-data(const associate-data& other)
4067
+ noexcept(is_nothrow_copy_constructible_v<wrap-sender> &&
4068
+ noexcept(other.token.try_associate()));
4069
+
4070
+ associate-data(associate-data&& other)
4071
+ noexcept(is_nothrow_move_constructible_v<wrap-sender>);
4072
+
4073
+ ~associate-data();
4074
+
4075
+ optional<pair<Token, wrap-sender>>
4076
+ release() && noexcept(is_nothrow_move_constructible_v<wrap-sender>);
4077
+
4078
+ private:
4079
+ optional<wrap-sender> sndr; // exposition only
4080
+ Token token; // exposition only
4081
+ };
4082
+
4083
+ template<scope_token Token, sender Sender>
4084
+ associate-data(Token, Sender&&) -> associate-data<Token, Sender>;
4085
+ }
4086
+ ```
4087
+
4088
+ For an *`associate-data`* object `a`, `a.sndr.has_value()` is `true` if
4089
+ and only if an association was successfully made and is owned by `a`.
4090
+
4091
+ ``` cpp
4092
+ associate-data(const associate-data& other)
4093
+ noexcept(is_nothrow_copy_constructible_v<wrap-sender> &&
4094
+ noexcept(other.token.try_associate()));
4095
+ ```
4096
+
4097
+ *Constraints:* `copy_constructible<`*`wrap-sender`*`>` is `true`.
4098
+
4099
+ *Effects:* Value-initializes *sndr* and initializes *token* with
4100
+ `other.`*`token`*. If `other.`*`sndr`*`.has_value()` is `false`, no
4101
+ further effects; otherwise, calls *`token`*`.try_associate()` and, if
4102
+ that returns `true`, calls *`sndr`*`.emplace(*other.`*`sndr`*`)` and, if
4103
+ that exits with an exception, calls *`token`*`.disassociate()` before
4104
+ propagating the exception.
4105
+
4106
+ ``` cpp
4107
+ associate-data(associate-data&& other)
4108
+ noexcept(is_nothrow_move_constructible_v<wrap-sender>);
4109
+ ```
4110
+
4111
+ *Effects:* Initializes *sndr* with `std::move(other.`*`sndr`*`)` and
4112
+ initializes *token* with `std::move(other.`*`token`*`)` and then calls
4113
+ `other.`*`sndr`*`.reset()`.
4114
+
4115
+ ``` cpp
4116
+ ~associate-data();
4117
+ ```
4118
+
4119
+ *Effects:* If *`sndr`*`.has_value()` returns `false` then no effect;
4120
+ otherwise, invokes *`sndr`*`.reset()` before invoking
4121
+ *`token`*`.disassociate()`.
4122
+
4123
+ ``` cpp
4124
+ optional<pair<Token, wrap-sender>>
4125
+ release() && noexcept(is_nothrow_move_constructible_v<wrap-sender>);
4126
+ ```
4127
+
4128
+ *Effects:* If *`sndr`*`.has_value()` returns `false` then returns an
4129
+ `optional` that does not contain a value; otherwise returns an
4130
+ `optional` containing a value of type `pair<Token, `*`wrap-sender`*`>`
4131
+ as if by:
4132
+
4133
+ ``` cpp
4134
+ return optional(pair(token, std::move(*sndr)));
4135
+ ```
4136
+
4137
+ *Ensures:* *sndr* does not contain a value.
4138
+
4139
+ The name `associate` denotes a pipeable sender adaptor object. For
4140
+ subexpressions `sndr` and `token`:
4141
+
4142
+ - If `decltype((sndr))` does not satisfy `sender`, or
4143
+ `remove_cvref_t<decltype((token))>` does not satisfy `scope_token`,
4144
+ then `associate(sndr, token)` is ill-formed.
4145
+ - Otherwise, the expression `associate(sndr, token)` is
4146
+ expression-equivalent to:
4147
+ ``` cpp
4148
+ transform_sender(get-domain-early(sndr),
4149
+ make-sender(associate, associate-data(token, sndr)))
4150
+ ```
4151
+
4152
+ except that `sndr` is evaluated only once.
4153
+
4154
+ The exposition-only class template *`impls-for`* [[exec.snd.expos]] is
4155
+ specialized for `associate_t` as follows:
4156
+
4157
+ ``` cpp
4158
+ namespace std::execution {
4159
+ template<>
4160
+ struct impls-for<associate_t> : default-impls {
4161
+ static constexpr auto get-state = see below; // exposition only
4162
+ static constexpr auto start = see below; // exposition only
4163
+
4164
+ template<class Sndr, class... Env>
4165
+ static consteval void check-types() { // exposition only
4166
+ using associate_data_t = remove_cvref_t<data-type<Sndr>>;
4167
+ using child_type_t = associate_data_t::wrap-sender;
4168
+ (void)get_completion_signatures<child_type_t, FWD-ENV-T(Env)...>();
4169
+ }
4170
+ };
4171
+ }
4172
+ ```
4173
+
4174
+ The member `impls-for<associate_t>::get-state` is initialized with a
4175
+ callable object equivalent to the following lambda:
4176
+
4177
+ ``` cpp
4178
+ []<class Sndr, class Rcvr>(Sndr&& sndr, Rcvr& rcvr) noexcept(see below) {
4179
+ auto [_, data] = std::forward<Sndr>(sndr);
4180
+ auto dataParts = std::move(data).release();
4181
+
4182
+ using scope_tkn = decltype(dataParts->first);
4183
+ using wrap_sender = decltype(dataParts->second);
4184
+ using op_t = connect_result_t<wrap_sender, Rcvr>;
4185
+
4186
+ struct op_state {
4187
+ bool associated = false; // exposition only
4188
+ union {
4189
+ Rcvr* rcvr; // exposition only
4190
+ struct {
4191
+ scope_tkn token; // exposition only
4192
+ op_t op; // exposition only
4193
+ } assoc; // exposition only
4194
+ };
4195
+
4196
+ explicit op_state(Rcvr& r) noexcept
4197
+ : rcvr(addressof(r)) {}
4198
+
4199
+ explicit op_state(scope_tkn tkn, wrap_sender&& sndr, Rcvr& r) try
4200
+ : associated(true),
4201
+ assoc(tkn, connect(std::move(sndr), std::move(r))) {
4202
+ }
4203
+ catch (...) {
4204
+ tkn.disassociate();
4205
+ throw;
4206
+ }
4207
+
4208
+ op_state(op_state&&) = delete;
4209
+
4210
+ ~op_state() {
4211
+ if (associated) {
4212
+ assoc.op.~op_t();
4213
+ assoc.token.disassociate();
4214
+ assoc.token.~scope_tkn();
4215
+ }
4216
+ }
4217
+
4218
+ void run() noexcept { // exposition only
4219
+ if (associated)
4220
+ start(assoc.op);
4221
+ else
4222
+ set_stopped(std::move(*rcvr));
4223
+ }
4224
+ };
4225
+
4226
+ if (dataParts)
4227
+ return op_state{std::move(dataParts->first), std::move(dataParts->second), rcvr};
4228
+ else
4229
+ return op_state{rcvr};
4230
+ }
4231
+ ```
4232
+
4233
+ The expression in the `noexcept` clause of
4234
+ `impls-for<associate_t>::get-state` is
4235
+
4236
+ ``` cpp
4237
+ is_nothrow_constructible_v<remove_cvref_t<Sndr>, Sndr> &&
4238
+ is_nothrow_move_constructible_v<wrap-sender> &&
4239
+ nothrow-callable<connect_t, wrap-sender, Rcvr>
4240
+ ```
4241
+
4242
+ where *`wrap-sender`* is the type
4243
+ `remove_cvref_t<data-type<Sndr>>::wrap-sender`.
4244
+
4245
+ The member `impls-for<associate_t>::start` is initialized with a
4246
+ callable object equivalent to the following lambda:
4247
+
4248
+ ``` cpp
4249
+ [](auto& state, auto&) noexcept -> void {
4250
+ state.run();
4251
+ }
4252
+ ```
4253
+
4254
+ The evaluation of `associate(sndr, token)` may cause side effects
4255
+ observable via `token`'s associated async scope object.
4256
+
4257
+ #### Exposition-only `execution::stop-when` <a id="exec.stop.when">[[exec.stop.when]]</a>
4258
+
4259
+ *`stop-when`* fuses an additional stop token `t` into a sender so that,
4260
+ upon connecting to a receiver `r`, the resulting operation state
4261
+ receives stop requests from both `t` and the token returned from
4262
+ `get_stop_token(get_env(r))`.
4263
+
4264
+ The name *`stop-when`* denotes an exposition-only sender adaptor. For
4265
+ subexpressions `sndr` and `token`:
4266
+
4267
+ - If `decltype((sndr))` does not satisfy `sender`, or
4268
+ `remove_cvref_t<decltype((token))>` does not satisfy
4269
+ `stoppable_token`, then `stop-when(sndr, token)` is ill-formed.
4270
+ - Otherwise, if `remove_cvref_t<decltype((token))>` models
4271
+ `unstoppable_token` then `stop-when({}sndr, token)` is
4272
+ expression-equivalent to `sndr`.
4273
+ - Otherwise, `stop-when(sndr, token)` returns a sender `osndr`. If
4274
+ `osndr` is connected to a receiver `r`, let `rtoken` be the result of
4275
+ `get_stop_token(get_env(r))`.
4276
+ - If the type of `rtoken` models `unstoppable_token` then the effects
4277
+ of connecting `osndr` to `r` are equivalent to
4278
+ `connect(write_env(sndr, prop(get_stop_token, token)), r)`.
4279
+ - Otherwise, the effects of connecting `osndr` to `r` are equivalent
4280
+ to `connect(write_env(sndr, prop(get_stop_token, stoken)), r)` where
4281
+ `stoken` is an object of an exposition-only type *`stoken-t`* such
4282
+ that:
4283
+ - *`stoken-t`* models `stoppable_token`;
4284
+ - `stoken.stop_requested()` returns
4285
+ `token.stop_requested() || rtoken.stop_reques-{}ted()`;
4286
+ - `stoken.stop_possible()` returns
4287
+ `token.stop_possible() || rtoken.stop_possible()`; and
4288
+ - for types `Fn` and `Init` such that both `invocable<Fn>` and
4289
+ `constructible_from<Fn, Init>` are modeled,
4290
+ `stoken-t::callback_type<Fn>` models
4291
+ `stoppable-callback-for<Fn, stoken-t, Init>`.\begin{tailnote}
4292
+ For an object \texttt{fn} of type \texttt{Fn}
4293
+ constructed from a value, \texttt{init}, of type \texttt{Init},
4294
+ registering \texttt{fn} using
4295
+ \texttt{\exposid{stoken-t}::callback_type\<Fn\>(stoken, init)}
4296
+ results in an invocation of \texttt{fn} when
4297
+ a callback registered with \texttt{token} or \texttt{rtoken} would be invoked.
4298
+ \texttt{fn} is invoked at most once.
4299
+ \end{tailnote}
4300
+
4301
+ #### `execution::spawn_future` <a id="exec.spawn.future">[[exec.spawn.future]]</a>
4302
+
4303
+ `spawn_future` attempts to associate the given input sender with the
4304
+ given token’s async scope and, on success, eagerly starts the input
4305
+ sender; the return value is a sender that, when connected and started,
4306
+ completes with either the result of the eagerly-started input sender or
4307
+ with `set_stopped` if the input sender was not started.
4308
+
4309
+ The name `spawn_future` denotes a customization point object. For
4310
+ subexpressions `sndr`, `token`, and `env`,
4311
+
4312
+ - let `Sndr` be `decltype((sndr))`,
4313
+ - let `Token` be `remove_cvref_t<decltype((token))>`, and
4314
+ - let `Env` be `remove_cvref_t<decltype((env))>`.
4315
+
4316
+ If any of `sender<Sndr>`, `scope_token<Token>`, or `queryable<Env>` are
4317
+ not satisfied, the expression `spawn_future(sndr, token, env)` is
4318
+ ill-formed.
4319
+
4320
+ Let *`spawn-future-state-base`* be the exposition-only class template:
4321
+
4322
+ ``` cpp
4323
+ namespace std::execution {
4324
+ template<class Completions>
4325
+ struct spawn-future-state-base; // exposition only
4326
+
4327
+ template<class... Sigs>
4328
+ struct spawn-future-state-base<completion_signatures<Sigs...>> { // exposition only
4329
+ using variant-t = see below; // exposition only
4330
+ variant-t result; // exposition only
4331
+ virtual void complete() noexcept = 0; // exposition only
4332
+ };
4333
+ }
4334
+ ```
4335
+
4336
+ Let `Sigs` be the pack of arguments to the `completion_signatures`
4337
+ specialization provided as a parameter to the
4338
+ *`spawn-future-state-base`* class template. Let *`as-tuple`* be an alias
4339
+ template that transforms a completion signature `Tag(Args...)` into the
4340
+ tuple specialization `decayed-tuple<Tag, Args...>`.
4341
+
4342
+ - If `is_nothrow_constructible_v<decay_t<Arg>, Arg>` is `true` for every
4343
+ type `Arg` in every parameter pack `Args` in every completion
4344
+ signature `Tag(Args...)` in `Sigs` then *`variant-t`* denotes the type
4345
+ `variant<monostate, tuple<set_stopped_t>, as-tuple<Sigs>...>`, except
4346
+ with duplicate types removed.
4347
+ - Otherwise *`variant-t`* denotes the type
4348
+ `variant<monostate, tuple<set_stopped_t>, tuple<set_error_t, exception_ptr>, as-tuple<Sigs>...>`,
4349
+ except with duplicate types removed.
4350
+
4351
+ Let *`spawn-future-receiver`* be the exposition-only class template:
4352
+
4353
+ ``` cpp
4354
+ namespace std::execution {
4355
+ template<class Completions>
4356
+ struct spawn-future-receiver { // exposition only
4357
+ using receiver_concept = receiver_t;
4358
+
4359
+ spawn-future-state-base<Completions>* state; // exposition only
4360
+
4361
+ template<class... T>
4362
+ void set_value(T&&... t) && noexcept {
4363
+ set-complete<set_value_t>(std::forward<T>(t)...);
4364
+ }
4365
+
4366
+ template<class E>
4367
+ void set_error(E&& e) && noexcept {
4368
+ set-complete<set_error_t>(std::forward<E>(e));
4369
+ }
4370
+
4371
+ void set_stopped() && noexcept {
4372
+ set-complete<set_stopped_t>();
4373
+ }
4374
+
4375
+ private:
4376
+ template<class CPO, class... T>
4377
+ void set-complete(T&&... t) noexcept { // exposition only
4378
+ constexpr bool nothrow = (is_nothrow_constructible_v<decay_t<T>, T> && ...);
4379
+ try {
4380
+ state->result.template emplace<decayed-tuple<CPO, T...>>(CPO{},
4381
+ std::forward<T>(t)...);
4382
+ }
4383
+ catch (...) {
4384
+ if constexpr (!nothrow) {
4385
+ using tuple_t = decayed-tuple<set_error_t, exception_ptr>;
4386
+ state->result.template emplace<tuple_t>(set_error_t{}, current_exception());
4387
+ }
4388
+ }
4389
+ state->complete();
4390
+ }
4391
+ };
4392
+ }
4393
+ ```
4394
+
4395
+ Let `ssource-t` be an unspecified type that models `stoppable-source`
4396
+ and let `ssource` be an lvalue of type `ssource-t`. Let `stoken-t` be
4397
+ `decltype(ssource.get_token())`. Let *`future-spawned-sender`* be the
4398
+ alias template:
4399
+
4400
+ ``` cpp
4401
+ template<sender Sender, class Env>
4402
+ using future-spawned-sender = // exposition only
4403
+ decltype(write_env(stop-when(declval<Sender>(), declval<stoken-t>()), declval<Env>()));
4404
+ ```
4405
+
4406
+ Let *`spawn-future-state`* be the exposition-only class template:
4407
+
4408
+ ``` cpp
4409
+ namespace std::execution {
4410
+ template<class Alloc, scope_token Token, sender Sender, class Env>
4411
+ struct spawn-future-state // exposition only
4412
+ : spawn-future-state-base<completion_signatures_of_t<future-spawned-sender<Sender, Env>>> {
4413
+ using sigs-t = // exposition only
4414
+ completion_signatures_of_t<future-spawned-sender<Sender, Env>>;
4415
+ using receiver-t = // exposition only
4416
+ spawn-future-receiver<sigs-t>;
4417
+ using op-t = // exposition only
4418
+ connect_result_t<future-spawned-sender<Sender, Env>, receiver-t>;
4419
+
4420
+ spawn-future-state(Alloc alloc, Sender&& sndr, Token token, Env env) // exposition only
4421
+ : alloc(std::move(alloc)),
4422
+ op(connect(
4423
+ write_env(stop-when(std::forward<Sender>(sndr), ssource.get_token()), std::move(env)),
4424
+ receiver-t(this))),
4425
+ token(std::move(token)),
4426
+ associated(token.try_associate()) {
4427
+ if (associated)
4428
+ start(op);
4429
+ else
4430
+ set_stopped(receiver-t(this));
4431
+ }
4432
+
4433
+ void complete() noexcept override; // exposition only
4434
+ void consume(receiver auto& rcvr) noexcept; // exposition only
4435
+ void abandon() noexcept; // exposition only
4436
+
4437
+ private:
4438
+ using alloc-t = // exposition only
4439
+ allocator_traits<Alloc>::template rebind_alloc<spawn-future-state>;
4440
+
4441
+ alloc-t alloc; // exposition only
4442
+ ssource-t ssource; // exposition only
4443
+ op-t op; // exposition only
4444
+ Token token; // exposition only
4445
+ bool associated; // exposition only
4446
+
4447
+ void destroy() noexcept; // exposition only
4448
+ };
4449
+ }
4450
+ ```
4451
+
4452
+ For purposes of determining the existence of a data race, *`complete`*,
4453
+ *`consume`*, and *`abandon`* behave as atomic operations
4454
+ [[intro.multithread]]. These operations on a single object of a type
4455
+ that is a specialization of *`spawn-future-state`* appear to occur in a
4456
+ single total order.
4457
+
4458
+ ``` cpp
4459
+ void complete() noexcept;
4460
+ ```
4461
+
4462
+ *Effects:*
4463
+
4464
+ - No effects if this invocation of *complete* happens before an
4465
+ invocation of *consume* or *abandon* on `*this`;
4466
+ - otherwise, if an invocation of *consume* on `*this` happens before
4467
+ this invocation of *complete* then there is a receiver, `rcvr`,
4468
+ registered and that receiver is completed as if by
4469
+ *`consume`*`(rcvr)`;
4470
+ - otherwise, *destroy* is invoked.
4471
+
4472
+ ``` cpp
4473
+ void consume(receiver auto& rcvr) noexcept;
4474
+ ```
4475
+
4476
+ *Effects:*
4477
+
4478
+ - If this invocation of *consume* happens before an invocation of
4479
+ *complete* on `*this` then `rcvr` is registered to be completed when
4480
+ *complete* is subsequently invoked on `*this`;
4481
+ - otherwise, `rcvr` is completed as if by:
4482
+ ``` cpp
4483
+ std::move(this->result).visit(
4484
+ [&rcvr](auto&& tuple) noexcept {
4485
+ if constexpr (!same_as<remove_reference_t<decltype(tuple)>, monostate>) {
4486
+ apply([&rcvr](auto cpo, auto&&... vals) {
4487
+ cpo(std::move(rcvr), std::move(vals)...);
4488
+ }, std::move(tuple));
4489
+ }
4490
+ });
4491
+ ```
4492
+
4493
+ ``` cpp
4494
+ void abandon() noexcept;
4495
+ ```
4496
+
4497
+ *Effects:*
4498
+
4499
+ - If this invocation of *abandon* happens before an invocation of
4500
+ *complete* on `*this` then equivalent to:
4501
+ ``` cpp
4502
+ ssource.request_stop();
4503
+ ```
4504
+ - otherwise, *destroy* is invoked.
4505
+
4506
+ ``` cpp
4507
+ void destroy() noexcept;
4508
+ ```
4509
+
4510
+ *Effects:* Equivalent to:
4511
+
4512
+ ``` cpp
4513
+ auto token = std::move(this->token);
4514
+ bool associated = this->associated;
4515
+
4516
+ {
4517
+ auto alloc = std::move(this->alloc);
4518
+
4519
+ allocator_traits<alloc-t>::destroy(alloc, this);
4520
+ allocator_traits<alloc-t>::deallocate(alloc, this, 1);
4521
+ }
4522
+
4523
+ if (associated)
4524
+ token.disassociate();
4525
+ ```
4526
+
4527
+ The exposition-only class template *`impls-for`* [[exec.snd.expos]] is
4528
+ specialized for `spawn_future_t` as follows:
4529
+
4530
+ ``` cpp
4531
+ namespace std::execution {
4532
+ template<>
4533
+ struct impls-for<spawn_future_t> : default-impls {
4534
+ static constexpr auto start = see below; // exposition only
4535
+ };
4536
+ }
4537
+ ```
4538
+
4539
+ The member `impls-for<spawn_future_t>::start` is initialized with a
4540
+ callable object equivalent to the following lambda:
4541
+
4542
+ ``` cpp
4543
+ [](auto& state, auto& rcvr) noexcept -> void {
4544
+ state->consume(rcvr);
4545
+ }
4546
+ ```
4547
+
4548
+ For the expression `spawn_future(sndr, token, env)` let `new_sender` be
4549
+ the expression `token.wrap(sndr)` and let `alloc` and `senv` be defined
4550
+ as follows:
4551
+
4552
+ - if the expression `get_allocator(env)` is well-formed, then `alloc` is
4553
+ the result of `get_allocator(env)` and `senv` is the expression `env`;
4554
+ - otherwise, if the expression `get_allocator(get_env(new_sender))` is
4555
+ well-formed, then `alloc` is the result of
4556
+ `get_allocator(get_env(new_sender))` and `senv` is the expression
4557
+ `JOIN-ENV(prop(get_allocator, alloc), env)`;
4558
+ - otherwise, `alloc` is `allocator<void>()` and `senv` is the expression
4559
+ `env`.
4560
+
4561
+ The expression `spawn_future(sndr, token, env)` has the following
4562
+ effects:
4563
+
4564
+ - Uses `alloc` to allocate and construct an object `s` of a type that is
4565
+ a specialization of *`spawn-future-{}state`* from `alloc`,
4566
+ `token.wrap(sndr)`, `token`, and `senv`. If an exception is thrown
4567
+ then any constructed objects are destroyed and any allocated memory is
4568
+ deallocated.
4569
+ - Constructs an object `u` of a type that is a specialization of
4570
+ `unique_ptr` such that:
4571
+ - `u.get()` is equal to the address of `s`, and
4572
+ - `u.get_deleter()(u.release())` is equivalent to
4573
+ `u.release()->abandon()`.
4574
+ - Returns `make-sender(spawn_future, std::move(u))`.
4575
+
4576
+ The expression `spawn_future(sndr, token)` is expression-equivalent to
4577
+ `spawn_future(sndr, token, execution::env<>())`.
4578
+
4579
+ ### Sender consumers <a id="exec.consumers">[[exec.consumers]]</a>
4580
+
4581
+ #### `this_thread::sync_wait` <a id="exec.sync.wait">[[exec.sync.wait]]</a>
4582
+
4583
+ `this_thread::sync_wait` and `this_thread::sync_wait_with_variant` are
4584
+ used to block the current thread of execution until the specified sender
4585
+ completes and to return its async result. `sync_wait` mandates that the
4586
+ input sender has exactly one value completion signature.
4587
+
4588
+ Let *`sync-wait-env`* be the following exposition-only class type:
4589
+
4590
+ ``` cpp
4591
+ namespace std::this_thread {
4592
+ struct sync-wait-env {
4593
+ execution::run_loop* loop; // exposition only
4594
+
4595
+ auto query(execution::get_scheduler_t) const noexcept {
4596
+ return loop->get_scheduler();
4597
+ }
4598
+
4599
+ auto query(execution::get_delegation_scheduler_t) const noexcept {
4600
+ return loop->get_scheduler();
4601
+ }
4602
+ };
4603
+ }
4604
+ ```
4605
+
4606
+ Let *`sync-wait-result-type`* and *`sync-wait-with-variant-result-type`*
4607
+ be exposition-only alias templates defined as follows:
4608
+
4609
+ ``` cpp
4610
+ namespace std::this_thread {
4611
+ template<execution::sender_in<sync-wait-env> Sndr>
4612
+ using sync-wait-result-type =
4613
+ optional<execution::value_types_of_t<Sndr, sync-wait-env, decayed-tuple,
4614
+ type_identity_t>>;
4615
+
4616
+ template<execution::sender_in<sync-wait-env> Sndr>
4617
+ using sync-wait-with-variant-result-type =
4618
+ optional<execution::value_types_of_t<Sndr, sync-wait-env>>;
4619
+ }
4620
+ ```
4621
+
4622
+ The name `this_thread::sync_wait` denotes a customization point object.
4623
+ For a subexpression `sndr`, let `Sndr` be `decltype((sndr))`. The
4624
+ expression `this_thread::sync_wait(sndr)` is expression-equivalent to
4625
+ the following, except that `sndr` is evaluated only once:
4626
+
4627
+ ``` cpp
4628
+ apply_sender(get-domain-early(sndr), sync_wait, sndr)
4629
+ ```
4630
+
4631
+ *Mandates:*
4632
+
4633
+ - `sender_in<Sndr, sync-wait-env>` is `true`.
4634
+ - The type `sync-wait-result-type<Sndr>` is well-formed.
4635
+ - `same_as<decltype(e), sync-wait-result-type<Sndr>>` is `true`, where e
4636
+ is the `apply_sender` expression above.
4637
+
4638
+ Let *`sync-wait-state`* and *`sync-wait-receiver`* be the following
4639
+ exposition-only class templates:
4640
+
4641
+ ``` cpp
4642
+ namespace std::this_thread {
4643
+ template<class Sndr>
4644
+ struct sync-wait-state { // exposition only
4645
+ execution::run_loop loop; // exposition only
4646
+ exception_ptr error; // exposition only
4647
+ sync-wait-result-type<Sndr> result; // exposition only
4648
+ };
4649
+
4650
+ template<class Sndr>
4651
+ struct sync-wait-receiver { // exposition only
4652
+ using receiver_concept = execution::receiver_t;
4653
+ sync-wait-state<Sndr>* state; // exposition only
4654
+
4655
+ template<class... Args>
4656
+ void set_value(Args&&... args) && noexcept;
4657
+
4658
+ template<class Error>
4659
+ void set_error(Error&& err) && noexcept;
4660
+
4661
+ void set_stopped() && noexcept;
4662
+
4663
+ sync-wait-env get_env() const noexcept { return {&state->loop}; }
4664
+ };
4665
+ }
4666
+ ```
4667
+
4668
+ ``` cpp
4669
+ template<class... Args>
4670
+ void set_value(Args&&... args) && noexcept;
4671
+ ```
4672
+
4673
+ *Effects:* Equivalent to:
4674
+
4675
+ ``` cpp
4676
+ try {
4677
+ state->result.emplace(std::forward<Args>(args)...);
4678
+ } catch (...) {
4679
+ state->error = current_exception();
4680
+ }
4681
+ state->loop.finish();
4682
+ ```
4683
+
4684
+ ``` cpp
4685
+ template<class Error>
4686
+ void set_error(Error&& err) && noexcept;
4687
+ ```
4688
+
4689
+ *Effects:* Equivalent to:
4690
+
4691
+ ``` cpp
4692
+ state->error = AS-EXCEPT-PTR(std::forward<Error>(err)); // see [exec.general]
4693
+ state->loop.finish();
4694
+ ```
4695
+
4696
+ ``` cpp
4697
+ void set_stopped() && noexcept;
4698
+ ```
4699
+
4700
+ *Effects:* Equivalent to *`state`*`->`*`loop`*`.finish()`.
4701
+
4702
+ For a subexpression `sndr`, let `Sndr` be `decltype((sndr))`. If
4703
+ `sender_to<Sndr, sync-wait-receiver<Sndr>>` is `false`, the expression
4704
+ `sync_wait.apply_sender(sndr)` is ill-formed; otherwise, it is
4705
+ equivalent to:
4706
+
4707
+ ``` cpp
4708
+ sync-wait-state<Sndr> state;
4709
+ auto op = connect(sndr, sync-wait-receiver<Sndr>{&state});
4710
+ start(op);
4711
+
4712
+ state.loop.run();
4713
+ if (state.error) {
4714
+ rethrow_exception(std::move(state.error));
4715
+ }
4716
+ return std::move(state.result);
4717
+ ```
4718
+
4719
+ The behavior of `this_thread::sync_wait(sndr)` is undefined unless:
4720
+
4721
+ - It blocks the current thread of execution [[defns.block]] with forward
4722
+ progress guarantee delegation [[intro.progress]] until the specified
4723
+ sender completes. \[*Note 1*: The default implementation of
4724
+ `sync_wait` achieves forward progress guarantee delegation by
4725
+ providing a `run_loop` scheduler via the `get_delegation_scheduler`
4726
+ query on the *`sync-wait-receiver`*’s environment. The `run_loop` is
4727
+ driven by the current thread of execution. — *end note*]
4728
+ - It returns the specified sender’s async results as follows:
4729
+ - For a value completion, the result datums are returned in a `tuple`
4730
+ in an engaged `optional` object.
4731
+ - For an error completion, an exception is thrown.
4732
+ - For a stopped completion, a disengaged `optional` object is
4733
+ returned.
4734
+
4735
+ #### `this_thread::sync_wait_with_variant` <a id="exec.sync.wait.var">[[exec.sync.wait.var]]</a>
4736
+
4737
+ The name `this_thread::sync_wait_with_variant` denotes a customization
4738
+ point object. For a subexpression `sndr`, let `Sndr` be
4739
+ `decltype(into_variant(sndr))`. The expression
4740
+ `this_thread::sync_wait_with_variant(sndr)` is expression-equivalent to
4741
+ the following, except `sndr` is evaluated only once:
4742
+
4743
+ ``` cpp
4744
+ apply_sender(get-domain-early(sndr), sync_wait_with_variant, sndr)
4745
+ ```
4746
+
4747
+ *Mandates:*
4748
+
4749
+ - `sender_in<Sndr, sync-wait-env>` is `true`.
4750
+ - The type `sync-wait-with-variant-result-type<Sndr>` is well-formed.
4751
+ - `same_as<decltype(e), sync-wait-with-variant-result-type<Sndr>>` is
4752
+ `true`, where e is the `apply_sender` expression above.
4753
+
4754
+ The expression `sync_wait_with_variant.apply_sender(sndr)` is equivalent
4755
+ to:
4756
+
4757
+ ``` cpp
4758
+ using result_type = sync-wait-with-variant-result-type<Sndr>;
4759
+ if (auto opt_value = sync_wait(into_variant(sndr))) {
4760
+ return result_type(std::move(get<0>(*opt_value)));
4761
+ }
4762
+ return result_type(nullopt);
4763
+ ```
4764
+
4765
+ The behavior of `this_thread::sync_wait_with_variant(sndr)` is undefined
4766
+ unless:
4767
+
4768
+ - It blocks the current thread of execution [[defns.block]] with forward
4769
+ progress guarantee delegation [[intro.progress]] until the specified
4770
+ sender completes. \[*Note 1*: The default implementation of
4771
+ `sync_wait_with_variant` achieves forward progress guarantee
4772
+ delegation by relying on the forward progress guarantee delegation
4773
+ provided by `sync_wait`. — *end note*]
4774
+ - It returns the specified sender’s async results as follows:
4775
+ - For a value completion, the result datums are returned in an engaged
4776
+ `optional` object that contains a `variant` of `tuple`s.
4777
+ - For an error completion, an exception is thrown.
4778
+ - For a stopped completion, a disengaged `optional` object is
4779
+ returned.
4780
+
4781
+ #### `execution::spawn` <a id="exec.spawn">[[exec.spawn]]</a>
4782
+
4783
+ `spawn` attempts to associate the given input sender with the given
4784
+ token’s async scope and, on success, eagerly starts the input sender.
4785
+
4786
+ The name `spawn` denotes a customization point object. For
4787
+ subexpressions `sndr`, `token`, and `env`,
4788
+
4789
+ - let `Sndr` be `decltype((sndr))`,
4790
+ - let `Token` be `remove_cvref_t<decltype((token))>`, and
4791
+ - let `Env` be `remove_cvref_t<decltype((env))>`.
4792
+
4793
+ If any of `sender<Sndr>`, `scope_token<Token>`, or `queryable<Env>` are
4794
+ not satisfied, the expression `spawn({}sndr, token, env)` is ill-formed.
4795
+
4796
+ Let *`spawn-state-base`* be the exposition-only class:
4797
+
4798
+ ``` cpp
4799
+ namespace std::execution {
4800
+ struct spawn-state-base { // exposition only
4801
+ virtual void complete() noexcept = 0; // exposition only
4802
+ };
4803
+ }
4804
+ ```
4805
+
4806
+ Let *`spawn-receiver`* be the exposition-only class:
4807
+
4808
+ ``` cpp
4809
+ namespace std::execution {
4810
+ struct spawn-receiver { // exposition only
4811
+ using receiver_concept = receiver_t;
4812
+
4813
+ spawn-state-base* state; // exposition only
4814
+ void set_value() && noexcept { state->complete(); }
4815
+ void set_stopped() && noexcept { state->complete(); }
4816
+ };
4817
+ }
4818
+ ```
4819
+
4820
+ Let *`spawn-state`* be the exposition-only class template:
4821
+
4822
+ ``` cpp
4823
+ namespace std::execution {
4824
+ template<class Alloc, scope_token Token, sender Sender>
4825
+ struct spawn-state : spawn-state-base { // exposition only
4826
+ using op-t = connect_result_t<Sender, spawn-receiver>; // exposition only
4827
+
4828
+ spawn-state(Alloc alloc, Sender&& sndr, Token token); // exposition only
4829
+ void complete() noexcept override; // exposition only
4830
+ void run(); // exposition only
4831
+
4832
+ private:
4833
+ using alloc-t = // exposition only
4834
+ allocator_traits<Alloc>::template rebind_alloc<spawn-state>;
4835
+
4836
+ alloc-t alloc; // exposition only
4837
+ op-t op; // exposition only
4838
+ Token token; // exposition only
4839
+
4840
+ void destroy() noexcept; // exposition only
4841
+ };
4842
+ }
4843
+ ```
4844
+
4845
+ ``` cpp
4846
+ spawn-state(Alloc alloc, Sender&& sndr, Token token);
4847
+ ```
4848
+
4849
+ *Effects:* Initializes *alloc* with `alloc`, *token* with `token`, and
4850
+ *op* with:
4851
+
4852
+ ``` cpp
4853
+ connect(std::move(sndr), spawn-receiver(this))
4854
+ ```
4855
+
4856
+ ``` cpp
4857
+ void run();
4858
+ ```
4859
+
4860
+ *Effects:* Equivalent to:
4861
+
4862
+ ``` cpp
4863
+ if (token.try_associate())
4864
+ start(op);
4865
+ else
4866
+ destroy();
4867
+ ```
4868
+
4869
+ ``` cpp
4870
+ void complete() noexcept override;
4871
+ ```
4872
+
4873
+ *Effects:* Equivalent to:
4874
+
4875
+ ``` cpp
4876
+ auto token = std::move(this->token);
4877
+
4878
+ destroy();
4879
+ token.disassociate();
4880
+ ```
4881
+
4882
+ ``` cpp
4883
+ void destroy() noexcept;
4884
+ ```
4885
+
4886
+ *Effects:* Equivalent to:
4887
+
4888
+ ``` cpp
4889
+ auto alloc = std::move(this->alloc);
4890
+
4891
+ allocator_traits<alloc-t>::destroy(alloc, this);
4892
+ allocator_traits<alloc-t>::deallocate(alloc, this, 1);
4893
+ ```
4894
+
4895
+ For the expression `spawn(sndr, token, env)` let `new_sender` be the
4896
+ expression `token.wrap(sndr)` and let `alloc` and `senv` be defined as
4897
+ follows:
4898
+
4899
+ - if the expression `get_allocator(env)` is well-formed, then `alloc` is
4900
+ the result of `get_allocator(env)` and `senv` is the expression `env`,
4901
+ - otherwise if the expression `get_allocator(get_env(new_sender))` is
4902
+ well-formed, then `alloc` is the result of
4903
+ `get_allocator(get_env(new_sender))` and `senv` is the expression
4904
+ `JOIN-ENV(prop(get_allocator, alloc), env)`,
4905
+ - otherwise `alloc` is `allocator<void>()` and `senv` is the expression
4906
+ `env`.
4907
+
4908
+ The expression `spawn(sndr, token, env)` is of type `void` and has the
4909
+ following effects:
4910
+
4911
+ - Uses `alloc` to allocate and construct an object `o` of type that is a
4912
+ specialization of `spawn-state` from `alloc`,
4913
+ `write_env(token.wrap(sndr), senv)`, and `token` and then invokes
4914
+ `o.run()`. If an exception is thrown then any constructed objects are
4915
+ destroyed and any allocated memory is deallocated.
4916
+
4917
+ The expression `spawn(sndr, token)` is expression-equivalent to
4918
+ `spawn(sndr, token, execution::env<>({}))`.
4919
+
4920
+ ## Completion signatures <a id="exec.cmplsig">[[exec.cmplsig]]</a>
4921
+
4922
+ `completion_signatures` is a type that encodes a set of completion
4923
+ signatures [[exec.async.ops]].
4924
+
4925
+ [*Example 1*:
4926
+
4927
+ ``` cpp
4928
+ struct my_sender {
4929
+ using sender_concept = sender_t;
4930
+ using completion_signatures =
4931
+ execution::completion_signatures<
4932
+ set_value_t(),
4933
+ set_value_t(int, float),
4934
+ set_error_t(exception_ptr),
4935
+ set_error_t(error_code),
4936
+ set_stopped_t()>;
4937
+ };
4938
+ ```
4939
+
4940
+ Declares `my_sender` to be a sender that can complete by calling one of
4941
+ the following for a receiver expression `rcvr`:
4942
+
4943
+ - `set_value(rcvr)`
4944
+ - `set_value(rcvr, int{...}, float{...})`
4945
+ - `set_error(rcvr, exception_ptr{...})`
4946
+ - `set_error(rcvr, error_code{...})`
4947
+ - `set_stopped(rcvr)`
4948
+
4949
+ — *end example*]
4950
+
4951
+ This subclause makes use of the following exposition-only entities:
4952
+
4953
+ ``` cpp
4954
+ template<class Fn>
4955
+ concept completion-signature = see below;
4956
+ ```
4957
+
4958
+ A type `Fn` satisfies `completion-signature` if and only if it is a
4959
+ function type with one of the following forms:
4960
+
4961
+ - `set_value_t(Vs...)`, where `Vs` is a pack of object or reference
4962
+ types.
4963
+ - `set_error_t(Err)`, where `Err` is an object or reference type.
4964
+ - `set_stopped_t()`
4965
+
4966
+ ``` cpp
4967
+ template<bool>
4968
+ struct indirect-meta-apply {
4969
+ template<template<class...> class T, class... As>
4970
+ using meta-apply = T<As...>; // exposition only
4971
+ };
4972
+
4973
+ template<class...>
4974
+ concept always-true = true; // exposition only
4975
+
4976
+ template<class Tag,
4977
+ valid-completion-signatures Completions,
4978
+ template<class...> class Tuple,
4979
+ template<class...> class Variant>
4980
+ using gather-signatures = see below;
4981
+ ```
4982
+
4983
+ Let `Fns` be a pack of the arguments of the `completion_signatures`
4984
+ specialization named by `Completions`, let `TagFns` be a pack of the
4985
+ function types in `Fns` whose return types are `Tag`, and let `Tsₙ` be a
4986
+ pack of the function argument types in the n-th type in `TagFns`. Then,
4987
+ given two variadic templates `Tuple` and `Variant`, the type
4988
+ `gather-signatures<Tag, Completions, Tuple, Variant>` names the type
4989
+
4990
+ ``` cpp
4991
+ META-APPLY(Variant, META-APPLY(Tuple, Ts_0...),
4992
+ META-APPLY(Tuple, Ts_1...),
4993
+ …,
4994
+ META-APPLY(Tuple, Ts_{m-1}...))
4995
+ ```
4996
+
4997
+ where m is the size of the pack `TagFns` and `META-APPLY(T, As...)` is
4998
+ equivalent to:
4999
+
5000
+ ``` cpp
5001
+ typename indirect-meta-apply<always-true<As...>>::template meta-apply<T, As...>
5002
+ ```
5003
+
5004
+ [*Note 1*: The purpose of *`META-APPLY`* is to make it valid to use
5005
+ non-variadic templates as `Variant` and `Tuple` arguments to
5006
+ *`gather-signatures`*. — *end note*]
5007
+
5008
+ ``` cpp
5009
+ namespace std::execution {
5010
+ template<completion-signature... Fns>
5011
+ struct completion_signatures {
5012
+ template<class Tag>
5013
+ static constexpr size_t count-of(Tag) { return see below; }
5014
+
5015
+ template<class Fn>
5016
+ static constexpr void for-each(Fn&& fn) { // exposition only
5017
+ (std::forward<Fn>(fn)(static_cast<Fns*>(nullptr)), ...);
5018
+ }
5019
+ };
5020
+
5021
+ template<class Sndr, class Env = env<>,
5022
+ template<class...> class Tuple = decayed-tuple,
5023
+ template<class...> class Variant = variant-or-empty>
5024
+ requires sender_in<Sndr, Env>
5025
+ using value_types_of_t =
5026
+ gather-signatures<set_value_t, completion_signatures_of_t<Sndr, Env>, Tuple, Variant>;
5027
+
5028
+ template<class Sndr, class Env = env<>,
5029
+ template<class...> class Variant = variant-or-empty>
5030
+ requires sender_in<Sndr, Env>
5031
+ using error_types_of_t =
5032
+ gather-signatures<set_error_t, completion_signatures_of_t<Sndr, Env>,
5033
+ type_identity_t, Variant>;
5034
+
5035
+ template<class Sndr, class Env = env<>>
5036
+ requires sender_in<Sndr, Env>
5037
+ constexpr bool sends_stopped =
5038
+ !same_as<type-list<>,
5039
+ gather-signatures<set_stopped_t, completion_signatures_of_t<Sndr, Env>,
5040
+ type-list, type-list>>;
5041
+ }
5042
+ ```
5043
+
5044
+ For a subexpression `tag`, let `Tag` be the decayed type of `tag`.
5045
+ `completion_signatures<Fns...>::count-of({}tag)` returns the count of
5046
+ function types in `Fns...` that are of the form `Tag(Ts...)` where `Ts`
5047
+ is a pack of types.
5048
+
5049
+ ## Queryable utilities <a id="exec.envs">[[exec.envs]]</a>
5050
+
5051
+ ### Class template `prop` <a id="exec.prop">[[exec.prop]]</a>
5052
+
5053
+ ``` cpp
5054
+ namespace std::execution {
5055
+ template<class QueryTag, class ValueType>
5056
+ struct prop {
5057
+ QueryTag query_; // exposition only
5058
+ ValueType value_; // exposition only
5059
+
5060
+ constexpr const ValueType& query(QueryTag) const noexcept {
5061
+ return value_;
5062
+ }
5063
+ };
5064
+
5065
+ template<class QueryTag, class ValueType>
5066
+ prop(QueryTag, ValueType) -> prop<QueryTag, unwrap_reference_t<ValueType>>;
5067
+ }
5068
+ ```
5069
+
5070
+ Class template `prop` is for building a queryable object from a query
5071
+ object and a value.
5072
+
5073
+ *Mandates:* `callable<QueryTag, prop-like<ValueType>>` is modeled, where
5074
+ *`prop-like`* is the following exposition-only class template:
5075
+
5076
+ ``` cpp
5077
+ template<class ValueType>
5078
+ struct prop-like { // exposition only
5079
+ const ValueType& query(auto) const noexcept;
5080
+ };
5081
+ ```
5082
+
5083
+ [*Example 1*:
5084
+
5085
+ ``` cpp
5086
+ template<sender Sndr>
5087
+ sender auto parameterize_work(Sndr sndr) {
5088
+ // Make an environment such that get_allocator(env) returns a reference to a copy of my_alloc{}.
5089
+ auto e = prop(get_allocator, my_alloc{});
5090
+
5091
+ // Parameterize the input sender so that it will use our custom execution environment.
5092
+ return write_env(sndr, e);
5093
+ }
5094
+ ```
5095
+
5096
+ — *end example*]
5097
+
5098
+ Specializations of `prop` are not assignable.
5099
+
5100
+ ### Class template `env` <a id="exec.env">[[exec.env]]</a>
5101
+
5102
+ ``` cpp
5103
+ namespace std::execution {
5104
+ template<queryable... Envs>
5105
+ struct env {
5106
+ Envs_0 envs_0; // exposition only
5107
+ Envs_1 envs_1; // exposition only
5108
+
5109
+ Envs_{n-1} envs_{n-1}; // exposition only
5110
+
5111
+ template<class QueryTag>
5112
+ constexpr decltype(auto) query(QueryTag q) const noexcept(see below);
5113
+ };
5114
+
5115
+ template<class... Envs>
5116
+ env(Envs...) -> env<unwrap_reference_t<Envs>...>;
5117
+ }
5118
+ ```
5119
+
5120
+ The class template `env` is used to construct a queryable object from
5121
+ several queryable objects. Query invocations on the resulting object are
5122
+ resolved by attempting to query each subobject in lexical order.
5123
+
5124
+ Specializations of `env` are not assignable.
5125
+
5126
+ It is unspecified whether `env` supports initialization using a
5127
+ parenthesized *expression-list* [[dcl.init]], unless the
5128
+ *expression-list* consist of a single element of type (possibly const)
5129
+ `env`.
5130
+
5131
+ [*Example 1*:
5132
+
5133
+ ``` cpp
5134
+ template<sender Sndr>
5135
+ sender auto parameterize_work(Sndr sndr) {
5136
+ // Make an environment such that:
5137
+ // get_allocator(env) returns a reference to a copy of my_alloc{}
5138
+ // get_scheduler(env) returns a reference to a copy of my_sched{}
5139
+ auto e = env{prop(get_allocator, my_alloc{}),
5140
+ prop(get_scheduler, my_sched{})};
5141
+
5142
+ // Parameterize the input sender so that it will use our custom execution environment.
5143
+ return write_env(sndr, e);
5144
+ }
5145
+ ```
5146
+
5147
+ — *end example*]
5148
+
5149
+ ``` cpp
5150
+ template<class QueryTag>
5151
+ constexpr decltype(auto) query(QueryTag q) const noexcept(see below);
5152
+ ```
5153
+
5154
+ Let `has-query` be the following exposition-only concept:
5155
+
5156
+ ``` cpp
5157
+ template<class Env, class QueryTag>
5158
+ concept has-query = // exposition only
5159
+ requires (const Env& env) {
5160
+ env.query(QueryTag());
5161
+ };
5162
+ ```
5163
+
5164
+ Let *fe* be the first element of envs₀, envs₁, …, envsₙ₋₁ such that the
5165
+ expression *`fe`*`.query(q)` is well-formed.
5166
+
5167
+ *Constraints:* `(has-query<Envs, QueryTag> || ...)` is `true`.
5168
+
5169
+ *Effects:* Equivalent to: `return `*`fe`*`.query(q);`
5170
+
5171
+ *Remarks:* The expression in the `noexcept` clause is equivalent to
5172
+ `noexcept(`*`fe`*`.query(q))`.
5173
+
5174
+ ## Execution contexts <a id="exec.ctx">[[exec.ctx]]</a>
5175
+
5176
+ ### `execution::run_loop` <a id="exec.run.loop">[[exec.run.loop]]</a>
5177
+
5178
+ #### General <a id="exec.run.loop.general">[[exec.run.loop.general]]</a>
5179
+
5180
+ A `run_loop` is an execution resource on which work can be scheduled. It
5181
+ maintains a thread-safe first-in-first-out queue of work. Its `run`
5182
+ member function removes elements from the queue and executes them in a
5183
+ loop on the thread of execution that calls `run`.
5184
+
5185
+ A `run_loop` instance has an associated *count* that corresponds to the
5186
+ number of work items that are in its queue. Additionally, a `run_loop`
5187
+ instance has an associated state that can be one of *starting*,
5188
+ *running*, *finishing*, or *finished*.
5189
+
5190
+ Concurrent invocations of the member functions of `run_loop` other than
5191
+ `run` and its destructor do not introduce data races. The member
5192
+ functions *`pop-front`*, *`push-back`*, and `finish` execute atomically.
5193
+
5194
+ *Recommended practice:* Implementations should use an intrusive queue of
5195
+ operation states to hold the work units to make scheduling
5196
+ allocation-free.
5197
+
5198
+ ``` cpp
5199
+ namespace std::execution {
5200
+ class run_loop {
5201
+ // [exec.run.loop.types], associated types
5202
+ class run-loop-scheduler; // exposition only
5203
+ class run-loop-sender; // exposition only
5204
+ struct run-loop-opstate-base { // exposition only
5205
+ virtual void execute() = 0; // exposition only
5206
+ run_loop* loop; // exposition only
5207
+ run-loop-opstate-base* next; // exposition only
5208
+ };
5209
+ template<class Rcvr>
5210
+ using run-loop-opstate = unspecified; // exposition only
5211
+
5212
+ // [exec.run.loop.members], member functions
5213
+ run-loop-opstate-base* pop-front(); // exposition only
5214
+ void push-back(run-loop-opstate-base*); // exposition only
5215
+
5216
+ public:
5217
+ // [exec.run.loop.ctor], constructor and destructor
5218
+ run_loop() noexcept;
5219
+ run_loop(run_loop&&) = delete;
5220
+ ~run_loop();
5221
+
5222
+ // [exec.run.loop.members], member functions
5223
+ run-loop-scheduler get_scheduler();
5224
+ void run();
5225
+ void finish();
5226
+ };
5227
+ }
5228
+ ```
5229
+
5230
+ #### Associated types <a id="exec.run.loop.types">[[exec.run.loop.types]]</a>
5231
+
5232
+ ``` cpp
5233
+ class run-loop-scheduler;
5234
+ ```
5235
+
5236
+ *`run-loop-scheduler`* is an unspecified type that models `scheduler`.
5237
+
5238
+ Instances of *`run-loop-scheduler`* remain valid until the end of the
5239
+ lifetime of the `run_loop` instance from which they were obtained.
5240
+
5241
+ Two instances of *`run-loop-scheduler`* compare equal if and only if
5242
+ they were obtained from the same `run_loop` instance.
5243
+
5244
+ Let *`sch`* be an expression of type *`run-loop-scheduler`*. The
5245
+ expression `schedule(sch)` has type *`run-loop- sender`* and is not
5246
+ potentially-throwing if *`sch`* is not potentially-throwing.
5247
+
5248
+ ``` cpp
5249
+ class run-loop-sender;
5250
+ ```
5251
+
5252
+ *`run-loop-sender`* is an exposition-only type that satisfies `sender`.
5253
+ `completion_signatures_of_t<run-{loop-sender}>` is
5254
+
5255
+ ``` cpp
5256
+ completion_signatures<set_value_t(), set_error_t(exception_ptr), set_stopped_t()>
5257
+ ```
5258
+
5259
+ An instance of *`run-loop-sender`* remains valid until the end of the
5260
+ lifetime of its associated `run_loop` instance.
5261
+
5262
+ Let *`sndr`* be an expression of type *`run-loop-sender`*, let *`rcvr`*
5263
+ be an expression such that `receiver_of<decltype((rcvr)), CS>` is `true`
5264
+ where `CS` is the `completion_signatures` specialization above. Let `C`
5265
+ be either `set_value_t` or `set_stopped_t`. Then:
5266
+
5267
+ - The expression `connect(sndr, rcvr)` has type
5268
+ `run-loop-opstate<decay_t<decltype((rcvr))>>` and is
5269
+ potentially-throwing if and only if `(void(sndr), auto(rcvr))` is
5270
+ potentially-throwing.
5271
+ - The expression `get_completion_scheduler<C>(get_env(sndr))` is
5272
+ potentially-throwing if and only if *`sndr`* is potentially-throwing,
5273
+ has type *`run-loop-scheduler`*, and compares equal to the
5274
+ *`run-loop- scheduler`* instance from which *`sndr`* was obtained.
5275
+
5276
+ ``` cpp
5277
+ template<class Rcvr>
5278
+ struct run-loop-opstate;
5279
+ ```
5280
+
5281
+ `\exposid{run-loop-opstate}<Rcvr>`
5282
+
5283
+ inherits privately and unambiguously from *`run-loop-opstate-base`*.
5284
+
5285
+ Let o be a non-const lvalue of type `run-loop-opstate<Rcvr>`, and let
5286
+ `REC(o)` be a non-const lvalue reference to an instance of type `Rcvr`
5287
+ that was initialized with the expression *`rcvr`* passed to the
5288
+ invocation of connect that returned o. Then:
5289
+
5290
+ - The object to which `REC(o)` refers remains valid for the lifetime of
5291
+ the object to which o refers.
5292
+ - The type `run-loop-opstate<Rcvr>` overrides
5293
+ `run-loop-opstate-base::execute()` such that `o.execute()` is
5294
+ equivalent to:
5295
+ ``` cpp
5296
+ if (get_stop_token(REC(o)).stop_requested()) {
5297
+ set_stopped(std::move(REC(o)));
5298
+ } else {
5299
+ set_value(std::move(REC(o)));
5300
+ }
5301
+ ```
5302
+ - The expression `start(o)` is equivalent to:
5303
+ ``` cpp
5304
+ try {
5305
+ o.loop->push-back(addressof(o));
5306
+ } catch(...) {
5307
+ set_error(std::move(REC(o)), current_exception());
5308
+ }
5309
+ ```
5310
+
5311
+ #### Constructor and destructor <a id="exec.run.loop.ctor">[[exec.run.loop.ctor]]</a>
5312
+
5313
+ ``` cpp
5314
+ run_loop() noexcept;
5315
+ ```
5316
+
5317
+ *Ensures:* The `run_loop` instance’s count is 0 and its state is
5318
+ starting.
5319
+
5320
+ ``` cpp
5321
+ ~run_loop();
5322
+ ```
5323
+
5324
+ *Effects:* If the `run_loop` instance’s count is not 0 or if its state
5325
+ is running, invokes `terminate` [[except.terminate]]. Otherwise, has no
5326
+ effects.
5327
+
5328
+ #### Member functions <a id="exec.run.loop.members">[[exec.run.loop.members]]</a>
5329
+
5330
+ ``` cpp
5331
+ run-loop-opstate-base* pop-front();
5332
+ ```
5333
+
5334
+ *Effects:* Blocks [[defns.block]] until one of the following conditions
5335
+ is `true`:
5336
+
5337
+ - The `run_loop` instance’s count is 0 and its state is finishing, in
5338
+ which case *pop-front* sets the state to finished and returns
5339
+ `nullptr`; or
5340
+ - the `run_loop` instance’s count is greater than 0, in which case an
5341
+ item is removed from the front of the queue, the count is decremented
5342
+ by `1`, and the removed item is returned.
5343
+
5344
+ ``` cpp
5345
+ void push-back(run-loop-opstate-base* item);
5346
+ ```
5347
+
5348
+ *Effects:* Adds `item` to the back of the queue and increments the
5349
+ `run_loop` instance’s count by 1.
5350
+
5351
+ *Synchronization:* This operation synchronizes with the *pop-front*
5352
+ operation that obtains `item`.
5353
+
5354
+ ``` cpp
5355
+ run-loop-scheduler get_scheduler();
5356
+ ```
5357
+
5358
+ *Returns:* An instance of *run-loop-scheduler* that can be used to
5359
+ schedule work onto this `run_loop` instance.
5360
+
5361
+ ``` cpp
5362
+ void run();
5363
+ ```
5364
+
5365
+ *Preconditions:* The `run_loop` instance’s state is either starting or
5366
+ finishing.
5367
+
5368
+ *Effects:* If the `run_loop` instance’s state is starting, sets the
5369
+ state to running, otherwise leaves the state unchanged. Then, equivalent
5370
+ to:
5371
+
5372
+ ``` cpp
5373
+ while (auto* op = pop-front()) {
5374
+ op->execute();
5375
+ }
5376
+ ```
5377
+
5378
+ *Remarks:* When the `run_loop` instance’s state changes, it does so
5379
+ without introducing data races.
5380
+
5381
+ ``` cpp
5382
+ void finish();
5383
+ ```
5384
+
5385
+ *Preconditions:* The `run_loop` instance’s state is either starting or
5386
+ running.
5387
+
5388
+ *Effects:* Changes the `run_loop` instance’s state to finishing.
5389
+
5390
+ *Synchronization:* `finish` synchronizes with the *pop-front* operation
5391
+ that returns `nullptr`.
5392
+
5393
+ ## Coroutine utilities <a id="exec.coro.util">[[exec.coro.util]]</a>
5394
+
5395
+ ### `execution::as_awaitable` <a id="exec.as.awaitable">[[exec.as.awaitable]]</a>
5396
+
5397
+ `as_awaitable` transforms an object into one that is awaitable within a
5398
+ particular coroutine. Subclause [[exec.coro.util]] makes use of the
5399
+ following exposition-only entities:
5400
+
5401
+ ``` cpp
5402
+ namespace std::execution {
5403
+ template<class Sndr, class Promise>
5404
+ concept awaitable-sender =
5405
+ single-sender<Sndr, env_of_t<Promise>> &&
5406
+ sender_to<Sndr, awaitable-receiver> && // see below
5407
+ requires (Promise& p) {
5408
+ { p.unhandled_stopped() } -> convertible_to<coroutine_handle<>>;
5409
+ };
5410
+
5411
+ template<class Sndr>
5412
+ concept has-queryable-await-completion-adaptor = // exposition only
5413
+ sender<Sndr> &&
5414
+ requires(Sndr&& sender) {
5415
+ get_await_completion_adaptor(get_env(sender));
5416
+ };
5417
+
5418
+ template<class Sndr, class Promise>
5419
+ class sender-awaitable; // exposition only
5420
+ }
5421
+ ```
5422
+
5423
+ The type `sender-awaitable<Sndr, Promise>` is equivalent to:
5424
+
5425
+ ``` cpp
5426
+ namespace std::execution {
5427
+ template<class Sndr, class Promise>
5428
+ class sender-awaitable {
5429
+ struct unit {}; // exposition only
5430
+ using value-type = // exposition only
5431
+ single-sender-value-type<Sndr, env_of_t<Promise>>;
5432
+ using result-type = // exposition only
5433
+ conditional_t<is_void_v<value-type>, unit, value-type>;
5434
+ struct awaitable-receiver; // exposition only
5435
+
5436
+ variant<monostate, result-type, exception_ptr> result{}; // exposition only
5437
+ connect_result_t<Sndr, awaitable-receiver> state; // exposition only
5438
+
5439
+ public:
5440
+ sender-awaitable(Sndr&& sndr, Promise& p);
5441
+ static constexpr bool await_ready() noexcept { return false; }
5442
+ void await_suspend(coroutine_handle<Promise>) noexcept { start(state); }
5443
+ value-type await_resume();
5444
+ };
5445
+ }
5446
+ ```
5447
+
5448
+ *`awaitable-receiver`* is equivalent to:
5449
+
5450
+ ``` cpp
5451
+ struct awaitable-receiver {
5452
+ using receiver_concept = receiver_t;
5453
+ variant<monostate, result-type, exception_ptr>* result-ptr; // exposition only
5454
+ coroutine_handle<Promise> continuation; // exposition only
5455
+ // see below
5456
+ };
5457
+ ```
5458
+
5459
+ Let `rcvr` be an rvalue expression of type *`awaitable-receiver`*, let
5460
+ `crcvr` be a const lvalue that refers to `rcvr`, let `vs` be a pack of
5461
+ subexpressions, and let `err` be an expression of type `Err`. Then:
5462
+
5463
+ - If `constructible_from<result-type, decltype((vs))...>` is satisfied,
5464
+ the expression `set_value( rcvr, vs...)` is equivalent to:
5465
+ ``` cpp
5466
+ try {
5467
+ rcvr.result-ptr->template emplace<1>(vs...);
5468
+ } catch(...) {
5469
+ rcvr.result-ptr->template emplace<2>(current_exception());
5470
+ }
5471
+ rcvr.continuation.resume();
5472
+ ```
5473
+
5474
+ Otherwise, `set_value(rcvr, vs...)` is ill-formed.
5475
+ - The expression `set_error(rcvr, err)` is equivalent to:
5476
+ ``` cpp
5477
+ rcvr.result-ptr->template emplace<2>(AS-EXCEPT-PTR(err)); // see [exec.general]
5478
+ rcvr.continuation.resume();
5479
+ ```
5480
+ - The expression `set_stopped(rcvr)` is equivalent to:
5481
+ ``` cpp
5482
+ static_cast<coroutine_handle<>>(rcvr.continuation.promise().unhandled_stopped()).resume();
5483
+ ```
5484
+ - For any expression `tag` whose type satisfies `forwarding-query` and
5485
+ for any pack of subexpressions `as`,
5486
+ `get_env(crcvr).query(tag, as...)` is expression-equivalent to:
5487
+ ``` cpp
5488
+ tag(get_env(as_const(crcvr.continuation.promise())), as...)
5489
+ ```
5490
+
5491
+ ``` cpp
5492
+ sender-awaitable(Sndr&& sndr, Promise& p);
5493
+ ```
5494
+
5495
+ *Effects:* Initializes *state* with
5496
+
5497
+ ``` cpp
5498
+ connect(std::forward<Sndr>(sndr),
5499
+ awaitable-receiver{addressof(result), coroutine_handle<Promise>::from_promise(p)})
5500
+ ```
5501
+
5502
+ ``` cpp
5503
+ value-type await_resume();
5504
+ ```
5505
+
5506
+ *Effects:* Equivalent to:
5507
+
5508
+ ``` cpp
5509
+ if (result.index() == 2)
5510
+ rethrow_exception(get<2>(result));
5511
+ if constexpr (!is_void_v<value-type>)
5512
+ return std::forward<value-type>(get<1>(result));
5513
+ ```
5514
+
5515
+ `as_awaitable` is a customization point object. For subexpressions
5516
+ `expr` and `p` where `p` is an lvalue, `Expr` names the type
5517
+ `decltype((expr))` and `Promise` names the type
5518
+ `decay_t<decltype((p))>`, `as_awaitable(expr, p)` is
5519
+ expression-equivalent to, except that the evaluations of `expr` and `p`
5520
+ are indeterminately sequenced:
5521
+
5522
+ - `expr.as_awaitable(p)` if that expression is well-formed. *Mandates:*
5523
+ `is-awaitable<A, Promise>` is `true`, where `A` is the type of the
5524
+ expression above.
5525
+ - Otherwise, `(void(p), expr)` if `is-awaitable<Expr, U>` is `true`,
5526
+ where `U` is an unspecified class type that is not `Promise` and that
5527
+ lacks a member named `await_transform`. *Preconditions:*
5528
+ `is-awaitable<Expr, Promise>` is `true` and the expression
5529
+ `co_await expr` in a coroutine with promise type `U` is
5530
+ expression-equivalent to the same expression in a coroutine with
5531
+ promise type `Promise`.
5532
+ - Otherwise, `sender-awaitable{adapted-expr, p}` if
5533
+ ``` cpp
5534
+ has-queryable-await-completion-adaptor<Expr>
5535
+ ```
5536
+
5537
+ and
5538
+ ``` cpp
5539
+ awaitable-sender<decltype((adapted-expr)), Promise>
5540
+ ```
5541
+
5542
+ are both satisfied, where *`adapted-expr`* is
5543
+ `get_await_completion_adaptor(get_env(expr))(expr)`, except that
5544
+ `expr` is evaluated only once.
5545
+ - Otherwise, `sender-awaitable{expr, p}` if
5546
+ `awaitable-sender<Expr, Promise>` is `true`.
5547
+ - Otherwise, `(void(p), expr)`.
5548
+
5549
+ ### `execution::with_awaitable_senders` <a id="exec.with.awaitable.senders">[[exec.with.awaitable.senders]]</a>
5550
+
5551
+ `with_awaitable_senders`, when used as the base class of a coroutine
5552
+ promise type, makes senders awaitable in that coroutine type.
5553
+
5554
+ In addition, it provides a default implementation of `unhandled_stopped`
5555
+ such that if a sender completes by calling `set_stopped`, it is treated
5556
+ as if an uncatchable "stopped" exception were thrown from the
5557
+ *await-expression*.
5558
+
5559
+ [*Note 1*: The coroutine is never resumed, and the `unhandled_stopped`
5560
+ of the coroutine caller’s promise type is called. — *end note*]
5561
+
5562
+ ``` cpp
5563
+ namespace std::execution {
5564
+ template<class-type Promise>
5565
+ struct with_awaitable_senders {
5566
+ template<class OtherPromise>
5567
+ requires (!same_as<OtherPromise, void>)
5568
+ void set_continuation(coroutine_handle<OtherPromise> h) noexcept;
5569
+
5570
+ coroutine_handle<> continuation() const noexcept { return continuation; }
5571
+
5572
+ coroutine_handle<> unhandled_stopped() noexcept {
5573
+ return stopped-handler(continuation.address());
5574
+ }
5575
+
5576
+ template<class Value>
5577
+ see below await_transform(Value&& value);
5578
+
5579
+ private:
5580
+ [[noreturn]] static coroutine_handle<>
5581
+ default-unhandled-stopped(void*) noexcept { // exposition only
5582
+ terminate();
5583
+ }
5584
+ coroutine_handle<> continuation{}; // exposition only
5585
+ coroutine_handle<> (*stopped-handler)(void*) noexcept = // exposition only
5586
+ &default-unhandled-stopped;
5587
+ };
5588
+ }
5589
+ ```
5590
+
5591
+ ``` cpp
5592
+ template<class OtherPromise>
5593
+ requires (!same_as<OtherPromise, void>)
5594
+ void set_continuation(coroutine_handle<OtherPromise> h) noexcept;
5595
+ ```
5596
+
5597
+ *Effects:* Equivalent to:
5598
+
5599
+ ``` cpp
5600
+ continuation = h;
5601
+ if constexpr ( requires(OtherPromise& other) { other.unhandled_stopped(); } ) {
5602
+ stopped-handler = [](void* p) noexcept -> coroutine_handle<> {
5603
+ return coroutine_handle<OtherPromise>::from_address(p)
5604
+ .promise().unhandled_stopped();
5605
+ };
5606
+ } else {
5607
+ stopped-handler = &default-unhandled-stopped;
5608
+ }
5609
+ ```
5610
+
5611
+ ``` cpp
5612
+ template<class Value>
5613
+ call-result-t<as_awaitable_t, Value, Promise&> await_transform(Value&& value);
5614
+ ```
5615
+
5616
+ *Effects:* Equivalent to:
5617
+
5618
+ ``` cpp
5619
+ return as_awaitable(std::forward<Value>(value), static_cast<Promise&>(*this));
5620
+ ```
5621
+
5622
+ ### `execution::affine_on` <a id="exec.affine.on">[[exec.affine.on]]</a>
5623
+
5624
+ `affine_on` adapts a sender into one that completes on the specified
5625
+ scheduler. If the algorithm determines that the adapted sender already
5626
+ completes on the correct scheduler it can avoid any scheduling
5627
+ operation.
5628
+
5629
+ The name `affine_on` denotes a pipeable sender adaptor object. For
5630
+ subexpressions `sch` and `sndr`, if `decltype((sch))` does not satisfy
5631
+ `scheduler`, or `decltype((sndr))` does not satisfy `sender`,
5632
+ `affine_on(sndr, sch)` is ill-formed.
5633
+
5634
+ Otherwise, the expression `affine_on(sndr, sch)` is
5635
+ expression-equivalent to:
5636
+
5637
+ ``` cpp
5638
+ transform_sender(get-domain-early(sndr), make-sender(affine_on, sch, sndr))
5639
+ ```
5640
+
5641
+ except that `sndr` is evaluated only once.
5642
+
5643
+ The exposition-only class template *`impls-for`* [[exec.snd.expos]] is
5644
+ specialized for `affine_on_t` as follows:
5645
+
5646
+ ``` cpp
5647
+ namespace std::execution {
5648
+ template<>
5649
+ struct impls-for<affine_on_t> : default-impls {
5650
+ static constexpr auto get-attrs =
5651
+ [](const auto& data, const auto& child) noexcept -> decltype(auto) {
5652
+ return JOIN-ENV(SCHED-ATTRS(data), FWD-ENV(get_env(child)));
5653
+ };
5654
+ };
5655
+ }
5656
+ ```
5657
+
5658
+ Let `out_sndr` be a subexpression denoting a sender returned from
5659
+ `affine_on(sndr, sch)` or one equal to such, and let `OutSndr` be the
5660
+ type `decltype((out_sndr))`. Let `out_rcvr` be a subexpression denoting
5661
+ a receiver that has an environment of type `Env` such that
5662
+ `sender_in<OutSndr, Env>` is `true`. Let `op` be an lvalue referring to
5663
+ the operation state that results from connecting `out_sndr` to
5664
+ `out_rcvr`. Calling `start(op)` will start `sndr` on the current
5665
+ execution agent and execute completion operations on `out_rcvr` on an
5666
+ execution agent of the execution resource associated with `sch`. If the
5667
+ current execution resource is the same as the execution resource
5668
+ associated with `sch`, the completion operation on `out_rcvr` may be
5669
+ called before `start(op)` completes. If scheduling onto `sch` fails, an
5670
+ error completion on `out_rcvr` shall be executed on an unspecified
5671
+ execution agent.
5672
+
5673
+ ### `execution::inline_scheduler` <a id="exec.inline.scheduler">[[exec.inline.scheduler]]</a>
5674
+
5675
+ ``` cpp
5676
+ namespace std::execution {
5677
+ class inline_scheduler {
5678
+ class inline-sender; // exposition only
5679
+
5680
+ template<receiver R>
5681
+ class inline-state; // exposition only
5682
+
5683
+ public:
5684
+ using scheduler_concept = scheduler_t;
5685
+
5686
+ constexpr inline-sender schedule() noexcept { return {}; }
5687
+ constexpr bool operator==(const inline_scheduler&) const noexcept = default;
5688
+ };
5689
+ }
5690
+ ```
5691
+
5692
+ `inline_scheduler` is a class that models `scheduler` [[exec.sched]].
5693
+ All objects of type `inline_scheduler` are equal.
5694
+
5695
+ *`inline-sender`* is an exposition-only type that satisfies `sender`.
5696
+ The type `completion_signatures_of_t<inline-sender>` is
5697
+ `completion_signatures<set_value_t()>`.
5698
+
5699
+ Let `sndr` be an expression of type *`inline-sender`*, let `rcvr` be an
5700
+ expression such that `receiver_of<decltype((rcvr)), CS>` is `true` where
5701
+ `CS` is `completion_signatures<set_value_t()>`, then:
5702
+
5703
+ - the expression `connect(sndr, rcvr)` has type
5704
+ `inline-state<remove_cvref_t<decltype((rcvr))>>` and is
5705
+ potentially-throwing if and only if `((void)sndr, auto(rcvr))` is
5706
+ potentially-throwing, and
5707
+ - the expression `get_completion_scheduler<set_value_t>(get_env(sndr))`
5708
+ has type `inline_scheduler` and is potentially-throwing if and only if
5709
+ `get_env(sndr)` is potentially-throwing.
5710
+
5711
+ Let `o` be a non-`const` lvalue of type `inline-state<Rcvr>`, and let
5712
+ `REC(o)` be a non-`const` lvalue reference to an object of type `Rcvr`
5713
+ that was initialized with the expression `rcvr` passed to an invocation
5714
+ of `connect` that returned `o`, then:
5715
+
5716
+ - the object to which `REC(o)` refers remains valid for the lifetime of
5717
+ the object to which `o` refers, and
5718
+ - the expression `start(o)` is equivalent to
5719
+ `set_value(std::move(REC(o)))`.
5720
+
5721
+ ### `execution::task_scheduler` <a id="exec.task.scheduler">[[exec.task.scheduler]]</a>
5722
+
5723
+ ``` cpp
5724
+ namespace std::execution {
5725
+ class task_scheduler {
5726
+ class ts-sender; // exposition only
5727
+
5728
+ template<receiver R>
5729
+ class state; // exposition only
5730
+
5731
+ public:
5732
+ using scheduler_concept = scheduler_t;
5733
+
5734
+ template<class Sch, class Allocator = allocator<void>>
5735
+ requires (!same_as<task_scheduler, remove_cvref_t<Sch>>)
5736
+ && scheduler<Sch>
5737
+ explicit task_scheduler(Sch&& sch, Allocator alloc = {});
5738
+
5739
+ ts-sender schedule();
5740
+
5741
+ friend bool operator==(const task_scheduler& lhs, const task_scheduler& rhs)
5742
+ noexcept;
5743
+ template<class Sch>
5744
+ requires (!same_as<task_scheduler, Sch>)
5745
+ && scheduler<Sch>
5746
+ friend bool operator==(const task_scheduler& lhs, const Sch& rhs) noexcept;
5747
+
5748
+ private:
5749
+ shared_ptr<void> sch_; // exposition only
5750
+ };
5751
+ }
5752
+ ```
5753
+
5754
+ `task_scheduler` is a class that models `scheduler` [[exec.sched]].
5755
+ Given an object `s` of type `task_scheduler`, let `SCHED(s)` be the
5756
+ object owned by `s.sch_`.
5757
+
5758
+ ``` cpp
5759
+ template<class Sch, class Allocator = allocator<void>>
5760
+ requires(!same_as<task_scheduler, remove_cvref_t<Sch>>) && scheduler<Sch>
5761
+ explicit task_scheduler(Sch&& sch, Allocator alloc = {});
5762
+ ```
5763
+
5764
+ *Effects:* Initialize *sch\_* with
5765
+ `allocate_shared<remove_cvref_t<Sch>>(alloc, std::forward<Sch>(sch))`.
5766
+
5767
+ *Recommended practice:* Implementations should avoid the use of
5768
+ dynamically allocated memory for small scheduler objects.
5769
+
5770
+ *Remarks:* Any allocations performed by construction of *ts-sender* or
5771
+ *state* objects resulting from calls on `*this` are performed using a
5772
+ copy of `alloc`.
5773
+
5774
+ ``` cpp
5775
+ ts-sender schedule();
5776
+ ```
5777
+
5778
+ *Effects:* Returns an object of type *ts-sender* containing a sender
5779
+ initialized with `schedule(`*`SCHED`*`(*this))`.
5780
+
5781
+ ``` cpp
5782
+ bool operator==(const task_scheduler& lhs, const task_scheduler& rhs) noexcept;
5783
+ ```
5784
+
5785
+ *Effects:* Equivalent to: `return lhs == `*`SCHED`*`(rhs);`
5786
+
5787
+ ``` cpp
5788
+ template<class Sch>
5789
+ requires (!same_as<task_scheduler, Sch>)
5790
+ && scheduler<Sch>
5791
+ bool operator==(const task_scheduler& lhs, const Sch& rhs) noexcept;
5792
+ ```
5793
+
5794
+ *Returns:* `false` if the type of *`SCHED`*`(lhs)` is not `Sch`,
5795
+ otherwise *`SCHED`*`(lhs) == rhs`.
5796
+
5797
+ ``` cpp
5798
+ namespace std::execution {
5799
+ class task_scheduler::ts-sender { // exposition only
5800
+ public:
5801
+ using sender_concept = sender_t;
5802
+
5803
+ template<receiver Rcvr>
5804
+ state<Rcvr> connect(Rcvr&& rcvr);
5805
+ };
5806
+ }
5807
+ ```
5808
+
5809
+ *`ts-sender`* is an exposition-only class that models `sender`
5810
+ [[exec.snd]] and for which `completion_signatures_of_t<ts-sender>`
5811
+ denotes:
5812
+
5813
+ ``` cpp
5814
+ completion_signatures<
5815
+ set_value_t(),
5816
+ set_error_t(error_code),
5817
+ set_error_t(exception_ptr),
5818
+ set_stopped_t()>
5819
+ ```
5820
+
5821
+ Let `sch` be an object of type `task_scheduler` and let `sndr` be an
5822
+ object of type *`ts-sender`* obtained from `schedule(sch)`. Then
5823
+ `get_completion_scheduler<set_value_t>(get_env(sndr)) == sch` is `true`.
5824
+ The object `SENDER(sndr)` is the sender object contained by `sndr` or an
5825
+ object move constructed from it.
5826
+
5827
+ ``` cpp
5828
+ template<receiver Rcvr>
5829
+ state<Rcvr> connect(Rcvr&& rcvr);
5830
+ ```
5831
+
5832
+ *Effects:* Let *`r`* be an object of a type that models `receiver` and
5833
+ whose completion handlers result in invoking the corresponding
5834
+ completion handlers of `rcvr` or copy thereof. Returns an object of type
5835
+ *`state`*`<Rcvr>` containing an operation state object initialized with
5836
+ `connect(`*`SENDER`*`(*this), std::move(`*`r`*`))`.
5837
+
5838
+ ``` cpp
5839
+ namespace std::execution {
5840
+ template<receiver R>
5841
+ class task_scheduler::state { // exposition only
5842
+ public:
5843
+ using operation_state_concept = operation_state_t;
5844
+
5845
+ void start() & noexcept;
5846
+ };
5847
+ }
5848
+ ```
5849
+
5850
+ *`state`* is an exposition-only class template whose specializations
5851
+ model `operation_state` [[exec.opstate]].
5852
+
5853
+ ``` cpp
5854
+ void start() & noexcept;
5855
+ ```
5856
+
5857
+ *Effects:* Equivalent to `start(st)` where `st` is the operation state
5858
+ object contained by `*this`.
5859
+
5860
+ ### `execution::task` <a id="exec.task">[[exec.task]]</a>
5861
+
5862
+ #### `task` overview <a id="task.overview">[[task.overview]]</a>
5863
+
5864
+ The `task` class template represents a sender that can be used as the
5865
+ return type of coroutines. The first template parameter `T` defines the
5866
+ type of the value completion datum [[exec.async.ops]] if `T` is not
5867
+ `void`. Otherwise, there are no value completion datums. Inside
5868
+ coroutines returning `task<T, E>` the operand of `co_return` (if any)
5869
+ becomes the argument of `set_value`. The second template parameter
5870
+ `Environment` is used to customize the behavior of `task`.
5871
+
5872
+ #### Class template `task` <a id="task.class">[[task.class]]</a>
5873
+
5874
+ ``` cpp
5875
+ namespace std::execution {
5876
+ template<class T, class Environment>
5877
+ class task {
5878
+ // [task.state]
5879
+ template<receiver Rcvr>
5880
+ class state; // exposition only
5881
+
5882
+ public:
5883
+ using sender_concept = sender_t;
5884
+ using completion_signatures = see below;
5885
+ using allocator_type = see below;
5886
+ using scheduler_type = see below;
5887
+ using stop_source_type = see below;
5888
+ using stop_token_type = decltype(declval<stop_source_type>().get_token());
5889
+ using error_types = see below;
5890
+
5891
+ // [task.promise]
5892
+ class promise_type;
5893
+
5894
+ task(task&&) noexcept;
5895
+ ~task();
5896
+
5897
+ template<receiver Rcvr>
5898
+ state<Rcvr> connect(Rcvr&& rcvr);
5899
+
5900
+ private:
5901
+ coroutine_handle<promise_type> handle; // exposition only
5902
+ };
5903
+ }
5904
+ ```
5905
+
5906
+ `task<T, E>` models `sender` [[exec.snd]] if `T` is `void`, a reference
5907
+ type, or a cv-unqualified non-array object type and `E` is a class type.
5908
+ Otherwise a program that instantiates the definition of `task<T, E>` is
5909
+ ill-formed.
5910
+
5911
+ The nested types of `task` template specializations are determined based
5912
+ on the `Environment` parameter:
5913
+
5914
+ - `allocator_type` is `Environment::allocator_type` if that
5915
+ *qualified-id* is valid and denotes a type, `allocator<byte>`
5916
+ otherwise.
5917
+ - `scheduler_type` is `Environment::scheduler_type` if that
5918
+ *qualified-id* is valid and denotes a type, `task_scheduler`
5919
+ otherwise.
5920
+ - `stop_source_type` is `Environment::stop_source_type` if that
5921
+ *qualified-id* is valid and denotes a type, `inplace_stop_source`
5922
+ otherwise.
5923
+ - `error_types` is `Environment::error_types` if that *qualified-id* is
5924
+ valid and denotes a type,
5925
+ `completion_signatures<set_error_t(exception_ptr)>` otherwise.
5926
+
5927
+ A program is ill-formed if `error_types` is not a specialization of
5928
+ `execution::completion_signatures` or if the template arguments of that
5929
+ specialization contain an element which is not of the form
5930
+ `set_error_t(E)` for some type `E`.
5931
+
5932
+ The type alias `completion_signatures` is a specialization of
5933
+ `execution::completion_signatures` with the template arguments (in
5934
+ unspecified order):
5935
+
5936
+ - `set_value_t()` if `T` is `void`, and `set_value_t(T)` otherwise;
5937
+ - template arguments of the specialization of
5938
+ `execution::completion_signatures` denoted by `error_types`; and
5939
+ - `set_stopped_t()`.
5940
+
5941
+ `allocator_type` shall meet the *Cpp17Allocator* requirements.
5942
+
5943
+ #### `task` members <a id="task.members">[[task.members]]</a>
5944
+
5945
+ ``` cpp
5946
+ task(task&& other) noexcept;
5947
+ ```
5948
+
5949
+ *Effects:* Initializes *handle* with `exchange(other.`*`handle`*`, {})`.
5950
+
5951
+ ``` cpp
5952
+ ~task();
5953
+ ```
5954
+
5955
+ *Effects:* Equivalent to:
5956
+
5957
+ ``` cpp
5958
+ if (handle)
5959
+ handle.destroy();
5960
+ ```
5961
+
5962
+ ``` cpp
5963
+ template<receiver Rcvr>
5964
+ state<Rcvr> connect(Rcvr&& recv);
5965
+ ```
5966
+
5967
+ *Preconditions:* `bool(`*`handle`*`)` is `true`.
5968
+
5969
+ *Effects:* Equivalent to:
5970
+
5971
+ ``` cpp
5972
+ return state<Rcvr>(exchange(handle, {}), std::forward<Rcvr>(recv));
5973
+ ```
5974
+
5975
+ #### Class template `task::state` <a id="task.state">[[task.state]]</a>
5976
+
5977
+ ``` cpp
5978
+ namespace std::execution {
5979
+ template<class T, class Environment>
5980
+ template<receiver Rcvr>
5981
+ class task<T, Environment>::state { // exposition only
5982
+ public:
5983
+ using operation_state_concept = operation_state_t;
5984
+
5985
+ template<class R>
5986
+ state(coroutine_handle<promise_type> h, R&& rr);
5987
+
5988
+ ~state();
5989
+
5990
+ void start() & noexcept;
5991
+
5992
+ private:
5993
+ using own-env-t = see belownc; // exposition only
5994
+ coroutine_handle<promise_type> handle; // exposition only
5995
+ remove_cvref_t<Rcvr> rcvr; // exposition only
5996
+ own-env-t own-env; // exposition only
5997
+ Environment environment; // exposition only
5998
+ };
5999
+ }
6000
+ ```
6001
+
6002
+ The type *`own-env-t`* is `Environment::template
6003
+ env_type<decltype(get_env({}declval{}<Rcvr>({}))){}>` if that
6004
+ *qualified-id* is valid and denotes a type, `env<>` otherwise.
6005
+
6006
+ ``` cpp
6007
+ template<class R>
6008
+ state(coroutine_handle<promise_type> h, R&& rr);
6009
+ ```
6010
+
6011
+ *Effects:* Initializes
6012
+
6013
+ - *handle* with `std::move(h)`;
6014
+ - *rcvr* with `std::forward<R>(rr)`;
6015
+ - *own-env* with *`own-env-t`*`(get_env(`*`rcvr`*`))` if that expression
6016
+ is valid and *`own-env-t`*`()` otherwise. If neither of these
6017
+ expressions is valid, the program is ill-formed.
6018
+ - *environment* with `Environment(`*`own-env`*`)` if that expression is
6019
+ valid, otherwise `Environment(get_env(`*`rcvr`*`))` if this expression
6020
+ is valid, otherwise `Environment()`. If neither of these expressions
6021
+ is valid, the program is ill-formed.
6022
+
6023
+ ``` cpp
6024
+ ~state();
6025
+ ```
6026
+
6027
+ *Effects:* Equivalent to:
6028
+
6029
+ ``` cpp
6030
+ if (handle)
6031
+ handle.destroy();
6032
+ ```
6033
+
6034
+ ``` cpp
6035
+ void start() & noexcept;
6036
+ ```
6037
+
6038
+ *Effects:* Let *`prom`* be the object *`handle`*`.promise()`. Associates
6039
+ *`STATE`*`(`*`prom`*`)`, *`RCVR`*`(`*`prom`*`)`, and
6040
+ *`SCHED`*`(`*`prom`*`)` with `*this` as follows:
6041
+
6042
+ - *`STATE`*`(`*`prom`*`)` is `*this`.
6043
+ - *`RCVR`*`(`*`prom`*`)` is *rcvr*.
6044
+ - *`SCHED`*`(`*`prom`*`)` is the object initialized with
6045
+ `scheduler_type(get_scheduler(get_env(`*`rcvr`*`)))` if that
6046
+ expression is valid and `scheduler_type()` otherwise. If neither of
6047
+ these expressions is valid, the program is ill-formed.
6048
+
6049
+ Let *`st`* be `get_stop_token(get_env(`*`rcvr`*`))`. Initializes
6050
+ *`prom`*`.`*`token`* and *`prom`*`.`*`source`* such that
6051
+
6052
+ - *`prom`*`.`*`token`*`.stop_requested()` returns
6053
+ *`st`*`.stop_requested()`;
6054
+ - *`prom`*`.`*`token`*`.stop_possible()` returns
6055
+ *`st`*`.stop_possible()`; and
6056
+ - for types `Fn` and `Init` such that both `invocable<Fn>` and
6057
+ `constructible_from<Fn, Init>` are modeled,
6058
+ `stop_token_type::callback_type<Fn>` models
6059
+ `stoppable-callback-for<Fn, stop_token_type, Init>`.
6060
+
6061
+ After that invokes *`handle`*`.resume()`.
6062
+
6063
+ #### Class `task::promise_type` <a id="task.promise">[[task.promise]]</a>
6064
+
6065
+ ``` cpp
6066
+ namespace std::execution {
6067
+ template<class T, class Environment>
6068
+ class task<T, Environment>::promise_type {
6069
+ public:
6070
+ template<class... Args>
6071
+ promise_type(const Args&... args);
6072
+
6073
+ task get_return_object() noexcept;
6074
+
6075
+ auto initial_suspend() noexcept;
6076
+ auto final_suspend() noexcept;
6077
+
6078
+ void uncaught_exception();
6079
+ coroutine_handle<> unhandled_stopped();
6080
+
6081
+ void return_void(); // present only if is_void_v<T> is true
6082
+ template<class V>
6083
+ void return_value(V&& value); // present only if is_void_v<T> is false
6084
+
6085
+ template<class E>
6086
+ unspecified yield_value(with_error<E> error);
6087
+
6088
+ template<class A>
6089
+ auto await_transform(A&& a);
6090
+ template<class Sch>
6091
+ auto await_transform(change_coroutine_scheduler<Sch> sch);
6092
+
6093
+ unspecified get_env() const noexcept;
6094
+
6095
+ template<class... Args>
6096
+ void* operator new(size_t size, Args&&... args);
6097
+
6098
+ void operator delete(void* pointer, size_t size) noexcept;
6099
+
6100
+ private:
6101
+ using error-variant = see belownc; // exposition only
6102
+
6103
+ allocator_type alloc; // exposition only
6104
+ stop_source_type source; // exposition only
6105
+ stop_token_type token; // exposition only
6106
+ optional<T> result; // exposition only; present only if is_void_v<T> is false
6107
+ error-variant errors; // exposition only
6108
+ };
6109
+ }
6110
+ ```
6111
+
6112
+ Let `prom` be an object of `promise_type` and let `tsk` be the `task`
6113
+ object created by `prom.get_return_object()`. The description below
6114
+ refers to objects `STATE(prom)`, `RCVR(prom)`, and `SCHED(prom)`
6115
+ associated with `tsk` during evaluation of `task::state<Rcvr>::start`
6116
+ for some receiver `Rcvr`.
6117
+
6118
+ *`error-variant`* is a `variant<monostate,
6119
+ remove_cvref_t<E>...>`, with duplicate types removed, where `E...` are
6120
+ the parameter types of the template arguments of the specialization of
6121
+ `execution::completion_signatures` denoted by `error_types`.
6122
+
6123
+ ``` cpp
6124
+ template<class... Args>
6125
+ promise_type(const Args&... args);
6126
+ ```
6127
+
6128
+ *Mandates:* The first parameter of type `allocator_arg_t` (if any) is
6129
+ not the last parameter.
6130
+
6131
+ *Effects:* If `Args` contains an element of type `allocator_arg_t` then
6132
+ *alloc* is initialized with the corresponding next element of `args`.
6133
+ Otherwise, *alloc* is initialized with `allocator_type()`.
6134
+
6135
+ ``` cpp
6136
+ task get_return_object() noexcept;
6137
+ ```
6138
+
6139
+ *Returns:* A `task` object whose member *handle* is
6140
+ `coroutine_handle<promise_type>::from_promise(*this)`.
6141
+
6142
+ ``` cpp
6143
+ auto initial_suspend() noexcept;
6144
+ ```
6145
+
6146
+ *Returns:* An awaitable object of unspecified type [[expr.await]] whose
6147
+ member functions arrange for
6148
+
6149
+ - the calling coroutine to be suspended,
6150
+ - the coroutine to be resumed on an execution agent of the execution
6151
+ resource associated with *`SCHED`*`(*this)`.
6152
+
6153
+ ``` cpp
6154
+ auto final_suspend() noexcept;
6155
+ ```
6156
+
6157
+ *Returns:* An awaitable object of unspecified type [[expr.await]] whose
6158
+ member functions arrange for the completion of the asynchronous
6159
+ operation associated with *`STATE`*`(*this)` by invoking:
6160
+
6161
+ - `set_error(std::move(`*`RCVR`*`(*this)), std::move(e))` if
6162
+ *`errors`*`.index()` is greater than zero and `e` is the value held by
6163
+ *errors*, otherwise
6164
+ - `set_value(std::move(`*`RCVR`*`(*this)))` if `is_void<T>` is `true`,
6165
+ and otherwise
6166
+ - `set_value(std::move(`*`RCVR`*`(*this)), *`*`result`*`)`.
6167
+
6168
+ ``` cpp
6169
+ template<class Err>
6170
+ auto yield_value(with_error<Err> err);
6171
+ ```
6172
+
6173
+ *Mandates:* `std::move(err.error)` is convertible to exactly one of the
6174
+ `set_error_t` argument types of `error_types`. Let *`Cerr`* be that
6175
+ type.
6176
+
6177
+ *Returns:* An awaitable object of unspecified type [[expr.await]] whose
6178
+ member functions arrange for the calling coroutine to be suspended and
6179
+ then completes the asynchronous operation associated with
6180
+ *`STATE`*`(*this)` by invoking
6181
+ `set_error(std::move(`*`RCVR`*`(*this)), `*`Cerr`*`(std::move(err.error)))`.
6182
+
6183
+ ``` cpp
6184
+ template<sender Sender>
6185
+ auto await_transform(Sender&& sndr) noexcept;
6186
+ ```
6187
+
6188
+ *Returns:* If `same_as<inline_scheduler, scheduler_type>` is `true`
6189
+ returns `as_awaitable(std::forward<Sender>(sndr), *this)`; otherwise
6190
+ returns
6191
+ `as_awaitable(affine_on(std::forward<Sender>(sndr), `*`SCHED`*`(*this)), *this)`.
6192
+
6193
+ ``` cpp
6194
+ template<class Sch>
6195
+ auto await_transform(change_coroutine_scheduler<Sch> sch) noexcept;
6196
+ ```
6197
+
6198
+ *Effects:* Equivalent to:
6199
+
6200
+ ``` cpp
6201
+ return await_transform(just(exchange(SCHED(*this), scheduler_type(sch.scheduler))), *this);
6202
+ ```
6203
+
6204
+ ``` cpp
6205
+ void uncaught_exception();
6206
+ ```
6207
+
6208
+ *Effects:* If the signature `set_error_t(exception_ptr)` is not an
6209
+ element of `error_types`, calls `terminate()` [[except.terminate]].
6210
+ Otherwise, stores `current_exception()` into *errors*.
6211
+
6212
+ ``` cpp
6213
+ coroutine_handle<> unhandled_stopped();
6214
+ ```
6215
+
6216
+ *Effects:* Completes the asynchronous operation associated with
6217
+ *`STATE`*`(*this)` by invoking
6218
+ `set_stopped(std::move(`*`RCVR`*`(*this)))`.
6219
+
6220
+ *Returns:* `noop_coroutine()`.
6221
+
6222
+ ``` cpp
6223
+ unspecified get_env() const noexcept;
6224
+ ```
6225
+
6226
+ *Returns:* An object `env` such that queries are forwarded as follows:
6227
+
6228
+ - `env.query(get_scheduler)` returns
6229
+ `scheduler_type(`*`SCHED`*`(*this))`.
6230
+ - `env.query(get_allocator)` returns *alloc*.
6231
+ - `env.query(get_stop_token)` returns *token*.
6232
+ - For any other query `q` and arguments `a...` a call to
6233
+ `env.query(q, a...)` returns *`STATE`*`(*this)`.
6234
+ `environment.query(q, a...)` if this expression is well-formed and
6235
+ `forwarding_query(q)` is well-formed and is `true`. Otherwise
6236
+ `env.query(q, a...)` is ill-formed.
6237
+
6238
+ ``` cpp
6239
+ template<class... Args>
6240
+ void* operator new(size_t size, const Args&... args);
6241
+ ```
6242
+
6243
+ If there is no parameter with type `allocator_arg_t` then let `alloc` be
6244
+ `allocator_type()`. Otherwise, let `arg_next` be the parameter following
6245
+ the first `allocator_arg_t` parameter, and let `alloc` be
6246
+ `allocator_type(arg_next)`. Let `PAlloc` be
6247
+ `allocator_traits<allocator_type>::template rebind_alloc<U>`, where `U`
6248
+ is an unspecified type whose size and alignment are both
6249
+ \_\_STDCPP_DEFAULT_NEW_ALIGNMENT\_\_.
6250
+
6251
+ *Mandates:*
6252
+
6253
+ - The first parameter of type `allocator_arg_t` (if any) is not the last
6254
+ parameter.
6255
+ - `allocator_type(arg_next)` is a valid expression if there is a
6256
+ parameter of type `allocator_arg_t`.
6257
+ - `allocator_traits<PAlloc>::pointer` is a pointer type.
6258
+
6259
+ *Effects:* Initializes an allocator `palloc` of type `PAlloc` with
6260
+ `alloc`. Uses `palloc` to allocate storage for the smallest array of `U`
6261
+ sufficient to provide storage for a coroutine state of size `size`, and
6262
+ unspecified additional state necessary to ensure that `operator delete`
6263
+ can later deallocate this memory block with an allocator equal to
6264
+ `palloc`.
6265
+
6266
+ *Returns:* A pointer to the allocated storage.
6267
+
6268
+ ``` cpp
6269
+ void operator delete(void* pointer, size_t size) noexcept;
6270
+ ```
6271
+
6272
+ *Preconditions:* `pointer` was returned from an invocation of the above
6273
+ overload of `operator new` with a size argument equal to `size`.
6274
+
6275
+ *Effects:* Deallocates the storage pointed to by `pointer` using an
6276
+ allocator equal to that used to allocate it.
6277
+
6278
+ ## Execution scope utilities <a id="exec.scope">[[exec.scope]]</a>
6279
+
6280
+ ### Execution scope concepts <a id="exec.scope.concepts">[[exec.scope.concepts]]</a>
6281
+
6282
+ The `scope_token` concept defines the requirements on a type `Token`
6283
+ that can be used to create associations between senders and an async
6284
+ scope.
6285
+
6286
+ Let *test-sender* and *test-env* be unspecified types such that
6287
+ `sender_in<test-sender, test-env>` is modeled.
6288
+
6289
+ ``` cpp
6290
+ namespace std::execution {
6291
+ template<class Token>
6292
+ concept scope_token =
6293
+ copyable<Token> &&
6294
+ requires(const Token token) {
6295
+ { token.try_associate() } -> same_as<bool>;
6296
+ { token.disassociate() } noexcept -> same_as<void>;
6297
+ { token.wrap(declval<test-sender>()) } -> sender_in<test-env>;
6298
+ };
6299
+ }
6300
+ ```
6301
+
6302
+ A type `Token` models `scope_token` only if:
6303
+
6304
+ - no exceptions are thrown from copy construction, move construction,
6305
+ copy assignment, or move assignment of objects of type `Token`; and
6306
+ - given an lvalue `token` of type (possibly const) `Token`, for all
6307
+ expressions `sndr` such that `decltype(({}sndr))` models `sender`:
6308
+ - `token.wrap(sndr)` is a valid expression,
6309
+ - `decltype(token.wrap(sndr))` models `sender`, and
6310
+ - `completion_signatures_of_t<decltype(token.wrap(sndr)), E>` contains
6311
+ the same completion signatures as
6312
+ `completion_signatures_of_t<decltype((sndr)), E>` for all types `E`
6313
+ such that `sender_in<decltype((sndr)), E>` is modeled.
6314
+
6315
+ ### Counting Scopes <a id="exec.counting.scopes">[[exec.counting.scopes]]</a>
6316
+
6317
+ #### General <a id="exec.counting.scopes.general">[[exec.counting.scopes.general]]</a>
6318
+
6319
+ Scopes of type `simple_counting_scope` and `counting_scope` maintain
6320
+ counts of associations. Let:
6321
+
6322
+ - `Scope` be either `simple_counting_scope` or `counting_scope`,
6323
+ - `scope` be an object of type `Scope`,
6324
+ - `tkn` be an object of type `Scope::token` obtained from
6325
+ `scope.get_token()`,
6326
+ - `jsndr` be a sender obtained from `scope.join()`, and
6327
+ - `op` be an operation state obtained from connecting `jsndr` to a
6328
+ receiver.
6329
+
6330
+ During its lifetime `scope` goes through different states which govern
6331
+ what operations are allowed and the result of these operations:
6332
+
6333
+ - *`unused`*: a newly constructed object starts in the *`unused`* state.
6334
+ - *`open`*: when `tkn.try_associate()` is called while `scope` is in the
6335
+ *`unused`* state, `scope` moves to the *`open`* state.
6336
+ - *`open-and-joining`*: when the operation state `op` is started while
6337
+ `scope` is in the *`unused`* or *`open`* state, `scope` moves to the
6338
+ *`open-and-joining`* state.
6339
+ - *`closed`*: when `scope.close()` is called while `scope` is in the
6340
+ *`open`* state, `scope` moves to the *`closed`* state.
6341
+ - *`unused-and-closed`*: when `scope.close()` is called while `scope` is
6342
+ in the *`unused`* state, `scope` moves to the *`unused-and-closed`*
6343
+ state.
6344
+ - *`closed-and-joining`*: when `scope.close()` is called while `scope`
6345
+ is in the *`open-and-joining`* state or the operation state `op` is
6346
+ started while `scope` is in the *`closed`* or *`unused-and-closed`*
6347
+ state, `scope` moves to the *`closed-and-joining`* state.
6348
+ - *`joined`*: when the count of associations drops to zero while `scope`
6349
+ is in the *`open-and-joining`* or *`closed-and-joining`* state,
6350
+ `scope` moves to the *`joined`* state.
6351
+
6352
+ *Recommended practice:* For `simple_counting_scope` and
6353
+ `counting_scope`, implementations should store the state and the count
6354
+ of associations in a single member of type `size_t`.
6355
+
6356
+ Subclause [[exec.counting.scopes]] makes use of the following
6357
+ exposition-only entities:
6358
+
6359
+ ``` cpp
6360
+ struct scope-join-t {}; // exposition only
6361
+
6362
+ enum scope-state-type { // exposition only
6363
+ unused, // exposition only
6364
+ open, // exposition only
6365
+ closed, // exposition only
6366
+ open-and-joining, // exposition only
6367
+ closed-and-joining, // exposition only
6368
+ unused-and-closed, // exposition only
6369
+ joined, // exposition only
6370
+ };
6371
+ ```
6372
+
6373
+ The exposition-only class template *`impls-for`* [[exec.snd.expos]] is
6374
+ specialized for *`scope-join-t`* as follows:
6375
+
6376
+ ``` cpp
6377
+ namespace std::execution {
6378
+ template<>
6379
+ struct impls-for<scope-join-t> : default-impls {
6380
+ template<class Scope, class Rcvr>
6381
+ struct state { // exposition only
6382
+ struct rcvr-t { // exposition only
6383
+ using receiver_concept = receiver_t;
6384
+
6385
+ Rcvr& rcvr; // exposition only
6386
+
6387
+ void set_value() && noexcept {
6388
+ execution::set_value(std::move(rcvr));
6389
+ }
6390
+
6391
+ template<class E>
6392
+ void set_error(E&& e) && noexcept {
6393
+ execution::set_error(std::move(rcvr), std::forward<E>(e));
6394
+ }
6395
+
6396
+ void set_stopped() && noexcept {
6397
+ execution::set_stopped(std::move(rcvr));
6398
+ }
6399
+
6400
+ decltype(auto) get_env() const noexcept {
6401
+ return execution::get_env(rcvr);
6402
+ }
6403
+ };
6404
+
6405
+ using sched-sender = // exposition only
6406
+ decltype(schedule(get_scheduler(get_env(declval<Rcvr&>()))));
6407
+ using op-t = // exposition only
6408
+ connect_result_t<sched-sender, rcvr-t>;
6409
+
6410
+ Scope* scope; // exposition only
6411
+ Rcvr& receiver; // exposition only
6412
+ op-t op; // exposition only
6413
+
6414
+ state(Scope* scope, Rcvr& rcvr) // exposition only
6415
+ noexcept(nothrow-callable<connect_t, sched-sender, rcvr-t>)
6416
+ : scope(scope),
6417
+ receiver(rcvr),
6418
+ op(connect(schedule(get_scheduler(get_env(rcvr))), rcvr-t(rcvr))) {}
6419
+
6420
+ void complete() noexcept { // exposition only
6421
+ start(op);
6422
+ }
6423
+
6424
+ void complete-inline() noexcept { // exposition only
6425
+ set_value(std::move(receiver));
6426
+ }
6427
+ };
6428
+
6429
+ static constexpr auto get-state = // exposition only
6430
+ []<class Rcvr>(auto&& sender, Rcvr& receiver)
6431
+ noexcept(is_nothrow_constructible_v<state<Rcvr>, data-type<decltype(sender)>, Rcvr&>) {
6432
+ auto[_, self] = sender;
6433
+ return state(self, receiver);
6434
+ };
6435
+
6436
+ static constexpr auto start = // exposition only
6437
+ [](auto& s, auto&) noexcept {
6438
+ if (s.scope->start-join-sender(s))
6439
+ s.complete-inline();
6440
+ };
6441
+ };
6442
+ }
6443
+ ```
6444
+
6445
+ #### Simple Counting Scope <a id="exec.scope.simple.counting">[[exec.scope.simple.counting]]</a>
6446
+
6447
+ ##### General <a id="exec.scope.simple.counting.general">[[exec.scope.simple.counting.general]]</a>
6448
+
6449
+ ``` cpp
6450
+ namespace std::execution {
6451
+ class simple_counting_scope {
6452
+ public:
6453
+ // [exec.simple.counting.token], token
6454
+ struct token;
6455
+
6456
+ static constexpr size_t max_associations = implementation-defined;
6457
+
6458
+ // [exec.simple.counting.ctor], constructor and destructor
6459
+ simple_counting_scope() noexcept;
6460
+ simple_counting_scope(simple_counting_scope&&) = delete;
6461
+ ~simple_counting_scope();
6462
+
6463
+ // [exec.simple.counting.mem], members
6464
+ token get_token() noexcept;
6465
+ void close() noexcept;
6466
+ sender auto join() noexcept;
6467
+
6468
+ private:
6469
+ size_t count; // exposition only
6470
+ scope-state-type state; // exposition only
6471
+
6472
+ bool try-associate() noexcept; // exposition only
6473
+ void disassociate() noexcept; // exposition only
6474
+ template<class State>
6475
+ bool start-join-sender(State& state) noexcept; // exposition only
6476
+ };
6477
+ }
6478
+ ```
6479
+
6480
+ For purposes of determining the existence of a data race, `get_token`,
6481
+ `close`, `join`, *`try-associate`*, *`disassociate`*, and
6482
+ *`start-join-sender`* behave as atomic operations [[intro.multithread]].
6483
+ These operations on a single object of type `simple_counting_scope`
6484
+ appear to occur in a single total order.
6485
+
6486
+ ##### Constructor and Destructor <a id="exec.simple.counting.ctor">[[exec.simple.counting.ctor]]</a>
6487
+
6488
+ ``` cpp
6489
+ simple_counting_scope() noexcept;
6490
+ ```
6491
+
6492
+ *Ensures:* *count* is `0` and *state* is *unused*.
6493
+
6494
+ ``` cpp
6495
+ ~simple_counting_scope();
6496
+ ```
6497
+
6498
+ *Effects:* If *state* is not one of *joined*, *unused*, or
6499
+ *unused-and-closed*, invokes `terminate` [[except.terminate]].
6500
+ Otherwise, has no effects.
6501
+
6502
+ ##### Members <a id="exec.simple.counting.mem">[[exec.simple.counting.mem]]</a>
6503
+
6504
+ ``` cpp
6505
+ token get_token() noexcept;
6506
+ ```
6507
+
6508
+ *Returns:* An object `t` of type `simple_counting_scope::token` such
6509
+ that `t.`*`scope`*` == this` is `true`.
6510
+
6511
+ ``` cpp
6512
+ void close() noexcept;
6513
+ ```
6514
+
6515
+ *Effects:* If *state* is
6516
+
6517
+ - *unused*, then changes *state* to *unused-and-closed*;
6518
+ - *open*, then changes *state* to *closed*;
6519
+ - *open-and-joining*, then changes *state* to *closed-and-joining*;
6520
+ - otherwise, no effects.
6521
+
6522
+ *Ensures:* Any subsequent call to *`try-associate`*`()` on `*this`
6523
+ returns `false`.
6524
+
6525
+ ``` cpp
6526
+ sender auto join() noexcept;
6527
+ ```
6528
+
6529
+ *Returns:* *`make-sender`*`(`*`scope-join-t`*`(), this)`.
6530
+
6531
+ ``` cpp
6532
+ bool try-associate() noexcept;
6533
+ ```
6534
+
6535
+ *Effects:* If *count* is equal to `max_associations`, then no effects.
6536
+ Otherwise, if *state* is
6537
+
6538
+ - *unused*, then increments *count* and changes *state* to *open*;
6539
+ - *open* or *open-and-joining*, then increments *count*;
6540
+ - otherwise, no effects.
6541
+
6542
+ *Returns:* `true` if *count* was incremented, `false` otherwise.
6543
+
6544
+ ``` cpp
6545
+ void disassociate() noexcept;
6546
+ ```
6547
+
6548
+ *Preconditions:* *count* is greater than zero.
6549
+
6550
+ *Effects:* Decrements *count*. If *count* is zero after decrementing and
6551
+ *state* is *open-and-joining* or *closed-and-joining*, changes *state*
6552
+ to *joined* and calls *`complete`*`()` on all objects registered with
6553
+ `*this`.
6554
+
6555
+ [*Note 1*: Calling *`complete`*`()` on any registered object can cause
6556
+ `*this` to be destroyed. — *end note*]
6557
+
6558
+ ``` cpp
6559
+ template<class State>
6560
+ bool start-join-sender(State& st) noexcept;
6561
+ ```
6562
+
6563
+ *Effects:* If *state* is
6564
+
6565
+ - *unused*, *unused-and-closed*, or *joined*, then changes *state* to
6566
+ *joined* and returns `true`;
6567
+ - *open* or *open-and-joining*, then changes *state* to
6568
+ *open-and-joining*, registers `st` with `*this` and returns `false`;
6569
+ - *closed* or *closed-and-joining*, then changes *state* to
6570
+ *closed-and-joining*, registers `st` with `*this` and returns `false`.
6571
+
6572
+ ##### Token <a id="exec.simple.counting.token">[[exec.simple.counting.token]]</a>
6573
+
6574
+ ``` cpp
6575
+ namespace std::execution {
6576
+ struct simple_counting_scope::token {
6577
+ template<sender Sender>
6578
+ Sender&& wrap(Sender&& snd) const noexcept;
6579
+ bool try_associate() const noexcept;
6580
+ void disassociate() const noexcept;
6581
+
6582
+ private:
6583
+ simple_counting_scope* scope; // exposition only
6584
+ };
6585
+ }
6586
+ ```
6587
+
6588
+ ``` cpp
6589
+ template<sender Sender>
6590
+ Sender&& wrap(Sender&& snd) const noexcept;
6591
+ ```
6592
+
6593
+ *Returns:* `std::forward<Sender>(snd)`.
6594
+
6595
+ ``` cpp
6596
+ bool try_associate() const noexcept;
6597
+ ```
6598
+
6599
+ *Effects:* Equivalent to: `return `*`scope`*`->`*`try-associate`*`();`
6600
+
6601
+ ``` cpp
6602
+ void disassociate() const noexcept;
6603
+ ```
6604
+
6605
+ *Effects:* Equivalent to *`scope`*`->`*`disassociate`*`()`.
6606
+
6607
+ #### Counting Scope <a id="exec.scope.counting">[[exec.scope.counting]]</a>
6608
+
6609
+ ``` cpp
6610
+ namespace std::execution {
6611
+ class counting_scope {
6612
+ public:
6613
+ struct token {
6614
+ template<sender Sender>
6615
+ sender auto wrap(Sender&& snd) const noexcept(see below);
6616
+ bool try_associate() const noexcept;
6617
+ void disassociate() const noexcept;
6618
+
6619
+ private:
6620
+ counting_scope* scope; // exposition only
6621
+ };
6622
+
6623
+ static constexpr size_t max_associations = implementation-defined;
6624
+
6625
+ counting_scope() noexcept;
6626
+ counting_scope(counting_scope&&) = delete;
6627
+ ~counting_scope();
6628
+
6629
+ token get_token() noexcept;
6630
+ void close() noexcept;
6631
+ sender auto join() noexcept;
6632
+ void request_stop() noexcept;
6633
+
6634
+ private:
6635
+ size_t count; // exposition only
6636
+ scope-state-type state; // exposition only
6637
+ inplace_stop_source s_source; // exposition only
6638
+
6639
+ bool try-associate() noexcept; // exposition only
6640
+ void disassociate() noexcept; // exposition only
6641
+
6642
+ template<class State>
6643
+ bool start-join-sender(State& state) noexcept; // exposition only
6644
+ };
6645
+ }
6646
+ ```
6647
+
6648
+ `counting_scope` differs from `simple_counting_scope` by adding support
6649
+ for cancellation. Unless specified below, the semantics of members of
6650
+ `counting_scope` are the same as the corresponding members of
6651
+ `simple_counting_scope`.
6652
+
6653
+ ``` cpp
6654
+ token get_token() noexcept;
6655
+ ```
6656
+
6657
+ *Returns:* An object `t` of type `counting_scope::token` such that
6658
+ `t.`*`scope`*` == this` is `true`.
6659
+
6660
+ ``` cpp
6661
+ void request_stop() noexcept;
6662
+ ```
6663
+
6664
+ *Effects:* Equivalent to *`s_source`*`.request_stop()`.
6665
+
6666
+ *Remarks:* Calls to `request_stop` do not introduce data races.
6667
+
6668
+ ``` cpp
6669
+ template<sender Sender>
6670
+ sender auto counting_scope::token::wrap(Sender&& snd) const
6671
+ noexcept(is_nothrow_constructible_v<remove_cvref_t<Sender>, Sender>);
6672
+ ```
6673
+
6674
+ *Effects:* Equivalent to:
6675
+
6676
+ ``` cpp
6677
+ return stop-when(std::forward<Sender>(snd), scope->s_source.get_token());
6678
+ ```
6679
+
6680
+ ## Parallel scheduler <a id="exec.par.scheduler">[[exec.par.scheduler]]</a>
6681
+
6682
+ ``` cpp
6683
+ namespace std::execution {
6684
+ class parallel_scheduler {
6685
+ unspecified
6686
+ };
6687
+ }
6688
+ ```
6689
+
6690
+ `parallel_scheduler` models `scheduler`.
6691
+
6692
+ Let `sch` be an object of type `parallel_scheduler`, and let
6693
+ `BACKEND-OF(sch)` be `*ptr`, where `sch` is associated with `ptr`.
6694
+
6695
+ The expression `get_forward_progress_guarantee(sch)` has the value
6696
+ `forward_progress_guarantee::{}parallel`.
6697
+
6698
+ Let `sch2` be an object of type `parallel_scheduler`. Two objects `sch`
6699
+ and `sch2` compare equal if and only if `BACKEND-OF(sch)` and
6700
+ `BACKEND-OF(sch2)` refer to the same object.
6701
+
6702
+ Let `rcvr` be a receiver. A *proxy for `rcvr` with base `B`* is an
6703
+ lvalue `r` of type `B` such that:
6704
+
6705
+ - `r.set_value()` has effects equivalent to
6706
+ `set_value(std::move(rcvr))`.
6707
+ - `r.set_error(e)`, where `e` is an `exception_ptr` object, has effects
6708
+ equivalent to `set_error(std::move({}rcvr), std::move(e))`.
6709
+ - `r.set_stopped()` has effects equivalent to
6710
+ `set_stopped(std::move(rcvr))`.
6711
+
6712
+ A *preallocated backend storage for a proxy* `r` is an object `s` of
6713
+ type `span<byte>` such that the range `s` remains valid and may be
6714
+ overwritten until one of `set_value`, `set_error`, or `set_stopped` is
6715
+ called on `r`.
6716
+
6717
+ [*Note 1*: The storage referenced by `s` can be used as temporary
6718
+ storage for operations launched via calls to
6719
+ `parallel_scheduler_backend`. — *end note*]
6720
+
6721
+ A *bulk chunked proxy for `rcvr` with callable `f` and arguments `args`*
6722
+ is a proxy `r` for `rcvr` with base
6723
+ `system_context_replaceability::bulk_item_receiver_proxy` such that
6724
+ `r.execute(i, j)` for indices `i` and `j` has effects equivalent to
6725
+ `f(i, j, args...)`.
6726
+
6727
+ A *bulk unchunked proxy for `rcvr` with callable `f` and arguments
6728
+ `args`* is a proxy `r` for `rcvr` with base
6729
+ `system_context_replaceability::bulk_item_receiver_proxy` such that
6730
+ `r.execute(i, i + 1)` for index `i` has effects equivalent to
6731
+ `f(i, args...)`.
6732
+
6733
+ Let `b` be `BACKEND-OF(sch)`, let `sndr` be the object returned by
6734
+ `schedule(sch)`, and let `rcvr` be a receiver. If `rcvr` is connected to
6735
+ `sndr` and the resulting operation state is started, then:
6736
+
6737
+ - If `sndr` completes successfully, then `b.schedule(r, s)` is called,
6738
+ where
6739
+ - `r` is a proxy for `rcvr` with base
6740
+ `system_context_replaceability::receiver_proxy` and
6741
+ - `s` is a preallocated backend storage for `r`.
6742
+ - All other completion operations are forwarded unchanged.
6743
+
6744
+ `parallel_scheduler` provides a customized implementation of the
6745
+ `bulk_chunked` algorithm [[exec.bulk]]. If a receiver `rcvr` is
6746
+ connected to the sender returned by `bulk_chunked(sndr, pol, shape, f)`
6747
+ and the resulting operation state is started, then:
6748
+
6749
+ - If `sndr` completes with values `vals`, let `args` be a pack of lvalue
6750
+ subexpressions designating `vals`, then
6751
+ `b.schedule_bulk_chunked(shape, r, s)` is called, where
6752
+ - `r` is a bulk chunked proxy for `rcvr` with callable `f` and
6753
+ arguments `args` and
6754
+ - `s` is a preallocated backend storage for `r`.
6755
+ - All other completion operations are forwarded unchanged.
6756
+
6757
+ [*Note 2*: Customizing the behavior of `bulk_chunked` affects the
6758
+ default implementation of `bulk`. — *end note*]
6759
+
6760
+ `parallel_scheduler` provides a customized implementation of the
6761
+ `bulk_unchunked` algorithm [[exec.bulk]]. If a receiver `rcvr` is
6762
+ connected to the sender returned by
6763
+ `bulk_unchunked(sndr, pol, shape, f)` and the resulting operation state
6764
+ is started, then:
6765
+
6766
+ - If `sndr` completes with values `vals`, let `args` be a pack of lvalue
6767
+ subexpressions designating `vals`, then
6768
+ `b.schedule_bulk_unchunked(shape, r, s)` is called, where
6769
+ - `r` is a bulk unchunked proxy for `rcvr` with callable `f` and
6770
+ arguments `args` and
6771
+ - `s` is a preallocated backend storage for `r`.
6772
+ - All other completion operations are forwarded unchanged.
6773
+
6774
+ ``` cpp
6775
+ parallel_scheduler get_parallel_scheduler();
6776
+ ```
6777
+
6778
+ *Effects:* Let `eb` be the result of
6779
+ `system_context_replaceability::query_parallel_scheduler_backend()`. If
6780
+ `eb == nullptr` is `true`, calls `terminate` [[except.terminate]].
6781
+ Otherwise, returns a `parallel_scheduler` object associated with `eb`.
6782
+
6783
+ ## Namespace `system_context_replaceability` <a id="exec.sysctxrepl">[[exec.sysctxrepl]]</a>
6784
+
6785
+ ### General <a id="exec.sysctxrepl.general">[[exec.sysctxrepl.general]]</a>
6786
+
6787
+ Facilities in the `system_context_replaceability` namespace allow users
6788
+ to replace the default implementation of `parallel_scheduler`.
6789
+
6790
+ ### Receiver proxies <a id="exec.sysctxrepl.recvproxy">[[exec.sysctxrepl.recvproxy]]</a>
6791
+
6792
+ ``` cpp
6793
+ namespace std::execution::system_context_replaceability {
6794
+ struct receiver_proxy {
6795
+ virtual ~receiver_proxy() = default;
6796
+
6797
+ protected:
6798
+ virtual bool query-env(unspecified) noexcept = 0; // exposition only
6799
+
6800
+ public:
6801
+ virtual void set_value() noexcept = 0;
6802
+ virtual void set_error(exception_ptr) noexcept = 0;
6803
+ virtual void set_stopped() noexcept = 0;
6804
+
6805
+ template<class P, class-type Query>
6806
+ optional<P> try_query(Query q) noexcept;
6807
+ };
6808
+
6809
+ struct bulk_item_receiver_proxy : receiver_proxy {
6810
+ virtual void execute(size_t, size_t) noexcept = 0;
6811
+ };
6812
+ }
6813
+ ```
6814
+
6815
+ `receiver_proxy` represents a receiver that will be notified by the
6816
+ implementations of `parallel_scheduler_backend` to trigger the
6817
+ completion operations. `bulk_item_receiver_proxy` is derived from
6818
+ `receiver_proxy` and is used for `bulk_chunked` and `bulk_unchunked`
6819
+ customizations that will also receive notifications from implementations
6820
+ of `parallel_scheduler_backend` corresponding to different iterations.
6821
+
6822
+ ``` cpp
6823
+ template<class P, class-type Query>
6824
+ optional<P> try_query(Query q) noexcept;
6825
+ ```
6826
+
6827
+ *Mandates:* `P` is a cv-unqualified non-array object type.
6828
+
6829
+ *Returns:* Let `env` be the environment of the receiver represented by
6830
+ `*this`. If
6831
+
6832
+ - `Query` is not a member of an implementation-defined set of supported
6833
+ queries; or
6834
+ - `P` is not a member of an implementation-defined set of supported
6835
+ result types for `Query`; or
6836
+ - the expression `q(env)` is not well-formed or does not have type cv
6837
+ `P`,
6838
+
6839
+ then returns `nullopt`. Otherwise, returns `q(env)`.
6840
+
6841
+ *Remarks:* `get_stop_token_t` is in the implementation-defined set of
6842
+ supported queries, and `inplace_stop_token` is a member of the
6843
+ implementation-defined set of supported result types for
6844
+ `get_stop_token_t`.
6845
+
6846
+ ### `query_parallel_scheduler_backend` <a id="exec.sysctxrepl.query">[[exec.sysctxrepl.query]]</a>
6847
+
6848
+ ``` cpp
6849
+ shared_ptr<parallel_scheduler_backend> query_parallel_scheduler_backend();
6850
+ ```
6851
+
6852
+ `query_parallel_scheduler_backend()` returns the implementation object
6853
+ for a parallel scheduler.
6854
+
6855
+ *Returns:* A non-null shared pointer to an object that implements the
6856
+ `parallel_scheduler_backend` interface.
6857
+
6858
+ *Remarks:* This function is replaceable [[term.replaceable.function]].
6859
+
6860
+ ### Class `parallel_scheduler_backend` <a id="exec.sysctxrepl.psb">[[exec.sysctxrepl.psb]]</a>
6861
+
6862
+ ``` cpp
6863
+ namespace std::execution::system_context_replaceability {
6864
+ struct parallel_scheduler_backend {
6865
+ virtual ~parallel_scheduler_backend() = default;
6866
+
6867
+ virtual void schedule(receiver_proxy&, span<byte>) noexcept = 0;
6868
+ virtual void schedule_bulk_chunked(size_t, bulk_item_receiver_proxy&,
6869
+ span<byte>) noexcept = 0;
6870
+ virtual void schedule_bulk_unchunked(size_t, bulk_item_receiver_proxy&,
6871
+ span<byte>) noexcept = 0;
6872
+ };
6873
+ }
6874
+ ```
6875
+
6876
+ ``` cpp
6877
+ virtual void schedule(receiver_proxy& r, span<byte> s) noexcept = 0;
6878
+ ```
6879
+
6880
+ *Preconditions:* The ends of the lifetimes of `*this`, the object
6881
+ referred to by `r`, and any storage referenced by `s` all happen after
6882
+ the beginning of the evaluation of the call to `set_value`, `set_error`,
6883
+ or `set_done` on `r` (see below).
6884
+
6885
+ *Effects:* A derived class shall implement this function such that:
6886
+
6887
+ - One of the following expressions is evaluated:
6888
+ - `r.set_value()`, if no error occurs, and the work is successful;
6889
+ - `r.set_error(eptr)`, if an error occurs, where `eptr` is an object
6890
+ of type `exception_ptr`;
6891
+ - `r.set_stopped()`, if the work is canceled.
6892
+ - Any call to `r.set_value()` happens on an execution agent of the
6893
+ execution context represented by `*this`.
6894
+
6895
+ *Remarks:* The storage referenced by `s` may be used by `*this` as
6896
+ temporary storage for the duration of the operation launched by this
6897
+ call.
6898
+
6899
+ ``` cpp
6900
+ virtual void schedule_bulk_chunked(size_t n, bulk_item_receiver_proxy& r,
6901
+ span<byte> s) noexcept = 0;
6902
+ ```
6903
+
6904
+ *Preconditions:* The ends of the lifetimes of `*this`, the object
6905
+ referred to by `r`, and any storage referenced by `s` all happen after
6906
+ the beginning of the evaluation of one of the expressions below.
6907
+
6908
+ *Effects:* A derived class shall implement this function such that:
6909
+
6910
+ - Eventually, one of the following expressions is evaluated:
6911
+ - `r.set_value()`, if no error occurs, and the work is successful;
6912
+ - `r.set_error(eptr)`, if an error occurs, where `eptr` is an object
6913
+ of type `exception_ptr`;
6914
+ - `r.set_stopped()`, if the work is canceled.
6915
+ - If `r.execute(b, e)` is called, then `b` and `e` are in the range
6916
+ \[`0`, `n`\] and `b` < `e`.
6917
+ - For each i in \[`0`, `n`), there is at most one call to
6918
+ `r.execute(b, e)` such that i is in the range \[`b`, `e`).
6919
+ - If `r.set_value()` is called, then for each i in \[`0`, `n`), there is
6920
+ exactly one call to `r.execute(b, e)` such that i is in the range
6921
+ \[`b`, `e`).
6922
+ - All calls to `execute` on `r` happen before the call to either
6923
+ `set_value`, `set_error`, or `set_stopped` on `r`.
6924
+ - All calls to `execute` and `set_value` on `r` are made on execution
6925
+ agents of the execution context represented by `*this`.
6926
+
6927
+ *Remarks:* The storage referenced by `s` may be used by `*this` as
6928
+ temporary storage for the duration of the operation launched by this
6929
+ call.
6930
+
6931
+ ``` cpp
6932
+ virtual void schedule_bulk_unchunked(size_t n, bulk_item_receiver_proxy& r,
6933
+ span<byte> s) noexcept = 0;
6934
+ ```
6935
+
6936
+ *Preconditions:* The ends of the lifetimes of `*this`, the object
6937
+ referred to by `r`, and any storage referenced by `s` all happen after
6938
+ the beginning of the evaluation of one of the expressions below.
6939
+
6940
+ *Effects:* A derived class shall implement this function such that:
6941
+
6942
+ - Eventually, one of the following expressions is evaluated:
6943
+ - `r.set_value()`, if no error occurs, and the work is successful;
6944
+ - `r.set_error(eptr)`, if an error occurs, where `eptr` is an object
6945
+ of type `exception_ptr`;
6946
+ - `r.set_stopped()`, if the work is canceled.
6947
+ - If `r.execute(b, e)` is called, then `b` is in the range \[`0`, `n`)
6948
+ and `e` is equal to `b + 1`. For each i in \[`0`, `n`), there is at
6949
+ most one call to `r.execute(`i`, `i` + 1)`.
6950
+ - If `r.set_value()` is called, then for each i in \[`0`, `n`), there is
6951
+ exactly one call to `r.execute(`i`, `i` + 1)`.
6952
+ - All calls to `execute` on `r` happen before the call to either
6953
+ `set_value`, `set_error`, or `set_stopped` on `r`.
6954
+ - All calls to `execute` and `set_value` on `r` are made on execution
6955
+ agents of the execution context represented by `*this`.
6956
+
6957
+ *Remarks:* The storage referenced by `s` may be used by `*this` as
6958
+ temporary storage for the duration of the operation launched by this
6959
+ call.
6960
+
6961
+ <!-- Link reference definitions -->
6962
+ [algorithms.parallel.exec]: algorithms.md#algorithms.parallel.exec
6963
+ [allocator.requirements.general]: library.md#allocator.requirements.general
6964
+ [basic.def.odr]: basic.md#basic.def.odr
6965
+ [concepts.equality]: concepts.md#concepts.equality
6966
+ [customization.point.object]: library.md#customization.point.object
6967
+ [dcl.init]: dcl.md#dcl.init
6968
+ [dcl.struct.bind]: dcl.md#dcl.struct.bind
6969
+ [defns.block]: intro.md#defns.block
6970
+ [except.terminate]: except.md#except.terminate
6971
+ [exec]: #exec
6972
+ [exec.adapt]: #exec.adapt
6973
+ [exec.adapt.general]: #exec.adapt.general
6974
+ [exec.adapt.obj]: #exec.adapt.obj
6975
+ [exec.affine.on]: #exec.affine.on
6976
+ [exec.as.awaitable]: #exec.as.awaitable
6977
+ [exec.associate]: #exec.associate
6978
+ [exec.async.ops]: #exec.async.ops
6979
+ [exec.awaitable]: #exec.awaitable
6980
+ [exec.bulk]: #exec.bulk
6981
+ [exec.cmplsig]: #exec.cmplsig
6982
+ [exec.connect]: #exec.connect
6983
+ [exec.consumers]: #exec.consumers
6984
+ [exec.continues.on]: #exec.continues.on
6985
+ [exec.coro.util]: #exec.coro.util
6986
+ [exec.counting.scopes]: #exec.counting.scopes
6987
+ [exec.counting.scopes.general]: #exec.counting.scopes.general
6988
+ [exec.ctx]: #exec.ctx
6989
+ [exec.domain.default]: #exec.domain.default
6990
+ [exec.env]: #exec.env
6991
+ [exec.envs]: #exec.envs
6992
+ [exec.factories]: #exec.factories
6993
+ [exec.fwd.env]: #exec.fwd.env
6994
+ [exec.general]: #exec.general
6995
+ [exec.get.allocator]: #exec.get.allocator
6996
+ [exec.get.await.adapt]: #exec.get.await.adapt
6997
+ [exec.get.compl.sched]: #exec.get.compl.sched
6998
+ [exec.get.delegation.scheduler]: #exec.get.delegation.scheduler
6999
+ [exec.get.domain]: #exec.get.domain
7000
+ [exec.get.env]: #exec.get.env
7001
+ [exec.get.fwd.progress]: #exec.get.fwd.progress
7002
+ [exec.get.scheduler]: #exec.get.scheduler
7003
+ [exec.get.stop.token]: #exec.get.stop.token
7004
+ [exec.getcomplsigs]: #exec.getcomplsigs
7005
+ [exec.inline.scheduler]: #exec.inline.scheduler
7006
+ [exec.into.variant]: #exec.into.variant
7007
+ [exec.just]: #exec.just
7008
+ [exec.let]: #exec.let
7009
+ [exec.on]: #exec.on
7010
+ [exec.opstate]: #exec.opstate
7011
+ [exec.opstate.general]: #exec.opstate.general
7012
+ [exec.opstate.start]: #exec.opstate.start
7013
+ [exec.par.scheduler]: #exec.par.scheduler
7014
+ [exec.pos]: #exec.pos
7015
+ [exec.prop]: #exec.prop
7016
+ [exec.queries]: #exec.queries
7017
+ [exec.queryable]: #exec.queryable
7018
+ [exec.queryable.concept]: #exec.queryable.concept
7019
+ [exec.queryable.general]: #exec.queryable.general
7020
+ [exec.read.env]: #exec.read.env
7021
+ [exec.recv]: #exec.recv
7022
+ [exec.recv.concepts]: #exec.recv.concepts
7023
+ [exec.run.loop]: #exec.run.loop
7024
+ [exec.run.loop.ctor]: #exec.run.loop.ctor
7025
+ [exec.run.loop.general]: #exec.run.loop.general
7026
+ [exec.run.loop.members]: #exec.run.loop.members
7027
+ [exec.run.loop.types]: #exec.run.loop.types
7028
+ [exec.sched]: #exec.sched
7029
+ [exec.schedule]: #exec.schedule
7030
+ [exec.schedule.from]: #exec.schedule.from
7031
+ [exec.scope]: #exec.scope
7032
+ [exec.scope.concepts]: #exec.scope.concepts
7033
+ [exec.scope.counting]: #exec.scope.counting
7034
+ [exec.scope.simple.counting]: #exec.scope.simple.counting
7035
+ [exec.scope.simple.counting.general]: #exec.scope.simple.counting.general
7036
+ [exec.set.error]: #exec.set.error
7037
+ [exec.set.stopped]: #exec.set.stopped
7038
+ [exec.set.value]: #exec.set.value
7039
+ [exec.simple.counting.ctor]: #exec.simple.counting.ctor
7040
+ [exec.simple.counting.mem]: #exec.simple.counting.mem
7041
+ [exec.simple.counting.token]: #exec.simple.counting.token
7042
+ [exec.snd]: #exec.snd
7043
+ [exec.snd.apply]: #exec.snd.apply
7044
+ [exec.snd.concepts]: #exec.snd.concepts
7045
+ [exec.snd.expos]: #exec.snd.expos
7046
+ [exec.snd.general]: #exec.snd.general
7047
+ [exec.snd.transform]: #exec.snd.transform
7048
+ [exec.snd.transform.env]: #exec.snd.transform.env
7049
+ [exec.spawn]: #exec.spawn
7050
+ [exec.spawn.future]: #exec.spawn.future
7051
+ [exec.starts.on]: #exec.starts.on
7052
+ [exec.stop.when]: #exec.stop.when
7053
+ [exec.stopped.err]: #exec.stopped.err
7054
+ [exec.stopped.opt]: #exec.stopped.opt
7055
+ [exec.summary]: #exec.summary
7056
+ [exec.sync.wait]: #exec.sync.wait
7057
+ [exec.sync.wait.var]: #exec.sync.wait.var
7058
+ [exec.sysctxrepl]: #exec.sysctxrepl
7059
+ [exec.sysctxrepl.general]: #exec.sysctxrepl.general
7060
+ [exec.sysctxrepl.psb]: #exec.sysctxrepl.psb
7061
+ [exec.sysctxrepl.query]: #exec.sysctxrepl.query
7062
+ [exec.sysctxrepl.recvproxy]: #exec.sysctxrepl.recvproxy
7063
+ [exec.task]: #exec.task
7064
+ [exec.task.scheduler]: #exec.task.scheduler
7065
+ [exec.then]: #exec.then
7066
+ [exec.unstoppable]: #exec.unstoppable
7067
+ [exec.when.all]: #exec.when.all
7068
+ [exec.with.awaitable.senders]: #exec.with.awaitable.senders
7069
+ [exec.write.env]: #exec.write.env
7070
+ [execution.syn]: #execution.syn
7071
+ [expr.await]: expr.md#expr.await
7072
+ [expr.const]: expr.md#expr.const
7073
+ [func.def]: utilities.md#func.def
7074
+ [func.require]: utilities.md#func.require
7075
+ [function.objects]: utilities.md#function.objects
7076
+ [intro.multithread]: basic.md#intro.multithread
7077
+ [intro.progress]: basic.md#intro.progress
7078
+ [intro.races]: basic.md#intro.races
7079
+ [meta.trans.other]: meta.md#meta.trans.other
7080
+ [namespace.std]: library.md#namespace.std
7081
+ [task.class]: #task.class
7082
+ [task.members]: #task.members
7083
+ [task.overview]: #task.overview
7084
+ [task.promise]: #task.promise
7085
+ [task.state]: #task.state
7086
+ [term.replaceable.function]: dcl.md#term.replaceable.function
7087
+ [thread.req.lockable.general]: thread.md#thread.req.lockable.general