This commit is contained in:
2025-10-25 03:02:53 +03:00
commit 043225d523
3416 changed files with 681196 additions and 0 deletions

2142
cppdraft/exec/adapt.md Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,81 @@
[exec.adapt.general]
# 33 Execution control library [[exec]](./#exec)
## 33.9 Senders [[exec.snd]](exec.snd#exec.adapt.general)
### 33.9.12 Sender adaptors [[exec.adapt]](exec.adapt#general)
#### 33.9.12.1 General [exec.adapt.general]
[1](#1)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3002)
Subclause [[exec.adapt]](exec.adapt "33.9.12Sender adaptors") specifies a set of sender adaptors[.](#1.sentence-1)
[2](#2)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3005)
The bitwise inclusive or operator is overloaded
for the purpose of creating sender chains[.](#2.sentence-1)
The adaptors also support function call syntax with equivalent semantics[.](#2.sentence-2)
[3](#3)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3010)
Unless otherwise specified:
- [(3.1)](#3.1)
A sender adaptor is prohibited from causing observable effects,
apart from moving and copying its arguments,
before the returned sender is connected with a receiver using connect,
and start is called on the resulting operation state[.](#3.1.sentence-1)
- [(3.2)](#3.2)
A parent sender ([[exec.async.ops]](exec.async.ops "33.3Asynchronous operations")) with a single child sender sndr has
an associated attribute object equal to*FWD-ENV*(get_env(sndr)) ([[exec.fwd.env]](exec.fwd.env "33.5.1forwarding_­query"))[.](#3.2.sentence-1)
- [(3.3)](#3.3)
A parent sender with more than one child sender has
an associated attributes object equal to env<>{}[.](#3.3.sentence-1)
- [(3.4)](#3.4)
When a parent sender is connected to a receiver rcvr,
any receiver used to connect a child sender has
an associated environment equal to *FWD-ENV*(get_env(rcvr))[.](#3.4.sentence-1)
- [(3.5)](#3.5)
An adaptor whose child senders are all non-dependent ([[exec.async.ops]](exec.async.ops "33.3Asynchronous operations"))
is itself non-dependent[.](#3.5.sentence-1)
- [(3.6)](#3.6)
These requirements apply to any function
that is selected by the implementation of the sender adaptor[.](#3.6.sentence-1)
- [(3.7)](#3.7)
*Recommended practice*: Implementations should use
the completion signatures of the adaptors
to communicate type errors to users and
to propagate any such type errors from child senders[.](#3.7.sentence-1)
[4](#4)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3043)
If a sender returned from a sender adaptor specified in [[exec.adapt]](exec.adapt "33.9.12Sender adaptors") is specified to include set_error_t(Err) among its set of completion signatures
where decay_t<Err> denotes the type exception_ptr,
but the implementation does not potentially evaluate
an error completion operation with an exception_ptr argument,
the implementation is allowed to omit
the exception_ptr error completion signature from the set[.](#4.sentence-1)

112
cppdraft/exec/adapt/obj.md Normal file
View File

@@ -0,0 +1,112 @@
[exec.adapt.obj]
# 33 Execution control library [[exec]](./#exec)
## 33.9 Senders [[exec.snd]](exec.snd#exec.adapt.obj)
### 33.9.12 Sender adaptors [[exec.adapt]](exec.adapt#obj)
#### 33.9.12.2 Closure objects [exec.adapt.obj]
[1](#1)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3055)
A [*pipeable sender adaptor closure object*](#def:sender_adaptor_closure_object,pipeable "33.9.12.2Closure objects[exec.adapt.obj]") is a function object
that accepts one or more [sender](exec.snd.concepts#concept:sender "33.9.3Sender concepts[exec.snd.concepts]") arguments and returns a [sender](exec.snd.concepts#concept:sender "33.9.3Sender concepts[exec.snd.concepts]")[.](#1.sentence-1)
For a pipeable sender adaptor closure object c and
an expression sndr such that decltype((sndr)) models [sender](exec.snd.concepts#concept:sender "33.9.3Sender concepts[exec.snd.concepts]"),
the following expressions are equivalent and yield a [sender](exec.snd.concepts#concept:sender "33.9.3Sender concepts[exec.snd.concepts]"):c(sndr) sndr | c
Given an additional pipeable sender adaptor closure object d,
the expression c | d produces
another pipeable sender adaptor closure object e:
e is a perfect forwarding call wrapper ([[func.require]](func.require "22.10.4Requirements"))
with the following properties:
- [(1.1)](#1.1)
Its target object is an object d2 of type decltype(auto(d)) direct-non-list-initialized with d[.](#1.1.sentence-1)
- [(1.2)](#1.2)
It has one bound argument entity,
an object c2 of type decltype(auto(c)) direct-non-list-initialized with c[.](#1.2.sentence-1)
- [(1.3)](#1.3)
Its call pattern is d2(c2(arg)),
where arg is the argument used in a function call expression of e[.](#1.3.sentence-1)
The expression c | d is well-formed if and only if
the initializations of the state entities ([[func.def]](func.def "22.10.3Definitions")) of e are all well-formed[.](#1.sentence-5)
[2](#2)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3088)
An object t of type T is
a pipeable sender adaptor closure object
if T models [derived_from](concept.derived#concept:derived_from "18.4.3Concept derived_­from[concept.derived]")<sender_adaptor_closure<T>>,T has no other base classes
of type sender_adaptor_closure<U> for any other type U, andT does not satisfy [sender](exec.snd.concepts#concept:sender "33.9.3Sender concepts[exec.snd.concepts]")[.](#2.sentence-1)
[3](#3)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3096)
The template parameter D for sender_adaptor_closure can be
an incomplete type[.](#3.sentence-1)
Before any expression of type cv D appears as
an operand to the | operator,D shall be complete and
model [derived_from](concept.derived#concept:derived_from "18.4.3Concept derived_­from[concept.derived]")<sender_adaptor_closure<D>>[.](#3.sentence-2)
The behavior of an expression involving an object of type cv D as an operand to the | operator is undefined
if overload resolution selects a program-defined operator| function[.](#3.sentence-3)
[4](#4)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3107)
A [*pipeable sender adaptor object*](#def:sender_adaptor_object,pipeable "33.9.12.2Closure objects[exec.adapt.obj]") is a customization point object
that accepts a [sender](exec.snd.concepts#concept:sender "33.9.3Sender concepts[exec.snd.concepts]") as its first argument and
returns a [sender](exec.snd.concepts#concept:sender "33.9.3Sender concepts[exec.snd.concepts]")[.](#4.sentence-1)
If a pipeable sender adaptor object accepts only one argument,
then it is a pipeable sender adaptor closure object[.](#4.sentence-2)
[5](#5)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3114)
If a pipeable sender adaptor object adaptor accepts more than one argument,
then let sndr be an expression
such that decltype((sndr)) models [sender](exec.snd.concepts#concept:sender "33.9.3Sender concepts[exec.snd.concepts]"),
let args... be arguments
such that adaptor(sndr, args...) is a well-formed expression
as specified below, and
let BoundArgs be a pack that denotes decltype(auto(args))...[.](#5.sentence-1)
The expression adaptor(args...) produces
a pipeable sender adaptor closure object f that is a perfect forwarding call wrapper with the following properties:
- [(5.1)](#5.1)
Its target object is a copy of adaptor[.](#5.1.sentence-1)
- [(5.2)](#5.2)
Its bound argument entities bound_args consist of
objects of types BoundArgs... direct-non-list-initialized withstd::forward<decltype((args))>(args)..., respectively[.](#5.2.sentence-1)
- [(5.3)](#5.3)
Its call pattern is adaptor(rcvr, bound_args...),
where rcvr is
the argument used in a function call expression of f[.](#5.3.sentence-1)
The expression adaptor(args...) is well-formed if and only if
the initializations of the bound argument entities of the result,
as specified above, are all well-formed[.](#5.sentence-3)

View File

@@ -0,0 +1,65 @@
[exec.affine.on]
# 33 Execution control library [[exec]](./#exec)
## 33.13 Coroutine utilities [[exec.coro.util]](exec.coro.util#exec.affine.on)
### 33.13.3 execution::affine_on [exec.affine.on]
[1](#1)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6877)
affine_on adapts a sender into one that completes on
the specified scheduler[.](#1.sentence-1)
If the algorithm determines that the adapted sender already completes
on the correct scheduler it can avoid any scheduling operation[.](#1.sentence-2)
[2](#2)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6883)
The name affine_on denotes a pipeable sender adaptor
object[.](#2.sentence-1)
For subexpressions sch and sndr, if decltype((sch)) does not satisfy [scheduler](exec.sched#concept:scheduler "33.6Schedulers[exec.sched]"), or decltype((sndr)) does not satisfy [sender](exec.snd.concepts#concept:sender "33.9.3Sender concepts[exec.snd.concepts]"), affine_on(sndr, sch) is ill-formed[.](#2.sentence-2)
[3](#3)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6891)
Otherwise, the expression affine_on(sndr, sch) is
expression-equivalent to:transform_sender(*get-domain-early*(sndr), *make-sender*(affine_on, sch, sndr)) except that sndr is evaluated only once[.](#3.sentence-1)
[4](#4)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6899)
The exposition-only class template *impls-for* ([[exec.snd.expos]](exec.snd.expos "33.9.2Exposition-only entities"))
is specialized for affine_on_t as follows:
namespace std::execution {template<>struct *impls-for*<affine_on_t> : *default-impls* {static constexpr auto *get-attrs* =[](const auto& data, const auto& child) noexcept -> decltype(auto) {return *JOIN-ENV*(*SCHED-ATTRS*(data), *FWD-ENV*(get_env(child))); }; };}
[5](#5)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6915)
Let *out_sndr* be a subexpression denoting a sender
returned from affine_on(sndr, sch) or one equal to such,
and let *OutSndr* be the type decltype((*out_sndr*))[.](#5.sentence-1)
Let *out_rcvr* be a subexpression denoting a receiver that
has an environment of type Env such that [sender_in](exec.snd.concepts#concept:sender_in "33.9.3Sender concepts[exec.snd.concepts]")<*OutSndr*, Env> is true[.](#5.sentence-2)
Let *op* be an lvalue referring to the operation state that
results from connecting *out_sndr* to *out_rcvr*[.](#5.sentence-3)
Calling start(*op*) will start sndr on the current
execution agent and execute completion operations on *out_rcvr* on an execution agent of the execution resource associated withsch[.](#5.sentence-4)
If the current execution resource is the same as the execution
resource associated with sch, the completion operation on*out_rcvr* may be called before start(*op*) completes[.](#5.sentence-5)
If scheduling onto sch fails, an error completion on*out_rcvr* shall be executed on an unspecified execution
agent[.](#5.sentence-6)

View File

@@ -0,0 +1,123 @@
[exec.as.awaitable]
# 33 Execution control library [[exec]](./#exec)
## 33.13 Coroutine utilities [[exec.coro.util]](exec.coro.util#exec.as.awaitable)
### 33.13.1 execution::as_awaitable [exec.as.awaitable]
[1](#1)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6609)
as_awaitable transforms an object into one
that is awaitable within a particular coroutine[.](#1.sentence-1)
Subclause [[exec.coro.util]](exec.coro.util "33.13Coroutine utilities") makes use of
the following exposition-only entities:namespace std::execution {template<class Sndr, class Promise>concept [*awaitable-sender*](#concept:awaitable-sender "33.13.1execution::as_­awaitable[exec.as.awaitable]") =[*single-sender*](execution.syn#concept:single-sender "33.4Header <execution> synopsis[execution.syn]")<Sndr, env_of_t<Promise>> &&[sender_to](exec.snd.concepts#concept:sender_to "33.9.3Sender concepts[exec.snd.concepts]")<Sndr, *awaitable-receiver*> && // *see below*requires (Promise& p) {{ p.unhandled_stopped() } -> [convertible_to](concept.convertible#concept:convertible_to "18.4.4Concept convertible_­to[concept.convertible]")<coroutine_handle<>>; }; template<class Sndr>concept [*has-queryable-await-completion-adaptor*](#concept:has-queryable-await-completion-adaptor "33.13.1execution::as_­awaitable[exec.as.awaitable]") = // *exposition only*[sender](exec.snd.concepts#concept:sender "33.9.3Sender concepts[exec.snd.concepts]")<Sndr> &&requires(Sndr&& sender) { get_await_completion_adaptor(get_env(sender)); }; template<class Sndr, class Promise>class *sender-awaitable*; // *exposition only*}
[2](#2)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6636)
The type *sender-awaitable*<Sndr, Promise> is equivalent to:
namespace std::execution {template<class Sndr, class Promise>class *sender-awaitable* {struct *unit* {}; // *exposition only*using *value-type* = // *exposition only**single-sender-value-type*<Sndr, env_of_t<Promise>>; using *result-type* = // *exposition only* conditional_t<is_void_v<*value-type*>, unit, *value-type*>; struct *awaitable-receiver*; // *exposition only* variant<monostate, *result-type*, exception_ptr> *result*{}; // *exposition only* connect_result_t<Sndr, *awaitable-receiver*> *state*; // *exposition only*public:*sender-awaitable*(Sndr&& sndr, Promise& p); static constexpr bool await_ready() noexcept { return false; }void await_suspend(coroutine_handle<Promise>) noexcept { start(*state*); }*value-type* await_resume(); };}
[3](#3)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6662)
*awaitable-receiver* is equivalent to:struct *awaitable-receiver* {using receiver_concept = receiver_t;
variant<monostate, *result-type*, exception_ptr>* *result-ptr*; // *exposition only* coroutine_handle<Promise> *continuation*; // *exposition only*// *see below*};
[4](#4)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6673)
Let rcvr be an rvalue expression of type *awaitable-receiver*,
let crcvr be a const lvalue that refers to rcvr,
let vs be a pack of subexpressions, and
let err be an expression of type Err[.](#4.sentence-1)
Then:
- [(4.1)](#4.1)
If [constructible_from](concept.constructible#concept:constructible_from "18.4.11Concept constructible_­from[concept.constructible]")<*result-type*, decltype((vs))...> is satisfied,
the expression set_value(
rcvr, vs...) is equivalent to:try { rcvr.*result-ptr*->template emplace<1>(vs...);} catch(...) { rcvr.*result-ptr*->template emplace<2>(current_exception());} rcvr.*continuation*.resume();
Otherwise, set_value(rcvr, vs...) is ill-formed[.](#4.1.sentence-2)
- [(4.2)](#4.2)
The expression set_error(rcvr, err) is equivalent to:rcvr.*result-ptr*->template emplace<2>(*AS-EXCEPT-PTR*(err)); // see [[exec.general]](exec.general "33.1General") rcvr.*continuation*.resume();
- [(4.3)](#4.3)
The expression set_stopped(rcvr) is equivalent to:static_cast<coroutine_handle<>>(rcvr.*continuation*.promise().unhandled_stopped()).resume();
- [(4.4)](#4.4)
For any expression tag whose type satisfies [*forwarding-query*](execution.syn#concept:forwarding-query "33.4Header <execution> synopsis[execution.syn]") and
for any pack of subexpressions as,get_env(crcvr).query(tag, as...) is expression-equivalent to:tag(get_env(as_const(crcvr.*continuation*.promise())), as...)
[🔗](#itemdecl:1)
`sender-awaitable(Sndr&& sndr, Promise& p);
`
[5](#5)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6718)
*Effects*: Initializes *state* withconnect(std::forward<Sndr>(sndr), *awaitable-receiver*{addressof(result), coroutine_handle<Promise>::from_promise(p)})
[🔗](#itemdecl:2)
`value-type await_resume();
`
[6](#6)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6732)
*Effects*: Equivalent to:if (*result*.index() == 2) rethrow_exception(get<2>(*result*));if constexpr (!is_void_v<*value-type*>)return std::forward<*value-type*>(get<1>(*result*));
[7](#7)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6743)
as_awaitable is a customization point object[.](#7.sentence-1)
For subexpressions expr and p where p is an lvalue,Expr names the type decltype((expr)) andPromise names the type decay_t<decltype((p))>,as_awaitable(expr, p) is expression-equivalent to,
except that the evaluations of expr and p are indeterminately sequenced:
- [(7.1)](#7.1)
expr.as_awaitable(p) if that expression is well-formed[.](#7.1.sentence-1)
*Mandates*: [*is-awaitable*](exec.awaitable#concept:is-awaitable "33.9.4Awaitable helpers[exec.awaitable]")<A, Promise> is true,
where A is the type of the expression above[.](#7.1.sentence-2)
- [(7.2)](#7.2)
Otherwise, (void(p), expr) if [*is-awaitable*](exec.awaitable#concept:is-awaitable "33.9.4Awaitable helpers[exec.awaitable]")<Expr, U> is true,
where U is an unspecified class type
that is not Promise and
that lacks a member named await_transform[.](#7.2.sentence-1)
*Preconditions*: [*is-awaitable*](exec.awaitable#concept:is-awaitable "33.9.4Awaitable helpers[exec.awaitable]")<Expr, Promise> is true and
the expression co_await expr in a coroutine with promise type U is expression-equivalent to
the same expression in a coroutine with promise type Promise[.](#7.2.sentence-2)
- [(7.3)](#7.3)
Otherwise, *sender-awaitable*{*adapted-expr*, p} if*has-queryable-await-completion-adaptor*<Expr> and*awaitable-sender*<decltype((*adapted-expr*)), Promise> are both satisfied, where *adapted-expr* isget_await_completion_adaptor(get_env(expr))(expr),
except that expr is evaluated only once[.](#7.3.sentence-1)
- [(7.4)](#7.4)
Otherwise, *sender-awaitable*{expr, p} if [*awaitable-sender*](#concept:awaitable-sender "33.13.1execution::as_­awaitable[exec.as.awaitable]")<Expr, Promise> is true[.](#7.4.sentence-1)
- [(7.5)](#7.5)
Otherwise, (void(p), expr)[.](#7.5.sentence-1)

167
cppdraft/exec/associate.md Normal file
View File

@@ -0,0 +1,167 @@
[exec.associate]
# 33 Execution control library [[exec]](./#exec)
## 33.9 Senders [[exec.snd]](exec.snd#exec.associate)
### 33.9.12 Sender adaptors [[exec.adapt]](exec.adapt#exec.associate)
#### 33.9.12.16 execution::associate [exec.associate]
[1](#1)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4897)
associate tries to associate
a sender with an async scope such that
the scope can track the lifetime of any asynchronous operations
created with the sender[.](#1.sentence-1)
[2](#2)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4903)
Let *associate-data* be the following exposition-only class template:
[🔗](#lib:execution::associate-data)
namespace std::execution {template<[scope_token](exec.scope.concepts#concept:scope_token "33.14.1Execution scope concepts[exec.scope.concepts]") Token, [sender](exec.snd.concepts#concept:sender "33.9.3Sender concepts[exec.snd.concepts]") Sender>struct *associate-data* { // *exposition only*using *wrap-sender* = // *exposition only* remove_cvref_t<decltype(declval<Token&>().wrap(declval<Sender>()))>; explicit *associate-data*(Token t, Sender&& s): *sndr*(t.wrap(std::forward<Sender>(s))), *token*(t) {if (!*token*.try_associate())*sndr*.reset(); }*associate-data*(const *associate-data*& other)noexcept(is_nothrow_copy_constructible_v<*wrap-sender*> &&noexcept(other.*token*.try_associate())); *associate-data*(*associate-data*&& other)noexcept(is_nothrow_move_constructible_v<*wrap-sender*>); ~*associate-data*();
optional<pair<Token, *wrap-sender*>> release() && noexcept(is_nothrow_move_constructible_v<*wrap-sender*>); private: optional<*wrap-sender*> *sndr*; // *exposition only* Token *token*; // *exposition only*}; template<[scope_token](exec.scope.concepts#concept:scope_token "33.14.1Execution scope concepts[exec.scope.concepts]") Token, [sender](exec.snd.concepts#concept:sender "33.9.3Sender concepts[exec.snd.concepts]") Sender>*associate-data*(Token, Sender&&) -> *associate-data*<Token, Sender>;}
[3](#3)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4943)
For an *associate-data* object a,a.*sndr*.has_value() is true if and only if
an association was successfully made and is owned by a[.](#3.sentence-1)
[🔗](#lib:execution::associate-data,constructor)
`associate-data(const associate-data& other)
noexcept(is_nothrow_copy_constructible_v<wrap-sender> &&
noexcept(other.token.try_associate()));
`
[4](#4)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4957)
*Constraints*: [copy_constructible](concept.copyconstructible#concept:copy_constructible "18.4.14Concept copy_­constructible[concept.copyconstructible]")<*wrap-sender*> is true[.](#4.sentence-1)
[5](#5)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4961)
*Effects*: Value-initializes *sndr* and
initializes *token* with other.*token*[.](#5.sentence-1)
If other.*sndr*.has_value() is false,
no further effects;
otherwise,
calls *token*.try_associate() and,
if that returns true,
calls *sndr*.emplace(*other.*sndr*) and,
if that exits with an exception,
calls *token*.disassociate() before propagating the exception[.](#5.sentence-2)
[🔗](#lib:execution::associate-data,constructor_)
`associate-data(associate-data&& other)
noexcept(is_nothrow_move_constructible_v<wrap-sender>);
`
[6](#6)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4982)
*Effects*: Initializes *sndr* with std::move(other.*sndr*) and
initializes *token* with std::move(other.*token*) and
then calls other.*sndr*.reset()[.](#6.sentence-1)
[🔗](#lib:execution::associate-data,destructor)
`~associate-data();
`
[7](#7)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4995)
*Effects*: If *sndr*.has_value() returns false then no effect;
otherwise, invokes *sndr*.reset() before invoking *token*.disassociate()[.](#7.sentence-1)
[🔗](#lib:release,execution::associate-data)
`optional<pair<Token, wrap-sender>>
release() && noexcept(is_nothrow_move_constructible_v<wrap-sender>);
`
[8](#8)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5009)
*Effects*: If *sndr*.has_value() returns false then
returns an optional that does not contain a value;
otherwise returns an optional containing a value of type pair<Token, *wrap-sender*> as if by:return optional(pair(*token*, std::move(**sndr*)));
[9](#9)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5020)
*Postconditions*: *sndr* does not contain a value[.](#9.sentence-1)
[10](#10)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5025)
The name associate denotes a pipeable sender adaptor object[.](#10.sentence-1)
For subexpressions sndr and token:
- [(10.1)](#10.1)
If decltype((sndr)) does not satisfy [sender](exec.snd.concepts#concept:sender "33.9.3Sender concepts[exec.snd.concepts]"), orremove_cvref_t<decltype((token))> does not satisfy [scope_token](exec.scope.concepts#concept:scope_token "33.14.1Execution scope concepts[exec.scope.concepts]"), thenassociate(sndr, token) is ill-formed.
- [(10.2)](#10.2)
Otherwise,
the expression associate(sndr, token) is expression-equivalent to:transform_sender(*get-domain-early*(sndr), *make-sender*(associate, *associate-data*(token, sndr))) except that sndr is evaluated only once.
[11](#11)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5045)
The exposition-only class template *impls-for* ([[exec.snd.expos]](exec.snd.expos "33.9.2Exposition-only entities"))
is specialized for associate_t as follows:
[🔗](#lib:execution::impls-for%3cassociate_t%3e)
namespace std::execution {template<>struct *impls-for*<associate_t> : *default-impls* {static constexpr auto *get-state* = *see below*; // *exposition only*static constexpr auto *start* = *see below*; // *exposition only*template<class Sndr, class... Env>static consteval void *check-types*() { // *exposition only*using associate_data_t = remove_cvref_t<*data-type*<Sndr>>; using child_type_t = typename associate_data_t::*wrap-sender*; (void)get_completion_signatures<child_type_t, *FWD-ENV-T*(Env)...>(); }};}
[12](#12)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5066)
The member *impls-for*<associate_t>::*get-state* is initialized with a callable object equivalent to the following lambda:
[]<class Sndr, class Rcvr>(Sndr&& sndr, Rcvr& rcvr) noexcept(*see below*) {auto [_, data] = std::forward<Sndr>(sndr); auto dataParts = std::move(data).release(); using scope_tkn = decltype(dataParts->first); using wrap_sender = decltype(dataParts->second); using op_t = connect_result_t<wrap_sender, Rcvr>; struct op_state {bool *associated* = false; // *exposition only*union { Rcvr* *rcvr*; // *exposition only*struct { scope_tkn *token*; // *exposition only* op_t *op*; // *exposition only*} *assoc*; // *exposition only*}; explicit op_state(Rcvr& r) noexcept: *rcvr*(addressof(r)) {}explicit op_state(scope_tkn tkn, wrap_sender&& sndr, Rcvr& r) try: *associated*(true), *assoc*(tkn, connect(std::move(sndr), std::move(r))) {}catch (...) { tkn.disassociate(); throw; } op_state(op_state&&) = delete; ~op_state() {if (*associated*) {*assoc*.*op*.~op_t(); *assoc*.*token*.disassociate(); *assoc*.*token*.~scope_tkn(); }}void *run*() noexcept { // *exposition only*if (*associated*) start(*assoc*.*op*); else set_stopped(std::move(**rcvr*)); }}; if (dataParts)return op_state{std::move(dataParts->first), std::move(dataParts->second), rcvr}; elsereturn op_state{rcvr};}
[13](#13)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5126)
The expression in the noexcept clause of*impls-for*<associate_t>::*get-state* isis_nothrow_constructible_v<remove_cvref_t<Sndr>, Sndr> && is_nothrow_move_constructible_v<*wrap-sender*> &&[*nothrow-callable*](functional.syn#concept:nothrow-callable "22.10.2Header <functional> synopsis[functional.syn]")<connect_t, *wrap-sender*, Rcvr> where *wrap-sender* is the typeremove_cvref_t<*data-type*<Sndr>>::*wrap-sender*[.](#13.sentence-1)
[14](#14)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5137)
The member *impls-for*<associate_t>::*start* is initialized with a callable object equivalent to the following lambda:[](auto& state, auto&) noexcept -> void { state.*run*();}
[15](#15)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5146)
The evaluation of associate(sndr, token) may cause side effects observable
via token's associated async scope object[.](#15.sentence-1)

343
cppdraft/exec/async/ops.md Normal file
View File

@@ -0,0 +1,343 @@
[exec.async.ops]
# 33 Execution control library [[exec]](./#exec)
## 33.3 Asynchronous operations [exec.async.ops]
[1](#1)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L172)
An [*execution resource*](#def:resource,execution "33.3Asynchronous operations[exec.async.ops]") is a program entity that manages
a (possibly dynamic) set of execution agents ([[thread.req.lockable.general]](thread.req.lockable.general "32.2.5.1General")),
which it uses to execute parallel work on behalf of callers[.](#1.sentence-1)
[*Example [1](#example-1)*:
The currently active thread,
a system-provided thread pool, and
uses of an API associated with an external hardware accelerator
are all examples of execution resources[.](#1.sentence-2)
— *end example*]
Execution resources execute asynchronous operations[.](#1.sentence-3)
An execution resource is either valid or invalid[.](#1.sentence-4)
[2](#2)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L185)
An [*asynchronous operation*](#def:operation,asynchronous "33.3Asynchronous operations[exec.async.ops]") is
a distinct unit of program execution that
- [(2.1)](#2.1)
is explicitly created;
- [(2.2)](#2.2)
can be explicitly started once at most;
- [(2.3)](#2.3)
once started, eventually completes exactly once
with a (possibly empty) set of result datums and
in exactly one of three [*dispositions*](#def:disposition "33.3Asynchronous operations[exec.async.ops]"):
success, failure, or cancellation;
* [(2.3.1)](#2.3.1)
A successful completion, also known as a [*value completion*](#def:completion,value "33.3Asynchronous operations[exec.async.ops]"),
can have an arbitrary number of result datums[.](#2.3.1.sentence-1)
* [(2.3.2)](#2.3.2)
A failure completion, also known as an [*error completion*](#def:completion,error "33.3Asynchronous operations[exec.async.ops]"),
has a single result datum[.](#2.3.2.sentence-1)
* [(2.3.3)](#2.3.3)
A cancellation completion, also known as a [*stopped completion*](#def:completion,stopped "33.3Asynchronous operations[exec.async.ops]"),
has no result datum[.](#2.3.3.sentence-1)
An asynchronous operation's [*async result*](#def:result,async "33.3Asynchronous operations[exec.async.ops]") is its disposition and its (possibly empty) set of result datums[.](#2.3.sentence-2)
- [(2.4)](#2.4)
can complete on a different execution resource
than the execution resource on which it started; and
- [(2.5)](#2.5)
can create and start other asynchronous operations
called [*child operations*](#def:operations,child "33.3Asynchronous operations[exec.async.ops]")[.](#2.5.sentence-1)
A child operation is an asynchronous operation
that is created by the parent operation and,
if started, completes before the parent operation completes[.](#2.5.sentence-2)
A [*parent operation*](#def:operation,parent "33.3Asynchronous operations[exec.async.ops]") is the asynchronous operation
that created a particular child operation[.](#2.5.sentence-3)
[*Note [1](#note-1)*:
An asynchronous operation can execute synchronously;
that is, it can complete during the execution of its start operation
on the thread of execution that started it[.](#2.sentence-2)
— *end note*]
[3](#3)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L229)
An asynchronous operation has associated state
known as its [*operation state*](#def:state,operation "33.3Asynchronous operations[exec.async.ops]")[.](#3.sentence-1)
[4](#4)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L233)
An asynchronous operation has an associated environment[.](#4.sentence-1)
An [*environment*](#def:environment "33.3Asynchronous operations[exec.async.ops]") is a queryable object ([[exec.queryable]](exec.queryable "33.2Queries and queryables"))
representing the execution-time properties of the operation's caller[.](#4.sentence-2)
The caller of an asynchronous operation is
its parent operation or the function that created it[.](#4.sentence-3)
[5](#5)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L240)
An asynchronous operation has an associated receiver[.](#5.sentence-1)
A [*receiver*](#def:receiver "33.3Asynchronous operations[exec.async.ops]") is an aggregation of three handlers
for the three asynchronous completion dispositions:
- [(5.1)](#5.1)
a value completion handler for a value completion,
- [(5.2)](#5.2)
an error completion handler for an error completion, and
- [(5.3)](#5.3)
a stopped completion handler for a stopped completion[.](#5.sentence-2)
A receiver has an associated environment[.](#5.sentence-3)
An asynchronous operation's operation state owns the operation's receiver[.](#5.sentence-4)
The environment of an asynchronous operation
is equal to its receiver's environment[.](#5.sentence-5)
[6](#6)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L254)
For each completion disposition, there is a [*completion function*](#def:function,completion "33.3Asynchronous operations[exec.async.ops]")[.](#6.sentence-1)
A completion function is
a customization point object ([[customization.point.object]](customization.point.object "16.3.3.3.5Customization Point Object types"))
that accepts an asynchronous operation's receiver as the first argument and
the result datums of the asynchronous operation as additional arguments[.](#6.sentence-2)
The value completion function invokes
the receiver's value completion handler with the value result datums;
likewise for the error completion function and the stopped completion function[.](#6.sentence-3)
A completion function has
an associated type known as its [*completion tag*](#def:tag,completion "33.3Asynchronous operations[exec.async.ops]") that is the unqualified type of the completion function[.](#6.sentence-4)
A valid invocation of a completion function is called
a [*completion operation*](#def:operation,completion "33.3Asynchronous operations[exec.async.ops]")[.](#6.sentence-5)
[7](#7)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L269)
The [*lifetime of an asynchronous operation*](#def:lifetime_of_an_asynchronous_operation "33.3Asynchronous operations[exec.async.ops]"),
also known as the operation's [*async lifetime*](#def:async_lifetime "33.3Asynchronous operations[exec.async.ops]"),
begins when its start operation begins executing and
ends when its completion operation begins executing[.](#7.sentence-1)
If the lifetime of an asynchronous operation's associated operation state
ends before the lifetime of the asynchronous operation,
the behavior is undefined[.](#7.sentence-2)
After an asynchronous operation executes a completion operation,
its associated operation state is invalid[.](#7.sentence-3)
Accessing any part of an invalid operation state is undefined behavior[.](#7.sentence-4)
[8](#8)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L281)
An asynchronous operation shall not execute a completion operation
before its start operation has begun executing[.](#8.sentence-1)
After its start operation has begun executing,
exactly one completion operation shall execute[.](#8.sentence-2)
The lifetime of an asynchronous operation's operation state can end
during the execution of the completion operation[.](#8.sentence-3)
[9](#9)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L289)
A [*sender*](#def:sender "33.3Asynchronous operations[exec.async.ops]") is a factory for one or more asynchronous operations[.](#9.sentence-1)
[*Connecting*](#def:connect "33.3Asynchronous operations[exec.async.ops]") a sender and a receiver creates
an asynchronous operation[.](#9.sentence-2)
The asynchronous operation's associated receiver is equal to
the receiver used to create it, and
its associated environment is equal to
the environment associated with the receiver used to create it[.](#9.sentence-3)
The lifetime of an asynchronous operation's associated operation state
does not depend on the lifetimes of either the sender or the receiver
from which it was created[.](#9.sentence-4)
A sender is started when it is connected to a receiver and
the resulting asynchronous operation is started[.](#9.sentence-5)
A sender's async result is the async result of the asynchronous operation
created by connecting it to a receiver[.](#9.sentence-6)
A sender sends its results by way of the asynchronous operation(s) it produces,
and a receiver receives those results[.](#9.sentence-7)
A sender is either valid or invalid;
it becomes invalid when its parent sender (see below) becomes invalid[.](#9.sentence-8)
[10](#10)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L309)
A [*scheduler*](#def:scheduler "33.3Asynchronous operations[exec.async.ops]") is an abstraction of an execution resource
with a uniform, generic interface for scheduling work onto that resource[.](#10.sentence-1)
It is a factory for senders
whose asynchronous operations execute value completion operations
on an execution agent belonging to
the scheduler's associated execution resource[.](#10.sentence-2)
A [*schedule-expression*](#def:schedule-expression "33.3Asynchronous operations[exec.async.ops]") obtains such a sender from a scheduler[.](#10.sentence-3)
A [*schedule sender*](#def:schedule_sender "33.3Asynchronous operations[exec.async.ops]") is the result of a schedule expression[.](#10.sentence-4)
On success, an asynchronous operation produced by a schedule sender executes
a value completion operation with an empty set of result datums[.](#10.sentence-5)
Multiple schedulers can refer to the same execution resource[.](#10.sentence-6)
A scheduler can be valid or invalid[.](#10.sentence-7)
A scheduler becomes invalid when the execution resource to which it refers
becomes invalid,
as do any schedule senders obtained from the scheduler, and
any operation states obtained from those senders[.](#10.sentence-8)
[11](#11)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L327)
An asynchronous operation has one or more associated completion schedulers
for each of its possible dispositions[.](#11.sentence-1)
A [*completion scheduler*](#def:completion_scheduler "33.3Asynchronous operations[exec.async.ops]") is a scheduler
whose associated execution resource is used to execute
a completion operation for an asynchronous operation[.](#11.sentence-2)
A value completion scheduler is a scheduler
on which an asynchronous operation's value completion operation can execute[.](#11.sentence-3)
Likewise for error completion schedulers and stopped completion schedulers[.](#11.sentence-4)
[12](#12)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L337)
A sender has an associated queryable object ([[exec.queryable]](exec.queryable "33.2Queries and queryables"))
known as its [*attributes*](#def:attribute "33.3Asynchronous operations[exec.async.ops]") that describes various characteristics of the sender and
of the asynchronous operation(s) it produces[.](#12.sentence-1)
For each disposition,
there is a query object for reading the associated completion scheduler
from a sender's attributes;
i.e., a value completion scheduler query object
for reading a sender's value completion scheduler, etc[.](#12.sentence-2)
If a completion scheduler query is well-formed,
the returned completion scheduler is unique
for that disposition for any asynchronous operation the sender creates[.](#12.sentence-3)
A schedule sender is required to have a value completion scheduler attribute
whose value is equal to the scheduler that produced the schedule sender[.](#12.sentence-4)
[13](#13)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L353)
A [*completion signature*](#def:completion_signature "33.3Asynchronous operations[exec.async.ops]") is a function type
that describes a completion operation[.](#13.sentence-1)
An asynchronous operation has a finite set of possible completion signatures
corresponding to the completion operations
that the asynchronous operation potentially evaluates ([[basic.def.odr]](basic.def.odr "6.3One-definition rule"))[.](#13.sentence-2)
For a completion function set,
receiver rcvr, and
pack of arguments args,
let c be the completion operation set(rcvr, args...), and
let F be
the function type decltype(auto(set))(decltype((args))...)[.](#13.sentence-3)
A completion signature Sig is associated with c if and only if*MATCHING-SIG*(Sig, F) is true ([[exec.general]](exec.general "33.1General"))[.](#13.sentence-4)
Together, a sender type and an environment type Env determine
the set of completion signatures of an asynchronous operation
that results from connecting the sender with a receiver
that has an environment of type Env[.](#13.sentence-5)
The type of the receiver does not affect
an asynchronous operation's completion signatures,
only the type of the receiver's environment[.](#13.sentence-6)
A [*non-dependent sender*](#def:sender,non-dependent "33.3Asynchronous operations[exec.async.ops]") is a sender type
whose completion signatures are knowable
independent of an execution environment[.](#13.sentence-7)
[14](#14)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L379)
A sender algorithm is a function that takes and/or returns a sender[.](#14.sentence-1)
There are three categories of sender algorithms:
- [(14.1)](#14.1)
A [*sender factory*](#def:sender_factory "33.3Asynchronous operations[exec.async.ops]") is a function
that takes non-senders as arguments and that returns a sender[.](#14.1.sentence-1)
- [(14.2)](#14.2)
A [*sender adaptor*](#def:sender_adaptor "33.3Asynchronous operations[exec.async.ops]") is a function
that constructs and returns a parent sender
from a set of one or more child senders and
a (possibly empty) set of additional arguments[.](#14.2.sentence-1)
An asynchronous operation created by a parent sender is
a parent operation to the child operations created by the child senders[.](#14.2.sentence-2)
- [(14.3)](#14.3)
A [*sender consumer*](#def:sender_consumer "33.3Asynchronous operations[exec.async.ops]") is a function
that takes one or more senders and
a (possibly empty) set of additional arguments, and
whose return type is not the type of a sender[.](#14.3.sentence-1)

View File

@@ -0,0 +1,94 @@
[exec.awaitable]
# 33 Execution control library [[exec]](./#exec)
## 33.9 Senders [[exec.snd]](exec.snd#exec.awaitable)
### 33.9.4 Awaitable helpers [exec.awaitable]
[1](#1)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2304)
The sender concepts recognize awaitables as senders[.](#1.sentence-1)
For [[exec]](exec "33Execution control library"), an [*awaitable*](#def:awaitable "33.9.4Awaitable helpers[exec.awaitable]") is an expression
that would be well-formed as the operand of a co_await expression
within a given context[.](#1.sentence-2)
[2](#2)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2310)
For a subexpression c,
let *GET-AWAITER*(c, p) be expression-equivalent to
the series of transformations and conversions applied to c as the operand of an [*await-expression*](expr.await#nt:await-expression "7.6.2.4Await[expr.await]") in a coroutine,
resulting in lvalue e as described by [[expr.await]](expr.await "7.6.2.4Await"),
where p is an lvalue referring to the coroutine's promise,
which has type Promise[.](#2.sentence-1)
[*Note [1](#note-1)*:
This includes the invocation of
the promise type's await_transform member if any,
the invocation of the operator co_await picked by overload resolution if any, and
any necessary implicit conversions and materializations[.](#2.sentence-2)
— *end note*]
Let *GET-AWAITER*(c) be
expression-equivalent to *GET-AWAITER*(c, q) where q is an lvalue of
an unspecified empty class type *none-such* that
lacks an await_transform member, and
where coroutine_handle<*none-such*> behaves ascoroutine_handle<void>[.](#2.sentence-3)
[3](#3)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2334)
Let [*is-awaitable*](#concept:is-awaitable "33.9.4Awaitable helpers[exec.awaitable]") be the following exposition-only concept:namespace std {template<class T>concept [*await-suspend-result*](#concept:await-suspend-result "33.9.4Awaitable helpers[exec.awaitable]") = *see below*; // *exposition only*template<class A, class... Promise>concept [*is-awaiter*](#concept:is-awaiter "33.9.4Awaitable helpers[exec.awaitable]") = // *exposition only*requires (A& a, coroutine_handle<Promise...> h) { a.await_ready() ? 1 : 0; { a.await_suspend(h) } -> [*await-suspend-result*](#concept:await-suspend-result "33.9.4Awaitable helpers[exec.awaitable]");
a.await_resume(); }; template<class C, class... Promise>concept [*is-awaitable*](#concept:is-awaitable "33.9.4Awaitable helpers[exec.awaitable]") = // *exposition only*requires (C (*fc)() noexcept, Promise&... p) {{ *GET-AWAITER*(fc(), p...) } -> [*is-awaiter*](#concept:is-awaiter "33.9.4Awaitable helpers[exec.awaitable]")<Promise...>; };}
[*await-suspend-result*](#concept:await-suspend-result "33.9.4Awaitable helpers[exec.awaitable]")<T> is true if and only if one of the following is true:
- [(3.1)](#3.1)
T is void, or
- [(3.2)](#3.2)
T is bool, or
- [(3.3)](#3.3)
T is a specialization of coroutine_handle[.](#3.sentence-2)
[4](#4)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2365)
For a subexpression c such that decltype((c)) is type C, and
an lvalue p of type Promise,*await-result-
type*<C, Promise> denotes
the type decltype(*GET-AWAITER*(c, p).await_resume()) and*await-result-type*<C> denotes
the type decltype(*GET-AWAITER*(c).await_resume())[.](#4.sentence-1)
[5](#5)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2374)
Let *with-await-transform* be the exposition-only class template:namespace std::execution {template<class T, class Promise>concept [*has-as-awaitable*](#concept:has-as-awaitable "33.9.4Awaitable helpers[exec.awaitable]") = // *exposition only*requires (T&& t, Promise& p) {{ std::forward<T>(t).as_awaitable(p) } -> [*is-awaitable*](#concept:is-awaitable "33.9.4Awaitable helpers[exec.awaitable]")<Promise&>; }; template<class Derived>struct *with-await-transform* { // *exposition only*template<class T> T&& await_transform(T&& value) noexcept {return std::forward<T>(value); }template<[*has-as-awaitable*](#concept:has-as-awaitable "33.9.4Awaitable helpers[exec.awaitable]")<Derived> T>auto await_transform(T&& value)noexcept(noexcept(std::forward<T>(value).as_awaitable(declval<Derived&>())))-> decltype(std::forward<T>(value).as_awaitable(declval<Derived&>())) {return std::forward<T>(value).as_awaitable(static_cast<Derived&>(*this)); }};}
[6](#6)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2401)
Let *env-promise* be the exposition-only class template:namespace std::execution {template<class Env>struct *env-promise* : *with-await-transform*<*env-promise*<Env>> { // *exposition only**unspecified* get_return_object() noexcept; *unspecified* initial_suspend() noexcept; *unspecified* final_suspend() noexcept; void unhandled_exception() noexcept; void return_void() noexcept;
coroutine_handle<> unhandled_stopped() noexcept; const Env& get_env() const noexcept; };}
[*Note [2](#note-2)*:
Specializations of *env-promise* are used only for the purpose of type computation;
its members need not be defined[.](#6.sentence-1)
— *end note*]

222
cppdraft/exec/bulk.md Normal file
View File

@@ -0,0 +1,222 @@
[exec.bulk]
# 33 Execution control library [[exec]](./#exec)
## 33.9 Senders [[exec.snd]](exec.snd#exec.bulk)
### 33.9.12 Sender adaptors [[exec.adapt]](exec.adapt#exec.bulk)
#### 33.9.12.11 execution::bulk, execution::bulk_chunked, and execution::bulk_unchunked [exec.bulk]
[1](#1)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4109)
bulk, bulk_chunked, and bulk_unchunked run a task repeatedly for every index in an index space[.](#1.sentence-1)
[2](#2)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4113)
The names bulk, bulk_chunked, and bulk_unchunked denote pipeable sender adaptor objects[.](#2.sentence-1)
Let *bulk-algo* be eitherbulk, bulk_chunked, or bulk_unchunked[.](#2.sentence-2)
For subexpressions sndr, policy, shape, and f,
letPolicy be remove_cvref_t<decltype(policy)>,Shape be decltype(auto(shape)), andFunc be decay_t<decltype((f))>[.](#2.sentence-3)
If
- [(2.1)](#2.1)
decltype((sndr)) does not satisfy [sender](exec.snd.concepts#concept:sender "33.9.3Sender concepts[exec.snd.concepts]"), or
- [(2.2)](#2.2)
is_execution_policy_v<Policy> is false, or
- [(2.3)](#2.3)
Shape does not satisfy [integral](concepts.arithmetic#concept:integral "18.4.7Arithmetic concepts[concepts.arithmetic]"), or
- [(2.4)](#2.4)
Func does not model [copy_constructible](concept.copyconstructible#concept:copy_constructible "18.4.14Concept copy_­constructible[concept.copyconstructible]"),
*bulk-algo*(sndr, policy, shape, f) is ill-formed[.](#2.sentence-4)
[3](#3)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4136)
Otherwise,
the expression *bulk-algo*(sndr, policy, shape, f) is expression-equivalent to:
transform_sender(*get-domain-early*(sndr), *make-sender*(*bulk-algo*, *product-type*<*see below*, Shape, Func>{policy, shape, f}, sndr)) except that sndr is evaluated only once[.](#3.sentence-2)
The first template argument of *product-type* is Policy if Policy models [copy_constructible](concept.copyconstructible#concept:copy_constructible "18.4.14Concept copy_­constructible[concept.copyconstructible]"), andconst Policy& otherwise[.](#3.sentence-3)
[4](#4)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4150)
Let sndr and env be subexpressions such thatSndr is decltype((sndr))[.](#4.sentence-1)
If [*sender-for*](exec.snd.concepts#concept:sender-for "33.9.3Sender concepts[exec.snd.concepts]")<Sndr, bulk_t> is false, then
the expression bulk.transform_sender(sndr, env) is ill-formed;
otherwise, it is equivalent to:auto [_, data, child] = sndr;auto& [policy, shape, f] = data;auto new_f = [func = std::move(f)](Shape begin, Shape end, auto&&... vs)noexcept(noexcept(f(begin, vs...))) {while (begin != end) func(begin++, vs...);}return bulk_chunked(std::move(child), policy, shape, std::move(new_f));
[*Note [1](#note-1)*:
This causes the bulk(sndr, policy, shape, f) sender to be
expressed in terms of bulk_chunked(sndr, policy, shape, f) when
it is connected to a receiver whose
execution domain does not customize bulk[.](#4.sentence-2)
— *end note*]
[5](#5)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4172)
The exposition-only class template *impls-for* ([[exec.snd.expos]](exec.snd.expos "33.9.2Exposition-only entities"))
is specialized for bulk_chunked_t as follows:
[🔗](#lib:impls-for%3cbulk_chunked_t%3e)
namespace std::execution {template<>struct *impls-for*<bulk_chunked_t> : *default-impls* {static constexpr auto *complete* = *see below*; template<class Sndr, class... Env>static consteval void *check-types*(); };}
The member *impls-for*<bulk_chunked_t>::*complete* is initialized with a callable object equivalent to the following lambda:[]<class Index, class State, class Rcvr, class Tag, class... Args>(Index, State& state, Rcvr& rcvr, Tag, Args&&... args) noexcept-> void requires *see below* {if constexpr ([same_as](concept.same#concept:same_as "18.4.2Concept same_­as[concept.same]")<Tag, set_value_t>) {auto& [policy, shape, f] = state; constexpr bool nothrow = noexcept(f(auto(shape), auto(shape), args...)); *TRY-EVAL*(rcvr, [&]() noexcept(nothrow) { f(static_cast<decltype(auto(shape))>(0), auto(shape), args...);
Tag()(std::move(rcvr), std::forward<Args>(args)...); }()); } else { Tag()(std::move(rcvr), std::forward<Args>(args)...); }}
The expression in the [*requires-clause*](temp.pre#nt:requires-clause "13.1Preamble[temp.pre]") of the lambda above istrue if and only
if Tag denotes a type other than set_value_t or
if the expression f(auto(shape), auto(shape), args...) is well-formed[.](#5.sentence-3)
[6](#6)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4210)
The exposition-only class template *impls-for* ([[exec.snd.expos]](exec.snd.expos "33.9.2Exposition-only entities"))
is specialized for bulk_unchunked_t as follows:namespace std::execution {template<>struct *impls-for*<bulk_unchunked_t> : *default-impls* {static constexpr auto *complete* = *see below*; };}
The member *impls-for*<bulk_unchunked_t>::*complete* is initialized with a callable object equivalent to the following lambda:[]<class Index, class State, class Rcvr, class Tag, class... Args>(Index, State& state, Rcvr& rcvr, Tag, Args&&... args) noexcept-> void requires *see below* {if constexpr ([same_as](concept.same#concept:same_as "18.4.2Concept same_­as[concept.same]")<Tag, set_value_t>) {auto& [shape, f] = state; constexpr bool nothrow = noexcept(f(auto(shape), args...)); *TRY-EVAL*(rcvr, [&]() noexcept(nothrow) {for (decltype(auto(shape)) i = 0; i < shape; ++i) { f(auto(i), args...); } Tag()(std::move(rcvr), std::forward<Args>(args)...); }()); } else { Tag()(std::move(rcvr), std::forward<Args>(args)...); }}
The expression in the [*requires-clause*](temp.pre#nt:requires-clause "13.1Preamble[temp.pre]") of the lambda above
is true if and only
if Tag denotes a type other than set_value_t or
if the expression f(auto(shape), args...) is well-formed[.](#6.sentence-3)
[🔗](#lib:check-types,impls-for%3cbulk_t%3e)
`template<class Sndr, class... Env>
static consteval void check-types();
`
[7](#7)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4253)
*Effects*: Equivalent to:auto cs = get_completion_signatures<*child-type*<Sndr>, *FWD-ENV-T*(Env)...>();auto fn = []<class... Ts>(set_value_t(*)(Ts...)) {if constexpr (![invocable](concept.invocable#concept:invocable "18.7.2Concept invocable[concept.invocable]")<remove_cvref_t<*data-type*<Sndr>>, Ts&...>)throw *unspecified-exception*();};
cs.*for-each*(*overload-set*(fn, [](auto){})); where *unspecified-exception* is
a type derived from exception[.](#7.sentence-1)
[8](#8)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4268)
Let the subexpression out_sndr denote
the result of the invocation*bulk-algo*(sndr, policy, shape, f) or
an object equal to such, and
let the subexpression rcvr denote a receiver
such that the expression connect(out_sndr, rcvr) is well-formed[.](#8.sentence-1)
The expression connect(out_sndr, rcvr) has undefined behavior
unless it creates an asynchronous operation ([[exec.async.ops]](exec.async.ops "33.3Asynchronous operations")) that,
when started:
- [(8.1)](#8.1)
If sndr has a successful completion, whereargs is a pack of lvalue subexpressions
referring to the value completion result datums of sndr, or
decayed copies of those values if they model [copy_constructible](concept.copyconstructible#concept:copy_constructible "18.4.14Concept copy_­constructible[concept.copyconstructible]"),
then:
* [(8.1.1)](#8.1.1)
If out_sndr also completes successfully, then:
+
[(8.1.1.1)](#8.1.1.1)
for bulk,
invokes f(i, args...) for every i of type Shape from 0 to shape;
+
[(8.1.1.2)](#8.1.1.2)
for bulk_unchunked,
invokes f(i, args...) for every i of type Shape from 0 to shape;
*Recommended practice*: The underlying scheduler should execute each iteration
on a distinct execution agent[.](#8.1.1.2.sentence-2)
+
[(8.1.1.3)](#8.1.1.3)
for bulk_chunked,
invokes f(b, e, args...) zero or more times
with pairs of b and e of type Shape in range [0, shape],
such that b<e and
for every i of type Shape from 0 to shape,
there is exactly one invocation with a pair b and e,
such that i is in the range [b, e)[.](#8.1.1.3.sentence-1)
* [(8.1.2)](#8.1.2)
If out_sndr completes with set_error(rcvr, eptr), then
the asynchronous operation may invoke a subset of
the invocations of f before the error completion handler is called, and eptr is an exception_ptr containing either:
+
[(8.1.2.1)](#8.1.2.1)
an exception thrown by an invocation of f, or
+
[(8.1.2.2)](#8.1.2.2)
a bad_alloc exception if
the implementation fails to allocate required resources, or
+
[(8.1.2.3)](#8.1.2.3)
an exception derived from runtime_error[.](#8.1.2.sentence-1)
* [(8.1.3)](#8.1.3)
If out_sndr completes with set_stopped(rcvr), then
the asynchronous operation may invoke a subset of
the invocations of f before the stopped completion handler[.](#8.1.3.sentence-1)
- [(8.2)](#8.2)
If sndr does not complete with set_value, then
the completion is forwarded to recv[.](#8.2.sentence-1)
- [(8.3)](#8.3)
For *bulk-algo*,
the parameter policy describes
the manner in which
the execution of the asynchronous operations corresponding to these algorithms
may be parallelized and
the manner in which
they apply f[.](#8.3.sentence-1)
Permissions and requirements
on parallel algorithm element access functions ([[algorithms.parallel.exec]](algorithms.parallel.exec "26.3.3Effect of execution policies on algorithm execution"))
apply to f[.](#8.3.sentence-2)
[9](#9)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4358)
[*Note [2](#note-2)*:
The asynchronous operation corresponding to*bulk-algo*(sndr, policy, shape, f) can complete with set_stopped if cancellation is requested or
ignore cancellation requests[.](#9.sentence-1)
— *end note*]

128
cppdraft/exec/cmplsig.md Normal file
View File

@@ -0,0 +1,128 @@
[exec.cmplsig]
# 33 Execution control library [[exec]](./#exec)
## 33.10 Completion signatures [exec.cmplsig]
[1](#1)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6027)
completion_signatures is a type
that encodes a set of completion signatures ([[exec.async.ops]](exec.async.ops "33.3Asynchronous operations"))[.](#1.sentence-1)
[2](#2)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6031)
[*Example [1](#example-1)*: struct my_sender {using sender_concept = sender_t; using completion_signatures = execution::completion_signatures< set_value_t(),
set_value_t(int, float),
set_error_t(exception_ptr),
set_error_t(error_code),
set_stopped_t()>;};
Declares my_sender to be a sender
that can complete by calling one of the following
for a receiver expression rcvr:
- [(2.1)](#2.1)
set_value(rcvr)
- [(2.2)](#2.2)
set_value(rcvr, int{...}, float{...})
- [(2.3)](#2.3)
set_error(rcvr, exception_ptr{...})
- [(2.4)](#2.4)
set_error(rcvr, error_code{...})
- [(2.5)](#2.5)
set_stopped(rcvr)
— *end example*]
[3](#3)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6057)
This subclause makes use of the following exposition-only entities:template<class Fn>concept [*completion-signature*](#concept:completion-signature "33.10Completion signatures[exec.cmplsig]") = *see below*;
[4](#4)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6064)
A type Fn satisfies [*completion-signature*](#concept:completion-signature "33.10Completion signatures[exec.cmplsig]") if and only if it is a function type with one of the following forms:
- [(4.1)](#4.1)
set_value_t(Vs...),
where Vs is a pack of object or reference types[.](#4.1.sentence-1)
- [(4.2)](#4.2)
set_error_t(Err),
where Err is an object or reference type[.](#4.2.sentence-1)
- [(4.3)](#4.3)
set_stopped_t()
[5](#5)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6078)
template<bool>struct *indirect-meta-apply* {template<template<class...> class T, class... As>using *meta-apply* = T<As...>; // *exposition only*};
template<class...>concept [*always-true*](#concept:always-true "33.10Completion signatures[exec.cmplsig]") = true; // *exposition only*template<class Tag, [*valid-completion-signatures*](execution.syn#concept:valid-completion-signatures "33.4Header <execution> synopsis[execution.syn]") Completions, template<class...> class Tuple, template<class...> class Variant>using *gather-signatures* = *see below*;
[6](#6)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6096)
Let Fns be a pack of the arguments of
the completion_signatures specialization named by Completions,
let TagFns be a pack of the function types in Fns whose return types are Tag, and
let Tsn be a pack of the function argument types
in the n-th type in TagFns[.](#6.sentence-1)
Then, given two variadic templates Tuple and Variant,
the type *gather-signatures*<Tag, Completions, Tuple, Variant> names the type*META-APPLY*(Variant, *META-APPLY*(Tuple, Ts0...), *META-APPLY*(Tuple, Ts1...),
…, *META-APPLY*(Tuple, Tsm−1...)) where m is the size of the pack TagFns andMETA-APPLY(T, As...) is equivalent to:typename *indirect-meta-apply*<*always-true*<As...>>::template *meta-apply*<T, As...>
[7](#7)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6118)
[*Note [1](#note-1)*:
The purpose of *META-APPLY* is to make it valid
to use non-variadic templates as Variant and Tuple arguments
to *gather-signatures*[.](#7.sentence-1)
— *end note*]
[8](#8)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6125)
[🔗](#lib:execution::completion_signatures)
namespace std::execution {template<[*completion-signature*](#concept:completion-signature "33.10Completion signatures[exec.cmplsig]")... Fns>struct completion_signatures {template<class Tag>static constexpr size_t *count-of*(Tag) { return *see below*; }template<class Fn>static constexpr void *for-each*(Fn&& fn) { // *exposition only*(std::forward<Fn>(fn)(static_cast<Fns*>(nullptr)), ...); }}; template<class Sndr, class Env = env<>, template<class...> class Tuple = *decayed-tuple*, template<class...> class Variant = *variant-or-empty*>requires [sender_in](exec.snd.concepts#concept:sender_in "33.9.3Sender concepts[exec.snd.concepts]")<Sndr, Env>using value_types_of_t =*gather-signatures*<set_value_t, completion_signatures_of_t<Sndr, Env>, Tuple, Variant>; template<class Sndr, class Env = env<>, template<class...> class Variant = *variant-or-empty*>requires [sender_in](exec.snd.concepts#concept:sender_in "33.9.3Sender concepts[exec.snd.concepts]")<Sndr, Env>using error_types_of_t =*gather-signatures*<set_error_t, completion_signatures_of_t<Sndr, Env>,
type_identity_t, Variant>; template<class Sndr, class Env = env<>>requires [sender_in](exec.snd.concepts#concept:sender_in "33.9.3Sender concepts[exec.snd.concepts]")<Sndr, Env>constexpr bool sends_stopped =![same_as](concept.same#concept:same_as "18.4.2Concept same_­as[concept.same]")<*type-list*<>, *gather-signatures*<set_stopped_t, completion_signatures_of_t<Sndr, Env>, *type-list*, *type-list*>>;}
[9](#9)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6165)
For a subexpression tag,
let Tag be the decayed type of tag[.](#9.sentence-1)
completion_signatures<Fns...>::*count-of*(
tag) returns the count of function types in Fns... that
are of the form Tag(Ts...) where Ts is a pack of types[.](#9.sentence-2)

73
cppdraft/exec/connect.md Normal file
View File

@@ -0,0 +1,73 @@
[exec.connect]
# 33 Execution control library [[exec]](./#exec)
## 33.9 Senders [[exec.snd]](exec.snd#exec.connect)
### 33.9.10 execution::connect [exec.connect]
[1](#1)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2734)
connect connects ([[exec.async.ops]](exec.async.ops "33.3Asynchronous operations")) a sender with a receiver[.](#1.sentence-1)
[2](#2)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2737)
The name connect denotes a customization point object[.](#2.sentence-1)
For subexpressions sndr and rcvr,
let Sndr be decltype((sndr)) andRcvr be decltype((rcvr)),
let new_sndr be the expressiontransform_sender(decltype(*get-domain-late*(sndr, get_env(rcvr))){}, sndr, get_env(rcvr)) and let DS and DR bedecay_t<decltype((new_sndr))> and decay_t<Rcvr>, respectively[.](#2.sentence-2)
[3](#3)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2749)
Let *connect-awaitable-promise* be the following exposition-only class:
namespace std::execution {struct *connect-awaitable-promise* : *with-await-transform*<*connect-awaitable-promise*> {*connect-awaitable-promise*(DS&, DR& rcvr) noexcept : *rcvr*(rcvr) {} suspend_always initial_suspend() noexcept { return {}; }[[noreturn]] suspend_always final_suspend() noexcept { terminate(); }[[noreturn]] void unhandled_exception() noexcept { terminate(); }[[noreturn]] void return_void() noexcept { terminate(); } coroutine_handle<> unhandled_stopped() noexcept { set_stopped(std::move(*rcvr*)); return noop_coroutine(); }*operation-state-task* get_return_object() noexcept {return *operation-state-task*{ coroutine_handle<*connect-awaitable-promise*>::from_promise(*this)}; } env_of_t<DR> get_env() const noexcept {return execution::get_env(*rcvr*); }private: DR& *rcvr*; // *exposition only*};}
[4](#4)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2783)
Let *operation-state-task* be the following exposition-only class:namespace std::execution {struct *operation-state-task* { // *exposition only*using operation_state_concept = operation_state_t; using promise_type = *connect-awaitable-promise*; explicit *operation-state-task*(coroutine_handle<> h) noexcept : coro(h) {}*operation-state-task*(*operation-state-task*&&) = delete; ~*operation-state-task*() { *coro*.destroy(); }void start() & noexcept {*coro*.resume(); }private: coroutine_handle<> *coro*; // *exposition only*};}
[5](#5)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2805)
Let V name the type*await-result-type*<DS, *connect-awaitable-promise*>,
let Sigs name the typecompletion_signatures<*SET-VALUE-SIG*(V), // see [[exec.snd.concepts]](exec.snd.concepts "33.9.3Sender concepts") set_error_t(exception_ptr),
set_stopped_t()> and let *connect-awaitable* be an exposition-only coroutine
defined as follows:namespace std::execution {template<class Fun, class... Ts>auto *suspend-complete*(Fun fun, Ts&&... as) noexcept { // *exposition only*auto fn = [&, fun]() noexcept { fun(std::forward<Ts>(as)...); }; struct awaiter {decltype(fn) *fn*; // *exposition only*static constexpr bool await_ready() noexcept { return false; }void await_suspend(coroutine_handle<>) noexcept { *fn*(); }[[noreturn]] void await_resume() noexcept { unreachable(); }}; return awaiter{fn}; }*operation-state-task* *connect-awaitable*(DS sndr, DR rcvr) requires [receiver_of](exec.recv.concepts#concept:receiver_of "33.7.1Receiver concepts[exec.recv.concepts]")<DR, Sigs> { exception_ptr ep; try {if constexpr ([same_as](concept.same#concept:same_as "18.4.2Concept same_­as[concept.same]")<V, void>) {co_await std::move(sndr); co_await *suspend-complete*(set_value, std::move(rcvr)); } else {co_await *suspend-complete*(set_value, std::move(rcvr), co_await std::move(sndr)); }} catch(...) { ep = current_exception(); }co_await *suspend-complete*(set_error, std::move(rcvr), std::move(ep)); }}
[6](#6)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2850)
The expression connect(sndr, rcvr) is expression-equivalent to:
- [(6.1)](#6.1)
new_sndr.connect(rcvr) if that expression is well-formed[.](#6.1.sentence-1)
*Mandates*: The type of the expression above satisfies [operation_state](exec.opstate.general#concept:operation_state "33.8.1General[exec.opstate.general]")[.](#6.1.sentence-2)
- [(6.2)](#6.2)
Otherwise, *connect-awaitable*(new_sndr, rcvr)[.](#6.2.sentence-1)
Except that rcvr is evaluated only once[.](#6.sentence-2)
*Mandates*: The following are true:
- [(6.3)](#6.3)
[sender_in](exec.snd.concepts#concept:sender_in "33.9.3Sender concepts[exec.snd.concepts]")<Sndr, env_of_t<Rcvr>>
- [(6.4)](#6.4)
[receiver_of](exec.recv.concepts#concept:receiver_of "33.7.1Receiver concepts[exec.recv.concepts]")<Rcvr, completion_signatures_of_t<Sndr, env_of_t<Rcvr>>>

368
cppdraft/exec/consumers.md Normal file
View File

@@ -0,0 +1,368 @@
[exec.consumers]
# 33 Execution control library [[exec]](./#exec)
## 33.9 Senders [[exec.snd]](exec.snd#exec.consumers)
### 33.9.13 Sender consumers [exec.consumers]
#### [33.9.13.1](#exec.sync.wait) this_thread::sync_wait [[exec.sync.wait]](exec.sync.wait)
[1](#exec.sync.wait-1)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5595)
this_thread::sync_wait and this_thread::sync_wait_with_variant are used
to block the current thread of execution
until the specified sender completes and
to return its async result[.](#exec.sync.wait-1.sentence-1)
sync_wait mandates
that the input sender has exactly one value completion signature[.](#exec.sync.wait-1.sentence-2)
[2](#exec.sync.wait-2)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5604)
Let *sync-wait-env* be the following exposition-only class type:namespace std::this_thread {struct *sync-wait-env* { execution::run_loop* *loop*; // *exposition only*auto query(execution::get_scheduler_t) const noexcept {return *loop*->get_scheduler(); }auto query(execution::get_delegation_scheduler_t) const noexcept {return *loop*->get_scheduler(); }};}
[3](#exec.sync.wait-3)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5622)
Let *sync-wait-result-type* and*sync-wait-with-variant-result-type* be exposition-only alias templates defined as follows:namespace std::this_thread {template<execution::[sender_in](exec.snd.concepts#concept:sender_in "33.9.3Sender concepts[exec.snd.concepts]")<*sync-wait-env*> Sndr>using *sync-wait-result-type* = optional<execution::value_types_of_t<Sndr, *sync-wait-env*, *decayed-tuple*,
type_identity_t>>; template<execution::[sender_in](exec.snd.concepts#concept:sender_in "33.9.3Sender concepts[exec.snd.concepts]")<*sync-wait-env*> Sndr>using *sync-wait-with-variant-result-type* = optional<execution::value_types_of_t<Sndr, *sync-wait-env*>>;}
[4](#exec.sync.wait-4)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5639)
The name this_thread::sync_wait denotes a customization point object[.](#exec.sync.wait-4.sentence-1)
For a subexpression sndr, let Sndr be decltype((sndr))[.](#exec.sync.wait-4.sentence-2)
The expression this_thread::sync_wait(sndr) is expression-equivalent to the following,
except that sndr is evaluated only once:apply_sender(*get-domain-early*(sndr), sync_wait, sndr)*Mandates*:
- [(4.1)](#exec.sync.wait-4.1)
[sender_in](exec.snd.concepts#concept:sender_in "33.9.3Sender concepts[exec.snd.concepts]")<Sndr, *sync-wait-env*> is true[.](#exec.sync.wait-4.1.sentence-1)
- [(4.2)](#exec.sync.wait-4.2)
The type *sync-wait-result-type*<Sndr> is well-formed[.](#exec.sync.wait-4.2.sentence-1)
- [(4.3)](#exec.sync.wait-4.3)
[same_as](concept.same#concept:same_as "18.4.2Concept same_­as[concept.same]")<decltype(e), *sync-wait-result-type*<Sndr>> is true, where e is the apply_sender expression above[.](#exec.sync.wait-4.3.sentence-1)
[5](#exec.sync.wait-5)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5659)
Let *sync-wait-state* and *sync-wait-receiver* be the following exposition-only class templates:namespace std::this_thread {template<class Sndr>struct *sync-wait-state* { // *exposition only* execution::run_loop *loop*; // *exposition only* exception_ptr *error*; // *exposition only**sync-wait-result-type*<Sndr> *result*; // *exposition only*}; template<class Sndr>struct *sync-wait-receiver* { // *exposition only*using receiver_concept = execution::receiver_t; *sync-wait-state*<Sndr>* *state*; // *exposition only*template<class... Args>void set_value(Args&&... args) && noexcept; template<class Error>void set_error(Error&& err) && noexcept; void set_stopped() && noexcept; *sync-wait-env* get_env() const noexcept { return {&*state*->*loop*}; }};}
[🔗](#exec.sync.wait-itemdecl:1)
`template<class... Args>
void set_value(Args&&... args) && noexcept;
`
[6](#exec.sync.wait-6)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5695)
*Effects*: Equivalent to:try {*state*->*result*.emplace(std::forward<Args>(args)...);} catch (...) {*state*->*error* = current_exception();}*state*->*loop*.finish();
[🔗](#exec.sync.wait-itemdecl:2)
`template<class Error>
void set_error(Error&& err) && noexcept;
`
[7](#exec.sync.wait-7)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5714)
*Effects*: Equivalent to:*state*->*error* = *AS-EXCEPT-PTR*(std::forward<Error>(err)); // see [[exec.general]](exec.general "33.1General")*state*->*loop*.finish();
[🔗](#exec.sync.wait-itemdecl:3)
`void set_stopped() && noexcept;
`
[8](#exec.sync.wait-8)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5728)
*Effects*: Equivalent to *state*->*loop*.finish()[.](#exec.sync.wait-8.sentence-1)
[9](#exec.sync.wait-9)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5733)
For a subexpression sndr, let Sndr be decltype((sndr))[.](#exec.sync.wait-9.sentence-1)
If [sender_to](exec.snd.concepts#concept:sender_to "33.9.3Sender concepts[exec.snd.concepts]")<Sndr, *sync-wait-receiver*<Sndr>> is false,
the expression sync_wait.apply_sender(sndr) is ill-formed;
otherwise, it is equivalent to:*sync-wait-state*<Sndr> state;auto op = connect(sndr, *sync-wait-receiver*<Sndr>{&state});
start(op);
state.*loop*.run();if (state.*error*) { rethrow_exception(std::move(state.*error*));}return std::move(state.*result*);
[10](#exec.sync.wait-10)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5751)
The behavior of this_thread::sync_wait(sndr) is undefined unless:
- [(10.1)](#exec.sync.wait-10.1)
It blocks the current thread of execution ([[defns.block]](defns.block "3.6block"))
with forward progress guarantee delegation ([[intro.progress]](intro.progress "6.10.2.3Forward progress"))
until the specified sender completes[.](#exec.sync.wait-10.1.sentence-1)
[*Note [1](#exec.sync.wait-note-1)*:
The default implementation of sync_wait achieves
forward progress guarantee delegation by providing a run_loop scheduler
via the get_delegation_scheduler query
on the *sync-wait-receiver*'s environment[.](#exec.sync.wait-10.1.sentence-2)
The run_loop is driven by the current thread of execution[.](#exec.sync.wait-10.1.sentence-3)
— *end note*]
- [(10.2)](#exec.sync.wait-10.2)
It returns the specified sender's async results as follows:
* [(10.2.1)](#exec.sync.wait-10.2.1)
For a value completion,
the result datums are returned in
a tuple in an engaged optional object[.](#exec.sync.wait-10.2.1.sentence-1)
* [(10.2.2)](#exec.sync.wait-10.2.2)
For an error completion, an exception is thrown[.](#exec.sync.wait-10.2.2.sentence-1)
* [(10.2.3)](#exec.sync.wait-10.2.3)
For a stopped completion, a disengaged optional object is returned[.](#exec.sync.wait-10.2.3.sentence-1)
#### [33.9.13.2](#exec.sync.wait.var) this_thread::sync_wait_with_variant [[exec.sync.wait.var]](exec.sync.wait.var)
[1](#exec.sync.wait.var-1)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5781)
The name this_thread::sync_wait_with_variant denotes
a customization point object[.](#exec.sync.wait.var-1.sentence-1)
For a subexpression sndr,
let Sndr be decltype(into_variant(sndr))[.](#exec.sync.wait.var-1.sentence-2)
The expression this_thread::sync_wait_with_variant(sndr) is expression-equivalent to the following,
except sndr is evaluated only once:apply_sender(*get-domain-early*(sndr), sync_wait_with_variant, sndr)*Mandates*:
- [(1.1)](#exec.sync.wait.var-1.1)
[sender_in](exec.snd.concepts#concept:sender_in "33.9.3Sender concepts[exec.snd.concepts]")<Sndr, *sync-wait-env*> is true[.](#exec.sync.wait.var-1.1.sentence-1)
- [(1.2)](#exec.sync.wait.var-1.2)
The type *sync-wait-with-variant-result-type*<Sndr> is well-formed[.](#exec.sync.wait.var-1.2.sentence-1)
- [(1.3)](#exec.sync.wait.var-1.3)
[same_as](concept.same#concept:same_as "18.4.2Concept same_­as[concept.same]")<decltype(e), *sync-wait-with-variant-result-type*<Sndr>> is true, where e is the apply_sender expression above[.](#exec.sync.wait.var-1.3.sentence-1)
[2](#exec.sync.wait.var-2)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5804)
The expression sync_wait_with_variant.apply_sender(sndr) is equivalent to:using result_type = *sync-wait-with-variant-result-type*<Sndr>;if (auto opt_value = sync_wait(into_variant(sndr))) {return result_type(std::move(get<0>(*opt_value)));}return result_type(nullopt);
[3](#exec.sync.wait.var-3)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5814)
The behavior of this_thread::sync_wait_with_variant(sndr) is undefined unless:
- [(3.1)](#exec.sync.wait.var-3.1)
It blocks the current thread of execution ([[defns.block]](defns.block "3.6block"))
with forward progress guarantee delegation ([[intro.progress]](intro.progress "6.10.2.3Forward progress"))
until the specified sender completes[.](#exec.sync.wait.var-3.1.sentence-1)
[*Note [1](#exec.sync.wait.var-note-1)*:
The default implementation of sync_wait_with_variant achieves
forward progress guarantee delegation by relying on
the forward progress guarantee delegation provided by sync_wait[.](#exec.sync.wait.var-3.1.sentence-2)
— *end note*]
- [(3.2)](#exec.sync.wait.var-3.2)
It returns the specified sender's async results as follows:
* [(3.2.1)](#exec.sync.wait.var-3.2.1)
For a value completion,
the result datums are returned in an engaged optional object
that contains a variant of tuples[.](#exec.sync.wait.var-3.2.1.sentence-1)
* [(3.2.2)](#exec.sync.wait.var-3.2.2)
For an error completion, an exception is thrown[.](#exec.sync.wait.var-3.2.2.sentence-1)
* [(3.2.3)](#exec.sync.wait.var-3.2.3)
For a stopped completion, a disengaged optional object is returned[.](#exec.sync.wait.var-3.2.3.sentence-1)
#### [33.9.13.3](#exec.spawn) execution::spawn [[exec.spawn]](exec.spawn)
[1](#exec.spawn-1)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5843)
spawn attempts to associate the given input sender with
the given token's async scope and, on success,
eagerly starts the input sender[.](#exec.spawn-1.sentence-1)
[2](#exec.spawn-2)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5848)
The name spawn denotes a customization point object[.](#exec.spawn-2.sentence-1)
For subexpressions sndr, token, and env,
- [(2.1)](#exec.spawn-2.1)
let Sndr be decltype((sndr)),
- [(2.2)](#exec.spawn-2.2)
let Token be remove_cvref_t<decltype((token))>, and
- [(2.3)](#exec.spawn-2.3)
let Env be remove_cvref_t<decltype((env))>[.](#exec.spawn-2.sentence-2)
If any of[sender](exec.snd.concepts#concept:sender "33.9.3Sender concepts[exec.snd.concepts]")<Sndr>,[scope_token](exec.scope.concepts#concept:scope_token "33.14.1Execution scope concepts[exec.scope.concepts]")<Token>, or[*queryable*](exec.queryable.concept#concept:queryable "33.2.2queryable concept[exec.queryable.concept]")<Env> are not satisfied,
the expression spawn(sndr, token, env) is ill-formed[.](#exec.spawn-2.sentence-3)
[3](#exec.spawn-3)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5863)
Let *spawn-state-base* be the exposition-only class:
[🔗](#lib:execution::spawn-state-base)
namespace std::execution {struct *spawn-state-base* { // *exposition only*virtual void *complete*() noexcept = 0; // *exposition only*};}
[4](#exec.spawn-4)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5875)
Let *spawn-receiver* be the exposition-only class:
[🔗](#lib:execution::spawn-receiver)
namespace std::execution {struct *spawn-receiver* { // *exposition only*using receiver_concept = receiver_t; *spawn-state-base** *state*; // *exposition only*void set_value() && noexcept { *state*->*complete*(); }void set_stopped() && noexcept { *state*->*complete*(); }};}
[5](#exec.spawn-5)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5891)
Let *spawn-state* be the exposition-only class template:
[🔗](#lib:execution::spawn-state)
namespace std::execution {template<class Alloc, [scope_token](exec.scope.concepts#concept:scope_token "33.14.1Execution scope concepts[exec.scope.concepts]") Token, [sender](exec.snd.concepts#concept:sender "33.9.3Sender concepts[exec.snd.concepts]") Sender>struct *spawn-state* : *spawn-state-base* { // *exposition only*using *op-t* = connect_result_t<Sender, *spawn-receiver*>; // *exposition only**spawn-state*(Alloc alloc, Sender&& sndr, Token token); // *exposition only*void *complete*() noexcept override; // *exposition only*void *run*(); // *exposition only*private:using *alloc-t* = // *exposition only*typename allocator_traits<Alloc>::template rebind_alloc<*spawn-state*>; *alloc-t* *alloc*; // *exposition only**op-t* *op*; // *exposition only* Token *token*; // *exposition only*void *destroy*() noexcept; // *exposition only*};}
[🔗](#lib:execution::spawn-state,constructor)
`spawn-state(Alloc alloc, Sender&& sndr, Token token);
`
[6](#exec.spawn-6)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5924)
*Effects*: Initializes*alloc* with alloc,*token* with token, and*op* with:connect(std::move(sndr), *spawn-receiver*(this))
[🔗](#lib:run,execution::spawn-state)
`void run();
`
[7](#exec.spawn-7)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5941)
*Effects*: Equivalent to:if (*token*.try_associate()) start(*op*);else*destroy*();
[🔗](#lib:complete,execution::spawn-state)
`void complete() noexcept override;
`
[8](#exec.spawn-8)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5958)
*Effects*: Equivalent to:auto token = std::move(this->*token*);
*destroy*();
token.disassociate();
[🔗](#lib:destroy,execution::spawn-state)
`void destroy() noexcept;
`
[9](#exec.spawn-9)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5975)
*Effects*: Equivalent to:auto alloc = std::move(this->*alloc*);
allocator_traits<*alloc-t*>::destroy(alloc, this);
allocator_traits<*alloc-t*>::deallocate(alloc, this, 1);
[10](#exec.spawn-10)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5986)
For the expression spawn(sndr, token, env) let new_sender be the expression token.wrap(sndr) and
let alloc and senv be defined as follows:
- [(10.1)](#exec.spawn-10.1)
if the expression get_allocator(env) is well-formed, thenalloc is the result of get_allocator(env) andsenv is the expression env,
- [(10.2)](#exec.spawn-10.2)
otherwise
if the expression get_allocator(get_env(new_sender)) is well-formed, thenalloc is the result of get_allocator(get_env(new_sender)) andsenv is the expression *JOIN-ENV*(prop(get_allocator, alloc), env),
- [(10.3)](#exec.spawn-10.3)
otherwisealloc is allocator<void>() andsenv is the expression env[.](#exec.spawn-10.sentence-1)
[11](#exec.spawn-11)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6006)
The expression spawn(sndr, token, env) is of type void and
has the following effects:
- [(11.1)](#exec.spawn-11.1)
Uses alloc to allocate and construct an object o of
type that is a specialization of *spawn-state* fromalloc, write_env(token.wrap(sndr), senv), and token and then
invokes o.*run*()[.](#exec.spawn-11.sentence-1)
If an exception is thrown then
any constructed objects are destroyed and any allocated memory is deallocated[.](#exec.spawn-11.1.sentence-2)
[12](#exec.spawn-12)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6021)
The expression spawn(sndr, token) is expression-equivalent tospawn(sndr, token, execution::env<>())[.](#exec.spawn-12.sentence-1)

View File

@@ -0,0 +1,80 @@
[exec.continues.on]
# 33 Execution control library [[exec]](./#exec)
## 33.9 Senders [[exec.snd]](exec.snd#exec.continues.on)
### 33.9.12 Sender adaptors [[exec.adapt]](exec.adapt#exec.continues.on)
#### 33.9.12.6 execution::continues_on [exec.continues.on]
[1](#1)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3289)
continues_on adapts a sender into one
that completes on the specified scheduler[.](#1.sentence-1)
[2](#2)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3293)
The name continues_on denotes a pipeable sender adaptor object[.](#2.sentence-1)
For subexpressions sch and sndr,
if decltype((sch)) does not satisfy [scheduler](exec.sched#concept:scheduler "33.6Schedulers[exec.sched]"), ordecltype((sndr)) does not satisfy [sender](exec.snd.concepts#concept:sender "33.9.3Sender concepts[exec.snd.concepts]"),continues_on(sndr, sch) is ill-formed[.](#2.sentence-2)
[3](#3)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3300)
Otherwise,
the expression continues_on(sndr, sch) is expression-equivalent to:transform_sender(*get-domain-early*(sndr), *make-sender*(continues_on, sch, sndr)) except that sndr is evaluated only once[.](#3.sentence-1)
[4](#4)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3308)
The exposition-only class template *impls-for* ([[exec.snd.expos]](exec.snd.expos "33.9.2Exposition-only entities"))
is specialized for continues_on_t as follows:namespace std::execution {template<>struct *impls-for*<continues_on_t> : *default-impls* {static constexpr auto *get-attrs* =[](const auto& data, const auto& child) noexcept -> decltype(auto) {return *JOIN-ENV*(*SCHED-ATTRS*(data), *FWD-ENV*(get_env(child))); }; };}
[5](#5)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3323)
Let sndr and env be subexpressions
such that Sndr is decltype((sndr))[.](#5.sentence-1)
If [*sender-for*](exec.snd.concepts#concept:sender-for "33.9.3Sender concepts[exec.snd.concepts]")<Sndr, continues_on_t> is false,
then
the expression continues_on.transform_sender(sndr, env) is ill-formed;
otherwise, it is equal to:auto [_, data, child] = sndr;return schedule_from(std::move(data), std::move(child));
[*Note [1](#note-1)*:
This causes the continues_on(sndr, sch) sender to becomeschedule_from(sch, sndr) when it is connected with a receiver
whose execution domain does not customize continues_on[.](#5.sentence-2)
— *end note*]
[6](#6)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3340)
Let out_sndr be a subexpression denoting
a sender returned from continues_on(sndr, sch) or one equal to such, and
let OutSndr be the type decltype((out_sndr))[.](#6.sentence-1)
Let out_rcvr be a subexpression denoting a receiver
that has an environment of type Env such that [sender_in](exec.snd.concepts#concept:sender_in "33.9.3Sender concepts[exec.snd.concepts]")<OutSndr, Env> is true[.](#6.sentence-2)
Let op be an lvalue referring to the operation state
that results from connecting out_sndr with out_rcvr[.](#6.sentence-3)
Calling start(op) shall
start sndr on the current execution agent and
execute completion operations on out_rcvr on an execution agent of the execution resource associated with sch[.](#6.sentence-4)
If scheduling onto sch fails,
an error completion on out_rcvr shall be executed
on an unspecified execution agent[.](#6.sentence-5)

948
cppdraft/exec/coro/util.md Normal file
View File

@@ -0,0 +1,948 @@
[exec.coro.util]
# 33 Execution control library [[exec]](./#exec)
## 33.13 Coroutine utilities [exec.coro.util]
### [33.13.1](#exec.as.awaitable) execution::as_awaitable [[exec.as.awaitable]](exec.as.awaitable)
[1](#exec.as.awaitable-1)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6609)
as_awaitable transforms an object into one
that is awaitable within a particular coroutine[.](#exec.as.awaitable-1.sentence-1)
Subclause [exec.coro.util] makes use of
the following exposition-only entities:namespace std::execution {template<class Sndr, class Promise>concept [*awaitable-sender*](#concept:awaitable-sender "33.13.1execution::as_­awaitable[exec.as.awaitable]") =[*single-sender*](execution.syn#concept:single-sender "33.4Header <execution> synopsis[execution.syn]")<Sndr, env_of_t<Promise>> &&[sender_to](exec.snd.concepts#concept:sender_to "33.9.3Sender concepts[exec.snd.concepts]")<Sndr, *awaitable-receiver*> && // *see below*requires (Promise& p) {{ p.unhandled_stopped() } -> [convertible_to](concept.convertible#concept:convertible_to "18.4.4Concept convertible_­to[concept.convertible]")<coroutine_handle<>>; }; template<class Sndr>concept [*has-queryable-await-completion-adaptor*](#concept:has-queryable-await-completion-adaptor "33.13.1execution::as_­awaitable[exec.as.awaitable]") = // *exposition only*[sender](exec.snd.concepts#concept:sender "33.9.3Sender concepts[exec.snd.concepts]")<Sndr> &&requires(Sndr&& sender) { get_await_completion_adaptor(get_env(sender)); }; template<class Sndr, class Promise>class *sender-awaitable*; // *exposition only*}
[2](#exec.as.awaitable-2)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6636)
The type *sender-awaitable*<Sndr, Promise> is equivalent to:
namespace std::execution {template<class Sndr, class Promise>class *sender-awaitable* {struct *unit* {}; // *exposition only*using *value-type* = // *exposition only**single-sender-value-type*<Sndr, env_of_t<Promise>>; using *result-type* = // *exposition only* conditional_t<is_void_v<*value-type*>, unit, *value-type*>; struct *awaitable-receiver*; // *exposition only* variant<monostate, *result-type*, exception_ptr> *result*{}; // *exposition only* connect_result_t<Sndr, *awaitable-receiver*> *state*; // *exposition only*public:*sender-awaitable*(Sndr&& sndr, Promise& p); static constexpr bool await_ready() noexcept { return false; }void await_suspend(coroutine_handle<Promise>) noexcept { start(*state*); }*value-type* await_resume(); };}
[3](#exec.as.awaitable-3)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6662)
*awaitable-receiver* is equivalent to:struct *awaitable-receiver* {using receiver_concept = receiver_t;
variant<monostate, *result-type*, exception_ptr>* *result-ptr*; // *exposition only* coroutine_handle<Promise> *continuation*; // *exposition only*// *see below*};
[4](#exec.as.awaitable-4)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6673)
Let rcvr be an rvalue expression of type *awaitable-receiver*,
let crcvr be a const lvalue that refers to rcvr,
let vs be a pack of subexpressions, and
let err be an expression of type Err[.](#exec.as.awaitable-4.sentence-1)
Then:
- [(4.1)](#exec.as.awaitable-4.1)
If [constructible_from](concept.constructible#concept:constructible_from "18.4.11Concept constructible_­from[concept.constructible]")<*result-type*, decltype((vs))...> is satisfied,
the expression set_value(
rcvr, vs...) is equivalent to:try { rcvr.*result-ptr*->template emplace<1>(vs...);} catch(...) { rcvr.*result-ptr*->template emplace<2>(current_exception());} rcvr.*continuation*.resume();
Otherwise, set_value(rcvr, vs...) is ill-formed[.](#exec.as.awaitable-4.1.sentence-2)
- [(4.2)](#exec.as.awaitable-4.2)
The expression set_error(rcvr, err) is equivalent to:rcvr.*result-ptr*->template emplace<2>(*AS-EXCEPT-PTR*(err)); // see [[exec.general]](exec.general "33.1General") rcvr.*continuation*.resume();
- [(4.3)](#exec.as.awaitable-4.3)
The expression set_stopped(rcvr) is equivalent to:static_cast<coroutine_handle<>>(rcvr.*continuation*.promise().unhandled_stopped()).resume();
- [(4.4)](#exec.as.awaitable-4.4)
For any expression tag whose type satisfies [*forwarding-query*](execution.syn#concept:forwarding-query "33.4Header <execution> synopsis[execution.syn]") and
for any pack of subexpressions as,get_env(crcvr).query(tag, as...) is expression-equivalent to:tag(get_env(as_const(crcvr.*continuation*.promise())), as...)
[🔗](#exec.as.awaitable-itemdecl:1)
`sender-awaitable(Sndr&& sndr, Promise& p);
`
[5](#exec.as.awaitable-5)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6718)
*Effects*: Initializes *state* withconnect(std::forward<Sndr>(sndr), *awaitable-receiver*{addressof(result), coroutine_handle<Promise>::from_promise(p)})
[🔗](#exec.as.awaitable-itemdecl:2)
`value-type await_resume();
`
[6](#exec.as.awaitable-6)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6732)
*Effects*: Equivalent to:if (*result*.index() == 2) rethrow_exception(get<2>(*result*));if constexpr (!is_void_v<*value-type*>)return std::forward<*value-type*>(get<1>(*result*));
[7](#exec.as.awaitable-7)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6743)
as_awaitable is a customization point object[.](#exec.as.awaitable-7.sentence-1)
For subexpressions expr and p where p is an lvalue,Expr names the type decltype((expr)) andPromise names the type decay_t<decltype((p))>,as_awaitable(expr, p) is expression-equivalent to,
except that the evaluations of expr and p are indeterminately sequenced:
- [(7.1)](#exec.as.awaitable-7.1)
expr.as_awaitable(p) if that expression is well-formed[.](#exec.as.awaitable-7.1.sentence-1)
*Mandates*: [*is-awaitable*](exec.awaitable#concept:is-awaitable "33.9.4Awaitable helpers[exec.awaitable]")<A, Promise> is true,
where A is the type of the expression above[.](#exec.as.awaitable-7.1.sentence-2)
- [(7.2)](#exec.as.awaitable-7.2)
Otherwise, (void(p), expr) if [*is-awaitable*](exec.awaitable#concept:is-awaitable "33.9.4Awaitable helpers[exec.awaitable]")<Expr, U> is true,
where U is an unspecified class type
that is not Promise and
that lacks a member named await_transform[.](#exec.as.awaitable-7.2.sentence-1)
*Preconditions*: [*is-awaitable*](exec.awaitable#concept:is-awaitable "33.9.4Awaitable helpers[exec.awaitable]")<Expr, Promise> is true and
the expression co_await expr in a coroutine with promise type U is expression-equivalent to
the same expression in a coroutine with promise type Promise[.](#exec.as.awaitable-7.2.sentence-2)
- [(7.3)](#exec.as.awaitable-7.3)
Otherwise, *sender-awaitable*{*adapted-expr*, p} if*has-queryable-await-completion-adaptor*<Expr> and*awaitable-sender*<decltype((*adapted-expr*)), Promise> are both satisfied, where *adapted-expr* isget_await_completion_adaptor(get_env(expr))(expr),
except that expr is evaluated only once[.](#exec.as.awaitable-7.3.sentence-1)
- [(7.4)](#exec.as.awaitable-7.4)
Otherwise, *sender-awaitable*{expr, p} if [*awaitable-sender*](#concept:awaitable-sender "33.13.1execution::as_­awaitable[exec.as.awaitable]")<Expr, Promise> is true[.](#exec.as.awaitable-7.4.sentence-1)
- [(7.5)](#exec.as.awaitable-7.5)
Otherwise, (void(p), expr)[.](#exec.as.awaitable-7.5.sentence-1)
### [33.13.2](#exec.with.awaitable.senders) execution::with_awaitable_senders [[exec.with.awaitable.senders]](exec.with.awaitable.senders)
[1](#exec.with.awaitable.senders-1)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6793)
with_awaitable_senders,
when used as the base class of a coroutine promise type,
makes senders awaitable in that coroutine type[.](#exec.with.awaitable.senders-1.sentence-1)
In addition, it provides a default implementation of unhandled_stopped such that if a sender completes by calling set_stopped,
it is treated as if an uncatchable "stopped" exception were thrown
from the [*await-expression*](expr.await#nt:await-expression "7.6.2.4Await[expr.await]")[.](#exec.with.awaitable.senders-1.sentence-2)
[*Note [1](#exec.with.awaitable.senders-note-1)*:
The coroutine is never resumed, and
the unhandled_stopped of the coroutine caller's promise type is called[.](#exec.with.awaitable.senders-1.sentence-3)
— *end note*]
namespace std::execution {template<[*class-type*](execution.syn#concept:class-type "33.4Header <execution> synopsis[execution.syn]") Promise>struct [with_awaitable_senders](#lib:with_awaitable_senders "33.13.2execution::with_­awaitable_­senders[exec.with.awaitable.senders]") {template<class OtherPromise>requires (![same_as](concept.same#concept:same_as "18.4.2Concept same_­as[concept.same]")<OtherPromise, void>)void set_continuation(coroutine_handle<OtherPromise> h) noexcept;
coroutine_handle<> [continuation](#lib:with_awaitable_senders,continuation "33.13.2execution::with_­awaitable_­senders[exec.with.awaitable.senders]")() const noexcept { return *continuation*; } coroutine_handle<> [unhandled_stopped](#lib:with_awaitable_senders,unhandled_stopped "33.13.2execution::with_­awaitable_­senders[exec.with.awaitable.senders]")() noexcept {return *stopped-handler*(*continuation*.address()); }template<class Value>*see below* await_transform(Value&& value); private:[[noreturn]] static coroutine_handle<>*default-unhandled-stopped*(void*) noexcept { // *exposition only* terminate(); } coroutine_handle<> *continuation*{}; // *exposition only* coroutine_handle<> (**stopped-handler*)(void*) noexcept = // *exposition only*&*default-unhandled-stopped*; };}
[🔗](#lib:set_continuation,with_awaitable_senders)
`template<class OtherPromise>
requires (![same_as](concept.same#concept:same_as "18.4.2Concept same_­as[concept.same]")<OtherPromise, void>)
void set_continuation(coroutine_handle<OtherPromise> h) noexcept;
`
[2](#exec.with.awaitable.senders-2)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6844)
*Effects*: Equivalent to:*continuation* = h;if constexpr ( requires(OtherPromise& other) { other.unhandled_stopped(); } ) {*stopped-handler* = [](void* p) noexcept -> coroutine_handle<> {return coroutine_handle<OtherPromise>::from_address(p).promise().unhandled_stopped(); };} else {*stopped-handler* = &*default-unhandled-stopped*;}
[🔗](#lib:await_transform,with_awaitable_senders)
`template<class Value>
call-result-t<as_awaitable_t, Value, Promise&> await_transform(Value&& value);
`
[3](#exec.with.awaitable.senders-3)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6867)
*Effects*: Equivalent to:return as_awaitable(std::forward<Value>(value), static_cast<Promise&>(*this));
### [33.13.3](#exec.affine.on) execution::affine_on [[exec.affine.on]](exec.affine.on)
[1](#exec.affine.on-1)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6877)
affine_on adapts a sender into one that completes on
the specified scheduler[.](#exec.affine.on-1.sentence-1)
If the algorithm determines that the adapted sender already completes
on the correct scheduler it can avoid any scheduling operation[.](#exec.affine.on-1.sentence-2)
[2](#exec.affine.on-2)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6883)
The name affine_on denotes a pipeable sender adaptor
object[.](#exec.affine.on-2.sentence-1)
For subexpressions sch and sndr, if decltype((sch)) does not satisfy [scheduler](exec.sched#concept:scheduler "33.6Schedulers[exec.sched]"), or decltype((sndr)) does not satisfy [sender](exec.snd.concepts#concept:sender "33.9.3Sender concepts[exec.snd.concepts]"), affine_on(sndr, sch) is ill-formed[.](#exec.affine.on-2.sentence-2)
[3](#exec.affine.on-3)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6891)
Otherwise, the expression affine_on(sndr, sch) is
expression-equivalent to:transform_sender(*get-domain-early*(sndr), *make-sender*(affine_on, sch, sndr)) except that sndr is evaluated only once[.](#exec.affine.on-3.sentence-1)
[4](#exec.affine.on-4)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6899)
The exposition-only class template *impls-for* ([[exec.snd.expos]](exec.snd.expos "33.9.2Exposition-only entities"))
is specialized for affine_on_t as follows:
namespace std::execution {template<>struct *impls-for*<affine_on_t> : *default-impls* {static constexpr auto *get-attrs* =[](const auto& data, const auto& child) noexcept -> decltype(auto) {return *JOIN-ENV*(*SCHED-ATTRS*(data), *FWD-ENV*(get_env(child))); }; };}
[5](#exec.affine.on-5)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6915)
Let *out_sndr* be a subexpression denoting a sender
returned from affine_on(sndr, sch) or one equal to such,
and let *OutSndr* be the type decltype((*out_sndr*))[.](#exec.affine.on-5.sentence-1)
Let *out_rcvr* be a subexpression denoting a receiver that
has an environment of type Env such that [sender_in](exec.snd.concepts#concept:sender_in "33.9.3Sender concepts[exec.snd.concepts]")<*OutSndr*, Env> is true[.](#exec.affine.on-5.sentence-2)
Let *op* be an lvalue referring to the operation state that
results from connecting *out_sndr* to *out_rcvr*[.](#exec.affine.on-5.sentence-3)
Calling start(*op*) will start sndr on the current
execution agent and execute completion operations on *out_rcvr* on an execution agent of the execution resource associated withsch[.](#exec.affine.on-5.sentence-4)
If the current execution resource is the same as the execution
resource associated with sch, the completion operation on*out_rcvr* may be called before start(*op*) completes[.](#exec.affine.on-5.sentence-5)
If scheduling onto sch fails, an error completion on*out_rcvr* shall be executed on an unspecified execution
agent[.](#exec.affine.on-5.sentence-6)
### [33.13.4](#exec.inline.scheduler) execution::inline_scheduler [[exec.inline.scheduler]](exec.inline.scheduler)
namespace std::execution {class [inline_scheduler](#lib:inline_scheduler "33.13.4execution::inline_­scheduler[exec.inline.scheduler]") {class *inline-sender*; // *exposition only*template<[receiver](exec.recv.concepts#concept:receiver "33.7.1Receiver concepts[exec.recv.concepts]") R>class *inline-state*; // *exposition only*public:using scheduler_concept = scheduler_t; constexpr *inline-sender* schedule() noexcept { return {}; }constexpr bool operator==(const inline_scheduler&) const noexcept = default; };}
[1](#exec.inline.scheduler-1)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6954)
inline_scheduler is a class that models[scheduler](exec.sched#concept:scheduler "33.6Schedulers[exec.sched]") ([[exec.sched]](exec.sched "33.6Schedulers"))[.](#exec.inline.scheduler-1.sentence-1)
All objects of type inline_scheduler are equal[.](#exec.inline.scheduler-1.sentence-2)
[2](#exec.inline.scheduler-2)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6959)
*inline-sender* is an exposition-only type that satisfies[sender](exec.snd.concepts#concept:sender "33.9.3Sender concepts[exec.snd.concepts]")[.](#exec.inline.scheduler-2.sentence-1)
The type completion_signatures_of_t<*inline-sender*> is completion_signatures<set_value_t()>[.](#exec.inline.scheduler-2.sentence-2)
[3](#exec.inline.scheduler-3)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6965)
Let sndr be an expression of type *inline-sender*,
let rcvr be an expression such that[receiver_of](exec.recv.concepts#concept:receiver_of "33.7.1Receiver concepts[exec.recv.concepts]")<decltype((rcvr)), CS> is true where CS is completion_signatures<set_value_t()>,
then:
- [(3.1)](#exec.inline.scheduler-3.1)
the expression connect(sndr, rcvr) has
type *inline-state*<remove_cvref_t<decltype((rcvr))>> and is potentially-throwing if and only if((void)sndr, auto(rcvr)) is potentially-throwing, and
- [(3.2)](#exec.inline.scheduler-3.2)
the expressionget_completion_scheduler<set_value_t>(get_env(sndr)) has
type inline_scheduler and is potentially-throwing
if and only if get_env(sndr) is potentially-throwing[.](#exec.inline.scheduler-3.sentence-1)
[4](#exec.inline.scheduler-4)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6982)
Let *o* be a non-const lvalue of type*inline-state*<Rcvr>, and let REC(*o*) be
a non-const lvalue reference to an object of type Rcvr that
was initialized with the expression rcvr passed to an
invocation of connect that returned *o*, then:
- [(4.1)](#exec.inline.scheduler-4.1)
the object to which REC(*o*) refers remains valid for
the lifetime of the object to which *o* refers, and
- [(4.2)](#exec.inline.scheduler-4.2)
the expression start(*o*) is equivalent toset_value(std::move(REC(*o*)))[.](#exec.inline.scheduler-4.sentence-1)
### [33.13.5](#exec.task.scheduler) execution::task_scheduler [[exec.task.scheduler]](exec.task.scheduler)
namespace std::execution {class [task_scheduler](#lib:task_scheduler "33.13.5execution::task_­scheduler[exec.task.scheduler]") {class *ts-sender*; // *exposition only*template<[receiver](exec.recv.concepts#concept:receiver "33.7.1Receiver concepts[exec.recv.concepts]") R>class *state*; // *exposition only*public:using scheduler_concept = scheduler_t; template<class Sch, class Allocator = allocator<void>>requires (![same_as](concept.same#concept:same_as "18.4.2Concept same_­as[concept.same]")<task_scheduler, remove_cvref_t<Sch>>)&& [scheduler](exec.sched#concept:scheduler "33.6Schedulers[exec.sched]")<Sch>explicit task_scheduler(Sch&& sch, Allocator alloc = {}); *ts-sender* schedule(); friend bool operator==(const task_scheduler& lhs, const task_scheduler& rhs)noexcept; template<class Sch>requires (![same_as](concept.same#concept:same_as "18.4.2Concept same_­as[concept.same]")<task_scheduler, Sch>)&& [scheduler](exec.sched#concept:scheduler "33.6Schedulers[exec.sched]")<Sch>friend bool operator==(const task_scheduler& lhs, const Sch& rhs) noexcept; private: shared_ptr<void> *sch_*; // *exposition only*};}
[1](#exec.task.scheduler-1)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L7028)
task_scheduler is a class that models[scheduler](exec.sched#concept:scheduler "33.6Schedulers[exec.sched]") ([[exec.sched]](exec.sched "33.6Schedulers"))[.](#exec.task.scheduler-1.sentence-1)
Given an object s of type task_scheduler, let*SCHED*(s) be the object owned by s.*sch_*[.](#exec.task.scheduler-1.sentence-2)
[🔗](#lib:task_scheduler,constructor)
`template<class Sch, class Allocator = allocator<void>>
requires(![same_as](concept.same#concept:same_as "18.4.2Concept same_­as[concept.same]")<task_scheduler, remove_cvref_t<Sch>>) && [scheduler](exec.sched#concept:scheduler "33.6Schedulers[exec.sched]")<Sch>
explicit task_scheduler(Sch&& sch, Allocator alloc = {});
`
[2](#exec.task.scheduler-2)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L7041)
*Effects*: Initialize *sch_* withallocate_shared<remove_cvref_t<Sch>>(alloc, std::forward<Sch>(sch))[.](#exec.task.scheduler-2.sentence-1)
[3](#exec.task.scheduler-3)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L7046)
*Recommended practice*: Implementations should avoid the use of dynamically
allocated memory for small scheduler objects[.](#exec.task.scheduler-3.sentence-1)
[4](#exec.task.scheduler-4)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L7051)
*Remarks*: Any allocations performed by construction of *ts-sender* or*state* objects resulting from calls on *this are
performed using a copy of alloc[.](#exec.task.scheduler-4.sentence-1)
[🔗](#lib:scheduler,task_scheduler)
`ts-sender schedule();
`
[5](#exec.task.scheduler-5)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L7063)
*Effects*: Returns an object of type *ts-sender* containing a sender
initialized with schedule(*SCHED*(*this))[.](#exec.task.scheduler-5.sentence-1)
[🔗](#lib:operator==,task_scheduler)
`bool operator==(const task_scheduler& lhs, const task_scheduler& rhs) noexcept;
`
[6](#exec.task.scheduler-6)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L7074)
*Effects*: Equivalent to: return lhs == *SCHED*(rhs);
[🔗](#lib:operator==,task_scheduler_)
`template<class Sch>
requires (![same_as](concept.same#concept:same_as "18.4.2Concept same_­as[concept.same]")<task_scheduler, Sch>)
&& [scheduler](exec.sched#concept:scheduler "33.6Schedulers[exec.sched]")<Sch>
bool operator==(const task_scheduler& lhs, const Sch& rhs) noexcept;
`
[7](#exec.task.scheduler-7)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L7087)
*Returns*: false if the type of *SCHED*(lhs) is not Sch,
otherwise *SCHED*(lhs) == rhs[.](#exec.task.scheduler-7.sentence-1)
[8](#exec.task.scheduler-8)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L7093)
namespace std::execution {class task_scheduler::*ts-sender* { // *exposition only*public:using sender_concept = sender_t; template<[receiver](exec.recv.concepts#concept:receiver "33.7.1Receiver concepts[exec.recv.concepts]") Rcvr>*state*<Rcvr> connect(Rcvr&& rcvr); };}*ts-sender* is an exposition-only class that models[sender](exec.snd.concepts#concept:sender "33.9.3Sender concepts[exec.snd.concepts]") ([[exec.snd]](exec.snd "33.9Senders")) and for whichcompletion_signatures_of_t<*ts-sender*> denotes:completion_signatures< set_value_t(),
set_error_t(error_code),
set_error_t(exception_ptr),
set_stopped_t()>
[9](#exec.task.scheduler-9)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L7116)
Let *sch* be an object of type task_scheduler and let sndr be an object of type *ts-sender* obtained
from schedule(*sch*)[.](#exec.task.scheduler-9.sentence-1)
Then get_completion_scheduler<set_value_t>(get_env(sndr)) == *sch* is true[.](#exec.task.scheduler-9.sentence-2)
The object *SENDER*(sndr) is the sender object contained bysndr or an object move constructed from it[.](#exec.task.scheduler-9.sentence-3)
[🔗](#lib:connect,task_scheduler::ts-sender)
`template<[receiver](exec.recv.concepts#concept:receiver "33.7.1Receiver concepts[exec.recv.concepts]") Rcvr>
state<Rcvr> connect(Rcvr&& rcvr);
`
[10](#exec.task.scheduler-10)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L7131)
*Effects*: Let *r* be an object of a type that models [receiver](exec.recv.concepts#concept:receiver "33.7.1Receiver concepts[exec.recv.concepts]") and whose completion handlers result in invoking the corresponding
completion handlers of rcvr or copy thereof[.](#exec.task.scheduler-10.sentence-1)
Returns an object of type *state*<Rcvr> containing
an operation state object initialized with connect(*SENDER*(*this),
std::move(*r*))[.](#exec.task.scheduler-10.sentence-2)
[11](#exec.task.scheduler-11)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L7141)
namespace std::execution {template<[receiver](exec.recv.concepts#concept:receiver "33.7.1Receiver concepts[exec.recv.concepts]") R>class task_scheduler::*state* { // *exposition only*public:using operation_state_concept = operation_state_t; void start() & noexcept; };}*state* is an exposition-only class template whose
specializations model [operation_state](exec.opstate.general#concept:operation_state "33.8.1General[exec.opstate.general]") ([[exec.opstate]](exec.opstate "33.8Operation states"))[.](#exec.task.scheduler-11.sentence-1)
[🔗](#lib:start,task_scheduler::state)
`void start() & noexcept;
`
[12](#exec.task.scheduler-12)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L7161)
*Effects*: Equivalent to start(st) where st is the operation
state object contained by *this[.](#exec.task.scheduler-12.sentence-1)
### [33.13.6](#exec.task) execution::task [[exec.task]](exec.task)
#### [33.13.6.1](#task.overview) task overview [[task.overview]](task.overview)
[1](#task.overview-1)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L7171)
The task class template represents a sender that can
be used as the return type of coroutines[.](#task.overview-1.sentence-1)
The first template parameter T defines the type of the value
completion datum ([[exec.async.ops]](exec.async.ops "33.3Asynchronous operations")) if T is not void[.](#task.overview-1.sentence-2)
Otherwise, there are no value completion datums[.](#task.overview-1.sentence-3)
Inside coroutines returning task<T, E> the operand ofco_return (if any) becomes the argument of set_value[.](#task.overview-1.sentence-4)
The second template parameter Environment is used to customize
the behavior of task[.](#task.overview-1.sentence-5)
#### [33.13.6.2](#task.class) Class template task [[task.class]](task.class)
namespace std::execution {template<class T, class Environment>class [task](#lib:task "33.13.6.2Class template task[task.class]") {// [[task.state]](#task.state "33.13.6.4Class template task::state")template<[receiver](exec.recv.concepts#concept:receiver "33.7.1Receiver concepts[exec.recv.concepts]") Rcvr>class *state*; // *exposition only*public:using sender_concept = sender_t; using completion_signatures = *see below*; using allocator_type = *see below*; using scheduler_type = *see below*; using stop_source_type = *see below*; using stop_token_type = decltype(declval<stop_source_type>().get_token()); using error_types = *see below*; // [[task.promise]](#task.promise "33.13.6.5Class task::promise_­type")class promise_type;
task(task&&) noexcept; ~task(); template<[receiver](exec.recv.concepts#concept:receiver "33.7.1Receiver concepts[exec.recv.concepts]") Rcvr>*state*<Rcvr> connect(Rcvr&& rcvr); private: coroutine_handle<promise_type> *handle*; // *exposition only*};}
[1](#task.class-1)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L7216)
task<T, E> models [sender](exec.snd.concepts#concept:sender "33.9.3Sender concepts[exec.snd.concepts]") ([[exec.snd]](exec.snd "33.9Senders"))
if T is void, a reference type, or a cv-unqualified
non-array object type and E is a class type[.](#task.class-1.sentence-1)
Otherwise a program that instantiates the definition of task<T, E> is ill-formed[.](#task.class-1.sentence-2)
[2](#task.class-2)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L7223)
The nested types of task template specializations
are determined based on the Environment parameter:
- [(2.1)](#task.class-2.1)
allocator_type is Environment::allocator_type if that [*qualified-id*](expr.prim.id.qual#nt:qualified-id "7.5.5.3Qualified names[expr.prim.id.qual]") is valid and denotes a type,allocator<byte> otherwise[.](#task.class-2.1.sentence-1)
- [(2.2)](#task.class-2.2)
scheduler_type is Environment::scheduler_type if that [*qualified-id*](expr.prim.id.qual#nt:qualified-id "7.5.5.3Qualified names[expr.prim.id.qual]") is valid and denotes a type,task_scheduler otherwise[.](#task.class-2.2.sentence-1)
- [(2.3)](#task.class-2.3)
stop_source_type is Environment::stop_source_type if that [*qualified-id*](expr.prim.id.qual#nt:qualified-id "7.5.5.3Qualified names[expr.prim.id.qual]") is valid and denotes a type,inplace_stop_source otherwise[.](#task.class-2.3.sentence-1)
- [(2.4)](#task.class-2.4)
error_types is Environment::error_types if
that [*qualified-id*](expr.prim.id.qual#nt:qualified-id "7.5.5.3Qualified names[expr.prim.id.qual]") is valid and denotes a type,completion_signatures<set_error_t(exception_ptr)> otherwise[.](#task.class-2.4.sentence-1)
[3](#task.class-3)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L7241)
A program is ill-formed if error_types is not a
specialization of completion_signatures<ErrorSigs...> orErrorSigs contains an element which is not of the formset_error_t(E) for some type E[.](#task.class-3.sentence-1)
[4](#task.class-4)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L7247)
The type alias completion_signatures is a specialization
of execution::completion_signatures with the template
arguments (in unspecified order):
- [(4.1)](#task.class-4.1)
set_value_t() if T is void,
and set_value_t(T) otherwise;
- [(4.2)](#task.class-4.2)
template arguments of the specialization ofexecution::completion_signatures denoted by error_types;
and
- [(4.3)](#task.class-4.3)
set_stopped_t()[.](#task.class-4.sentence-1)
[5](#task.class-5)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L7260)
allocator_type shall meet the *Cpp17Allocator* requirements[.](#task.class-5.sentence-1)
#### [33.13.6.3](#task.members) task members [[task.members]](task.members)
[🔗](#lib:task,constructor)
`task(task&& other) noexcept;
`
[1](#task.members-1)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L7271)
*Effects*: Initializes *handle* with exchange(other.*handle*,{})[.](#task.members-1.sentence-1)
[🔗](#lib:task,destructor)
`~task();
`
[2](#task.members-2)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L7282)
*Effects*: Equivalent to:if (*handle*)*handle*.destroy();
[🔗](#lib:connect,task)
`template<[receiver](exec.recv.concepts#concept:receiver "33.7.1Receiver concepts[exec.recv.concepts]") Rcvr>
state<Rcvr> connect(Rcvr&& recv);
`
[3](#task.members-3)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L7297)
*Preconditions*: bool(*handle*) is true[.](#task.members-3.sentence-1)
[4](#task.members-4)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L7301)
*Effects*: Equivalent to:return *state*<Rcvr>(exchange(*handle*, {}), std::forward<Rcvr>(recv));
#### [33.13.6.4](#task.state) Class template task::*state* [[task.state]](task.state)
namespace std::execution {template<class T, class Environment>template<[receiver](exec.recv.concepts#concept:receiver "33.7.1Receiver concepts[exec.recv.concepts]") Rcvr>class task<T, Environment>::*state* { // *exposition only*public:using operation_state_concept = operation_state_t; template<class R>*state*(coroutine_handle<promise_type> h, R&& rr); ~*state*(); void start() & noexcept; private:using *own-env-t* = *see below*; // *exposition only* coroutine_handle<promise_type> *handle*; // *exposition only* remove_cvref_t<Rcvr> *rcvr*; // *exposition only**own-env-t* *own-env*; // *exposition only* Environment *environment*; // *exposition only*};}
[1](#task.state-1)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L7336)
The type *own-env-t* is Environment::template env_type<decltype(get_env(declval<Rcvr>()))> if that[*qualified-id*](expr.prim.id.qual#nt:qualified-id "7.5.5.3Qualified names[expr.prim.id.qual]") is valid and denotes a type, env<> otherwise[.](#task.state-1.sentence-1)
[🔗](#lib:task::state,constructor)
`template<class R>
state(coroutine_handle<promise_type> h, R&& rr);
`
[2](#task.state-2)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L7347)
*Effects*: Initializes
- [(2.1)](#task.state-2.1)
*handle* with std::move(h);
- [(2.2)](#task.state-2.2)
*rcvr* with std::forward<R>(rr);
- [(2.3)](#task.state-2.3)
*own-env* with *own-env-t*(get_env(*rcvr*)) if that expression
is valid and *own-env-t*() otherwise[.](#task.state-2.3.sentence-1)
If neither of these expressions is valid, the program is ill-formed[.](#task.state-2.3.sentence-2)
- [(2.4)](#task.state-2.4)
*environment* withEnvironment(*own-env*) if that expression is
valid, otherwise Environment(get_env(*rcvr*)) if this expression is valid, otherwise Environment()[.](#task.state-2.4.sentence-1)
If neither of these expressions is valid, the program is ill-formed[.](#task.state-2.4.sentence-2)
[🔗](#lib:task::state,destructor)
`~state();
`
[3](#task.state-3)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L7370)
*Effects*: Equivalent to:if (*handle*)*handle*.destroy();
[🔗](#lib:start,task::state)
`void start() & noexcept;
`
[4](#task.state-4)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L7384)
*Effects*: Let *prom* be the object *handle*.promise()[.](#task.state-4.sentence-1)
Associates *STATE*(*prom*), *RCVR*(*prom*), and *SCHED*(*prom*) with *this as follows:
- [(4.1)](#task.state-4.1)
*STATE*(*prom*) is *this[.](#task.state-4.1.sentence-1)
- [(4.2)](#task.state-4.2)
*RCVR*(*prom*) is *rcvr*[.](#task.state-4.2.sentence-1)
- [(4.3)](#task.state-4.3)
*SCHED*(*prom*) is the object initialized
with scheduler_type(get_scheduler(get_env(*rcvr*))) if that expression is valid and scheduler_type() otherwise[.](#task.state-4.3.sentence-1)
If neither of these expressions is valid, the program is ill-formed[.](#task.state-4.3.sentence-2)
Let *st* be get_stop_token(get_env(*rcvr*))[.](#task.state-4.sentence-3)
Initializes *prom*.*token* and*prom*.*source* such that
- [(4.4)](#task.state-4.4)
*prom*.*token*.stop_requested() returns*st*.stop_requested();
- [(4.5)](#task.state-4.5)
*prom*.*token*.stop_possible() returns*st*.stop_possible(); and
- [(4.6)](#task.state-4.6)
for types Fn and Init such that both[invocable](concept.invocable#concept:invocable "18.7.2Concept invocable[concept.invocable]")<Fn> and[constructible_from](concept.constructible#concept:constructible_from "18.4.11Concept constructible_­from[concept.constructible]")<Fn, Init> are modeled,stop_token_type::callback_type<Fn> models[*stoppable-callback-for*](stoptoken.concepts#concept:stoppable-callback-for "32.3.3Stop token concepts[stoptoken.concepts]")<Fn, stop_token_type, Init>[.](#task.state-4.sentence-4)
After that invokes *handle*.resume()[.](#task.state-4.sentence-5)
#### [33.13.6.5](#task.promise) Class task::promise_type [[task.promise]](task.promise)
namespace std::execution {template<class T, class Environment>class task<T, Environment>::promise_type {public:template<class... Args> promise_type(const Args&... args);
task get_return_object() noexcept; auto initial_suspend() noexcept; auto final_suspend() noexcept; void uncaught_exception();
coroutine_handle<> unhandled_stopped(); void return_void(); // present only if is_void_v<T> is truetemplate<class V>void return_value(V&& value); // present only if is_void_v<T> is falsetemplate<class E>*unspecified* yield_value(with_error<E> error); template<class A>auto await_transform(A&& a); template<class Sch>auto await_transform(change_coroutine_scheduler<Sch> sch); *unspecified* get_env() const noexcept; template<class... Args>void* operator new(size_t size, Args&&... args); void operator delete(void* pointer, size_t size) noexcept; private:using *error-variant* = *see below*; // *exposition only* allocator_type *alloc*; // *exposition only* stop_source_type *source*; // *exposition only* stop_token_type *token*; // *exposition only* optional<T> *result*; // *exposition only*; present only if is_void_v<T> is false*error-variant* *errors*; // *exposition only*};}
[1](#task.promise-1)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L7466)
Let *prom* be an object of promise_type and let *tsk* be the task object
created by *prom*.get_return_object()[.](#task.promise-1.sentence-1)
The description below
refers to objects *STATE*(*prom*),*RCVR*(*prom*),
and *SCHED*(*prom*) associated with *tsk* during evaluation of task::*state*<Rcvr>::start for some receiver Rcvr[.](#task.promise-1.sentence-2)
[2](#task.promise-2)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L7478)
*error-variant* is a variant<monostate,
remove_cvref_t<E>...>, with duplicate types removed, where E... are the parameter types of the template arguments of the specialization ofexecution::completion_signatures denoted byerror_types[.](#task.promise-2.sentence-1)
[🔗](#lib:task::promise_type,constructor)
`template<class... Args>
promise_type(const Args&... args);
`
[3](#task.promise-3)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L7491)
*Mandates*: The first parameter of type allocator_arg_t (if any) is not
the last parameter[.](#task.promise-3.sentence-1)
[4](#task.promise-4)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L7496)
*Effects*: If Args contains an element of type allocator_arg_t then *alloc* is initialized with the corresponding next
element of args[.](#task.promise-4.sentence-1)
Otherwise, *alloc* is initialized with allocator_type()[.](#task.promise-4.sentence-2)
[🔗](#lib:get_return_object,task::promise_type)
`task get_return_object() noexcept;
`
[5](#task.promise-5)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L7509)
*Returns*: A task object whose member *handle* iscoroutine_handle<promise_type>::from_promise(*this)[.](#task.promise-5.sentence-1)
[🔗](#lib:initial_suspend,task::promise_type)
`auto initial_suspend() noexcept;
`
[6](#task.promise-6)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L7520)
*Returns*: An awaitable object of unspecified type ([[expr.await]](expr.await "7.6.2.4Await")) whose
member functions arrange for
- [(6.1)](#task.promise-6.1)
the calling coroutine to be suspended,
- [(6.2)](#task.promise-6.2)
the coroutine to be resumed on an execution agent of the
execution resource associated with *SCHED*(*this)[.](#task.promise-6.sentence-1)
[🔗](#lib:final_suspend,task::promise_type)
`auto final_suspend() noexcept;
`
[7](#task.promise-7)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L7536)
*Returns*: An awaitable object of unspecified type ([[expr.await]](expr.await "7.6.2.4Await")) whose
member functions arrange for the completion of the asynchronous
operation associated with *STATE*(*this) by invoking:
- [(7.1)](#task.promise-7.1)
set_error(std::move(*RCVR*(*this)), std::move(e)) if *errors*.index() is greater than zero ande is the value held by *errors*, otherwise
- [(7.2)](#task.promise-7.2)
set_value(std::move(*RCVR*(*this))) if is_void<T> is true,
and otherwise
- [(7.3)](#task.promise-7.3)
set_value(std::move(*RCVR*(*this)), **result*)[.](#task.promise-7.sentence-1)
[🔗](#lib:yield_value,task::promise_type)
`template<class Err>
auto yield_value(with_error<Err> err);
`
[8](#task.promise-8)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L7560)
*Mandates*: std::move(err.error) is convertible to exactly one of theset_error_t argument types of error_types[.](#task.promise-8.sentence-1)
Let *Cerr* be that type[.](#task.promise-8.sentence-2)
[9](#task.promise-9)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L7566)
*Returns*: An awaitable object of unspecified type ([[expr.await]](expr.await "7.6.2.4Await")) whose
member functions arrange for the calling coroutine to be suspended
and then completes the asynchronous operation associated with*STATE*(*this) by invoking set_error(std::move(*RCVR*(*this)),*Cerr*(std::move(err.error)))[.](#task.promise-9.sentence-1)
[🔗](#lib:await_transform,task::promise_type)
`template<[sender](exec.snd.concepts#concept:sender "33.9.3Sender concepts[exec.snd.concepts]") Sender>
auto await_transform(Sender&& sndr) noexcept;
`
[10](#task.promise-10)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L7581)
*Returns*: If [same_as](concept.same#concept:same_as "18.4.2Concept same_­as[concept.same]")<inline_scheduler, scheduler_type> is true returns as_awaitable(std::forward<Sender>(sndr), *this);
otherwise returnsas_awaitable(affine_on(std::forward<Sender>(sndr), *SCHED*(*this)), *this)[.](#task.promise-10.sentence-1)
[🔗](#lib:await_transform,task::promise_type_)
`template<class Sch>
auto await_transform(change_coroutine_scheduler<Sch> sch) noexcept;
`
[11](#task.promise-11)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L7595)
*Effects*: Equivalent to:return await_transform(just(exchange(*SCHED*(*this), scheduler_type(sch.scheduler))), *this);
[🔗](#lib:uncaught_exception,task::promise_type)
`void uncaught_exception();
`
[12](#task.promise-12)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L7608)
*Effects*: If the signature set_error_t(exception_ptr) is not an element
of error_types, calls terminate() ([[except.terminate]](except.terminate "14.6.2The std::terminate function"))[.](#task.promise-12.sentence-1)
Otherwise, stores current_exception() into *errors*[.](#task.promise-12.sentence-2)
[🔗](#lib:unhandled_stopped,task::promise_type)
`coroutine_handle<> unhandled_stopped();
`
[13](#task.promise-13)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L7620)
*Effects*: Completes the asynchronous operation associated with *STATE*(*this) by invoking set_stopped(std::move(*RCVR*(*this)))[.](#task.promise-13.sentence-1)
[14](#task.promise-14)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L7626)
*Returns*: noop_coroutine()[.](#task.promise-14.sentence-1)
[🔗](#lib:get_env,task::promise_type)
`unspecified get_env() const noexcept;
`
[15](#task.promise-15)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L7636)
*Returns*: An object env such that queries are forwarded as follows:
- [(15.1)](#task.promise-15.1)
env.query(get_scheduler) returns scheduler_type(*SCHED*(*this))[.](#task.promise-15.1.sentence-1)
- [(15.2)](#task.promise-15.2)
env.query(get_allocator) returns *alloc*[.](#task.promise-15.2.sentence-1)
- [(15.3)](#task.promise-15.3)
env.query(get_stop_token) returns *token*[.](#task.promise-15.3.sentence-1)
- [(15.4)](#task.promise-15.4)
For any other query q and arguments a... a
call to env.query(q, a...) returns*STATE*(*this)[.](#task.promise-15.4.sentence-1)
environment.query(q, a...) if this expression
is well-formed and forwarding_query(q) is well-formed and is true[.](#task.promise-15.4.sentence-2)
Otherwise env.query(q, a...) is ill-formed[.](#task.promise-15.4.sentence-3)
[🔗](#lib:operator_new,task::promise_type)
`template<class... Args>
void* operator new(size_t size, const Args&... args);
`
[16](#task.promise-16)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L7657)
If there is no parameter with type allocator_arg_t then letalloc be allocator_type()[.](#task.promise-16.sentence-1)
Otherwise, let arg_next be the parameter
following the first allocator_arg_t parameter,
and let alloc be allocator_type(arg_next)[.](#task.promise-16.sentence-2)
Let PAlloc be allocator_traits<allocator_type>::template rebind_alloc<U>, where U is an unspecified type
whose size and alignment are both __STDCPP_DEFAULT_NEW_ALIGNMENT__[.](#task.promise-16.sentence-3)
[17](#task.promise-17)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L7667)
*Mandates*:
- [(17.1)](#task.promise-17.1)
The first parameter of type allocator_arg_t (if any) is not the last parameter[.](#task.promise-17.1.sentence-1)
- [(17.2)](#task.promise-17.2)
allocator_type(arg_next) is a valid expression if there is a parameter
of type allocator_arg_t[.](#task.promise-17.2.sentence-1)
- [(17.3)](#task.promise-17.3)
allocator_traits<PAlloc>::pointer is a pointer type[.](#task.promise-17.3.sentence-1)
[18](#task.promise-18)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L7676)
*Effects*: Initializes an allocator palloc of type PAlloc withalloc[.](#task.promise-18.sentence-1)
Uses palloc to allocate storage for the
smallest array of U sufficient to provide storage for a
coroutine state of size size, and unspecified additional
state necessary to ensure that operator delete can later
deallocate this memory block with an allocator equal to palloc[.](#task.promise-18.sentence-2)
[19](#task.promise-19)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L7686)
*Returns*: A pointer to the allocated storage[.](#task.promise-19.sentence-1)
[🔗](#lib:operator_delete,task::promise_type)
`void operator delete(void* pointer, size_t size) noexcept;
`
[20](#task.promise-20)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L7696)
*Preconditions*: pointer was returned from an invocation of the above overload
of operator new with a size argument equal to size[.](#task.promise-20.sentence-1)
[21](#task.promise-21)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L7701)
*Effects*: Deallocates the storage pointed to by pointer using an
allocator equal to that used to allocate it[.](#task.promise-21.sentence-1)

View File

@@ -0,0 +1,410 @@
[exec.counting.scopes]
# 33 Execution control library [[exec]](./#exec)
## 33.14 Execution scope utilities [[exec.scope]](exec.scope#exec.counting.scopes)
### 33.14.2 Counting Scopes [exec.counting.scopes]
#### [33.14.2.1](#general) General [[exec.counting.scopes.general]](exec.counting.scopes.general)
[1](#general-1)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L7769)
Scopes of type simple_counting_scope and counting_scope maintain counts of associations[.](#general-1.sentence-1)
Let:
- [(1.1)](#general-1.1)
Scope be either simple_counting_scope or counting_scope,
- [(1.2)](#general-1.2)
scope be an object of type Scope,
- [(1.3)](#general-1.3)
tkn be an object of type Scope::token obtained from scope.get_token(),
- [(1.4)](#general-1.4)
jsndr be a sender obtained from scope.join(), and
- [(1.5)](#general-1.5)
op be an operation state obtained from
connecting jsndr to a receiver[.](#general-1.sentence-2)
During its lifetime scope goes through different states
which govern what operations are allowed and the result of these operations:
- [(1.6)](#general-1.6)
*unused*:
a newly constructed object starts in the *unused* state[.](#general-1.6.sentence-1)
- [(1.7)](#general-1.7)
*open*:
when tkn.try_associate() is called
while scope is in the *unused* state,scope moves to the *open* state[.](#general-1.7.sentence-1)
- [(1.8)](#general-1.8)
*open-and-joining*:
when the operation state op is started
while scope is in the *unused* or *open* state,scope moves to the *open-and-joining* state[.](#general-1.8.sentence-1)
- [(1.9)](#general-1.9)
*closed*:
when scope.close() is called
while scope is in the *open* state,scope moves to the *closed* state[.](#general-1.9.sentence-1)
- [(1.10)](#general-1.10)
*unused-and-closed*:
when scope.close() is called
while scope is in the *unused* state,scope moves to the *unused-and-closed* state[.](#general-1.10.sentence-1)
- [(1.11)](#general-1.11)
*closed-and-joining*:
when scope.close() is called
while scope is in the *open-and-joining* state or
the operation state op is started
while scope is in
the *closed* or *unused-and-closed* state,scope moves to the *closed-and-joining* state[.](#general-1.11.sentence-1)
- [(1.12)](#general-1.12)
*joined*:
when the count of associations drops to zero
while scope is in
the *open-and-joining* or *closed-and-joining* state,scope moves to the *joined* state[.](#general-1.12.sentence-1)
[2](#general-2)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L7836)
*Recommended practice*: For simple_counting_scope and counting_scope,
implementations should store the state and the count of associations
in a single member of type size_t[.](#general-2.sentence-1)
[3](#general-3)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L7842)
Subclause [exec.counting.scopes] makes use of
the following exposition-only entities:
struct *scope-join-t* {}; // *exposition only*enum *scope-state-type* { // *exposition only**unused*, // *exposition only**open*, // *exposition only**closed*, // *exposition only**open-and-joining*, // *exposition only**closed-and-joining*, // *exposition only**unused-and-closed*, // *exposition only**joined*, // *exposition only*};
[4](#general-4)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L7860)
The exposition-only class template *impls-for* ([[exec.snd.expos]](exec.snd.expos "33.9.2Exposition-only entities"))
is specialized for *scope-join-t* as follows:
namespace std::execution {template<>struct *impls-for*<*scope-join-t*> : *default-impls* {template<class Scope, class Rcvr>struct *state* { // *exposition only*struct *rcvr-t* { // *exposition only*using receiver_concept = receiver_t;
Rcvr& *rcvr*; // *exposition only*void set_value() && noexcept { execution::set_value(std::move(*rcvr*)); }template<class E>void set_error(E&& e) && noexcept { execution::set_error(std::move(*rcvr*), std::forward<E>(e)); }void set_stopped() && noexcept { execution::set_stopped(std::move(*rcvr*)); }decltype(auto) get_env() const noexcept {return execution::get_env(*rcvr*); }}; using *sched-sender* = // *exposition only*decltype(schedule(get_scheduler(get_env(declval<Rcvr&>())))); using *op-t* = // *exposition only* connect_result_t<*sched-sender*, *rcvr-t*>;
Scope* *scope*; // *exposition only* Rcvr& *receiver*; // *exposition only**op-t* *op*; // *exposition only**state*(Scope* scope, Rcvr& rcvr) // *exposition only*noexcept([*nothrow-callable*](functional.syn#concept:nothrow-callable "22.10.2Header <functional> synopsis[functional.syn]")<connect_t, *sched-sender*, *rcvr-t*>): *scope*(scope), *receiver*(rcvr), *op*(connect(schedule(get_scheduler(get_env(rcvr))), *rcvr-t*(rcvr))) {}void *complete*() noexcept { // *exposition only* start(*op*); }void *complete-inline*() noexcept { // *exposition only* set_value(std::move(*receiver*)); }}; static constexpr auto *get-state* = // *exposition only*[]<class Rcvr>(auto&& sender, Rcvr& receiver)noexcept(is_nothrow_constructible_v<*state*<Rcvr>, *data-type*<decltype(sender)>, Rcvr&>) {auto[_, self] = sender; return *state*(self, receiver); }; static constexpr auto *start* = // *exposition only*[](auto& s, auto&) noexcept {if (s.*scope*->*start-join-sender*(s)) s.*complete-inline*(); }; };}
#### [33.14.2.2](#exec.scope.simple.counting) Simple Counting Scope [[exec.scope.simple.counting]](exec.scope.simple.counting)
#### [33.14.2.2.1](#exec.scope.simple.counting.general) General [[exec.scope.simple.counting.general]](exec.scope.simple.counting.general)
[🔗](#lib:execution::simple_counting_scope)
namespace std::execution {class simple_counting_scope {public:// [[exec.simple.counting.token]](#exec.simple.counting.token "33.14.2.2.4Token"), tokenstruct token; static constexpr size_t max_associations = *implementation-defined*; // [[exec.simple.counting.ctor]](#exec.simple.counting.ctor "33.14.2.2.2Constructor and Destructor"), constructor and destructor simple_counting_scope() noexcept;
simple_counting_scope(simple_counting_scope&&) = delete; ~simple_counting_scope(); // [[exec.simple.counting.mem]](#exec.simple.counting.mem "33.14.2.2.3Members"), members token get_token() noexcept; void close() noexcept; [sender](exec.snd.concepts#concept:sender "33.9.3Sender concepts[exec.snd.concepts]") auto join() noexcept; private: size_t *count*; // *exposition only**scope-state-type* *state*; // *exposition only*bool *try-associate*() noexcept; // *exposition only*void *disassociate*() noexcept; // *exposition only*template<class State>bool *start-join-sender*(State& state) noexcept; // *exposition only*};}
[1](#exec.scope.simple.counting.general-1)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L7971)
For purposes of determining the existence of a data race,get_token,close,join,*try-associate*,*disassociate*, and*start-join-sender* behave as atomic operations ([[intro.multithread]](intro.multithread "6.10.2Multi-threaded executions and data races"))[.](#exec.scope.simple.counting.general-1.sentence-1)
These operations on a single object of
type simple_counting_scope appear to occur in a single total order[.](#exec.scope.simple.counting.general-1.sentence-2)
#### [33.14.2.2.2](#exec.simple.counting.ctor) Constructor and Destructor [[exec.simple.counting.ctor]](exec.simple.counting.ctor)
[🔗](#lib:execution::simple_counting_scope,constructor)
`simple_counting_scope() noexcept;
`
[1](#exec.simple.counting.ctor-1)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L7991)
*Postconditions*: *count* is 0 and *state* is *unused*[.](#exec.simple.counting.ctor-1.sentence-1)
[🔗](#lib:execution::simple_counting_scope,destructor)
`~simple_counting_scope();
`
[2](#exec.simple.counting.ctor-2)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L8002)
*Effects*: If *state* is not one of*joined*, *unused*, or *unused-and-closed*,
invokes terminate ([[except.terminate]](except.terminate "14.6.2The std::terminate function"))[.](#exec.simple.counting.ctor-2.sentence-1)
Otherwise, has no effects[.](#exec.simple.counting.ctor-2.sentence-2)
#### [33.14.2.2.3](#exec.simple.counting.mem) Members [[exec.simple.counting.mem]](exec.simple.counting.mem)
[🔗](#lib:get_token,execution::simple_counting_scope)
`token get_token() noexcept;
`
[1](#exec.simple.counting.mem-1)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L8018)
*Returns*: An object t of type simple_counting_scope::token such thatt.*scope* == this is true[.](#exec.simple.counting.mem-1.sentence-1)
[🔗](#lib:close,execution::simple_counting_scope)
`void close() noexcept;
`
[2](#exec.simple.counting.mem-2)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L8030)
*Effects*: If *state* is
- [(2.1)](#exec.simple.counting.mem-2.1)
*unused*, then changes *state* to *unused-and-closed*;
- [(2.2)](#exec.simple.counting.mem-2.2)
*open*, then changes *state* to *closed*;
- [(2.3)](#exec.simple.counting.mem-2.3)
*open-and-joining*,
then changes *state* to *closed-and-joining*;
- [(2.4)](#exec.simple.counting.mem-2.4)
otherwise, no effects[.](#exec.simple.counting.mem-2.sentence-1)
[3](#exec.simple.counting.mem-3)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L8045)
*Postconditions*: Any subsequent call to *try-associate*() on *this returns false[.](#exec.simple.counting.mem-3.sentence-1)
[🔗](#lib:join,execution::simple_counting_scope)
`[sender](exec.snd.concepts#concept:sender "33.9.3Sender concepts[exec.snd.concepts]") auto join() noexcept;
`
[4](#exec.simple.counting.mem-4)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L8057)
*Returns*: *make-sender*(*scope-join-t*(), this)[.](#exec.simple.counting.mem-4.sentence-1)
[🔗](#lib:try-associate,execution::simple_counting_scope)
`bool try-associate() noexcept;
`
[5](#exec.simple.counting.mem-5)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L8068)
*Effects*: If *count* is equal to max_associations, then no effects[.](#exec.simple.counting.mem-5.sentence-1)
Otherwise, if *state* is
- [(5.1)](#exec.simple.counting.mem-5.1)
*unused*,
then increments *count* and changes *state* to *open*;
- [(5.2)](#exec.simple.counting.mem-5.2)
*open* or *open-and-joining*,
then increments *count*;
- [(5.3)](#exec.simple.counting.mem-5.3)
otherwise, no effects[.](#exec.simple.counting.mem-5.sentence-2)
[6](#exec.simple.counting.mem-6)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L8083)
*Returns*: true if *count* was incremented, false otherwise[.](#exec.simple.counting.mem-6.sentence-1)
[🔗](#lib:disassociate,execution::simple_counting_scope)
`void disassociate() noexcept;
`
[7](#exec.simple.counting.mem-7)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L8094)
*Preconditions*: *count* is greater than zero[.](#exec.simple.counting.mem-7.sentence-1)
[8](#exec.simple.counting.mem-8)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L8098)
*Effects*: Decrements *count*[.](#exec.simple.counting.mem-8.sentence-1)
If *count* is zero after decrementing and*state* is *open-and-joining* or *closed-and-joining*,
changes *state* to *joined* and
calls *complete*() on all objects registered with *this[.](#exec.simple.counting.mem-8.sentence-2)
[*Note [1](#exec.simple.counting.mem-note-1)*:
Calling *complete*() on any registered object
can cause *this to be destroyed[.](#exec.simple.counting.mem-8.sentence-3)
— *end note*]
[🔗](#lib:start-join-sender,execution::simple_counting_scope)
`template<class State>
bool start-join-sender(State& st) noexcept;
`
[9](#exec.simple.counting.mem-9)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L8118)
*Effects*: If *state* is
- [(9.1)](#exec.simple.counting.mem-9.1)
*unused*, *unused-and-closed*, or *joined*, then
changes *state* to *joined* and returns true;
- [(9.2)](#exec.simple.counting.mem-9.2)
*open* or *open-and-joining*, then
changes *state* to *open-and-joining*,
registers st with *this and returns false;
- [(9.3)](#exec.simple.counting.mem-9.3)
*closed* or *closed-and-joining*, then
changes *state* to *closed-and-joining*,
registers st with *this and returns false[.](#exec.simple.counting.mem-9.sentence-1)
#### [33.14.2.2.4](#exec.simple.counting.token) Token [[exec.simple.counting.token]](exec.simple.counting.token)
[🔗](#lib:execution::simple_counting_scope::token)
namespace std::execution {struct simple_counting_scope::token {template<[sender](exec.snd.concepts#concept:sender "33.9.3Sender concepts[exec.snd.concepts]") Sender> Sender&& wrap(Sender&& snd) const noexcept; bool try_associate() const noexcept; void disassociate() const noexcept; private: simple_counting_scope* *scope*; // *exposition only*};}
[🔗](#lib:wrap,execution::simple_counting_scope::token)
`template<[sender](exec.snd.concepts#concept:sender "33.9.3Sender concepts[exec.snd.concepts]") Sender>
Sender&& wrap(Sender&& snd) const noexcept;
`
[1](#exec.simple.counting.token-1)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L8160)
*Returns*: std::forward<Sender>(snd)[.](#exec.simple.counting.token-1.sentence-1)
[🔗](#lib:try_associate,execution::simple_counting_scope::token)
`bool try_associate() const noexcept;
`
[2](#exec.simple.counting.token-2)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L8171)
*Effects*: Equivalent to: return *scope*->*try-associate*();
[🔗](#lib:disassociate,execution::simple_counting_scope::token)
`void disassociate() const noexcept;
`
[3](#exec.simple.counting.token-3)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L8182)
*Effects*: Equivalent to *scope*->*disassociate*()[.](#exec.simple.counting.token-3.sentence-1)
#### [33.14.2.3](#exec.scope.counting) Counting Scope [[exec.scope.counting]](exec.scope.counting)
[🔗](#lib:execution::counting_scope)
namespace std::execution {class counting_scope {public:struct token {template<[sender](exec.snd.concepts#concept:sender "33.9.3Sender concepts[exec.snd.concepts]") Sender>[sender](exec.snd.concepts#concept:sender "33.9.3Sender concepts[exec.snd.concepts]") auto wrap(Sender&& snd) const noexcept(*see below*); bool try_associate() const noexcept; void disassociate() const noexcept; private: counting_scope* *scope*; // *exposition only*}; static constexpr size_t max_associations = *implementation-defined*;
counting_scope() noexcept;
counting_scope(counting_scope&&) = delete; ~counting_scope();
token get_token() noexcept; void close() noexcept; [sender](exec.snd.concepts#concept:sender "33.9.3Sender concepts[exec.snd.concepts]") auto join() noexcept; void request_stop() noexcept; private: size_t *count*; // *exposition only**scope-state-type* *state*; // *exposition only* inplace_stop_source *s_source*; // *exposition only*bool *try-associate*() noexcept; // *exposition only*void *disassociate*() noexcept; // *exposition only*template<class State>bool *start-join-sender*(State& state) noexcept; // *exposition only*};}
[1](#exec.scope.counting-1)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L8230)
counting_scope differs from simple_counting_scope by
adding support for cancellation[.](#exec.scope.counting-1.sentence-1)
Unless specified below, the semantics of members of counting_scope are the same as the corresponding members of simple_counting_scope[.](#exec.scope.counting-1.sentence-2)
[🔗](#lib:get_token,execution::counting_scope)
`token get_token() noexcept;
`
[2](#exec.scope.counting-2)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L8242)
*Returns*: An object t of type counting_scope::token such thatt.*scope* == this is true[.](#exec.scope.counting-2.sentence-1)
[🔗](#lib:request_stop,execution::counting_scope)
`void request_stop() noexcept;
`
[3](#exec.scope.counting-3)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L8254)
*Effects*: Equivalent to *s_source*.request_stop()[.](#exec.scope.counting-3.sentence-1)
[4](#exec.scope.counting-4)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L8258)
*Remarks*: Calls to request_stop do not introduce data races[.](#exec.scope.counting-4.sentence-1)
[🔗](#lib:wrap,execution::counting_scope::token)
`template<[sender](exec.snd.concepts#concept:sender "33.9.3Sender concepts[exec.snd.concepts]") Sender>
[sender](exec.snd.concepts#concept:sender "33.9.3Sender concepts[exec.snd.concepts]") auto counting_scope::token::wrap(Sender&& snd) const
noexcept(is_nothrow_constructible_v<remove_cvref_t<Sender>, Sender>);
`
[5](#exec.scope.counting-5)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L8271)
*Effects*: Equivalent to:return *stop-when*(std::forward<Sender>(snd), *scope*->*s_source*.get_token());

View File

@@ -0,0 +1,116 @@
[exec.counting.scopes.general]
# 33 Execution control library [[exec]](./#exec)
## 33.14 Execution scope utilities [[exec.scope]](exec.scope#exec.counting.scopes.general)
### 33.14.2 Counting Scopes [[exec.counting.scopes]](exec.counting.scopes#general)
#### 33.14.2.1 General [exec.counting.scopes.general]
[1](#1)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L7769)
Scopes of type simple_counting_scope and counting_scope maintain counts of associations[.](#1.sentence-1)
Let:
- [(1.1)](#1.1)
Scope be either simple_counting_scope or counting_scope,
- [(1.2)](#1.2)
scope be an object of type Scope,
- [(1.3)](#1.3)
tkn be an object of type Scope::token obtained from scope.get_token(),
- [(1.4)](#1.4)
jsndr be a sender obtained from scope.join(), and
- [(1.5)](#1.5)
op be an operation state obtained from
connecting jsndr to a receiver[.](#1.sentence-2)
During its lifetime scope goes through different states
which govern what operations are allowed and the result of these operations:
- [(1.6)](#1.6)
*unused*:
a newly constructed object starts in the *unused* state[.](#1.6.sentence-1)
- [(1.7)](#1.7)
*open*:
when tkn.try_associate() is called
while scope is in the *unused* state,scope moves to the *open* state[.](#1.7.sentence-1)
- [(1.8)](#1.8)
*open-and-joining*:
when the operation state op is started
while scope is in the *unused* or *open* state,scope moves to the *open-and-joining* state[.](#1.8.sentence-1)
- [(1.9)](#1.9)
*closed*:
when scope.close() is called
while scope is in the *open* state,scope moves to the *closed* state[.](#1.9.sentence-1)
- [(1.10)](#1.10)
*unused-and-closed*:
when scope.close() is called
while scope is in the *unused* state,scope moves to the *unused-and-closed* state[.](#1.10.sentence-1)
- [(1.11)](#1.11)
*closed-and-joining*:
when scope.close() is called
while scope is in the *open-and-joining* state or
the operation state op is started
while scope is in
the *closed* or *unused-and-closed* state,scope moves to the *closed-and-joining* state[.](#1.11.sentence-1)
- [(1.12)](#1.12)
*joined*:
when the count of associations drops to zero
while scope is in
the *open-and-joining* or *closed-and-joining* state,scope moves to the *joined* state[.](#1.12.sentence-1)
[2](#2)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L7836)
*Recommended practice*: For simple_counting_scope and counting_scope,
implementations should store the state and the count of associations
in a single member of type size_t[.](#2.sentence-1)
[3](#3)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L7842)
Subclause [[exec.counting.scopes]](exec.counting.scopes "33.14.2Counting Scopes") makes use of
the following exposition-only entities:
struct *scope-join-t* {}; // *exposition only*enum *scope-state-type* { // *exposition only**unused*, // *exposition only**open*, // *exposition only**closed*, // *exposition only**open-and-joining*, // *exposition only**closed-and-joining*, // *exposition only**unused-and-closed*, // *exposition only**joined*, // *exposition only*};
[4](#4)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L7860)
The exposition-only class template *impls-for* ([[exec.snd.expos]](exec.snd.expos "33.9.2Exposition-only entities"))
is specialized for *scope-join-t* as follows:
namespace std::execution {template<>struct *impls-for*<*scope-join-t*> : *default-impls* {template<class Scope, class Rcvr>struct *state* { // *exposition only*struct *rcvr-t* { // *exposition only*using receiver_concept = receiver_t;
Rcvr& *rcvr*; // *exposition only*void set_value() && noexcept { execution::set_value(std::move(*rcvr*)); }template<class E>void set_error(E&& e) && noexcept { execution::set_error(std::move(*rcvr*), std::forward<E>(e)); }void set_stopped() && noexcept { execution::set_stopped(std::move(*rcvr*)); }decltype(auto) get_env() const noexcept {return execution::get_env(*rcvr*); }}; using *sched-sender* = // *exposition only*decltype(schedule(get_scheduler(get_env(declval<Rcvr&>())))); using *op-t* = // *exposition only* connect_result_t<*sched-sender*, *rcvr-t*>;
Scope* *scope*; // *exposition only* Rcvr& *receiver*; // *exposition only**op-t* *op*; // *exposition only**state*(Scope* scope, Rcvr& rcvr) // *exposition only*noexcept([*nothrow-callable*](functional.syn#concept:nothrow-callable "22.10.2Header <functional> synopsis[functional.syn]")<connect_t, *sched-sender*, *rcvr-t*>): *scope*(scope), *receiver*(rcvr), *op*(connect(schedule(get_scheduler(get_env(rcvr))), *rcvr-t*(rcvr))) {}void *complete*() noexcept { // *exposition only* start(*op*); }void *complete-inline*() noexcept { // *exposition only* set_value(std::move(*receiver*)); }}; static constexpr auto *get-state* = // *exposition only*[]<class Rcvr>(auto&& sender, Rcvr& receiver)noexcept(is_nothrow_constructible_v<*state*<Rcvr>, *data-type*<decltype(sender)>, Rcvr&>) {auto[_, self] = sender; return *state*(self, receiver); }; static constexpr auto *start* = // *exposition only*[](auto& s, auto&) noexcept {if (s.*scope*->*start-join-sender*(s)) s.*complete-inline*(); }; };}

298
cppdraft/exec/ctx.md Normal file
View File

@@ -0,0 +1,298 @@
[exec.ctx]
# 33 Execution control library [[exec]](./#exec)
## 33.12 Execution contexts [exec.ctx]
### [33.12.1](#exec.run.loop) execution::run_loop [[exec.run.loop]](exec.run.loop)
#### [33.12.1.1](#exec.run.loop.general) General [[exec.run.loop.general]](exec.run.loop.general)
[1](#exec.run.loop.general-1)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6321)
A run_loop is an execution resource on which work can be scheduled[.](#exec.run.loop.general-1.sentence-1)
It maintains a thread-safe first-in-first-out queue of work[.](#exec.run.loop.general-1.sentence-2)
Its run member function removes elements from the queue and
executes them in a loop on the thread of execution that calls run[.](#exec.run.loop.general-1.sentence-3)
[2](#exec.run.loop.general-2)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6327)
A run_loop instance has an associated [*count*](#def:count "33.12.1.1General[exec.run.loop.general]") that corresponds to the number of work items that are in its queue[.](#exec.run.loop.general-2.sentence-1)
Additionally, a run_loop instance has an associated state
that can be one of[*starting*](#def:starting "33.12.1.1General[exec.run.loop.general]"), [*running*](#def:running "33.12.1.1General[exec.run.loop.general]"), [*finishing*](#def:finishing "33.12.1.1General[exec.run.loop.general]"), or [*finished*](#def:finished "33.12.1.1General[exec.run.loop.general]")[.](#exec.run.loop.general-2.sentence-2)
[3](#exec.run.loop.general-3)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6334)
Concurrent invocations of the member functions of run_loop other than run and its destructor do not introduce data races[.](#exec.run.loop.general-3.sentence-1)
The member functions*pop-front*, *push-back*, and finish execute atomically[.](#exec.run.loop.general-3.sentence-2)
[4](#exec.run.loop.general-4)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6341)
*Recommended practice*: Implementations should use an intrusive queue of operation states
to hold the work units to make scheduling allocation-free[.](#exec.run.loop.general-4.sentence-1)
namespace std::execution {class [run_loop](#lib:run_loop "33.12.1.1General[exec.run.loop.general]") {// [[exec.run.loop.types]](#exec.run.loop.types "33.12.1.2Associated types"), associated typesclass *run-loop-scheduler*; // *exposition only*class *run-loop-sender*; // *exposition only*struct *run-loop-opstate-base* { // *exposition only*virtual void *execute*() = 0; // *exposition only* run_loop* *loop*; // *exposition only**run-loop-opstate-base** *next*; // *exposition only*}; template<class Rcvr>using *run-loop-opstate* = *unspecified*; // *exposition only*// [[exec.run.loop.members]](#exec.run.loop.members "33.12.1.4Member functions"), member functions*run-loop-opstate-base** *pop-front*(); // *exposition only*void *push-back*(*run-loop-opstate-base**); // *exposition only*public:// [[exec.run.loop.ctor]](#exec.run.loop.ctor "33.12.1.3Constructor and destructor"), constructor and destructor run_loop() noexcept;
run_loop(run_loop&&) = delete; ~run_loop(); // [[exec.run.loop.members]](#exec.run.loop.members "33.12.1.4Member functions"), member functions*run-loop-scheduler* get_scheduler(); void run(); void finish(); };}
#### [33.12.1.2](#exec.run.loop.types) Associated types [[exec.run.loop.types]](exec.run.loop.types)
[🔗](#exec.run.loop.types-itemdecl:1)
`class run-loop-scheduler;
`
[1](#exec.run.loop.types-1)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6384)
*run-loop-scheduler* is an unspecified type
that models [scheduler](exec.sched#concept:scheduler "33.6Schedulers[exec.sched]")[.](#exec.run.loop.types-1.sentence-1)
[2](#exec.run.loop.types-2)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6388)
Instances of *run-loop-scheduler* remain valid
until the end of the lifetime of the run_loop instance
from which they were obtained[.](#exec.run.loop.types-2.sentence-1)
[3](#exec.run.loop.types-3)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6393)
Two instances of *run-loop-scheduler* compare equal
if and only if they were obtained from the same run_loop instance[.](#exec.run.loop.types-3.sentence-1)
[4](#exec.run.loop.types-4)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6397)
Let *sch* be an expression of type *run-loop-scheduler*[.](#exec.run.loop.types-4.sentence-1)
The expression schedule(*sch*) has type *run-loop-
sender* and
is not potentially-throwing if *sch* is not potentially-throwing[.](#exec.run.loop.types-4.sentence-2)
[🔗](#exec.run.loop.types-itemdecl:2)
`class run-loop-sender;
`
[5](#exec.run.loop.types-5)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6407)
*run-loop-sender* is an exposition-only type
that satisfies [sender](exec.snd.concepts#concept:sender "33.9.3Sender concepts[exec.snd.concepts]")[.](#exec.run.loop.types-5.sentence-1)
completion_signatures_of_t<*run-
loop-sender*> iscompletion_signatures<set_value_t(), set_error_t(exception_ptr), set_stopped_t()>
[6](#exec.run.loop.types-6)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6415)
An instance of *run-loop-sender* remains valid
until the end of the lifetime of its associated run_loop instance[.](#exec.run.loop.types-6.sentence-1)
[7](#exec.run.loop.types-7)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6419)
Let *sndr* be an expression of type *run-loop-sender*,
let *rcvr* be an expression
such that [receiver_of](exec.recv.concepts#concept:receiver_of "33.7.1Receiver concepts[exec.recv.concepts]")<decltype((*rcvr*)), CS> is true where CS is the completion_signatures specialization above[.](#exec.run.loop.types-7.sentence-1)
Let C be either set_value_t or set_stopped_t[.](#exec.run.loop.types-7.sentence-2)
Then:
- [(7.1)](#exec.run.loop.types-7.1)
The expression connect(*sndr*, *rcvr*) has type *run-loop-opstate*<decay_t<decltype((*rcvr*))>> and is potentially-throwing if and only if(void(*sndr*), auto(*rcvr*)) is potentially-throwing[.](#exec.run.loop.types-7.1.sentence-1)
- [(7.2)](#exec.run.loop.types-7.2)
The expression get_completion_scheduler<C>(get_env(*sndr*)) is potentially-throwing if and only if *sndr* is potentially-throwing,
has type *run-loop-scheduler*, and
compares equal to the *run-loop-
scheduler* instance
from which *sndr* was obtained[.](#exec.run.loop.types-7.2.sentence-1)
[🔗](#exec.run.loop.types-itemdecl:3)
`template<class Rcvr>
struct run-loop-opstate;
`
[8](#exec.run.loop.types-8)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6445)
*run-loop-opstate*<Rcvr> inherits privately and unambiguously from *run-loop-opstate-base*[.](#exec.run.loop.types-8.sentence-1)
[9](#exec.run.loop.types-9)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6449)
Let o be a non-const lvalue of type *run-loop-opstate*<Rcvr>,
and let REC(o) be a non-const lvalue reference to an instance of type Rcvr that was initialized with the expression *rcvr* passed to the invocation of connect that returned o[.](#exec.run.loop.types-9.sentence-1)
Then:
- [(9.1)](#exec.run.loop.types-9.1)
The object to which *REC*(o) refers
remains valid for the lifetime of the object to which o refers.
- [(9.2)](#exec.run.loop.types-9.2)
The type *run-loop-opstate*<Rcvr> overrides*run-loop-opstate-base*::*execute*() such that o.*execute*() is equivalent to:if (get_stop_token(*REC*(o)).stop_requested()) { set_stopped(std::move(*REC*(o)));} else { set_value(std::move(*REC*(o)));}
- [(9.3)](#exec.run.loop.types-9.3)
The expression start(o) is equivalent to:try {o.*loop*->*push-back*(addressof(o));} catch(...) { set_error(std::move(*REC*(o)), current_exception());}
#### [33.12.1.3](#exec.run.loop.ctor) Constructor and destructor [[exec.run.loop.ctor]](exec.run.loop.ctor)
[🔗](#lib:run_loop,constructor)
`run_loop() noexcept;
`
[1](#exec.run.loop.ctor-1)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6489)
*Postconditions*: *count* is 0 and *state* is *starting*[.](#exec.run.loop.ctor-1.sentence-1)
[🔗](#lib:run_loop,destructor)
`~run_loop();
`
[2](#exec.run.loop.ctor-2)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6500)
*Effects*: If *count* is not 0 or if *state* is *running*,
invokes terminate ([[except.terminate]](except.terminate "14.6.2The std::terminate function"))[.](#exec.run.loop.ctor-2.sentence-1)
Otherwise, has no effects[.](#exec.run.loop.ctor-2.sentence-2)
#### [33.12.1.4](#exec.run.loop.members) Member functions [[exec.run.loop.members]](exec.run.loop.members)
[🔗](#exec.run.loop.members-itemdecl:1)
`run-loop-opstate-base* pop-front();
`
[1](#exec.run.loop.members-1)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6514)
*Effects*: Blocks ([[defns.block]](defns.block "3.6block")) until one of the following conditions is true:
- [(1.1)](#exec.run.loop.members-1.1)
*count* is 0 and *state* is *finishing*,
in which case *pop-front* sets *state* to *finished* and returns nullptr; or
- [(1.2)](#exec.run.loop.members-1.2)
*count* is greater than 0,
in which case an item is removed from the front of the queue,*count* is decremented by 1, and
the removed item is returned[.](#exec.run.loop.members-1.sentence-1)
[🔗](#exec.run.loop.members-itemdecl:2)
`void push-back(run-loop-opstate-base* item);
`
[2](#exec.run.loop.members-2)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6535)
*Effects*: Adds item to the back of the queue and
increments *count* by 1[.](#exec.run.loop.members-2.sentence-1)
[3](#exec.run.loop.members-3)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6540)
*Synchronization*: This operation synchronizes with
the *pop-front* operation that obtains item[.](#exec.run.loop.members-3.sentence-1)
[🔗](#lib:get_scheduler,run_loop)
`run-loop-scheduler get_scheduler();
`
[4](#exec.run.loop.members-4)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6552)
*Returns*: An instance of *run-loop-scheduler* that can be used to schedule work onto this run_loop instance[.](#exec.run.loop.members-4.sentence-1)
[🔗](#lib:run,run_loop)
`void run();
`
[5](#exec.run.loop.members-5)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6564)
*Preconditions*: *state* is either *starting* or *finishing*[.](#exec.run.loop.members-5.sentence-1)
[6](#exec.run.loop.members-6)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6568)
*Effects*: If *state* is *starting*,
sets the *state* to *running*,
otherwise leaves *state* unchanged[.](#exec.run.loop.members-6.sentence-1)
Then, equivalent to:while (auto* op = *pop-front*()) { op->*execute*();}
[7](#exec.run.loop.members-7)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6580)
*Remarks*: When *state* changes, it does so without introducing data races[.](#exec.run.loop.members-7.sentence-1)
[🔗](#lib:finish,run_loop)
`void finish();
`
[8](#exec.run.loop.members-8)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6591)
*Preconditions*: *state* is either *starting* or *running*[.](#exec.run.loop.members-8.sentence-1)
[9](#exec.run.loop.members-9)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6595)
*Effects*: Changes *state* to *finishing*[.](#exec.run.loop.members-9.sentence-1)
[10](#exec.run.loop.members-10)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6599)
*Synchronization*: finish synchronizes with the *pop-front* operation
that returns nullptr[.](#exec.run.loop.members-10.sentence-1)

View File

@@ -0,0 +1,96 @@
[exec.domain.default]
# 33 Execution control library [[exec]](./#exec)
## 33.9 Senders [[exec.snd]](exec.snd#exec.domain.default)
### 33.9.5 execution::default_domain [exec.domain.default]
[1](#1)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2425)
namespace std::execution {struct [default_domain](#lib:default_domain "33.9.5execution::default_­domain[exec.domain.default]") {template<[sender](exec.snd.concepts#concept:sender "33.9.3Sender concepts[exec.snd.concepts]") Sndr, [*queryable*](exec.queryable.concept#concept:queryable "33.2.2queryable concept[exec.queryable.concept]")... Env>requires (sizeof...(Env) <= 1)static constexpr [sender](exec.snd.concepts#concept:sender "33.9.3Sender concepts[exec.snd.concepts]") decltype(auto) transform_sender(Sndr&& sndr, const Env&... env)noexcept(*see below*); template<[sender](exec.snd.concepts#concept:sender "33.9.3Sender concepts[exec.snd.concepts]") Sndr, [*queryable*](exec.queryable.concept#concept:queryable "33.2.2queryable concept[exec.queryable.concept]") Env>static constexpr [*queryable*](exec.queryable.concept#concept:queryable "33.2.2queryable concept[exec.queryable.concept]") decltype(auto) transform_env(Sndr&& sndr, Env&& env) noexcept; template<class Tag, [sender](exec.snd.concepts#concept:sender "33.9.3Sender concepts[exec.snd.concepts]") Sndr, class... Args>static constexpr decltype(auto) apply_sender(Tag, Sndr&& sndr, Args&&... args)noexcept(*see below*); };}
[🔗](#lib:transform_sender,default_domain)
`template<[sender](exec.snd.concepts#concept:sender "33.9.3Sender concepts[exec.snd.concepts]") Sndr, [queryable](exec.queryable.concept#concept:queryable "33.2.2queryable concept[exec.queryable.concept]")... Env>
requires (sizeof...(Env) <= 1)
constexpr [sender](exec.snd.concepts#concept:sender "33.9.3Sender concepts[exec.snd.concepts]") decltype(auto) transform_sender(Sndr&& sndr, const Env&... env)
noexcept(see below);
`
[2](#2)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2453)
Let e be the expressiontag_of_t<Sndr>().transform_sender(std::forward<Sndr>(sndr), env...) if that expression is well-formed;
otherwise, std::forward<Sndr>(sndr)[.](#2.sentence-1)
[3](#3)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2461)
*Returns*: e[.](#3.sentence-1)
[4](#4)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2465)
*Remarks*: The exception specification is equivalent to noexcept(e)[.](#4.sentence-1)
[🔗](#lib:transform_env,default_domain)
`template<[sender](exec.snd.concepts#concept:sender "33.9.3Sender concepts[exec.snd.concepts]") Sndr, [queryable](exec.queryable.concept#concept:queryable "33.2.2queryable concept[exec.queryable.concept]") Env>
constexpr [queryable](exec.queryable.concept#concept:queryable "33.2.2queryable concept[exec.queryable.concept]") decltype(auto) transform_env(Sndr&& sndr, Env&& env) noexcept;
`
[5](#5)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2477)
Let e be the expressiontag_of_t<Sndr>().transform_env(std::forward<Sndr>(sndr), std::forward<Env>(env)) if that expression is well-formed;
otherwise, *FWD-ENV*(std::forward<Env>(env))[.](#5.sentence-1)
[6](#6)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2485)
*Mandates*: noexcept(e) is true[.](#6.sentence-1)
[7](#7)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2489)
*Returns*: e[.](#7.sentence-1)
[🔗](#lib:apply_sender,default_domain)
`template<class Tag, [sender](exec.snd.concepts#concept:sender "33.9.3Sender concepts[exec.snd.concepts]") Sndr, class... Args>
constexpr decltype(auto) apply_sender(Tag, Sndr&& sndr, Args&&... args)
noexcept(see below);
`
[8](#8)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2502)
Let e be the expression Tag().apply_sender(std::forward<Sndr>(sndr), std::forward<Args>(args)...)
[9](#9)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2508)
*Constraints*: e is a well-formed expression[.](#9.sentence-1)
[10](#10)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2512)
*Returns*: e[.](#10.sentence-1)
[11](#11)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2516)
*Remarks*: The exception specification is equivalent to noexcept(e)[.](#11.sentence-1)

80
cppdraft/exec/env.md Normal file
View File

@@ -0,0 +1,80 @@
[exec.env]
# 33 Execution control library [[exec]](./#exec)
## 33.11 Queryable utilities [[exec.envs]](exec.envs#exec.env)
### 33.11.2 Class template env [exec.env]
namespace std::execution {template<[*queryable*](exec.queryable.concept#concept:queryable "33.2.2queryable concept[exec.queryable.concept]")... Envs>struct [env](#lib:env "33.11.2Class template env[exec.env]") { Envs0 envs0; // *exposition only* Envs1 envs1; // *exposition only*
Envsn−1 envsn−1; // *exposition only*template<class QueryTag>constexpr decltype(auto) query(QueryTag q) const noexcept(*see below*); }; template<class... Envs> env(Envs...) -> env<unwrap_reference_t<Envs>...>;}
[1](#1)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6246)
The class template env is used to construct a queryable object
from several queryable objects[.](#1.sentence-1)
Query invocations on the resulting object are resolved
by attempting to query each subobject in lexical order[.](#1.sentence-2)
[2](#2)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6252)
Specializations of env are not assignable[.](#2.sentence-1)
[3](#3)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6255)
It is unspecified
whether env supports initialization
using a parenthesized [*expression-list*](expr.post.general#nt:expression-list "7.6.1.1General[expr.post.general]") ([[dcl.init]](dcl.init "9.5Initializers")),
unless the [*expression-list*](expr.post.general#nt:expression-list "7.6.1.1General[expr.post.general]") consist of
a single element of type (possibly const) env[.](#3.sentence-1)
[4](#4)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6262)
[*Example [1](#example-1)*: template<[sender](exec.snd.concepts#concept:sender "33.9.3Sender concepts[exec.snd.concepts]") Sndr> sender auto parameterize_work(Sndr sndr) {// Make an environment such that:// get_allocator(env) returns a reference to a copy of my_alloc{}// get_scheduler(env) returns a reference to a copy of my_sched{}auto e = env{prop(get_allocator, my_alloc{}),
prop(get_scheduler, my_sched{})}; // Parameterize the input sender so that it will use our custom execution environment.return write_env(sndr, e);} — *end example*]
[🔗](#lib:query,env)
`template<class QueryTag>
constexpr decltype(auto) query(QueryTag q) const noexcept(see below);
`
[5](#5)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6286)
Let [*has-query*](#concept:has-query "33.11.2Class template env[exec.env]") be the following exposition-only concept:template<class Env, class QueryTag>concept [*has-query*](#concept:has-query "33.11.2Class template env[exec.env]") = // *exposition only*requires (const Env& env) { env.query(QueryTag()); };
[6](#6)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6296)
Let *fe* be the first element ofenvs0, envs1, …, envsn−1 such that the expression *fe*.query(q) is well-formed[.](#6.sentence-1)
[7](#7)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6301)
*Constraints*: ([*has-query*](#concept:has-query "33.11.2Class template env[exec.env]")<Envs, QueryTag> || ...) is true[.](#7.sentence-1)
[8](#8)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6305)
*Effects*: Equivalent to: return *fe*.query(q);
[9](#9)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6309)
*Remarks*: The expression in the noexcept clause is equivalent
to noexcept(*fe*.query(q))[.](#9.sentence-1)

110
cppdraft/exec/envs.md Normal file
View File

@@ -0,0 +1,110 @@
[exec.envs]
# 33 Execution control library [[exec]](./#exec)
## 33.11 Queryable utilities [exec.envs]
### [33.11.1](#exec.prop) Class template prop [[exec.prop]](exec.prop)
namespace std::execution {template<class QueryTag, class ValueType>struct [prop](#lib:prop "33.11.1Class template prop[exec.prop]") { QueryTag *query_*; // *exposition only* ValueType *value_*; // *exposition only*constexpr const ValueType& query(QueryTag) const noexcept {return *value_*; }}; template<class QueryTag, class ValueType> prop(QueryTag, ValueType) -> prop<QueryTag, unwrap_reference_t<ValueType>>;}
[1](#exec.prop-1)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6193)
Class template prop is for building a queryable object
from a query object and a value[.](#exec.prop-1.sentence-1)
[2](#exec.prop-2)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6197)
*Mandates*: [*callable*](functional.syn#concept:callable "22.10.2Header <functional> synopsis[functional.syn]")<QueryTag, *prop-like*<ValueType>> is modeled,
where *prop-like* is the following exposition-only class template:template<class ValueType>struct *prop-like* { // *exposition only*const ValueType& query(auto) const noexcept;};
[3](#exec.prop-3)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6209)
[*Example [1](#exec.prop-example-1)*: template<[sender](exec.snd.concepts#concept:sender "33.9.3Sender concepts[exec.snd.concepts]") Sndr> sender auto parameterize_work(Sndr sndr) {// Make an environment such that get_allocator(env) returns a reference to a copy of my_alloc{}.auto e = prop(get_allocator, my_alloc{}); // Parameterize the input sender so that it will use our custom execution environment.return write_env(sndr, e);} — *end example*]
[4](#exec.prop-4)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6223)
Specializations of prop are not assignable[.](#exec.prop-4.sentence-1)
### [33.11.2](#exec.env) Class template env [[exec.env]](exec.env)
namespace std::execution {template<[*queryable*](exec.queryable.concept#concept:queryable "33.2.2queryable concept[exec.queryable.concept]")... Envs>struct [env](#lib:env "33.11.2Class template env[exec.env]") { Envs0 envs0; // *exposition only* Envs1 envs1; // *exposition only*
Envsn−1 envsn−1; // *exposition only*template<class QueryTag>constexpr decltype(auto) query(QueryTag q) const noexcept(*see below*); }; template<class... Envs> env(Envs...) -> env<unwrap_reference_t<Envs>...>;}
[1](#exec.env-1)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6246)
The class template env is used to construct a queryable object
from several queryable objects[.](#exec.env-1.sentence-1)
Query invocations on the resulting object are resolved
by attempting to query each subobject in lexical order[.](#exec.env-1.sentence-2)
[2](#exec.env-2)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6252)
Specializations of env are not assignable[.](#exec.env-2.sentence-1)
[3](#exec.env-3)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6255)
It is unspecified
whether env supports initialization
using a parenthesized [*expression-list*](expr.post.general#nt:expression-list "7.6.1.1General[expr.post.general]") ([[dcl.init]](dcl.init "9.5Initializers")),
unless the [*expression-list*](expr.post.general#nt:expression-list "7.6.1.1General[expr.post.general]") consist of
a single element of type (possibly const) env[.](#exec.env-3.sentence-1)
[4](#exec.env-4)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6262)
[*Example [1](#exec.env-example-1)*: template<[sender](exec.snd.concepts#concept:sender "33.9.3Sender concepts[exec.snd.concepts]") Sndr> sender auto parameterize_work(Sndr sndr) {// Make an environment such that:// get_allocator(env) returns a reference to a copy of my_alloc{}// get_scheduler(env) returns a reference to a copy of my_sched{}auto e = env{prop(get_allocator, my_alloc{}),
prop(get_scheduler, my_sched{})}; // Parameterize the input sender so that it will use our custom execution environment.return write_env(sndr, e);} — *end example*]
[🔗](#lib:query,env)
`template<class QueryTag>
constexpr decltype(auto) query(QueryTag q) const noexcept(see below);
`
[5](#exec.env-5)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6286)
Let [*has-query*](#concept:has-query "33.11.2Class template env[exec.env]") be the following exposition-only concept:template<class Env, class QueryTag>concept [*has-query*](#concept:has-query "33.11.2Class template env[exec.env]") = // *exposition only*requires (const Env& env) { env.query(QueryTag()); };
[6](#exec.env-6)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6296)
Let *fe* be the first element ofenvs0, envs1, …, envsn−1 such that the expression *fe*.query(q) is well-formed[.](#exec.env-6.sentence-1)
[7](#exec.env-7)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6301)
*Constraints*: ([*has-query*](#concept:has-query "33.11.2Class template env[exec.env]")<Envs, QueryTag> || ...) is true[.](#exec.env-7.sentence-1)
[8](#exec.env-8)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6305)
*Effects*: Equivalent to: return *fe*.query(q);
[9](#exec.env-9)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6309)
*Remarks*: The expression in the noexcept clause is equivalent
to noexcept(*fe*.query(q))[.](#exec.env-9.sentence-1)

134
cppdraft/exec/factories.md Normal file
View File

@@ -0,0 +1,134 @@
[exec.factories]
# 33 Execution control library [[exec]](./#exec)
## 33.9 Senders [[exec.snd]](exec.snd#exec.factories)
### 33.9.11 Sender factories [exec.factories]
#### [33.9.11.1](#exec.schedule) execution::schedule [[exec.schedule]](exec.schedule)
[1](#exec.schedule-1)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2877)
schedule obtains a schedule sender ([[exec.async.ops]](exec.async.ops "33.3Asynchronous operations"))
from a scheduler[.](#exec.schedule-1.sentence-1)
[2](#exec.schedule-2)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2881)
The name schedule denotes a customization point object[.](#exec.schedule-2.sentence-1)
For a subexpression sch,
the expression schedule(sch) is expression-equivalent tosch.schedule()[.](#exec.schedule-2.sentence-2)
[3](#exec.schedule-3)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2887)
*Mandates*: The type of sch.schedule() satisfies [sender](exec.snd.concepts#concept:sender "33.9.3Sender concepts[exec.snd.concepts]")[.](#exec.schedule-3.sentence-1)
[4](#exec.schedule-4)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2891)
If the expressionget_completion_scheduler<set_value_t>(get_env(sch.schedule())) == sch is ill-formed or evaluates to false,
the behavior of calling schedule(sch) is undefined[.](#exec.schedule-4.sentence-1)
#### [33.9.11.2](#exec.just) execution::just, execution::just_error, execution::just_stopped [[exec.just]](exec.just)
[1](#exec.just-1)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2901)
just, just_error, and just_stopped are sender factories
whose asynchronous operations complete synchronously in their start operation
with a value completion operation,
an error completion operation, or
a stopped completion operation, respectively[.](#exec.just-1.sentence-1)
[2](#exec.just-2)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2908)
The names just, just_error, and just_stopped denote
customization point objects[.](#exec.just-2.sentence-1)
Let *just-cpo* be one ofjust, just_error, or just_stopped[.](#exec.just-2.sentence-2)
For a pack of subexpressions ts,
let Ts be the pack of types decltype((ts))[.](#exec.just-2.sentence-3)
The expression *just-cpo*(ts...) is ill-formed if
- [(2.1)](#exec.just-2.1)
([*movable-value*](exec.general#concept:movable-value "33.1General[exec.general]")<Ts> &&...) is false, or
- [(2.2)](#exec.just-2.2)
*just-cpo* is just_error andsizeof...(ts) == 1 is false, or
- [(2.3)](#exec.just-2.3)
*just-cpo* is just_stopped andsizeof...(ts) == 0 is false[.](#exec.just-2.sentence-4)
Otherwise, it is expression-equivalent to*make-sender*(*just-cpo*, *product-type*{ts...})[.](#exec.just-2.sentence-5)
For just, just_error, and just_stopped,
let *set-cpo* beset_value, set_error, and set_stopped, respectively[.](#exec.just-2.sentence-6)
The exposition-only class template *impls-for* ([[exec.snd.expos]](exec.snd.expos "33.9.2Exposition-only entities"))
is specialized for *just-cpo* as follows:namespace std::execution {template<>struct *impls-for*<*decayed-typeof*<*just-cpo*>> : *default-impls* {static constexpr auto *start* =[](auto& state, auto& rcvr) noexcept -> void {auto& [...ts] = state; *set-cpo*(std::move(rcvr), std::move(ts)...); }; };}
#### [33.9.11.3](#exec.read.env) execution::read_env [[exec.read.env]](exec.read.env)
[1](#exec.read.env-1)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2950)
read_env is a sender factory for a sender
whose asynchronous operation completes synchronously in its start operation
with a value completion result equal to
a value read from the receiver's associated environment[.](#exec.read.env-1.sentence-1)
[2](#exec.read.env-2)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2956)
read_env is a customization point object[.](#exec.read.env-2.sentence-1)
For some query object q,
the expression read_env(q) is expression-equivalent to*make-sender*(read_env, q)[.](#exec.read.env-2.sentence-2)
[3](#exec.read.env-3)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2962)
The exposition-only class template *impls-for* ([[exec.snd.expos]](exec.snd.expos "33.9.2Exposition-only entities"))
is specialized for read_env as follows:
[🔗](#lib:impls-for%3cdecayed-typeof%3cread_env%3e%3e)
namespace std::execution {template<>struct *impls-for*<*decayed-typeof*<read_env>> : *default-impls* {static constexpr auto start =[](auto query, auto& rcvr) noexcept -> void {*TRY-SET-VALUE*(rcvr, query(get_env(rcvr))); }; }; template<class Sndr, class Env>static consteval void *check-types*();}
[🔗](#lib:check-types,impls-for%3cdecayed-typeof%3cread_env%3e%3e)
`template<class Sndr, class Env>
static consteval void check-types();
`
[4](#exec.read.env-4)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2988)
Let Q be decay_t<*data-type*<Sndr>>[.](#exec.read.env-4.sentence-1)
[5](#exec.read.env-5)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2991)
*Throws*: An exception of an unspecified type derived from exception if
the expression Q()(env) is ill-formed or has type void, whereenv is an lvalue subexpression whose type is Env[.](#exec.read.env-5.sentence-1)

36
cppdraft/exec/fwd/env.md Normal file
View File

@@ -0,0 +1,36 @@
[exec.fwd.env]
# 33 Execution control library [[exec]](./#exec)
## 33.5 Queries [[exec.queries]](exec.queries#exec.fwd.env)
### 33.5.1 forwarding_query [exec.fwd.env]
[1](#1)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L837)
forwarding_query asks a query object
whether it should be forwarded through queryable adaptors[.](#1.sentence-1)
[2](#2)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L841)
The name forwarding_query denotes a query object[.](#2.sentence-1)
For some query object q of type Q,forwarding_query(q) is expression-equivalent to:
- [(2.1)](#2.1)
*MANDATE-NOTHROW*(q.query(forwarding_query)) if that expression is well-formed[.](#2.1.sentence-1)
*Mandates*: The expression above has type bool and
is a core constant expression if q is a core constant expression[.](#2.1.sentence-2)
- [(2.2)](#2.2)
Otherwise, true if [derived_from](concept.derived#concept:derived_from "18.4.3Concept derived_­from[concept.derived]")<Q, forwarding_query_t> is true[.](#2.2.sentence-1)
- [(2.3)](#2.3)
Otherwise, false[.](#2.3.sentence-1)

103
cppdraft/exec/general.md Normal file
View File

@@ -0,0 +1,103 @@
[exec.general]
# 33 Execution control library [[exec]](./#exec)
## 33.1 General [exec.general]
[1](#1)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6)
This Clause describes components
supporting execution of function objects ([[function.objects]](function.objects "22.10Function objects"))[.](#1.sentence-1)
[2](#2)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L10)
The following subclauses describe
the requirements, concepts, and components
for execution control primitives as summarized in Table [157](#tab:exec.summary "Table 157: Execution control library summary")[.](#2.sentence-1)
Table [157](#tab:exec.summary) — Execution control library summary [[tab:exec.summary]](./tab:exec.summary)
| [🔗](#tab:exec.summary-row-1) | **Subclause** | **Header** |
| --- | --- | --- |
| [🔗](#tab:exec.summary-row-2)<br>[[exec.sched]](exec.sched "33.6Schedulers") | Schedulers | <execution> |
| [🔗](#tab:exec.summary-row-3)<br>[[exec.recv]](exec.recv "33.7Receivers") | Receivers | |
| [🔗](#tab:exec.summary-row-4)<br>[[exec.opstate]](exec.opstate "33.8Operation states") | Operation states | |
| [🔗](#tab:exec.summary-row-5)<br>[[exec.snd]](exec.snd "33.9Senders") | Senders | |
[3](#3)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L22)
Table [158](#tab:exec.pos "Table 158: Types of customization point objects in the execution control library") shows
the types of customization point objects ([[customization.point.object]](customization.point.object "16.3.3.3.5Customization Point Object types"))
used in the execution control library[.](#3.sentence-1)
Table [158](#tab:exec.pos) — Types of customization point objects in the execution control library [[tab:exec.pos]](./tab:exec.pos)
| [🔗](#tab:exec.pos-row-1)<br>**Customization point** | **Purpose** | **Examples** |
| --- | --- | --- |
| [🔗](#tab:exec.pos-row-2)<br>**object type** | | |
| [🔗](#tab:exec.pos-row-3)<br>core | provide core execution functionality, and connection between core components | e.g., [connect](execution.syn#lib:connect "33.4Header <execution> synopsis[execution.syn]"), [start](execution.syn#lib:start "33.4Header <execution> synopsis[execution.syn]") |
| [🔗](#tab:exec.pos-row-4)<br>completion functions | called by senders to announce the completion of the work (success, error, or cancellation) | [set_value](execution.syn#lib:set_value "33.4Header <execution> synopsis[execution.syn]"), [set_error](execution.syn#lib:set_error "33.4Header <execution> synopsis[execution.syn]"), [set_stopped](execution.syn#lib:set_stopped "33.4Header <execution> synopsis[execution.syn]") |
| [🔗](#tab:exec.pos-row-5)<br>senders | allow the specialization of the provided sender algorithms | sender factories (e.g., [schedule](execution.syn#lib:schedule "33.4Header <execution> synopsis[execution.syn]"), [just](execution.syn#lib:just "33.4Header <execution> synopsis[execution.syn]"), [read_env](execution.syn#lib:read_env "33.4Header <execution> synopsis[execution.syn]"))sender adaptors (e.g., [continues_on](execution.syn#lib:continues_on "33.4Header <execution> synopsis[execution.syn]"), [then](execution.syn#lib:then "33.4Header <execution> synopsis[execution.syn]"), [let_value](execution.syn#lib:let_value "33.4Header <execution> synopsis[execution.syn]"))sender consumers (e.g., [sync_wait](execution.syn#lib:sync_wait "33.4Header <execution> synopsis[execution.syn]")) |
| [🔗](#tab:exec.pos-row-6)<br>queries | allow querying different properties of objects | general queries (e.g., [get_allocator](execution.syn#lib:get_allocator "33.4Header <execution> synopsis[execution.syn]"), [get_stop_token](execution.syn#lib:get_stop_token "33.4Header <execution> synopsis[execution.syn]"))environment queries (e.g., get_scheduler, [get_delegation_scheduler](execution.syn#lib:get_delegation_scheduler "33.4Header <execution> synopsis[execution.syn]"))scheduler queries (e.g., [get_forward_progress_guarantee](execution.syn#lib:get_forward_progress_guarantee "33.4Header <execution> synopsis[execution.syn]"))sender attribute queries (e.g., [get_completion_scheduler](execution.syn#lib:get_completion_scheduler "33.4Header <execution> synopsis[execution.syn]")) |
[4](#4)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L57)
This clause makes use of the following exposition-only entities[.](#4.sentence-1)
[5](#5)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L60)
For a subexpression expr,
let *MANDATE-NOTHROW*(expr) be
expression-equivalent to expr[.](#5.sentence-1)
*Mandates*: noexcept(expr) is true[.](#5.sentence-2)
[6](#6)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L68)
namespace std {template<class T>concept [*movable-value*](#concept:movable-value "33.1General[exec.general]") = // *exposition only*[move_constructible](concept.moveconstructible#concept:move_constructible "18.4.13Concept move_­constructible[concept.moveconstructible]")<decay_t<T>> &&[constructible_from](concept.constructible#concept:constructible_from "18.4.11Concept constructible_­from[concept.constructible]")<decay_t<T>, T> &&(!is_array_v<remove_reference_t<T>>);}
[7](#7)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L79)
For function types F1 and F2 denotingR1(Args1...) and R2(Args2...), respectively,*MATCHING-SIG*(F1, F2) is true if and only if[same_as](concept.same#concept:same_as "18.4.2Concept same_­as[concept.same]")<R1(Args1&&...), R2(Args2&&...)> is true[.](#7.sentence-1)
[8](#8)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L86)
For a subexpression err,
let Err be decltype((err)) and
let *AS-EXCEPT-PTR*(err) be:
- [(8.1)](#8.1)
err if decay_t<Err> denotes the type exception_ptr[.](#8.1.sentence-1)
*Preconditions*: !err is false[.](#8.1.sentence-2)
- [(8.2)](#8.2)
Otherwise,make_exception_ptr(system_error(err)) if decay_t<Err> denotes the type error_code[.](#8.2.sentence-1)
- [(8.3)](#8.3)
Otherwise, make_exception_ptr(err)[.](#8.3.sentence-1)
[9](#9)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L104)
For a subexpression expr,
let *AS-CONST*(expr) be expression-equivalent to[](const auto& x) noexcept -> const auto& { return x; }(expr)

View File

@@ -0,0 +1,31 @@
[exec.get.allocator]
# 33 Execution control library [[exec]](./#exec)
## 33.5 Queries [[exec.queries]](exec.queries#exec.get.allocator)
### 33.5.2 get_allocator [exec.get.allocator]
[1](#1)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L861)
get_allocator asks a queryable object for its associated allocator[.](#1.sentence-1)
[2](#2)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L864)
The name get_allocator denotes a query object[.](#2.sentence-1)
For a subexpression env,get_allocator(env) is expression-equivalent to*MANDATE-NOTHROW*(*AS-CONST*(env).query(get_allocator))[.](#2.sentence-2)
*Mandates*: If the expression above is well-formed,
its type satisfies[*simple-allocator*](allocator.requirements.general#concept:simple-allocator "16.4.4.6.1General[allocator.requirements.general]") ([[allocator.requirements.general]](allocator.requirements.general "16.4.4.6.1General"))[.](#2.sentence-3)
[3](#3)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L875)
forwarding_query(get_allocator) is a core constant expression and
has value true[.](#3.sentence-1)

View File

@@ -0,0 +1,28 @@
[exec.get.await.adapt]
# 33 Execution control library [[exec]](./#exec)
## 33.5 Queries [[exec.queries]](exec.queries#exec.get.await.adapt)
### 33.5.10 execution::get_await_completion_adaptor [exec.get.await.adapt]
[1](#1)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L1078)
get_await_completion_adaptor asks a queryable object for
its associated awaitable completion adaptor[.](#1.sentence-1)
[2](#2)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L1082)
The name get_await_completion_adaptor denotes a query object[.](#2.sentence-1)
For a subexpression env,get_await_completion_adaptor(env) is expression-equivalent to*MANDATE-NOTHROW*(*AS-CONST*(env).query(get_await_completion_adaptor))
[3](#3)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L1093)
forwarding_query(execution::get_await_completion_adaptor) is a core constant expression and has value true[.](#3.sentence-1)

View File

@@ -0,0 +1,50 @@
[exec.get.compl.sched]
# 33 Execution control library [[exec]](./#exec)
## 33.5 Queries [[exec.queries]](exec.queries#exec.get.compl.sched)
### 33.5.9 execution::get_completion_scheduler [exec.get.compl.sched]
[1](#1)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L1035)
get_completion_scheduler<*completion-tag>* obtains
the completion scheduler associated with a completion tag
from a sender's attributes[.](#1.sentence-1)
[2](#2)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L1040)
The name get_completion_scheduler denotes a query object template[.](#2.sentence-1)
For a subexpression q,
the expression get_completion_scheduler<*completion-tag*>(q) is ill-formed if *completion-tag* is not one ofset_value_t, set_error_t, or set_stopped_t[.](#2.sentence-2)
Otherwise, get_completion_scheduler<*completion-tag*>(q) is expression-equivalent to*MANDATE-NOTHROW*(*AS-CONST*(q).query(get_completion_scheduler<*completion-tag*>))*Mandates*: If the expression above is well-formed,
its type satisfies [scheduler](exec.sched#concept:scheduler "33.6Schedulers[exec.sched]")[.](#2.sentence-3)
[3](#3)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L1055)
Let *completion-fn* be a completion function ([[exec.async.ops]](exec.async.ops "33.3Asynchronous operations"));
let *completion-tag* be
the associated completion tag of *completion-fn*;
let args be a pack of subexpressions; and
let sndr be a subexpression
such that [sender](exec.snd.concepts#concept:sender "33.9.3Sender concepts[exec.snd.concepts]")<decltype((sndr))> is true andget_completion_scheduler<*completion-tag*>(get_env(sndr)) is well-formed and denotes a scheduler sch[.](#3.sentence-1)
If an asynchronous operation
created by connecting sndr with a receiver rcvr causes the evaluation of *completion-fn*(rcvr, args...),
the behavior is undefined
unless the evaluation happens on an execution agent
that belongs to sch's associated execution resource[.](#3.sentence-2)
[4](#4)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L1071)
The expressionforwarding_query(get_completion_scheduler<*completion-tag*>) is a core constant expression and has value true[.](#4.sentence-1)

View File

@@ -0,0 +1,33 @@
[exec.get.delegation.scheduler]
# 33 Execution control library [[exec]](./#exec)
## 33.5 Queries [[exec.queries]](exec.queries#exec.get.delegation.scheduler)
### 33.5.7 execution::get_delegation_scheduler [exec.get.delegation.scheduler]
[1](#1)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L970)
get_delegation_scheduler asks a queryable object for a scheduler
that can be used to delegate work to
for the purpose of forward progress delegation ([[intro.progress]](intro.progress "6.10.2.3Forward progress"))[.](#1.sentence-1)
[2](#2)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L975)
The name get_delegation_scheduler denotes a query object[.](#2.sentence-1)
For a subexpression env,get_delegation_scheduler(env) is expression-equivalent to*MANDATE-NOTHROW*(*AS-CONST*(env).query(get_delegation_scheduler))[.](#2.sentence-2)
*Mandates*: If the expression above is well-formed,
its type satisfies [scheduler](exec.sched#concept:scheduler "33.6Schedulers[exec.sched]")[.](#2.sentence-3)
[3](#3)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L985)
forwarding_query(execution::get_delegation_scheduler) is
a core constant expression and has value true[.](#3.sentence-1)

View File

@@ -0,0 +1,29 @@
[exec.get.domain]
# 33 Execution control library [[exec]](./#exec)
## 33.5 Queries [[exec.queries]](exec.queries#exec.get.domain)
### 33.5.5 execution::get_domain [exec.get.domain]
[1](#1)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L935)
get_domain asks a queryable object
for its associated execution domain tag[.](#1.sentence-1)
[2](#2)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L939)
The name get_domain denotes a query object[.](#2.sentence-1)
For a subexpression env,get_domain(env) is expression-equivalent to*MANDATE-NOTHROW*(*AS-CONST*(env).query(get_domain))[.](#2.sentence-2)
[3](#3)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L945)
forwarding_query(execution::get_domain) is
a core constant expression and has value true[.](#3.sentence-1)

42
cppdraft/exec/get/env.md Normal file
View File

@@ -0,0 +1,42 @@
[exec.get.env]
# 33 Execution control library [[exec]](./#exec)
## 33.5 Queries [[exec.queries]](exec.queries#exec.get.env)
### 33.5.4 execution::get_env [exec.get.env]
[1](#1)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L906)
execution::get_env is a customization point object[.](#1.sentence-1)
For a subexpression o,execution::get_env(o) is expression-equivalent to:
- [(1.1)](#1.1)
*MANDATE-NOTHROW*(*AS-CONST*(o).get_env()) if that expression is well-formed[.](#1.1.sentence-1)
*Mandates*: The type of the expression above satisfies[*queryable*](exec.queryable.concept#concept:queryable "33.2.2queryable concept[exec.queryable.concept]") ([[exec.queryable]](exec.queryable "33.2Queries and queryables"))[.](#1.1.sentence-2)
- [(1.2)](#1.2)
Otherwise, env<>{}[.](#1.2.sentence-1)
[2](#2)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L922)
The value of get_env(o) shall be valid while o is valid[.](#2.sentence-1)
[3](#3)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L925)
[*Note [1](#note-1)*:
When passed a sender object,get_env returns the sender's associated attributes[.](#3.sentence-1)
When passed a receiver,get_env returns the receiver's associated execution environment[.](#3.sentence-2)
— *end note*]

View File

@@ -0,0 +1,53 @@
[exec.get.fwd.progress]
# 33 Execution control library [[exec]](./#exec)
## 33.5 Queries [[exec.queries]](exec.queries#exec.get.fwd.progress)
### 33.5.8 execution::get_forward_progress_guarantee [exec.get.fwd.progress]
namespace std::execution {enum class [forward_progress_guarantee](#lib:forward_progress_guarantee "33.5.8execution::get_­forward_­progress_­guarantee[exec.get.fwd.progress]") { concurrent,
parallel,
weakly_parallel };}
[1](#1)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L1001)
get_forward_progress_guarantee asks a scheduler about
the forward progress guarantee of execution agents
created by that scheduler's associated execution resource ([[intro.progress]](intro.progress "6.10.2.3Forward progress"))[.](#1.sentence-1)
[2](#2)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L1006)
The name get_forward_progress_guarantee denotes a query object[.](#2.sentence-1)
For a subexpression sch, let Sch be decltype((sch))[.](#2.sentence-2)
If Sch does not satisfy [scheduler](exec.sched#concept:scheduler "33.6Schedulers[exec.sched]"),get_forward_progress_guarantee is ill-formed[.](#2.sentence-3)
Otherwise,get_forward_progress_guarantee(sch) is expression-equivalent to:
- [(2.1)](#2.1)
*MANDATE-NOTHROW*(*AS-CONST*(sch).query(get_forward_progress_guarantee)),
if that expression is well-formed[.](#2.1.sentence-1)
*Mandates*: The type of the expression above is forward_progress_guarantee[.](#2.1.sentence-2)
- [(2.2)](#2.2)
Otherwise, forward_progress_guarantee::weakly_parallel[.](#2.2.sentence-1)
[3](#3)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L1024)
If get_forward_progress_guarantee(sch) for some scheduler sch returns forward_progress_guarantee::concurrent,
all execution agents created by that scheduler's associated execution resource
shall provide the concurrent forward progress guarantee[.](#3.sentence-1)
If it returns forward_progress_guarantee::parallel,
all such execution agents
shall provide at least the parallel forward progress guarantee[.](#3.sentence-2)

View File

@@ -0,0 +1,31 @@
[exec.get.scheduler]
# 33 Execution control library [[exec]](./#exec)
## 33.5 Queries [[exec.queries]](exec.queries#exec.get.scheduler)
### 33.5.6 execution::get_scheduler [exec.get.scheduler]
[1](#1)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L951)
get_scheduler asks a queryable object for its associated scheduler[.](#1.sentence-1)
[2](#2)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L954)
The name get_scheduler denotes a query object[.](#2.sentence-1)
For a subexpression env,get_scheduler(env) is expression-equivalent to*MANDATE-NOTHROW*(*AS-CONST*(env).query(get_scheduler))[.](#2.sentence-2)
*Mandates*: If the expression above is well-formed,
its type satisfies [scheduler](exec.sched#concept:scheduler "33.6Schedulers[exec.sched]")[.](#2.sentence-3)
[3](#3)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L964)
forwarding_query(execution::get_scheduler) is
a core constant expression and has value true[.](#3.sentence-1)

View File

@@ -0,0 +1,37 @@
[exec.get.stop.token]
# 33 Execution control library [[exec]](./#exec)
## 33.5 Queries [[exec.queries]](exec.queries#exec.get.stop.token)
### 33.5.3 get_stop_token [exec.get.stop.token]
[1](#1)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L881)
get_stop_token asks a queryable object for an associated stop token[.](#1.sentence-1)
[2](#2)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L884)
The name get_stop_token denotes a query object[.](#2.sentence-1)
For a subexpression env,get_stop_token(env) is expression-equivalent to:
- [(2.1)](#2.1)
*MANDATE-NOTHROW*(*AS-CONST*(env).query(get_stop_token)) if that expression is well-formed[.](#2.1.sentence-1)
*Mandates*: The type of the expression above satisfies [stoppable_token](stoptoken.concepts#concept:stoppable_token "32.3.3Stop token concepts[stoptoken.concepts]")[.](#2.1.sentence-2)
- [(2.2)](#2.2)
Otherwise, never_stop_token{}[.](#2.2.sentence-1)
[3](#3)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L900)
forwarding_query(get_stop_token) is a core constant expression and
has value true[.](#3.sentence-1)

View File

@@ -0,0 +1,94 @@
[exec.getcomplsigs]
# 33 Execution control library [[exec]](./#exec)
## 33.9 Senders [[exec.snd]](exec.snd#exec.getcomplsigs)
### 33.9.9 execution::get_completion_signatures [exec.getcomplsigs]
[🔗](#itemdecl:1)
`template<class Sndr, class... Env>
consteval auto get_completion_signatures() -> [valid-completion-signatures](execution.syn#concept:valid-completion-signatures "33.4Header <execution> synopsis[execution.syn]") auto;
`
[1](#1)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2634)
Let except be an rvalue subexpression of
an unspecified class type Except such that[move_constructible](concept.moveconstructible#concept:move_constructible "18.4.13Concept move_­constructible[concept.moveconstructible]")<Except> && [derived_from](concept.derived#concept:derived_from "18.4.3Concept derived_­from[concept.derived]")<Except, exception> is true[.](#1.sentence-1)
Let *CHECKED-COMPLSIGS*(e) be e if e is a core constant expression whose
type satisfies [*valid-completion-signatures*](execution.syn#concept:valid-completion-signatures "33.4Header <execution> synopsis[execution.syn]");
otherwise, it is the following expression:(e, throw except, completion_signatures())
Let *get-complsigs*<Sndr, Env...>() be expression-equivalent toremove_reference_t<Sndr>::template get_completion_signatures<Sndr, Env...>()[.](#1.sentence-3)
Let NewSndr be Sndr if sizeof...(Env) == 0 is true;
otherwise, decltype(s) where s is the following expression:transform_sender(*get-domain-late*(declval<Sndr>(), declval<Env>()...),
declval<Sndr>(),
declval<Env>()...)
[2](#2)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2660)
*Constraints*: sizeof...(Env) <= 1 is true[.](#2.sentence-1)
[3](#3)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2664)
*Effects*: Equivalent to: return e; where e is expression-equivalent to the following:
- [(3.1)](#3.1)
*CHECKED-COMPLSIGS*(*get-complsigs*<NewSndr, Env...>()) if *get-complsigs*<NewSndr, Env
...>() is a well-formed expression[.](#3.1.sentence-1)
- [(3.2)](#3.2)
Otherwise,*CHECKED-COMPLSIGS*(*get-complsigs*<NewSndr>()) if *get-complsigs*<NewSndr>() is a well-formed expression[.](#3.2.sentence-1)
- [(3.3)](#3.3)
Otherwise,completion_signatures<*SET-VALUE-SIG*(*await-result-type*<NewSndr, *env-promise*<Env>...>), // [[exec.snd.concepts]](exec.snd.concepts "33.9.3Sender concepts") set_error_t(exception_ptr),
set_stopped_t()> if [*is-awaitable*](exec.awaitable#concept:is-awaitable "33.9.4Awaitable helpers[exec.awaitable]")<NewSndr, *env-promise*<Env>...> is true[.](#3.3.sentence-1)
- [(3.4)](#3.4)
Otherwise,(throw *dependent-sender-error*(), completion_signatures()) if sizeof...(
Env) == 0 is true,
where *dependent-sender-error* isdependent_sender_error or
an unspecified type derived publicly and unambiguously fromdependent_sender_error[.](#3.4.sentence-1)
- [(3.5)](#3.5)
Otherwise,(throw except, completion_signatures())[.](#3.5.sentence-1)
[4](#4)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2706)
Given a type Env, ifcompletion_signatures_of_t<Sndr> andcompletion_signatures_of_t<Sndr, Env> are both well-formed,
they shall denote the same type[.](#4.sentence-1)
[5](#5)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2713)
Let rcvr be an rvalue
whose type Rcvr models [receiver](exec.recv.concepts#concept:receiver "33.7.1Receiver concepts[exec.recv.concepts]"), and
let Sndr be the type of a sender
such that [sender_in](exec.snd.concepts#concept:sender_in "33.9.3Sender concepts[exec.snd.concepts]")<Sndr, env_of_t<Rcvr>> is true[.](#5.sentence-1)
Let Sigs... be the template arguments of
the completion_signatures specialization
named by completion_signatures_of_t<Sndr, env_of_t<Rcvr>>[.](#5.sentence-2)
Let CSO be a completion function[.](#5.sentence-3)
If sender Sndr or its operation state cause
the expression CSO(rcvr, args...) to be potentially evaluated ([[basic.def.odr]](basic.def.odr "6.3One-definition rule"))
then there shall be a signature Sig in Sigs... such that*MATCHING-SIG*(*decayed-typeof*<CSO>(decltype(args)...), Sig) is true ([[exec.general]](exec.general "33.1General"))[.](#5.sentence-4)

View File

@@ -0,0 +1,62 @@
[exec.inline.scheduler]
# 33 Execution control library [[exec]](./#exec)
## 33.13 Coroutine utilities [[exec.coro.util]](exec.coro.util#exec.inline.scheduler)
### 33.13.4 execution::inline_scheduler [exec.inline.scheduler]
namespace std::execution {class [inline_scheduler](#lib:inline_scheduler "33.13.4execution::inline_­scheduler[exec.inline.scheduler]") {class *inline-sender*; // *exposition only*template<[receiver](exec.recv.concepts#concept:receiver "33.7.1Receiver concepts[exec.recv.concepts]") R>class *inline-state*; // *exposition only*public:using scheduler_concept = scheduler_t; constexpr *inline-sender* schedule() noexcept { return {}; }constexpr bool operator==(const inline_scheduler&) const noexcept = default; };}
[1](#1)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6954)
inline_scheduler is a class that models[scheduler](exec.sched#concept:scheduler "33.6Schedulers[exec.sched]") ([[exec.sched]](exec.sched "33.6Schedulers"))[.](#1.sentence-1)
All objects of type inline_scheduler are equal[.](#1.sentence-2)
[2](#2)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6959)
*inline-sender* is an exposition-only type that satisfies[sender](exec.snd.concepts#concept:sender "33.9.3Sender concepts[exec.snd.concepts]")[.](#2.sentence-1)
The type completion_signatures_of_t<*inline-sender*> is completion_signatures<set_value_t()>[.](#2.sentence-2)
[3](#3)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6965)
Let sndr be an expression of type *inline-sender*,
let rcvr be an expression such that[receiver_of](exec.recv.concepts#concept:receiver_of "33.7.1Receiver concepts[exec.recv.concepts]")<decltype((rcvr)), CS> is true where CS is completion_signatures<set_value_t()>,
then:
- [(3.1)](#3.1)
the expression connect(sndr, rcvr) has
type *inline-state*<remove_cvref_t<decltype((rcvr))>> and is potentially-throwing if and only if((void)sndr, auto(rcvr)) is potentially-throwing, and
- [(3.2)](#3.2)
the expressionget_completion_scheduler<set_value_t>(get_env(sndr)) has
type inline_scheduler and is potentially-throwing
if and only if get_env(sndr) is potentially-throwing[.](#3.sentence-1)
[4](#4)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6982)
Let *o* be a non-const lvalue of type*inline-state*<Rcvr>, and let REC(*o*) be
a non-const lvalue reference to an object of type Rcvr that
was initialized with the expression rcvr passed to an
invocation of connect that returned *o*, then:
- [(4.1)](#4.1)
the object to which REC(*o*) refers remains valid for
the lifetime of the object to which *o* refers, and
- [(4.2)](#4.2)
the expression start(*o*) is equivalent toset_value(std::move(REC(*o*)))[.](#4.sentence-1)

View File

@@ -0,0 +1,56 @@
[exec.into.variant]
# 33 Execution control library [[exec]](./#exec)
## 33.9 Senders [[exec.snd]](exec.snd#exec.into.variant)
### 33.9.12 Sender adaptors [[exec.adapt]](exec.adapt#exec.into.variant)
#### 33.9.12.13 execution::into_variant [exec.into.variant]
[1](#1)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4722)
into_variant adapts a sender with multiple value completion signatures
into a sender with just one value completion signature
consisting of a variant of tuples[.](#1.sentence-1)
[2](#2)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4727)
The name into_variant denotes a pipeable sender adaptor object[.](#2.sentence-1)
For a subexpression sndr, let Sndr be decltype((sndr))[.](#2.sentence-2)
If Sndr does not satisfy [sender](exec.snd.concepts#concept:sender "33.9.3Sender concepts[exec.snd.concepts]"),into_variant(sndr) is ill-formed[.](#2.sentence-3)
[3](#3)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4733)
Otherwise, the expression into_variant(sndr) is expression-equivalent to:transform_sender(*get-domain-early*(sndr), *make-sender*(into_variant, {}, sndr)) except that sndr is only evaluated once[.](#3.sentence-1)
[4](#4)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4741)
The exposition-only class template *impls-for* ([[exec.snd.expos]](exec.snd.expos "33.9.2Exposition-only entities"))
is specialized for into_variant as follows:
[🔗](#lib:impls-for%3cinto_variant_t%3e)
namespace std::execution {template<>struct *impls-for*<into_variant_t> : *default-impls* {static constexpr auto *get-state* = *see below*; static constexpr auto *complete* = *see below*; template<class Sndr, class... Env>static consteval void *check-types*() {auto cs = get_completion_signatures<*child-type*<Sndr>, *FWD-ENV-T*(Env)...>(); *decay-copyable-result-datums*(cs); // see [[exec.snd.expos]](exec.snd.expos "33.9.2Exposition-only entities")}};}
[5](#5)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4762)
The member *impls-for*<into_variant_t>::*get-state* is initialized with a callable object equivalent to the following lambda:[]<class Sndr, class Rcvr>(Sndr&& sndr, Rcvr& rcvr) noexcept-> type_identity<value_types_of_t<*child-type*<Sndr>, *FWD-ENV-T*(env_of_t<Rcvr>)>> {return {};}
[6](#6)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4772)
The member *impls-for*<into_variant_t>::*complete* is initialized with a callable object equivalent to the following lambda:[]<class State, class Rcvr, class Tag, class... Args>(auto, State, Rcvr& rcvr, Tag, Args&&... args) noexcept -> void {if constexpr ([same_as](concept.same#concept:same_as "18.4.2Concept same_­as[concept.same]")<Tag, set_value_t>) {using variant_type = typename State::type; *TRY-SET-VALUE*(rcvr, variant_type(*decayed-tuple*<Args...>{std::forward<Args>(args)...})); } else { Tag()(std::move(rcvr), std::forward<Args>(args)...); }}

53
cppdraft/exec/just.md Normal file
View File

@@ -0,0 +1,53 @@
[exec.just]
# 33 Execution control library [[exec]](./#exec)
## 33.9 Senders [[exec.snd]](exec.snd#exec.just)
### 33.9.11 Sender factories [[exec.factories]](exec.factories#exec.just)
#### 33.9.11.2 execution::just, execution::just_error, execution::just_stopped [exec.just]
[1](#1)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2901)
just, just_error, and just_stopped are sender factories
whose asynchronous operations complete synchronously in their start operation
with a value completion operation,
an error completion operation, or
a stopped completion operation, respectively[.](#1.sentence-1)
[2](#2)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2908)
The names just, just_error, and just_stopped denote
customization point objects[.](#2.sentence-1)
Let *just-cpo* be one ofjust, just_error, or just_stopped[.](#2.sentence-2)
For a pack of subexpressions ts,
let Ts be the pack of types decltype((ts))[.](#2.sentence-3)
The expression *just-cpo*(ts...) is ill-formed if
- [(2.1)](#2.1)
([*movable-value*](exec.general#concept:movable-value "33.1General[exec.general]")<Ts> &&...) is false, or
- [(2.2)](#2.2)
*just-cpo* is just_error andsizeof...(ts) == 1 is false, or
- [(2.3)](#2.3)
*just-cpo* is just_stopped andsizeof...(ts) == 0 is false[.](#2.sentence-4)
Otherwise, it is expression-equivalent to*make-sender*(*just-cpo*, *product-type*{ts...})[.](#2.sentence-5)
For just, just_error, and just_stopped,
let *set-cpo* beset_value, set_error, and set_stopped, respectively[.](#2.sentence-6)
The exposition-only class template *impls-for* ([[exec.snd.expos]](exec.snd.expos "33.9.2Exposition-only entities"))
is specialized for *just-cpo* as follows:namespace std::execution {template<>struct *impls-for*<*decayed-typeof*<*just-cpo*>> : *default-impls* {static constexpr auto *start* =[](auto& state, auto& rcvr) noexcept -> void {auto& [...ts] = state; *set-cpo*(std::move(rcvr), std::move(ts)...); }; };}

232
cppdraft/exec/let.md Normal file
View File

@@ -0,0 +1,232 @@
[exec.let]
# 33 Execution control library [[exec]](./#exec)
## 33.9 Senders [[exec.snd]](exec.snd#exec.let)
### 33.9.12 Sender adaptors [[exec.adapt]](exec.adapt#exec.let)
#### 33.9.12.10 execution::let_value, execution::let_error, execution::let_stopped [exec.let]
[1](#1)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3847)
let_value, let_error, and let_stopped transform
a sender's value, error, and stopped completions, respectively,
into a new child asynchronous operation
by passing the sender's result datums to a user-specified callable,
which returns a new sender that is connected and started[.](#1.sentence-1)
[2](#2)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3854)
For let_value, let_error, and let_stopped,
let *set-cpo* beset_value, set_error, and set_stopped, respectively[.](#2.sentence-1)
Let the expression *let-cpo* be one oflet_value, let_error, or let_stopped[.](#2.sentence-2)
For a subexpression sndr,
let *let-env*(sndr) be expression-equivalent to
the first well-formed expression below:
- [(2.1)](#2.1)
*SCHED-ENV*(get_completion_scheduler<*decayed-typeof*<*set-cpo*>>(get_env(sndr)))
- [(2.2)](#2.2)
*MAKE-ENV*(get_domain, get_domain(get_env(sndr)))
- [(2.3)](#2.3)
(void(sndr), env<>{})
[3](#3)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3872)
The names let_value, let_error, and let_stopped denote
pipeable sender adaptor objects[.](#3.sentence-1)
For subexpressions sndr and f,
let F be the decayed type of f[.](#3.sentence-2)
If decltype((sndr)) does not satisfy [sender](exec.snd.concepts#concept:sender "33.9.3Sender concepts[exec.snd.concepts]") or
if decltype((f)) does not satisfy [*movable-value*](exec.general#concept:movable-value "33.1General[exec.general]"),
the expression *let-cpo*(sndr, f) is ill-formed[.](#3.sentence-3)
If F does not satisfy [invocable](concept.invocable#concept:invocable "18.7.2Concept invocable[concept.invocable]"),
the expression let_stopped(sndr, f) is ill-formed[.](#3.sentence-4)
[4](#4)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3883)
Otherwise,
the expression *let-cpo*(sndr, f) is expression-equivalent to:transform_sender(*get-domain-early*(sndr), *make-sender*(*let-cpo*, f, sndr)) except that sndr is evaluated only once[.](#4.sentence-1)
[5](#5)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3891)
The exposition-only class template *impls-for* ([[exec.snd.expos]](exec.snd.expos "33.9.2Exposition-only entities"))
is specialized for *let-cpo* as follows:
[🔗](#lib:impls-for%3cdecayed-typeof%3clet-cpo%3e%3e)
namespace std::execution {template<class State, class Rcvr, class... Args>void *let-bind*(State& state, Rcvr& rcvr, Args&&... args); // *exposition only*template<>struct *impls-for*<*decayed-typeof*<*let-cpo*>> : *default-impls* {static constexpr auto *get-state* = *see below*; static constexpr auto *complete* = *see below*; template<class Sndr, class... Env>static consteval void *check-types*(); };}
[6](#6)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3911)
Let *receiver2* denote the following exposition-only class template:namespace std::execution {template<class Rcvr, class Env>struct *receiver2* {using receiver_concept = receiver_t; template<class... Args>void set_value(Args&&... args) && noexcept { execution::set_value(std::move(*rcvr*), std::forward<Args>(args)...); }template<class Error>void set_error(Error&& err) && noexcept { execution::set_error(std::move(*rcvr*), std::forward<Error>(err)); }void set_stopped() && noexcept { execution::set_stopped(std::move(*rcvr*)); }decltype(auto) get_env() const noexcept {return *see below*; } Rcvr& *rcvr*; // *exposition only* Env *env*; // *exposition only*};}
Invocation of the function *receiver2*::get_env returns an object e such that
- [(6.1)](#6.1)
decltype(e) models [*queryable*](exec.queryable.concept#concept:queryable "33.2.2queryable concept[exec.queryable.concept]") and
- [(6.2)](#6.2)
given a query object q,
the expression e.query(q) is expression-equivalent
to *env*.query(q) if that expression is valid;
otherwise,
if the type of q satisfies [*forwarding-query*](execution.syn#concept:forwarding-query "33.4Header <execution> synopsis[execution.syn]"),e.query(q) is expression-equivalent
to get_env(*rcvr*).query(q);
otherwise,e.query(q) is ill-formed[.](#6.sentence-2)
[🔗](#lib:check-types,impls-for%3cdecayed-typeof%3clet-cpo%3e%3e)
`template<class Sndr, class... Env>
static consteval void check-types();
`
[7](#7)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3966)
*Effects*: Equivalent to:using LetFn = remove_cvref_t<*data-type*<Sndr>>;auto cs = get_completion_signatures<*child-type*<Sndr>, *FWD-ENV-T*(Env)...>();auto fn = []<class... Ts>(*decayed-typeof*<*set-cpo*>(*)(Ts...)) {if constexpr (!*is-valid-let-sender*) // *see below*throw *unspecified-exception*();};
cs.*for-each*(*overload-set*(fn, [](auto){})); where *unspecified-exception* is
a type derived from exception, and
where *is-valid-let-sender* is true if and only if
all of the following are true:
- [(7.1)](#7.1)
([constructible_from](concept.constructible#concept:constructible_from "18.4.11Concept constructible_­from[concept.constructible]")<decay_t<Ts>, Ts> &&...)
- [(7.2)](#7.2)
[invocable](concept.invocable#concept:invocable "18.7.2Concept invocable[concept.invocable]")<LetFn, decay_t<Ts>&...>
- [(7.3)](#7.3)
[sender](exec.snd.concepts#concept:sender "33.9.3Sender concepts[exec.snd.concepts]")<invoke_result_t<LetFn, decay_t<Ts>&...>>
- [(7.4)](#7.4)
sizeof...(Env) == 0 || [sender_in](exec.snd.concepts#concept:sender_in "33.9.3Sender concepts[exec.snd.concepts]")<invoke_result_t<LetFn, decay_t<Ts>&...>, *env-t*
...>
where *env-t* is the packdecltype(*let-cpo*.transform_env(declval<Sndr>(), declval<Env>()))[.](#7.sentence-1)
[8](#8)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3993)
*impls-for*<*decayed-typeof*<*let-cpo*>>::*get-state* is initialized with a callable object equivalent to the following:[]<class Sndr, class Rcvr>(Sndr&& sndr, Rcvr& rcvr) requires *see below* {auto& [_, fn, child] = sndr; using fn_t = decay_t<decltype(fn)>; using env_t = decltype(*let-env*(child)); using args_variant_t = *see below*; using ops2_variant_t = *see below*; struct *state-type* { fn_t *fn*; // *exposition only* env_t *env*; // *exposition only* args_variant_t *args*; // *exposition only* ops2_variant_t *ops2*; // *exposition only*}; return *state-type*{*allocator-aware-forward*(std::forward_like<Sndr>(fn), rcvr), *let-env*(child), {}, {}};}
[9](#9)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4015)
Let Sigs be a pack of the arguments
to the completion_signatures specialization named bycompletion_signatures_of_t<*child-type*<Sndr>, *FWD-ENV-T*(env_of_t<Rcvr>)>[.](#9.sentence-1)
Let LetSigs be a pack of those types in Sigs with a return type of *decayed-typeof*<*set-cpo*>[.](#9.sentence-2)
Let *as-tuple* be an alias template
such that *as-tuple*<Tag(Args...)> denotes
the type *decayed-tuple*<Args...>[.](#9.sentence-3)
Then args_variant_t denotes
the type variant<monostate, *as-tuple*<LetSigs>...> except with duplicate types removed[.](#9.sentence-4)
[10](#10)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4028)
Given a type Tag and a pack Args,
let *as-sndr2* be an alias template
such that *as-sndr2*<Tag(Args...)> denotes
the type *call-result-t*<F, decay_t<Args>&...>[.](#10.sentence-1)
Then ops2_variant_t denotes
the typevariant<monostate, connect_result_t<*as-sndr2*<LetSigs>, *receiver2*<Rcvr, env_t>>...> except with duplicate types removed[.](#10.sentence-2)
[11](#11)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4040)
The [*requires-clause*](temp.pre#nt:requires-clause "13.1Preamble[temp.pre]") constraining the above lambda is satisfied
if and only if
the types args_variant_t and ops2_variant_t are well-formed[.](#11.sentence-1)
[12](#12)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4045)
The exposition-only function template *let-bind* has effects equivalent to:using args_t = *decayed-tuple*<Args...>;auto mkop2 = [&] {return connect( apply(std::move(state.fn),
state.args.template emplace<args_t>(std::forward<Args>(args)...)), *receiver2*{rcvr, std::move(state.env)});};
start(state.ops2.template emplace<decltype(mkop2())>(*emplace-from*{mkop2}));
[13](#13)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4059)
*impls-for*<*decayed-typeof*<let-cpo>>::*complete* is initialized with a callable object equivalent to the following:[]<class Tag, class... Args>(auto, auto& state, auto& rcvr, Tag, Args&&... args) noexcept -> void {if constexpr ([same_as](concept.same#concept:same_as "18.4.2Concept same_­as[concept.same]")<Tag, *decayed-typeof*<*set-cpo*>>) {*TRY-EVAL*(rcvr, *let-bind*(state, rcvr, std::forward<Args>(args)...)); } else { Tag()(std::move(rcvr), std::forward<Args>(args)...); }}
[14](#14)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4073)
Let sndr and env be subexpressions, and
let Sndr be decltype((sndr))[.](#14.sentence-1)
If[*sender-for*](exec.snd.concepts#concept:sender-for "33.9.3Sender concepts[exec.snd.concepts]")<Sndr, *decayed-typeof*<*let-cpo*>> is false,
then the expression *let-cpo*.transform_env(sndr, env) is ill-formed[.](#14.sentence-2)
Otherwise, it is equal to:auto& [_, _, child] = sndr;return *JOIN-ENV*(*let-env*(child), *FWD-ENV*(env));
[15](#15)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4087)
Let the subexpression out_sndr denote
the result of the invocation *let-cpo*(sndr, f) or
an object equal to such, and
let the subexpression rcvr denote a receiver
such that the expression connect(out_sndr, rcvr) is well-formed[.](#15.sentence-1)
The expression connect(out_sndr, rcvr) has undefined behavior
unless it creates an asynchronous operation ([[exec.async.ops]](exec.async.ops "33.3Asynchronous operations")) that,
when started:
- [(15.1)](#15.1)
invokes f when *set-cpo* is called
with sndr's result datums,
- [(15.2)](#15.2)
makes its completion dependent on
the completion of a sender returned by f, and
- [(15.3)](#15.3)
propagates the other completion operations sent by sndr[.](#15.sentence-2)

211
cppdraft/exec/on.md Normal file
View File

@@ -0,0 +1,211 @@
[exec.on]
# 33 Execution control library [[exec]](./#exec)
## 33.9 Senders [[exec.snd]](exec.snd#exec.on)
### 33.9.12 Sender adaptors [[exec.adapt]](exec.adapt#exec.on)
#### 33.9.12.8 execution::on [exec.on]
[1](#1)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3561)
The on sender adaptor has two forms:
- [(1.1)](#1.1)
on(sch, sndr),
which starts a sender sndr on an execution agent
belonging to a scheduler sch's associated execution resource and
that, upon sndr's completion,
transfers execution back to the execution resource
on which the on sender was started[.](#1.1.sentence-1)
- [(1.2)](#1.2)
on(sndr, sch, closure),
which upon completion of a sender sndr,
transfers execution to an execution agent
belonging to a scheduler sch's associated execution resource,
then executes a sender adaptor closure closure with the async results of the sender, and
that then transfers execution back to the execution resource
on which sndr completed[.](#1.2.sentence-1)
[2](#2)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3582)
The name on denotes a pipeable sender adaptor object[.](#2.sentence-1)
For subexpressions sch and sndr,on(sch, sndr) is ill-formed if any of the following is true:
- [(2.1)](#2.1)
decltype((sch)) does not satisfy [scheduler](exec.sched#concept:scheduler "33.6Schedulers[exec.sched]"), or
- [(2.2)](#2.2)
decltype((sndr)) does not satisfy [sender](exec.snd.concepts#concept:sender "33.9.3Sender concepts[exec.snd.concepts]") andsndr is not
a pipeable sender adaptor closure object ([[exec.adapt.obj]](exec.adapt.obj "33.9.12.2Closure objects")), or
- [(2.3)](#2.3)
decltype((sndr)) satisfies [sender](exec.snd.concepts#concept:sender "33.9.3Sender concepts[exec.snd.concepts]") andsndr is also a pipeable sender adaptor closure object[.](#2.sentence-2)
[3](#3)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3598)
Otherwise, if decltype((sndr)) satisfies [sender](exec.snd.concepts#concept:sender "33.9.3Sender concepts[exec.snd.concepts]"),
the expression on(sch, sndr) is expression-equivalent to:transform_sender(*query-with-default*(get_domain, sch, default_domain()), *make-sender*(on, sch, sndr)) except that sch is evaluated only once[.](#3.sentence-1)
[4](#4)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3608)
For subexpressions sndr, sch, and closure, if
- [(4.1)](#4.1)
decltype((sch)) does not satisfy [scheduler](exec.sched#concept:scheduler "33.6Schedulers[exec.sched]"), or
- [(4.2)](#4.2)
decltype((sndr)) does not satisfy [sender](exec.snd.concepts#concept:sender "33.9.3Sender concepts[exec.snd.concepts]"), or
- [(4.3)](#4.3)
closure is not a pipeable sender adaptor closure object ([[exec.adapt.obj]](exec.adapt.obj "33.9.12.2Closure objects")),
the expression on(sndr, sch, closure) is ill-formed;
otherwise, it is expression-equivalent to:transform_sender(*get-domain-early*(sndr), *make-sender*(on, *product-type*{sch, closure}, sndr)) except that sndr is evaluated only once[.](#4.sentence-1)
[5](#5)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3627)
Let out_sndr and env be subexpressions,
let OutSndr be decltype((out_sndr)), and
let Env be decltype((env))[.](#5.sentence-1)
If [*sender-for*](exec.snd.concepts#concept:sender-for "33.9.3Sender concepts[exec.snd.concepts]")<OutSndr, on_t> is false,
then the expressions on.transform_env(out_sndr, env) andon.transform_sender(out_sndr, env) are ill-formed[.](#5.sentence-2)
[6](#6)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3635)
Otherwise:
Let *not-a-scheduler* be an unspecified empty class type[.](#6.sentence-1)
[7](#7)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3639)
The expression on.transform_env(out_sndr, env) has effects equivalent to:auto&& [_, data, _] = out_sndr;if constexpr ([scheduler](exec.sched#concept:scheduler "33.6Schedulers[exec.sched]")<decltype(data)>) {return *JOIN-ENV*(*SCHED-ENV*(std::forward_like<OutSndr>(data)), *FWD-ENV*(std::forward<Env>(env)));} else {return std::forward<Env>(env);}
[8](#8)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3651)
The expression on.transform_sender(out_sndr, env) has effects equivalent to:auto&& [_, data, child] = out_sndr;if constexpr ([scheduler](exec.sched#concept:scheduler "33.6Schedulers[exec.sched]")<decltype(data)>) {auto orig_sch =*query-with-default*(get_scheduler, env, *not-a-scheduler*()); if constexpr ([same_as](concept.same#concept:same_as "18.4.2Concept same_­as[concept.same]")<decltype(orig_sch), *not-a-scheduler*>) {return *not-a-sender*{}; } else {return continues_on( starts_on(std::forward_like<OutSndr>(data), std::forward_like<OutSndr>(child)),
std::move(orig_sch)); }} else {auto& [sch, closure] = data; auto orig_sch = *query-with-default*( get_completion_scheduler<set_value_t>,
get_env(child), *query-with-default*(get_scheduler, env, *not-a-scheduler*())); if constexpr ([same_as](concept.same#concept:same_as "18.4.2Concept same_­as[concept.same]")<decltype(orig_sch), *not-a-scheduler*>) {return *not-a-sender*{}; } else {return write_env( continues_on( std::forward_like<OutSndr>(closure)( continues_on( write_env(std::forward_like<OutSndr>(child), *SCHED-ENV*(orig_sch)),
sch)),
orig_sch), *SCHED-ENV*(sch)); }}
[9](#9)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3689)
Let out_sndr be a subexpression denoting
a sender returned from on(sch, sndr) or one equal to such, and
let OutSndr be the type decltype((out_sndr))[.](#9.sentence-1)
Let out_rcvr be a subexpression denoting a receiver
that has an environment of type Env such that [sender_in](exec.snd.concepts#concept:sender_in "33.9.3Sender concepts[exec.snd.concepts]")<OutSndr, Env> is true[.](#9.sentence-2)
Let op be an lvalue referring to the operation state
that results from connecting out_sndr with out_rcvr[.](#9.sentence-3)
Calling start(op) shall
- [(9.1)](#9.1)
remember the current scheduler, get_scheduler(get_env(rcvr));
- [(9.2)](#9.2)
start sndr on an execution agent belonging tosch's associated execution resource;
- [(9.3)](#9.3)
upon sndr's completion,
transfer execution back to the execution resource
associated with the scheduler remembered in step 1; and
- [(9.4)](#9.4)
forward sndr's async result to out_rcvr[.](#9.sentence-4)
If any scheduling operation fails,
an error completion on out_rcvr shall be executed
on an unspecified execution agent[.](#9.sentence-5)
[10](#10)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3716)
Let out_sndr be a subexpression denoting
a sender returned from on(sndr, sch, closure) or one equal to such, and
let OutSndr be the type decltype((out_sndr))[.](#10.sentence-1)
Let out_rcvr be a subexpression denoting a receiver
that has an environment of type Env such that [sender_in](exec.snd.concepts#concept:sender_in "33.9.3Sender concepts[exec.snd.concepts]")<OutSndr, Env> is true[.](#10.sentence-2)
Let op be an lvalue referring to the operation state
that results from connecting out_sndr with out_rcvr[.](#10.sentence-3)
Calling start(op) shall
- [(10.1)](#10.1)
remember the current scheduler,
which is the first of the following expressions that is well-formed:
* [(10.1.1)](#10.1.1)
get_completion_scheduler<set_value_t>(get_env(sndr))
* [(10.1.2)](#10.1.2)
get_scheduler(get_env(rcvr));
- [(10.2)](#10.2)
start sndr on the current execution agent;
- [(10.3)](#10.3)
upon sndr's completion,
transfer execution to an agent
owned by sch's associated execution resource;
- [(10.4)](#10.4)
forward sndr's async result as if by
connecting and starting a sender closure(S),
where S is a sender
that completes synchronously with sndr's async result; and
- [(10.5)](#10.5)
upon completion of the operation started in the previous step,
transfer execution back to the execution resource
associated with the scheduler remembered in step 1 and
forward the operation's async result to out_rcvr[.](#10.sentence-4)
If any scheduling operation fails,
an error completion on out_rcvr shall be executed on
an unspecified execution agent[.](#10.sentence-5)

67
cppdraft/exec/opstate.md Normal file
View File

@@ -0,0 +1,67 @@
[exec.opstate]
# 33 Execution control library [[exec]](./#exec)
## 33.8 Operation states [exec.opstate]
### [33.8.1](#general) General [[exec.opstate.general]](exec.opstate.general)
[1](#general-1)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L1273)
The [operation_state](#concept:operation_state "33.8.1General[exec.opstate.general]") concept defines
the requirements of an operation state type ([[exec.async.ops]](exec.async.ops "33.3Asynchronous operations"))[.](#general-1.sentence-1)
namespace std::execution {template<class O>concept [operation_state](#concept:operation_state "33.8.1General[exec.opstate.general]") =[derived_from](concept.derived#concept:derived_from "18.4.3Concept derived_­from[concept.derived]")<typename O::operation_state_concept, operation_state_t> &&requires (O& o) { start(o); };}
[2](#general-2)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L1287)
If an [operation_state](#concept:operation_state "33.8.1General[exec.opstate.general]") object is destroyed
during the lifetime of its asynchronous operation ([[exec.async.ops]](exec.async.ops "33.3Asynchronous operations")),
the behavior is undefined[.](#general-2.sentence-1)
[*Note [1](#general-note-1)*:
The [operation_state](#concept:operation_state "33.8.1General[exec.opstate.general]") concept does not impose requirements
on any operations other than destruction and start,
including copy and move operations[.](#general-2.sentence-2)
Invoking any such operation on an object
whose type models [operation_state](#concept:operation_state "33.8.1General[exec.opstate.general]") can lead to undefined behavior[.](#general-2.sentence-3)
— *end note*]
[3](#general-3)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L1299)
The program is ill-formed
if it performs a copy or move construction or assignment operation on
an operation state object created by connecting a library-provided sender[.](#general-3.sentence-1)
### [33.8.2](#start) execution::start [[exec.opstate.start]](exec.opstate.start)
[1](#start-1)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L1306)
The name start denotes a customization point object
that starts ([[exec.async.ops]](exec.async.ops "33.3Asynchronous operations"))
the asynchronous operation associated with the operation state object[.](#start-1.sentence-1)
For a subexpression op,
the expression start(op) is ill-formed
if op is an rvalue[.](#start-1.sentence-2)
Otherwise, it is expression-equivalent to*MANDATE-NOTHROW*(op.start())[.](#start-1.sentence-3)
[2](#start-2)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L1316)
If op.start() does not start ([[exec.async.ops]](exec.async.ops "33.3Asynchronous operations"))
the asynchronous operation associated with the operation state op,
the behavior of calling start(op) is undefined[.](#start-2.sentence-1)

View File

@@ -0,0 +1,43 @@
[exec.opstate.general]
# 33 Execution control library [[exec]](./#exec)
## 33.8 Operation states [[exec.opstate]](exec.opstate#general)
### 33.8.1 General [exec.opstate.general]
[1](#1)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L1273)
The [operation_state](#concept:operation_state "33.8.1General[exec.opstate.general]") concept defines
the requirements of an operation state type ([[exec.async.ops]](exec.async.ops "33.3Asynchronous operations"))[.](#1.sentence-1)
namespace std::execution {template<class O>concept [operation_state](#concept:operation_state "33.8.1General[exec.opstate.general]") =[derived_from](concept.derived#concept:derived_from "18.4.3Concept derived_­from[concept.derived]")<typename O::operation_state_concept, operation_state_t> &&requires (O& o) { start(o); };}
[2](#2)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L1287)
If an [operation_state](#concept:operation_state "33.8.1General[exec.opstate.general]") object is destroyed
during the lifetime of its asynchronous operation ([[exec.async.ops]](exec.async.ops "33.3Asynchronous operations")),
the behavior is undefined[.](#2.sentence-1)
[*Note [1](#note-1)*:
The [operation_state](#concept:operation_state "33.8.1General[exec.opstate.general]") concept does not impose requirements
on any operations other than destruction and start,
including copy and move operations[.](#2.sentence-2)
Invoking any such operation on an object
whose type models [operation_state](#concept:operation_state "33.8.1General[exec.opstate.general]") can lead to undefined behavior[.](#2.sentence-3)
— *end note*]
[3](#3)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L1299)
The program is ill-formed
if it performs a copy or move construction or assignment operation on
an operation state object created by connecting a library-provided sender[.](#3.sentence-1)

View File

@@ -0,0 +1,29 @@
[exec.opstate.start]
# 33 Execution control library [[exec]](./#exec)
## 33.8 Operation states [[exec.opstate]](exec.opstate#start)
### 33.8.2 execution::start [exec.opstate.start]
[1](#1)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L1306)
The name start denotes a customization point object
that starts ([[exec.async.ops]](exec.async.ops "33.3Asynchronous operations"))
the asynchronous operation associated with the operation state object[.](#1.sentence-1)
For a subexpression op,
the expression start(op) is ill-formed
if op is an rvalue[.](#1.sentence-2)
Otherwise, it is expression-equivalent to*MANDATE-NOTHROW*(op.start())[.](#1.sentence-3)
[2](#2)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L1316)
If op.start() does not start ([[exec.async.ops]](exec.async.ops "33.3Asynchronous operations"))
the asynchronous operation associated with the operation state op,
the behavior of calling start(op) is undefined[.](#2.sentence-1)

View File

@@ -0,0 +1,188 @@
[exec.par.scheduler]
# 33 Execution control library [[exec]](./#exec)
## 33.15 Parallel scheduler [exec.par.scheduler]
[1](#1)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L8281)
parallel_scheduler models [scheduler](exec.sched#concept:scheduler "33.6Schedulers[exec.sched]")[.](#1.sentence-1)
[2](#2)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L8284)
Let sch be an object of type parallel_scheduler, and
let *BACKEND-OF*(sch) be *ptr,
where sch is associated with ptr[.](#2.sentence-1)
[3](#3)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L8289)
The expression get_forward_progress_guarantee(sch) has the valueforward_progress_guarantee::parallel[.](#3.sentence-1)
[4](#4)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L8293)
Let sch2 be an object of type parallel_scheduler[.](#4.sentence-1)
Two objects sch and sch2 compare equal if and only if*BACKEND-OF*(sch) and*BACKEND-OF*(sch2) refer to the same object[.](#4.sentence-2)
[5](#5)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L8299)
Let rcvr be a receiver[.](#5.sentence-1)
A [*proxy for rcvr with base B*](#def:proxy "33.15Parallel scheduler[exec.par.scheduler]") is
an lvalue r of type B such that:
- [(5.1)](#5.1)
r.set_value() has effects equivalent toset_value(std::move(rcvr))[.](#5.1.sentence-1)
- [(5.2)](#5.2)
r.set_error(e), where e is an exception_ptr object,
has effects equivalent to set_error(std::move(rcvr), std::move(e))[.](#5.2.sentence-1)
- [(5.3)](#5.3)
r.set_stopped() has effects equivalent toset_stopped(std::move(rcvr))[.](#5.3.sentence-1)
[6](#6)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L8315)
A [*preallocated backend storage for a proxy*](#def:preallocated_backend_storage_for_a_proxy "33.15Parallel scheduler[exec.par.scheduler]") r is
an object s of type span<byte> such that the range s remains valid and may be overwritten
until one of set_value, set_error, or set_stopped is called on r[.](#6.sentence-1)
[*Note [1](#note-1)*:
The storage referenced by s can be used as temporary storage
for operations launched via calls to parallel_scheduler_backend[.](#6.sentence-2)
— *end note*]
[7](#7)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L8326)
A [*bulk chunked proxy for rcvr with callable f and arguments args*](#def:proxy,bulk_chunked "33.15Parallel scheduler[exec.par.scheduler]") is a proxy r for rcvr with base system_context_replaceability::bulk_item_receiver_proxy such thatr.execute(i, j) for indices i and j has effects equivalent to f(i, j, args...)[.](#7.sentence-1)
[8](#8)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L8335)
A [*bulk unchunked proxy for rcvr with callable f and arguments args*](#def:proxy,bulk_unchunked "33.15Parallel scheduler[exec.par.scheduler]") is a proxy r for rcvr with base system_context_replaceability::bulk_item_receiver_proxy such thatr.execute(i, i + 1) for index i has effects equivalent to f(i, args...)[.](#8.sentence-1)
[9](#9)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L8344)
Let b be *BACKEND-OF*(sch),
let sndr be the object returned by schedule(sch), and
let rcvr be a receiver[.](#9.sentence-1)
If rcvr is connected to sndr and
the resulting operation state is started, then:
- [(9.1)](#9.1)
If sndr completes successfully,
then b.schedule(r, s) is called, where
* [(9.1.1)](#9.1.1)
r is a proxy for rcvr with base system_context_replaceability::receiver_proxy and
* [(9.1.2)](#9.1.2)
s is a preallocated backend storage for r[.](#9.1.sentence-1)
- [(9.2)](#9.2)
All other completion operations are forwarded unchanged[.](#9.2.sentence-1)
[10](#10)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L8365)
parallel_scheduler provides a customized implementation of
the bulk_chunked algorithm ([[exec.bulk]](exec.bulk "33.9.12.11execution::bulk, execution::bulk_­chunked, and execution::bulk_­unchunked"))[.](#10.sentence-1)
If a receiver rcvr is connected to the sender
returned by bulk_chunked(sndr, pol, shape, f) and
the resulting operation state is started, then:
- [(10.1)](#10.1)
If sndr completes with values vals,
let args be a pack of lvalue subexpressions designating vals,
then b.schedule_bulk_chunked(shape, r, s) is called, where
* [(10.1.1)](#10.1.1)
r is a bulk chunked proxy for rcvr with callable f and arguments args and
* [(10.1.2)](#10.1.2)
s is a preallocated backend storage for r[.](#10.1.sentence-1)
- [(10.2)](#10.2)
All other completion operations are forwarded unchanged[.](#10.2.sentence-1)
[*Note [2](#note-2)*:
Customizing the behavior of bulk_chunked affects the default implementation of bulk[.](#10.sentence-3)
— *end note*]
[11](#11)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L8391)
parallel_scheduler provides a customized implementation of
the bulk_unchunked algorithm ([[exec.bulk]](exec.bulk "33.9.12.11execution::bulk, execution::bulk_­chunked, and execution::bulk_­unchunked"))[.](#11.sentence-1)
If a receiver rcvr is connected to the sender
returned by bulk_unchunked(sndr, pol, shape, f) and
the resulting operation state is started, then:
- [(11.1)](#11.1)
If sndr completes with values vals,
let args be a pack of lvalue subexpressions designating vals,
then b.schedule_bulk_unchunked(shape, r, s) is called, where
* [(11.1.1)](#11.1.1)
r is a bulk unchunked proxy for rcvr with callable f and arguments args and
* [(11.1.2)](#11.1.2)
s is a preallocated backend storage for r[.](#11.1.sentence-1)
- [(11.2)](#11.2)
All other completion operations are forwarded unchanged[.](#11.2.sentence-1)
[🔗](#lib:get_parallel_scheduler)
`parallel_scheduler get_parallel_scheduler();
`
[12](#12)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L8419)
*Effects*: Let eb be the result of system_context_replaceability::query_parallel_scheduler_backend()[.](#12.sentence-1)
If eb == nullptr is true,
calls terminate ([[except.terminate]](except.terminate "14.6.2The std::terminate function"))[.](#12.sentence-2)
Otherwise, returns a parallel_scheduler object
associated with eb[.](#12.sentence-3)

35
cppdraft/exec/prop.md Normal file
View File

@@ -0,0 +1,35 @@
[exec.prop]
# 33 Execution control library [[exec]](./#exec)
## 33.11 Queryable utilities [[exec.envs]](exec.envs#exec.prop)
### 33.11.1 Class template prop [exec.prop]
namespace std::execution {template<class QueryTag, class ValueType>struct [prop](#lib:prop "33.11.1Class template prop[exec.prop]") { QueryTag *query_*; // *exposition only* ValueType *value_*; // *exposition only*constexpr const ValueType& query(QueryTag) const noexcept {return *value_*; }}; template<class QueryTag, class ValueType> prop(QueryTag, ValueType) -> prop<QueryTag, unwrap_reference_t<ValueType>>;}
[1](#1)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6193)
Class template prop is for building a queryable object
from a query object and a value[.](#1.sentence-1)
[2](#2)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6197)
*Mandates*: [*callable*](functional.syn#concept:callable "22.10.2Header <functional> synopsis[functional.syn]")<QueryTag, *prop-like*<ValueType>> is modeled,
where *prop-like* is the following exposition-only class template:template<class ValueType>struct *prop-like* { // *exposition only*const ValueType& query(auto) const noexcept;};
[3](#3)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6209)
[*Example [1](#example-1)*: template<[sender](exec.snd.concepts#concept:sender "33.9.3Sender concepts[exec.snd.concepts]") Sndr> sender auto parameterize_work(Sndr sndr) {// Make an environment such that get_allocator(env) returns a reference to a copy of my_alloc{}.auto e = prop(get_allocator, my_alloc{}); // Parameterize the input sender so that it will use our custom execution environment.return write_env(sndr, e);} — *end example*]
[4](#4)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6223)
Specializations of prop are not assignable[.](#4.sentence-1)

325
cppdraft/exec/queries.md Normal file
View File

@@ -0,0 +1,325 @@
[exec.queries]
# 33 Execution control library [[exec]](./#exec)
## 33.5 Queries [exec.queries]
### [33.5.1](#exec.fwd.env) forwarding_query [[exec.fwd.env]](exec.fwd.env)
[1](#exec.fwd.env-1)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L837)
forwarding_query asks a query object
whether it should be forwarded through queryable adaptors[.](#exec.fwd.env-1.sentence-1)
[2](#exec.fwd.env-2)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L841)
The name forwarding_query denotes a query object[.](#exec.fwd.env-2.sentence-1)
For some query object q of type Q,forwarding_query(q) is expression-equivalent to:
- [(2.1)](#exec.fwd.env-2.1)
*MANDATE-NOTHROW*(q.query(forwarding_query)) if that expression is well-formed[.](#exec.fwd.env-2.1.sentence-1)
*Mandates*: The expression above has type bool and
is a core constant expression if q is a core constant expression[.](#exec.fwd.env-2.1.sentence-2)
- [(2.2)](#exec.fwd.env-2.2)
Otherwise, true if [derived_from](concept.derived#concept:derived_from "18.4.3Concept derived_­from[concept.derived]")<Q, forwarding_query_t> is true[.](#exec.fwd.env-2.2.sentence-1)
- [(2.3)](#exec.fwd.env-2.3)
Otherwise, false[.](#exec.fwd.env-2.3.sentence-1)
### [33.5.2](#exec.get.allocator) get_allocator [[exec.get.allocator]](exec.get.allocator)
[1](#exec.get.allocator-1)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L861)
get_allocator asks a queryable object for its associated allocator[.](#exec.get.allocator-1.sentence-1)
[2](#exec.get.allocator-2)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L864)
The name get_allocator denotes a query object[.](#exec.get.allocator-2.sentence-1)
For a subexpression env,get_allocator(env) is expression-equivalent to*MANDATE-NOTHROW*(*AS-CONST*(env).query(get_allocator))[.](#exec.get.allocator-2.sentence-2)
*Mandates*: If the expression above is well-formed,
its type satisfies[*simple-allocator*](allocator.requirements.general#concept:simple-allocator "16.4.4.6.1General[allocator.requirements.general]") ([[allocator.requirements.general]](allocator.requirements.general "16.4.4.6.1General"))[.](#exec.get.allocator-2.sentence-3)
[3](#exec.get.allocator-3)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L875)
forwarding_query(get_allocator) is a core constant expression and
has value true[.](#exec.get.allocator-3.sentence-1)
### [33.5.3](#exec.get.stop.token) get_stop_token [[exec.get.stop.token]](exec.get.stop.token)
[1](#exec.get.stop.token-1)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L881)
get_stop_token asks a queryable object for an associated stop token[.](#exec.get.stop.token-1.sentence-1)
[2](#exec.get.stop.token-2)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L884)
The name get_stop_token denotes a query object[.](#exec.get.stop.token-2.sentence-1)
For a subexpression env,get_stop_token(env) is expression-equivalent to:
- [(2.1)](#exec.get.stop.token-2.1)
*MANDATE-NOTHROW*(*AS-CONST*(env).query(get_stop_token)) if that expression is well-formed[.](#exec.get.stop.token-2.1.sentence-1)
*Mandates*: The type of the expression above satisfies [stoppable_token](stoptoken.concepts#concept:stoppable_token "32.3.3Stop token concepts[stoptoken.concepts]")[.](#exec.get.stop.token-2.1.sentence-2)
- [(2.2)](#exec.get.stop.token-2.2)
Otherwise, never_stop_token{}[.](#exec.get.stop.token-2.2.sentence-1)
[3](#exec.get.stop.token-3)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L900)
forwarding_query(get_stop_token) is a core constant expression and
has value true[.](#exec.get.stop.token-3.sentence-1)
### [33.5.4](#exec.get.env) execution::get_env [[exec.get.env]](exec.get.env)
[1](#exec.get.env-1)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L906)
execution::get_env is a customization point object[.](#exec.get.env-1.sentence-1)
For a subexpression o,execution::get_env(o) is expression-equivalent to:
- [(1.1)](#exec.get.env-1.1)
*MANDATE-NOTHROW*(*AS-CONST*(o).get_env()) if that expression is well-formed[.](#exec.get.env-1.1.sentence-1)
*Mandates*: The type of the expression above satisfies[*queryable*](exec.queryable.concept#concept:queryable "33.2.2queryable concept[exec.queryable.concept]") ([[exec.queryable]](exec.queryable "33.2Queries and queryables"))[.](#exec.get.env-1.1.sentence-2)
- [(1.2)](#exec.get.env-1.2)
Otherwise, env<>{}[.](#exec.get.env-1.2.sentence-1)
[2](#exec.get.env-2)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L922)
The value of get_env(o) shall be valid while o is valid[.](#exec.get.env-2.sentence-1)
[3](#exec.get.env-3)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L925)
[*Note [1](#exec.get.env-note-1)*:
When passed a sender object,get_env returns the sender's associated attributes[.](#exec.get.env-3.sentence-1)
When passed a receiver,get_env returns the receiver's associated execution environment[.](#exec.get.env-3.sentence-2)
— *end note*]
### [33.5.5](#exec.get.domain) execution::get_domain [[exec.get.domain]](exec.get.domain)
[1](#exec.get.domain-1)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L935)
get_domain asks a queryable object
for its associated execution domain tag[.](#exec.get.domain-1.sentence-1)
[2](#exec.get.domain-2)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L939)
The name get_domain denotes a query object[.](#exec.get.domain-2.sentence-1)
For a subexpression env,get_domain(env) is expression-equivalent to*MANDATE-NOTHROW*(*AS-CONST*(env).query(get_domain))[.](#exec.get.domain-2.sentence-2)
[3](#exec.get.domain-3)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L945)
forwarding_query(execution::get_domain) is
a core constant expression and has value true[.](#exec.get.domain-3.sentence-1)
### [33.5.6](#exec.get.scheduler) execution::get_scheduler [[exec.get.scheduler]](exec.get.scheduler)
[1](#exec.get.scheduler-1)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L951)
get_scheduler asks a queryable object for its associated scheduler[.](#exec.get.scheduler-1.sentence-1)
[2](#exec.get.scheduler-2)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L954)
The name get_scheduler denotes a query object[.](#exec.get.scheduler-2.sentence-1)
For a subexpression env,get_scheduler(env) is expression-equivalent to*MANDATE-NOTHROW*(*AS-CONST*(env).query(get_scheduler))[.](#exec.get.scheduler-2.sentence-2)
*Mandates*: If the expression above is well-formed,
its type satisfies [scheduler](exec.sched#concept:scheduler "33.6Schedulers[exec.sched]")[.](#exec.get.scheduler-2.sentence-3)
[3](#exec.get.scheduler-3)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L964)
forwarding_query(execution::get_scheduler) is
a core constant expression and has value true[.](#exec.get.scheduler-3.sentence-1)
### [33.5.7](#exec.get.delegation.scheduler) execution::get_delegation_scheduler [[exec.get.delegation.scheduler]](exec.get.delegation.scheduler)
[1](#exec.get.delegation.scheduler-1)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L970)
get_delegation_scheduler asks a queryable object for a scheduler
that can be used to delegate work to
for the purpose of forward progress delegation ([[intro.progress]](intro.progress "6.10.2.3Forward progress"))[.](#exec.get.delegation.scheduler-1.sentence-1)
[2](#exec.get.delegation.scheduler-2)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L975)
The name get_delegation_scheduler denotes a query object[.](#exec.get.delegation.scheduler-2.sentence-1)
For a subexpression env,get_delegation_scheduler(env) is expression-equivalent to*MANDATE-NOTHROW*(*AS-CONST*(env).query(get_delegation_scheduler))[.](#exec.get.delegation.scheduler-2.sentence-2)
*Mandates*: If the expression above is well-formed,
its type satisfies [scheduler](exec.sched#concept:scheduler "33.6Schedulers[exec.sched]")[.](#exec.get.delegation.scheduler-2.sentence-3)
[3](#exec.get.delegation.scheduler-3)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L985)
forwarding_query(execution::get_delegation_scheduler) is
a core constant expression and has value true[.](#exec.get.delegation.scheduler-3.sentence-1)
### [33.5.8](#exec.get.fwd.progress) execution::get_forward_progress_guarantee [[exec.get.fwd.progress]](exec.get.fwd.progress)
namespace std::execution {enum class [forward_progress_guarantee](#lib:forward_progress_guarantee "33.5.8execution::get_­forward_­progress_­guarantee[exec.get.fwd.progress]") { concurrent,
parallel,
weakly_parallel };}
[1](#exec.get.fwd.progress-1)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L1001)
get_forward_progress_guarantee asks a scheduler about
the forward progress guarantee of execution agents
created by that scheduler's associated execution resource ([[intro.progress]](intro.progress "6.10.2.3Forward progress"))[.](#exec.get.fwd.progress-1.sentence-1)
[2](#exec.get.fwd.progress-2)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L1006)
The name get_forward_progress_guarantee denotes a query object[.](#exec.get.fwd.progress-2.sentence-1)
For a subexpression sch, let Sch be decltype((sch))[.](#exec.get.fwd.progress-2.sentence-2)
If Sch does not satisfy [scheduler](exec.sched#concept:scheduler "33.6Schedulers[exec.sched]"),get_forward_progress_guarantee is ill-formed[.](#exec.get.fwd.progress-2.sentence-3)
Otherwise,get_forward_progress_guarantee(sch) is expression-equivalent to:
- [(2.1)](#exec.get.fwd.progress-2.1)
*MANDATE-NOTHROW*(*AS-CONST*(sch).query(get_forward_progress_guarantee)),
if that expression is well-formed[.](#exec.get.fwd.progress-2.1.sentence-1)
*Mandates*: The type of the expression above is forward_progress_guarantee[.](#exec.get.fwd.progress-2.1.sentence-2)
- [(2.2)](#exec.get.fwd.progress-2.2)
Otherwise, forward_progress_guarantee::weakly_parallel[.](#exec.get.fwd.progress-2.2.sentence-1)
[3](#exec.get.fwd.progress-3)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L1024)
If get_forward_progress_guarantee(sch) for some scheduler sch returns forward_progress_guarantee::concurrent,
all execution agents created by that scheduler's associated execution resource
shall provide the concurrent forward progress guarantee[.](#exec.get.fwd.progress-3.sentence-1)
If it returns forward_progress_guarantee::parallel,
all such execution agents
shall provide at least the parallel forward progress guarantee[.](#exec.get.fwd.progress-3.sentence-2)
### [33.5.9](#exec.get.compl.sched) execution::get_completion_scheduler [[exec.get.compl.sched]](exec.get.compl.sched)
[1](#exec.get.compl.sched-1)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L1035)
get_completion_scheduler<*completion-tag>* obtains
the completion scheduler associated with a completion tag
from a sender's attributes[.](#exec.get.compl.sched-1.sentence-1)
[2](#exec.get.compl.sched-2)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L1040)
The name get_completion_scheduler denotes a query object template[.](#exec.get.compl.sched-2.sentence-1)
For a subexpression q,
the expression get_completion_scheduler<*completion-tag*>(q) is ill-formed if *completion-tag* is not one ofset_value_t, set_error_t, or set_stopped_t[.](#exec.get.compl.sched-2.sentence-2)
Otherwise, get_completion_scheduler<*completion-tag*>(q) is expression-equivalent to*MANDATE-NOTHROW*(*AS-CONST*(q).query(get_completion_scheduler<*completion-tag*>))*Mandates*: If the expression above is well-formed,
its type satisfies [scheduler](exec.sched#concept:scheduler "33.6Schedulers[exec.sched]")[.](#exec.get.compl.sched-2.sentence-3)
[3](#exec.get.compl.sched-3)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L1055)
Let *completion-fn* be a completion function ([[exec.async.ops]](exec.async.ops "33.3Asynchronous operations"));
let *completion-tag* be
the associated completion tag of *completion-fn*;
let args be a pack of subexpressions; and
let sndr be a subexpression
such that [sender](exec.snd.concepts#concept:sender "33.9.3Sender concepts[exec.snd.concepts]")<decltype((sndr))> is true andget_completion_scheduler<*completion-tag*>(get_env(sndr)) is well-formed and denotes a scheduler sch[.](#exec.get.compl.sched-3.sentence-1)
If an asynchronous operation
created by connecting sndr with a receiver rcvr causes the evaluation of *completion-fn*(rcvr, args...),
the behavior is undefined
unless the evaluation happens on an execution agent
that belongs to sch's associated execution resource[.](#exec.get.compl.sched-3.sentence-2)
[4](#exec.get.compl.sched-4)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L1071)
The expressionforwarding_query(get_completion_scheduler<*completion-tag*>) is a core constant expression and has value true[.](#exec.get.compl.sched-4.sentence-1)
### [33.5.10](#exec.get.await.adapt) execution::get_await_completion_adaptor [[exec.get.await.adapt]](exec.get.await.adapt)
[1](#exec.get.await.adapt-1)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L1078)
get_await_completion_adaptor asks a queryable object for
its associated awaitable completion adaptor[.](#exec.get.await.adapt-1.sentence-1)
[2](#exec.get.await.adapt-2)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L1082)
The name get_await_completion_adaptor denotes a query object[.](#exec.get.await.adapt-2.sentence-1)
For a subexpression env,get_await_completion_adaptor(env) is expression-equivalent to*MANDATE-NOTHROW*(*AS-CONST*(env).query(get_await_completion_adaptor))
[3](#exec.get.await.adapt-3)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L1093)
forwarding_query(execution::get_await_completion_adaptor) is a core constant expression and has value true[.](#exec.get.await.adapt-3.sentence-1)

View File

@@ -0,0 +1,82 @@
[exec.queryable]
# 33 Execution control library [[exec]](./#exec)
## 33.2 Queries and queryables [exec.queryable]
### [33.2.1](#general) General [[exec.queryable.general]](exec.queryable.general)
[1](#general-1)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L115)
A [*queryable object*](#def:object,queryable "33.2.1General[exec.queryable.general]") is
a read-only collection of key/value pair
where each key is a customization point object known as a [*query object*](#def:query_object "33.2.1General[exec.queryable.general]")[.](#general-1.sentence-1)
A [*query*](#def:query "33.2.1General[exec.queryable.general]") is an invocation of a query object
with a queryable object as its first argument and
a (possibly empty) set of additional arguments[.](#general-1.sentence-2)
A query imposes syntactic and semantic requirements on its invocations[.](#general-1.sentence-3)
[2](#general-2)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L124)
Let q be a query object,
let args be a (possibly empty) pack of subexpressions,
let env be a subexpression
that refers to a queryable object o of type O, and
let cenv be a subexpression referring to o such that decltype((cenv)) is const O&[.](#general-2.sentence-1)
The expression q(env, args...) is equal to ([[concepts.equality]](concepts.equality "18.2Equality preservation"))
the expression q(cenv, args...)[.](#general-2.sentence-2)
[3](#general-3)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L134)
The type of a query expression cannot be void[.](#general-3.sentence-1)
[4](#general-4)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L137)
The expression q(env, args...) is
equality-preserving ([[concepts.equality]](concepts.equality "18.2Equality preservation")) and
does not modify the query object or the arguments[.](#general-4.sentence-1)
[5](#general-5)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L142)
If the expression env.query(q, args...) is well-formed,
then it is expression-equivalent to q(env, args...)[.](#general-5.sentence-1)
[6](#general-6)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L146)
Unless otherwise specified,
the result of a query is valid as long as the queryable object is valid[.](#general-6.sentence-1)
### [33.2.2](#concept) queryable concept [[exec.queryable.concept]](exec.queryable.concept)
namespace std {template<class T>concept [*queryable*](#concept:queryable "33.2.2queryable concept[exec.queryable.concept]") = [destructible](concept.destructible#concept:destructible "18.4.10Concept destructible[concept.destructible]")<T>; // *exposition only*}
[1](#concept-1)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L159)
The exposition-only [*queryable*](#concept:queryable "33.2.2queryable concept[exec.queryable.concept]") concept specifies
the constraints on the types of queryable objects[.](#concept-1.sentence-1)
[2](#concept-2)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L163)
Let env be an object of type Env[.](#concept-2.sentence-1)
The type Env models [*queryable*](#concept:queryable "33.2.2queryable concept[exec.queryable.concept]") if for each callable object q and a pack of subexpressions args,
if requires { q(env, args...) } is true thenq(env, args...) meets any semantic requirements imposed by q[.](#concept-2.sentence-2)

View File

@@ -0,0 +1,25 @@
[exec.queryable.concept]
# 33 Execution control library [[exec]](./#exec)
## 33.2 Queries and queryables [[exec.queryable]](exec.queryable#concept)
### 33.2.2 queryable concept [exec.queryable.concept]
namespace std {template<class T>concept [*queryable*](#concept:queryable "33.2.2queryable concept[exec.queryable.concept]") = [destructible](concept.destructible#concept:destructible "18.4.10Concept destructible[concept.destructible]")<T>; // *exposition only*}
[1](#1)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L159)
The exposition-only [*queryable*](#concept:queryable "33.2.2queryable concept[exec.queryable.concept]") concept specifies
the constraints on the types of queryable objects[.](#1.sentence-1)
[2](#2)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L163)
Let env be an object of type Env[.](#2.sentence-1)
The type Env models [*queryable*](#concept:queryable "33.2.2queryable concept[exec.queryable.concept]") if for each callable object q and a pack of subexpressions args,
if requires { q(env, args...) } is true thenq(env, args...) meets any semantic requirements imposed by q[.](#2.sentence-2)

View File

@@ -0,0 +1,62 @@
[exec.queryable.general]
# 33 Execution control library [[exec]](./#exec)
## 33.2 Queries and queryables [[exec.queryable]](exec.queryable#general)
### 33.2.1 General [exec.queryable.general]
[1](#1)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L115)
A [*queryable object*](#def:object,queryable "33.2.1General[exec.queryable.general]") is
a read-only collection of key/value pair
where each key is a customization point object known as a [*query object*](#def:query_object "33.2.1General[exec.queryable.general]")[.](#1.sentence-1)
A [*query*](#def:query "33.2.1General[exec.queryable.general]") is an invocation of a query object
with a queryable object as its first argument and
a (possibly empty) set of additional arguments[.](#1.sentence-2)
A query imposes syntactic and semantic requirements on its invocations[.](#1.sentence-3)
[2](#2)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L124)
Let q be a query object,
let args be a (possibly empty) pack of subexpressions,
let env be a subexpression
that refers to a queryable object o of type O, and
let cenv be a subexpression referring to o such that decltype((cenv)) is const O&[.](#2.sentence-1)
The expression q(env, args...) is equal to ([[concepts.equality]](concepts.equality "18.2Equality preservation"))
the expression q(cenv, args...)[.](#2.sentence-2)
[3](#3)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L134)
The type of a query expression cannot be void[.](#3.sentence-1)
[4](#4)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L137)
The expression q(env, args...) is
equality-preserving ([[concepts.equality]](concepts.equality "18.2Equality preservation")) and
does not modify the query object or the arguments[.](#4.sentence-1)
[5](#5)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L142)
If the expression env.query(q, args...) is well-formed,
then it is expression-equivalent to q(env, args...)[.](#5.sentence-1)
[6](#6)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L146)
Unless otherwise specified,
the result of a query is valid as long as the queryable object is valid[.](#6.sentence-1)

57
cppdraft/exec/read/env.md Normal file
View File

@@ -0,0 +1,57 @@
[exec.read.env]
# 33 Execution control library [[exec]](./#exec)
## 33.9 Senders [[exec.snd]](exec.snd#exec.read.env)
### 33.9.11 Sender factories [[exec.factories]](exec.factories#exec.read.env)
#### 33.9.11.3 execution::read_env [exec.read.env]
[1](#1)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2950)
read_env is a sender factory for a sender
whose asynchronous operation completes synchronously in its start operation
with a value completion result equal to
a value read from the receiver's associated environment[.](#1.sentence-1)
[2](#2)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2956)
read_env is a customization point object[.](#2.sentence-1)
For some query object q,
the expression read_env(q) is expression-equivalent to*make-sender*(read_env, q)[.](#2.sentence-2)
[3](#3)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2962)
The exposition-only class template *impls-for* ([[exec.snd.expos]](exec.snd.expos "33.9.2Exposition-only entities"))
is specialized for read_env as follows:
[🔗](#lib:impls-for%3cdecayed-typeof%3cread_env%3e%3e)
namespace std::execution {template<>struct *impls-for*<*decayed-typeof*<read_env>> : *default-impls* {static constexpr auto start =[](auto query, auto& rcvr) noexcept -> void {*TRY-SET-VALUE*(rcvr, query(get_env(rcvr))); }; }; template<class Sndr, class Env>static consteval void *check-types*();}
[🔗](#lib:check-types,impls-for%3cdecayed-typeof%3cread_env%3e%3e)
`template<class Sndr, class Env>
static consteval void check-types();
`
[4](#4)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2988)
Let Q be decay_t<*data-type*<Sndr>>[.](#4.sentence-1)
[5](#5)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2991)
*Throws*: An exception of an unspecified type derived from exception if
the expression Q()(env) is ill-formed or has type void, whereenv is an lvalue subexpression whose type is Env[.](#5.sentence-1)

102
cppdraft/exec/recv.md Normal file
View File

@@ -0,0 +1,102 @@
[exec.recv]
# 33 Execution control library [[exec]](./#exec)
## 33.7 Receivers [exec.recv]
### [33.7.1](#concepts) Receiver concepts [[exec.recv.concepts]](exec.recv.concepts)
[1](#concepts-1)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L1173)
A receiver represents the continuation of an asynchronous operation[.](#concepts-1.sentence-1)
The [receiver](#concept:receiver "33.7.1Receiver concepts[exec.recv.concepts]") concept defines
the requirements for a receiver type ([[exec.async.ops]](exec.async.ops "33.3Asynchronous operations"))[.](#concepts-1.sentence-2)
The [receiver_of](#concept:receiver_of "33.7.1Receiver concepts[exec.recv.concepts]") concept defines
the requirements for a receiver type that is usable as
the first argument of a set of completion operations
corresponding to a set of completion signatures[.](#concepts-1.sentence-3)
The get_env customization point object is used to access
a receiver's associated environment[.](#concepts-1.sentence-4)
namespace std::execution {template<class Rcvr>concept [receiver](#concept:receiver "33.7.1Receiver concepts[exec.recv.concepts]") =[derived_from](concept.derived#concept:derived_from "18.4.3Concept derived_­from[concept.derived]")<typename remove_cvref_t<Rcvr>::receiver_concept, receiver_t> &&requires(const remove_cvref_t<Rcvr>& rcvr) {{ get_env(rcvr) } -> [*queryable*](exec.queryable.concept#concept:queryable "33.2.2queryable concept[exec.queryable.concept]"); } &&[move_constructible](concept.moveconstructible#concept:move_constructible "18.4.13Concept move_­constructible[concept.moveconstructible]")<remove_cvref_t<Rcvr>> && // rvalues are movable, and[constructible_from](concept.constructible#concept:constructible_from "18.4.11Concept constructible_­from[concept.constructible]")<remove_cvref_t<Rcvr>, Rcvr>; // lvalues are copyabletemplate<class Signature, class Rcvr>concept [*valid-completion-for*](#concept:valid-completion-for "33.7.1Receiver concepts[exec.recv.concepts]") = // *exposition only*requires (Signature* sig) {[]<class Tag, class... Args>(Tag(*)(Args...))requires [*callable*](functional.syn#concept:callable "22.10.2Header <functional> synopsis[functional.syn]")<Tag, remove_cvref_t<Rcvr>, Args...>{}(sig); }; template<class Rcvr, class Completions>concept [*has-completions*](#concept:has-completions "33.7.1Receiver concepts[exec.recv.concepts]") = // *exposition only*requires (Completions* completions) {[]<[*valid-completion-for*](#concept:valid-completion-for "33.7.1Receiver concepts[exec.recv.concepts]")<Rcvr>...Sigs>(completion_signatures<Sigs...>*){}(completions); }; template<class Rcvr, class Completions>concept [receiver_of](#concept:receiver_of "33.7.1Receiver concepts[exec.recv.concepts]") =[receiver](#concept:receiver "33.7.1Receiver concepts[exec.recv.concepts]")<Rcvr> && [*has-completions*](#concept:has-completions "33.7.1Receiver concepts[exec.recv.concepts]")<Rcvr, Completions>;}
[2](#concepts-2)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L1215)
Class types that are marked final do not model the [receiver](#concept:receiver "33.7.1Receiver concepts[exec.recv.concepts]") concept[.](#concepts-2.sentence-1)
[3](#concepts-3)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L1218)
Let rcvr be a receiver and
let op_state be an operation state associated with
an asynchronous operation created by connecting rcvr with a sender[.](#concepts-3.sentence-1)
Let token be a stop token equal toget_stop_token(get_env(rcvr))[.](#concepts-3.sentence-2)
token shall remain valid
for the duration of the asynchronous operation's lifetime ([[exec.async.ops]](exec.async.ops "33.3Asynchronous operations"))[.](#concepts-3.sentence-3)
[*Note [1](#concepts-note-1)*:
This means that, unless it knows about further guarantees
provided by the type of rcvr,
the implementation of op_state cannot use token after it executes a completion operation[.](#concepts-3.sentence-4)
This also implies that any stop callbacks registered on token
must be destroyed before the invocation of the completion operation[.](#concepts-3.sentence-5)
— *end note*]
### [33.7.2](#exec.set.value) execution::set_value [[exec.set.value]](exec.set.value)
[1](#exec.set.value-1)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L1237)
set_value is a value completion function ([[exec.async.ops]](exec.async.ops "33.3Asynchronous operations"))[.](#exec.set.value-1.sentence-1)
Its associated completion tag is set_value_t[.](#exec.set.value-1.sentence-2)
The expression set_value(rcvr, vs...) for a subexpression rcvr and
pack of subexpressions vs is ill-formed
if rcvr is an lvalue or an rvalue of const type[.](#exec.set.value-1.sentence-3)
Otherwise, it is expression-equivalent to*MANDATE-NOTHROW*(rcvr.set_value(vs...))[.](#exec.set.value-1.sentence-4)
### [33.7.3](#exec.set.error) execution::set_error [[exec.set.error]](exec.set.error)
[1](#exec.set.error-1)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L1249)
set_error is an error completion function ([[exec.async.ops]](exec.async.ops "33.3Asynchronous operations"))[.](#exec.set.error-1.sentence-1)
Its associated completion tag is set_error_t[.](#exec.set.error-1.sentence-2)
The expression set_error(rcvr, err) for some subexpressions rcvr and err is ill-formed
if rcvr is an lvalue or an rvalue of const type[.](#exec.set.error-1.sentence-3)
Otherwise, it is expression-equivalent to*MANDATE-NOTHROW*(rcvr.set_error(err))[.](#exec.set.error-1.sentence-4)
### [33.7.4](#exec.set.stopped) execution::set_stopped [[exec.set.stopped]](exec.set.stopped)
[1](#exec.set.stopped-1)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L1260)
set_stopped is a stopped completion function ([[exec.async.ops]](exec.async.ops "33.3Asynchronous operations"))[.](#exec.set.stopped-1.sentence-1)
Its associated completion tag is set_stopped_t[.](#exec.set.stopped-1.sentence-2)
The expression set_stopped(rcvr) for a subexpression rcvr is ill-formed
if rcvr is an lvalue or an rvalue of const type[.](#exec.set.stopped-1.sentence-3)
Otherwise, it is expression-equivalent to*MANDATE-NOTHROW*(rcvr.set_stopped())[.](#exec.set.stopped-1.sentence-4)

View File

@@ -0,0 +1,56 @@
[exec.recv.concepts]
# 33 Execution control library [[exec]](./#exec)
## 33.7 Receivers [[exec.recv]](exec.recv#concepts)
### 33.7.1 Receiver concepts [exec.recv.concepts]
[1](#1)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L1173)
A receiver represents the continuation of an asynchronous operation[.](#1.sentence-1)
The [receiver](#concept:receiver "33.7.1Receiver concepts[exec.recv.concepts]") concept defines
the requirements for a receiver type ([[exec.async.ops]](exec.async.ops "33.3Asynchronous operations"))[.](#1.sentence-2)
The [receiver_of](#concept:receiver_of "33.7.1Receiver concepts[exec.recv.concepts]") concept defines
the requirements for a receiver type that is usable as
the first argument of a set of completion operations
corresponding to a set of completion signatures[.](#1.sentence-3)
The get_env customization point object is used to access
a receiver's associated environment[.](#1.sentence-4)
namespace std::execution {template<class Rcvr>concept [receiver](#concept:receiver "33.7.1Receiver concepts[exec.recv.concepts]") =[derived_from](concept.derived#concept:derived_from "18.4.3Concept derived_­from[concept.derived]")<typename remove_cvref_t<Rcvr>::receiver_concept, receiver_t> &&requires(const remove_cvref_t<Rcvr>& rcvr) {{ get_env(rcvr) } -> [*queryable*](exec.queryable.concept#concept:queryable "33.2.2queryable concept[exec.queryable.concept]"); } &&[move_constructible](concept.moveconstructible#concept:move_constructible "18.4.13Concept move_­constructible[concept.moveconstructible]")<remove_cvref_t<Rcvr>> && // rvalues are movable, and[constructible_from](concept.constructible#concept:constructible_from "18.4.11Concept constructible_­from[concept.constructible]")<remove_cvref_t<Rcvr>, Rcvr>; // lvalues are copyabletemplate<class Signature, class Rcvr>concept [*valid-completion-for*](#concept:valid-completion-for "33.7.1Receiver concepts[exec.recv.concepts]") = // *exposition only*requires (Signature* sig) {[]<class Tag, class... Args>(Tag(*)(Args...))requires [*callable*](functional.syn#concept:callable "22.10.2Header <functional> synopsis[functional.syn]")<Tag, remove_cvref_t<Rcvr>, Args...>{}(sig); }; template<class Rcvr, class Completions>concept [*has-completions*](#concept:has-completions "33.7.1Receiver concepts[exec.recv.concepts]") = // *exposition only*requires (Completions* completions) {[]<[*valid-completion-for*](#concept:valid-completion-for "33.7.1Receiver concepts[exec.recv.concepts]")<Rcvr>...Sigs>(completion_signatures<Sigs...>*){}(completions); }; template<class Rcvr, class Completions>concept [receiver_of](#concept:receiver_of "33.7.1Receiver concepts[exec.recv.concepts]") =[receiver](#concept:receiver "33.7.1Receiver concepts[exec.recv.concepts]")<Rcvr> && [*has-completions*](#concept:has-completions "33.7.1Receiver concepts[exec.recv.concepts]")<Rcvr, Completions>;}
[2](#2)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L1215)
Class types that are marked final do not model the [receiver](#concept:receiver "33.7.1Receiver concepts[exec.recv.concepts]") concept[.](#2.sentence-1)
[3](#3)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L1218)
Let rcvr be a receiver and
let op_state be an operation state associated with
an asynchronous operation created by connecting rcvr with a sender[.](#3.sentence-1)
Let token be a stop token equal toget_stop_token(get_env(rcvr))[.](#3.sentence-2)
token shall remain valid
for the duration of the asynchronous operation's lifetime ([[exec.async.ops]](exec.async.ops "33.3Asynchronous operations"))[.](#3.sentence-3)
[*Note [1](#note-1)*:
This means that, unless it knows about further guarantees
provided by the type of rcvr,
the implementation of op_state cannot use token after it executes a completion operation[.](#3.sentence-4)
This also implies that any stop callbacks registered on token
must be destroyed before the invocation of the completion operation[.](#3.sentence-5)
— *end note*]

298
cppdraft/exec/run/loop.md Normal file
View File

@@ -0,0 +1,298 @@
[exec.run.loop]
# 33 Execution control library [[exec]](./#exec)
## 33.12 Execution contexts [[exec.ctx]](exec.ctx#exec.run.loop)
### 33.12.1 execution::run_loop [exec.run.loop]
#### [33.12.1.1](#general) General [[exec.run.loop.general]](exec.run.loop.general)
[1](#general-1)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6321)
A run_loop is an execution resource on which work can be scheduled[.](#general-1.sentence-1)
It maintains a thread-safe first-in-first-out queue of work[.](#general-1.sentence-2)
Its run member function removes elements from the queue and
executes them in a loop on the thread of execution that calls run[.](#general-1.sentence-3)
[2](#general-2)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6327)
A run_loop instance has an associated [*count*](#def:count "33.12.1.1General[exec.run.loop.general]") that corresponds to the number of work items that are in its queue[.](#general-2.sentence-1)
Additionally, a run_loop instance has an associated state
that can be one of[*starting*](#def:starting "33.12.1.1General[exec.run.loop.general]"), [*running*](#def:running "33.12.1.1General[exec.run.loop.general]"), [*finishing*](#def:finishing "33.12.1.1General[exec.run.loop.general]"), or [*finished*](#def:finished "33.12.1.1General[exec.run.loop.general]")[.](#general-2.sentence-2)
[3](#general-3)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6334)
Concurrent invocations of the member functions of run_loop other than run and its destructor do not introduce data races[.](#general-3.sentence-1)
The member functions*pop-front*, *push-back*, and finish execute atomically[.](#general-3.sentence-2)
[4](#general-4)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6341)
*Recommended practice*: Implementations should use an intrusive queue of operation states
to hold the work units to make scheduling allocation-free[.](#general-4.sentence-1)
namespace std::execution {class [run_loop](#lib:run_loop "33.12.1.1General[exec.run.loop.general]") {// [[exec.run.loop.types]](#types "33.12.1.2Associated types"), associated typesclass *run-loop-scheduler*; // *exposition only*class *run-loop-sender*; // *exposition only*struct *run-loop-opstate-base* { // *exposition only*virtual void *execute*() = 0; // *exposition only* run_loop* *loop*; // *exposition only**run-loop-opstate-base** *next*; // *exposition only*}; template<class Rcvr>using *run-loop-opstate* = *unspecified*; // *exposition only*// [[exec.run.loop.members]](#members "33.12.1.4Member functions"), member functions*run-loop-opstate-base** *pop-front*(); // *exposition only*void *push-back*(*run-loop-opstate-base**); // *exposition only*public:// [[exec.run.loop.ctor]](#ctor "33.12.1.3Constructor and destructor"), constructor and destructor run_loop() noexcept;
run_loop(run_loop&&) = delete; ~run_loop(); // [[exec.run.loop.members]](#members "33.12.1.4Member functions"), member functions*run-loop-scheduler* get_scheduler(); void run(); void finish(); };}
#### [33.12.1.2](#types) Associated types [[exec.run.loop.types]](exec.run.loop.types)
[🔗](#types-itemdecl:1)
`class run-loop-scheduler;
`
[1](#types-1)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6384)
*run-loop-scheduler* is an unspecified type
that models [scheduler](exec.sched#concept:scheduler "33.6Schedulers[exec.sched]")[.](#types-1.sentence-1)
[2](#types-2)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6388)
Instances of *run-loop-scheduler* remain valid
until the end of the lifetime of the run_loop instance
from which they were obtained[.](#types-2.sentence-1)
[3](#types-3)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6393)
Two instances of *run-loop-scheduler* compare equal
if and only if they were obtained from the same run_loop instance[.](#types-3.sentence-1)
[4](#types-4)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6397)
Let *sch* be an expression of type *run-loop-scheduler*[.](#types-4.sentence-1)
The expression schedule(*sch*) has type *run-loop-
sender* and
is not potentially-throwing if *sch* is not potentially-throwing[.](#types-4.sentence-2)
[🔗](#types-itemdecl:2)
`class run-loop-sender;
`
[5](#types-5)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6407)
*run-loop-sender* is an exposition-only type
that satisfies [sender](exec.snd.concepts#concept:sender "33.9.3Sender concepts[exec.snd.concepts]")[.](#types-5.sentence-1)
completion_signatures_of_t<*run-
loop-sender*> iscompletion_signatures<set_value_t(), set_error_t(exception_ptr), set_stopped_t()>
[6](#types-6)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6415)
An instance of *run-loop-sender* remains valid
until the end of the lifetime of its associated run_loop instance[.](#types-6.sentence-1)
[7](#types-7)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6419)
Let *sndr* be an expression of type *run-loop-sender*,
let *rcvr* be an expression
such that [receiver_of](exec.recv.concepts#concept:receiver_of "33.7.1Receiver concepts[exec.recv.concepts]")<decltype((*rcvr*)), CS> is true where CS is the completion_signatures specialization above[.](#types-7.sentence-1)
Let C be either set_value_t or set_stopped_t[.](#types-7.sentence-2)
Then:
- [(7.1)](#types-7.1)
The expression connect(*sndr*, *rcvr*) has type *run-loop-opstate*<decay_t<decltype((*rcvr*))>> and is potentially-throwing if and only if(void(*sndr*), auto(*rcvr*)) is potentially-throwing[.](#types-7.1.sentence-1)
- [(7.2)](#types-7.2)
The expression get_completion_scheduler<C>(get_env(*sndr*)) is potentially-throwing if and only if *sndr* is potentially-throwing,
has type *run-loop-scheduler*, and
compares equal to the *run-loop-
scheduler* instance
from which *sndr* was obtained[.](#types-7.2.sentence-1)
[🔗](#types-itemdecl:3)
`template<class Rcvr>
struct run-loop-opstate;
`
[8](#types-8)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6445)
*run-loop-opstate*<Rcvr> inherits privately and unambiguously from *run-loop-opstate-base*[.](#types-8.sentence-1)
[9](#types-9)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6449)
Let o be a non-const lvalue of type *run-loop-opstate*<Rcvr>,
and let REC(o) be a non-const lvalue reference to an instance of type Rcvr that was initialized with the expression *rcvr* passed to the invocation of connect that returned o[.](#types-9.sentence-1)
Then:
- [(9.1)](#types-9.1)
The object to which *REC*(o) refers
remains valid for the lifetime of the object to which o refers.
- [(9.2)](#types-9.2)
The type *run-loop-opstate*<Rcvr> overrides*run-loop-opstate-base*::*execute*() such that o.*execute*() is equivalent to:if (get_stop_token(*REC*(o)).stop_requested()) { set_stopped(std::move(*REC*(o)));} else { set_value(std::move(*REC*(o)));}
- [(9.3)](#types-9.3)
The expression start(o) is equivalent to:try {o.*loop*->*push-back*(addressof(o));} catch(...) { set_error(std::move(*REC*(o)), current_exception());}
#### [33.12.1.3](#ctor) Constructor and destructor [[exec.run.loop.ctor]](exec.run.loop.ctor)
[🔗](#lib:run_loop,constructor)
`run_loop() noexcept;
`
[1](#ctor-1)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6489)
*Postconditions*: *count* is 0 and *state* is *starting*[.](#ctor-1.sentence-1)
[🔗](#lib:run_loop,destructor)
`~run_loop();
`
[2](#ctor-2)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6500)
*Effects*: If *count* is not 0 or if *state* is *running*,
invokes terminate ([[except.terminate]](except.terminate "14.6.2The std::terminate function"))[.](#ctor-2.sentence-1)
Otherwise, has no effects[.](#ctor-2.sentence-2)
#### [33.12.1.4](#members) Member functions [[exec.run.loop.members]](exec.run.loop.members)
[🔗](#members-itemdecl:1)
`run-loop-opstate-base* pop-front();
`
[1](#members-1)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6514)
*Effects*: Blocks ([[defns.block]](defns.block "3.6block")) until one of the following conditions is true:
- [(1.1)](#members-1.1)
*count* is 0 and *state* is *finishing*,
in which case *pop-front* sets *state* to *finished* and returns nullptr; or
- [(1.2)](#members-1.2)
*count* is greater than 0,
in which case an item is removed from the front of the queue,*count* is decremented by 1, and
the removed item is returned[.](#members-1.sentence-1)
[🔗](#members-itemdecl:2)
`void push-back(run-loop-opstate-base* item);
`
[2](#members-2)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6535)
*Effects*: Adds item to the back of the queue and
increments *count* by 1[.](#members-2.sentence-1)
[3](#members-3)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6540)
*Synchronization*: This operation synchronizes with
the *pop-front* operation that obtains item[.](#members-3.sentence-1)
[🔗](#lib:get_scheduler,run_loop)
`run-loop-scheduler get_scheduler();
`
[4](#members-4)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6552)
*Returns*: An instance of *run-loop-scheduler* that can be used to schedule work onto this run_loop instance[.](#members-4.sentence-1)
[🔗](#lib:run,run_loop)
`void run();
`
[5](#members-5)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6564)
*Preconditions*: *state* is either *starting* or *finishing*[.](#members-5.sentence-1)
[6](#members-6)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6568)
*Effects*: If *state* is *starting*,
sets the *state* to *running*,
otherwise leaves *state* unchanged[.](#members-6.sentence-1)
Then, equivalent to:while (auto* op = *pop-front*()) { op->*execute*();}
[7](#members-7)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6580)
*Remarks*: When *state* changes, it does so without introducing data races[.](#members-7.sentence-1)
[🔗](#lib:finish,run_loop)
`void finish();
`
[8](#members-8)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6591)
*Preconditions*: *state* is either *starting* or *running*[.](#members-8.sentence-1)
[9](#members-9)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6595)
*Effects*: Changes *state* to *finishing*[.](#members-9.sentence-1)
[10](#members-10)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6599)
*Synchronization*: finish synchronizes with the *pop-front* operation
that returns nullptr[.](#members-10.sentence-1)

View File

@@ -0,0 +1,34 @@
[exec.run.loop.ctor]
# 33 Execution control library [[exec]](./#exec)
## 33.12 Execution contexts [[exec.ctx]](exec.ctx#exec.run.loop.ctor)
### 33.12.1 execution::run_loop [[exec.run.loop]](exec.run.loop#ctor)
#### 33.12.1.3 Constructor and destructor [exec.run.loop.ctor]
[🔗](#lib:run_loop,constructor)
`run_loop() noexcept;
`
[1](#1)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6489)
*Postconditions*: *count* is 0 and *state* is *starting*[.](#1.sentence-1)
[🔗](#lib:run_loop,destructor)
`~run_loop();
`
[2](#2)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6500)
*Effects*: If *count* is not 0 or if *state* is *running*,
invokes terminate ([[except.terminate]](except.terminate "14.6.2The std::terminate function"))[.](#2.sentence-1)
Otherwise, has no effects[.](#2.sentence-2)

View File

@@ -0,0 +1,47 @@
[exec.run.loop.general]
# 33 Execution control library [[exec]](./#exec)
## 33.12 Execution contexts [[exec.ctx]](exec.ctx#exec.run.loop.general)
### 33.12.1 execution::run_loop [[exec.run.loop]](exec.run.loop#general)
#### 33.12.1.1 General [exec.run.loop.general]
[1](#1)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6321)
A run_loop is an execution resource on which work can be scheduled[.](#1.sentence-1)
It maintains a thread-safe first-in-first-out queue of work[.](#1.sentence-2)
Its run member function removes elements from the queue and
executes them in a loop on the thread of execution that calls run[.](#1.sentence-3)
[2](#2)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6327)
A run_loop instance has an associated [*count*](#def:count "33.12.1.1General[exec.run.loop.general]") that corresponds to the number of work items that are in its queue[.](#2.sentence-1)
Additionally, a run_loop instance has an associated state
that can be one of[*starting*](#def:starting "33.12.1.1General[exec.run.loop.general]"), [*running*](#def:running "33.12.1.1General[exec.run.loop.general]"), [*finishing*](#def:finishing "33.12.1.1General[exec.run.loop.general]"), or [*finished*](#def:finished "33.12.1.1General[exec.run.loop.general]")[.](#2.sentence-2)
[3](#3)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6334)
Concurrent invocations of the member functions of run_loop other than run and its destructor do not introduce data races[.](#3.sentence-1)
The member functions*pop-front*, *push-back*, and finish execute atomically[.](#3.sentence-2)
[4](#4)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6341)
*Recommended practice*: Implementations should use an intrusive queue of operation states
to hold the work units to make scheduling allocation-free[.](#4.sentence-1)
namespace std::execution {class [run_loop](#lib:run_loop "33.12.1.1General[exec.run.loop.general]") {// [[exec.run.loop.types]](exec.run.loop.types "33.12.1.2Associated types"), associated typesclass *run-loop-scheduler*; // *exposition only*class *run-loop-sender*; // *exposition only*struct *run-loop-opstate-base* { // *exposition only*virtual void *execute*() = 0; // *exposition only* run_loop* *loop*; // *exposition only**run-loop-opstate-base** *next*; // *exposition only*}; template<class Rcvr>using *run-loop-opstate* = *unspecified*; // *exposition only*// [[exec.run.loop.members]](exec.run.loop.members "33.12.1.4Member functions"), member functions*run-loop-opstate-base** *pop-front*(); // *exposition only*void *push-back*(*run-loop-opstate-base**); // *exposition only*public:// [[exec.run.loop.ctor]](exec.run.loop.ctor "33.12.1.3Constructor and destructor"), constructor and destructor run_loop() noexcept;
run_loop(run_loop&&) = delete; ~run_loop(); // [[exec.run.loop.members]](exec.run.loop.members "33.12.1.4Member functions"), member functions*run-loop-scheduler* get_scheduler(); void run(); void finish(); };}

View File

@@ -0,0 +1,112 @@
[exec.run.loop.members]
# 33 Execution control library [[exec]](./#exec)
## 33.12 Execution contexts [[exec.ctx]](exec.ctx#exec.run.loop.members)
### 33.12.1 execution::run_loop [[exec.run.loop]](exec.run.loop#members)
#### 33.12.1.4 Member functions [exec.run.loop.members]
[🔗](#itemdecl:1)
`run-loop-opstate-base* pop-front();
`
[1](#1)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6514)
*Effects*: Blocks ([[defns.block]](defns.block "3.6block")) until one of the following conditions is true:
- [(1.1)](#1.1)
*count* is 0 and *state* is *finishing*,
in which case *pop-front* sets *state* to *finished* and returns nullptr; or
- [(1.2)](#1.2)
*count* is greater than 0,
in which case an item is removed from the front of the queue,*count* is decremented by 1, and
the removed item is returned[.](#1.sentence-1)
[🔗](#itemdecl:2)
`void push-back(run-loop-opstate-base* item);
`
[2](#2)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6535)
*Effects*: Adds item to the back of the queue and
increments *count* by 1[.](#2.sentence-1)
[3](#3)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6540)
*Synchronization*: This operation synchronizes with
the *pop-front* operation that obtains item[.](#3.sentence-1)
[🔗](#lib:get_scheduler,run_loop)
`run-loop-scheduler get_scheduler();
`
[4](#4)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6552)
*Returns*: An instance of *run-loop-scheduler* that can be used to schedule work onto this run_loop instance[.](#4.sentence-1)
[🔗](#lib:run,run_loop)
`void run();
`
[5](#5)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6564)
*Preconditions*: *state* is either *starting* or *finishing*[.](#5.sentence-1)
[6](#6)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6568)
*Effects*: If *state* is *starting*,
sets the *state* to *running*,
otherwise leaves *state* unchanged[.](#6.sentence-1)
Then, equivalent to:while (auto* op = *pop-front*()) { op->*execute*();}
[7](#7)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6580)
*Remarks*: When *state* changes, it does so without introducing data races[.](#7.sentence-1)
[🔗](#lib:finish,run_loop)
`void finish();
`
[8](#8)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6591)
*Preconditions*: *state* is either *starting* or *running*[.](#8.sentence-1)
[9](#9)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6595)
*Effects*: Changes *state* to *finishing*[.](#9.sentence-1)
[10](#10)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6599)
*Synchronization*: finish synchronizes with the *pop-front* operation
that returns nullptr[.](#10.sentence-1)

View File

@@ -0,0 +1,126 @@
[exec.run.loop.types]
# 33 Execution control library [[exec]](./#exec)
## 33.12 Execution contexts [[exec.ctx]](exec.ctx#exec.run.loop.types)
### 33.12.1 execution::run_loop [[exec.run.loop]](exec.run.loop#types)
#### 33.12.1.2 Associated types [exec.run.loop.types]
[🔗](#itemdecl:1)
`class run-loop-scheduler;
`
[1](#1)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6384)
*run-loop-scheduler* is an unspecified type
that models [scheduler](exec.sched#concept:scheduler "33.6Schedulers[exec.sched]")[.](#1.sentence-1)
[2](#2)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6388)
Instances of *run-loop-scheduler* remain valid
until the end of the lifetime of the run_loop instance
from which they were obtained[.](#2.sentence-1)
[3](#3)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6393)
Two instances of *run-loop-scheduler* compare equal
if and only if they were obtained from the same run_loop instance[.](#3.sentence-1)
[4](#4)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6397)
Let *sch* be an expression of type *run-loop-scheduler*[.](#4.sentence-1)
The expression schedule(*sch*) has type *run-loop-
sender* and
is not potentially-throwing if *sch* is not potentially-throwing[.](#4.sentence-2)
[🔗](#itemdecl:2)
`class run-loop-sender;
`
[5](#5)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6407)
*run-loop-sender* is an exposition-only type
that satisfies [sender](exec.snd.concepts#concept:sender "33.9.3Sender concepts[exec.snd.concepts]")[.](#5.sentence-1)
completion_signatures_of_t<*run-
loop-sender*> iscompletion_signatures<set_value_t(), set_error_t(exception_ptr), set_stopped_t()>
[6](#6)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6415)
An instance of *run-loop-sender* remains valid
until the end of the lifetime of its associated run_loop instance[.](#6.sentence-1)
[7](#7)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6419)
Let *sndr* be an expression of type *run-loop-sender*,
let *rcvr* be an expression
such that [receiver_of](exec.recv.concepts#concept:receiver_of "33.7.1Receiver concepts[exec.recv.concepts]")<decltype((*rcvr*)), CS> is true where CS is the completion_signatures specialization above[.](#7.sentence-1)
Let C be either set_value_t or set_stopped_t[.](#7.sentence-2)
Then:
- [(7.1)](#7.1)
The expression connect(*sndr*, *rcvr*) has type *run-loop-opstate*<decay_t<decltype((*rcvr*))>> and is potentially-throwing if and only if(void(*sndr*), auto(*rcvr*)) is potentially-throwing[.](#7.1.sentence-1)
- [(7.2)](#7.2)
The expression get_completion_scheduler<C>(get_env(*sndr*)) is potentially-throwing if and only if *sndr* is potentially-throwing,
has type *run-loop-scheduler*, and
compares equal to the *run-loop-
scheduler* instance
from which *sndr* was obtained[.](#7.2.sentence-1)
[🔗](#itemdecl:3)
`template<class Rcvr>
struct run-loop-opstate;
`
[8](#8)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6445)
*run-loop-opstate*<Rcvr> inherits privately and unambiguously from *run-loop-opstate-base*[.](#8.sentence-1)
[9](#9)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6449)
Let o be a non-const lvalue of type *run-loop-opstate*<Rcvr>,
and let REC(o) be a non-const lvalue reference to an instance of type Rcvr that was initialized with the expression *rcvr* passed to the invocation of connect that returned o[.](#9.sentence-1)
Then:
- [(9.1)](#9.1)
The object to which *REC*(o) refers
remains valid for the lifetime of the object to which o refers.
- [(9.2)](#9.2)
The type *run-loop-opstate*<Rcvr> overrides*run-loop-opstate-base*::*execute*() such that o.*execute*() is equivalent to:if (get_stop_token(*REC*(o)).stop_requested()) { set_stopped(std::move(*REC*(o)));} else { set_value(std::move(*REC*(o)));}
- [(9.3)](#9.3)
The expression start(o) is equivalent to:try {o.*loop*->*push-back*(addressof(o));} catch(...) { set_error(std::move(*REC*(o)), current_exception());}

78
cppdraft/exec/sched.md Normal file
View File

@@ -0,0 +1,78 @@
[exec.sched]
# 33 Execution control library [[exec]](./#exec)
## 33.6 Schedulers [exec.sched]
[1](#1)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L1099)
The [scheduler](#concept:scheduler "33.6Schedulers[exec.sched]") concept defines
the requirements of a scheduler type ([[exec.async.ops]](exec.async.ops "33.3Asynchronous operations"))[.](#1.sentence-1)
schedule is a customization point object
that accepts a scheduler[.](#1.sentence-2)
A valid invocation of schedule is a schedule-expression[.](#1.sentence-3)
namespace std::execution {template<class Sch>concept [scheduler](#concept:scheduler "33.6Schedulers[exec.sched]") =[derived_from](concept.derived#concept:derived_from "18.4.3Concept derived_­from[concept.derived]")<typename remove_cvref_t<Sch>::scheduler_concept, scheduler_t> &&[*queryable*](exec.queryable.concept#concept:queryable "33.2.2queryable concept[exec.queryable.concept]")<Sch> &&requires(Sch&& sch) {{ schedule(std::forward<Sch>(sch)) } -> [sender](exec.snd.concepts#concept:sender "33.9.3Sender concepts[exec.snd.concepts]"); { auto(get_completion_scheduler<set_value_t>( get_env(schedule(std::forward<Sch>(sch))))) }-> [same_as](concept.same#concept:same_as "18.4.2Concept same_­as[concept.same]")<remove_cvref_t<Sch>>; } &&[equality_comparable](concept.equalitycomparable#concept:equality_comparable "18.5.4Concept equality_­comparable[concept.equalitycomparable]")<remove_cvref_t<Sch>> &&[copyable](concepts.object#concept:copyable "18.6Object concepts[concepts.object]")<remove_cvref_t<Sch>>;}
[2](#2)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L1122)
Let Sch be the type of a scheduler and
let Env be the type of an execution environment
for which [sender_in](exec.snd.concepts#concept:sender_in "33.9.3Sender concepts[exec.snd.concepts]")<schedule_result_t<Sch>, Env> is satisfied[.](#2.sentence-1)
Then [*sender-in-of*](exec.snd.concepts#concept:sender-in-of "33.9.3Sender concepts[exec.snd.concepts]")<schedule_result_t<Sch>, Env> shall be modeled[.](#2.sentence-2)
[3](#3)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L1130)
No operation required by[copyable](concepts.object#concept:copyable "18.6Object concepts[concepts.object]")<remove_cvref_t<Sch>> and[equality_comparable](concept.equalitycomparable#concept:equality_comparable "18.5.4Concept equality_­comparable[concept.equalitycomparable]")<remove_cvref_t<Sch>> shall exit via an exception[.](#3.sentence-1)
None of these operations,
nor a scheduler type's schedule function,
shall introduce data races
as a result of potentially concurrent ([[intro.races]](intro.races "6.10.2.2Data races")) invocations
of those operations from different threads[.](#3.sentence-2)
[4](#4)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L1141)
For any two values sch1 and sch2 of some scheduler type Sch,sch1 == sch2 shall return true only if both sch1 and sch2 share
the same associated execution resource[.](#4.sentence-1)
[5](#5)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L1148)
For a given scheduler expression sch,
the expressionget_completion_scheduler<set_value_t>(get_env(schedule(sch))) shall compare equal to sch[.](#5.sentence-1)
[6](#6)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L1154)
For a given scheduler expression sch,
if the expression get_domain(sch) is well-formed,
then the expression get_domain(get_env(schedule(sch))) is also well-formed and has the same type[.](#6.sentence-1)
[7](#7)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L1160)
A scheduler type's destructor shall not block
pending completion of any receivers
connected to the sender objects returned from schedule[.](#7.sentence-1)
[*Note [1](#note-1)*:
The ability to wait for completion of submitted function objects
can be provided by the associated execution resource of the scheduler[.](#7.sentence-2)
— *end note*]

38
cppdraft/exec/schedule.md Normal file
View File

@@ -0,0 +1,38 @@
[exec.schedule]
# 33 Execution control library [[exec]](./#exec)
## 33.9 Senders [[exec.snd]](exec.snd#exec.schedule)
### 33.9.11 Sender factories [[exec.factories]](exec.factories#exec.schedule)
#### 33.9.11.1 execution::schedule [exec.schedule]
[1](#1)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2877)
schedule obtains a schedule sender ([[exec.async.ops]](exec.async.ops "33.3Asynchronous operations"))
from a scheduler[.](#1.sentence-1)
[2](#2)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2881)
The name schedule denotes a customization point object[.](#2.sentence-1)
For a subexpression sch,
the expression schedule(sch) is expression-equivalent tosch.schedule()[.](#2.sentence-2)
[3](#3)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2887)
*Mandates*: The type of sch.schedule() satisfies [sender](exec.snd.concepts#concept:sender "33.9.3Sender concepts[exec.snd.concepts]")[.](#3.sentence-1)
[4](#4)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2891)
If the expressionget_completion_scheduler<set_value_t>(get_env(sch.schedule())) == sch is ill-formed or evaluates to false,
the behavior of calling schedule(sch) is undefined[.](#4.sentence-1)

View File

@@ -0,0 +1,150 @@
[exec.schedule.from]
# 33 Execution control library [[exec]](./#exec)
## 33.9 Senders [[exec.snd]](exec.snd#exec.schedule.from)
### 33.9.12 Sender adaptors [[exec.adapt]](exec.adapt#exec.schedule.from)
#### 33.9.12.7 execution::schedule_from [exec.schedule.from]
[1](#1)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3359)
schedule_from schedules work dependent on the completion of a sender
onto a scheduler's associated execution resource[.](#1.sentence-1)
[*Note [1](#note-1)*:
schedule_from is not meant to be used in user code;
it is used in the implementation of continues_on[.](#1.sentence-2)
— *end note*]
[2](#2)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3367)
The name schedule_from denotes a customization point object[.](#2.sentence-1)
For some subexpressions sch and sndr,
let Sch be decltype((sch)) andSndr be decltype((sndr))[.](#2.sentence-2)
If Sch does not satisfy [scheduler](exec.sched#concept:scheduler "33.6Schedulers[exec.sched]"), orSndr does not satisfy [sender](exec.snd.concepts#concept:sender "33.9.3Sender concepts[exec.snd.concepts]"),schedule_from(sch, sndr) is ill-formed[.](#2.sentence-3)
[3](#3)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3376)
Otherwise,
the expression schedule_from(sch, sndr) is expression-equivalent to:transform_sender(*query-with-default*(get_domain, sch, default_domain()), *make-sender*(schedule_from, sch, sndr)) except that sch is evaluated only once[.](#3.sentence-1)
[4](#4)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3386)
The exposition-only class template *impls-for* ([[exec.snd.expos]](exec.snd.expos "33.9.2Exposition-only entities"))
is specialized for schedule_from_t as follows:
[🔗](#lib:impls-for%3cschedule_from_t%3e)
namespace std::execution {template<>struct *impls-for*<schedule_from_t> : *default-impls* {static constexpr auto *get-attrs* = *see below*; static constexpr auto *get-state* = *see below*; static constexpr auto *complete* = *see below*; template<class Sndr, class... Env>static consteval void *check-types*(); };}
[5](#5)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3404)
The member *impls-for*<schedule_from_t>::*get-attrs* is initialized with a callable object equivalent to the following lambda:[](const auto& data, const auto& child) noexcept -> decltype(auto) {return *JOIN-ENV*(*SCHED-ATTRS*(data), *FWD-ENV*(get_env(child)));}
[6](#6)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3413)
The member *impls-for*<schedule_from_t>::*get-state* is initialized with a callable object equivalent to the following lambda:[]<class Sndr, class Rcvr>(Sndr&& sndr, Rcvr& rcvr) noexcept(*see below*)requires [sender_in](exec.snd.concepts#concept:sender_in "33.9.3Sender concepts[exec.snd.concepts]")<*child-type*<Sndr>, *FWD-ENV-T*(env_of_t<Rcvr>)> {auto& [_, sch, child] = sndr; using sched_t = decltype(auto(sch)); using variant_t = *see below*; using receiver_t = *see below*; using operation_t = connect_result_t<schedule_result_t<sched_t>, receiver_t>; constexpr bool nothrow = noexcept(connect(schedule(sch), receiver_t{nullptr})); struct *state-type* { Rcvr& *rcvr*; // *exposition only* variant_t *async-result*; // *exposition only* operation_t *op-state*; // *exposition only*explicit *state-type*(sched_t sch, Rcvr& rcvr) noexcept(nothrow): *rcvr*(rcvr), *op-state*(connect(schedule(sch), receiver_t{this})) {}}; return *state-type*{sch, rcvr};}
[🔗](#lib:check-types,impls-for%3cschedule_from_t%3e)
`template<class Sndr, class... Env>
static consteval void check-types();
`
[7](#7)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3448)
*Effects*: Equivalent to:get_completion_signatures<schedule_result_t<*data-type*<Sndr>>, *FWD-ENV-T*(Env)...>();auto cs = get_completion_signatures<*child-type*<Sndr>, *FWD-ENV-T*(Env)...>();*decay-copyable-result-datums*(cs); // see [[exec.snd.expos]](exec.snd.expos "33.9.2Exposition-only entities")
[8](#8)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3458)
Objects of the local class *state-type* can be used
to initialize a structured binding[.](#8.sentence-1)
[9](#9)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3462)
Let Sigs be
a pack of the arguments to the completion_signatures specialization
named by completion_signatures_of_t<*child-type*<Sndr>, *FWD-ENV-T*(env_of_t<Rcvr>)>[.](#9.sentence-1)
Let *as-tuple* be an alias template such that*as-tuple*<Tag(Args...)> denotes
the type *decayed-tuple*<Tag, Args...>, and
let *is-nothrow-decay-copy-sig* be a variable template such thatauto(*is-nothrow-decay-copy-sig*<Tag(Args...
)>) is
a constant expression of type bool and
equal to (is_nothrow_constructible_v<decay_t<Args>, Args> && ...)[.](#9.sentence-2)
Let *error-completion* be a pack consisting of
the type set_error_t(exception_ptr) if (*is-nothrow-decay-copy-sig*<Sigs> &&...) is false,
and an empty pack otherwise[.](#9.sentence-3)
Then variant_t denotes
the type variant<monostate, *as-tuple*<Sigs>..., *error-completion*...>,
except with duplicate types removed[.](#9.sentence-4)
[10](#10)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3481)
receiver_t is an alias for the following exposition-only class:namespace std::execution {struct *receiver-type* {using receiver_concept = receiver_t; *state-type** *state*; // *exposition only*void set_value() && noexcept { visit([this]<class Tuple>(Tuple& result) noexcept -> void {if constexpr (![same_as](concept.same#concept:same_as "18.4.2Concept same_­as[concept.same]")<monostate, Tuple>) {auto& [tag, ...args] = result;
tag(std::move(*state*->*rcvr*), std::move(args)...); }}, *state*->*async-result*); }template<class Error>void set_error(Error&& err) && noexcept { execution::set_error(std::move(*state*->*rcvr*), std::forward<Error>(err)); }void set_stopped() && noexcept { execution::set_stopped(std::move(*state*->*rcvr*)); }decltype(auto) get_env() const noexcept {return *FWD-ENV*(execution::get_env(*state*->*rcvr*)); }};}
[11](#11)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3516)
The expression in the noexcept clause of the lambda is true if the construction of the returned *state-type* object
is not potentially throwing;
otherwise, false[.](#11.sentence-1)
[12](#12)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3522)
The member *impls-for*<schedule_from_t>::*complete* is initialized with a callable object equivalent to the following lambda:[]<class Tag, class... Args>(auto, auto& state, auto& rcvr, Tag, Args&&... args) noexcept-> void {using result_t = *decayed-tuple*<Tag, Args...>; constexpr bool nothrow = (is_nothrow_constructible_v<decay_t<Args>, Args> && ...); try { state.*async-result*.template emplace<result_t>(Tag(), std::forward<Args>(args)...); } catch (...) {if constexpr (!nothrow) state.*async-result*.template emplace<tuple<set_error_t,
exception_ptr>>(set_error, current_exception()); } start(state.*op-state*);};
[13](#13)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3542)
Let out_sndr be a subexpression denoting
a sender returned from schedule_from(sch, sndr) or one equal to such,
and let OutSndr be the type decltype((out_sndr))[.](#13.sentence-1)
Let out_rcvr be a subexpression denoting a receiver
that has an environment of type Env such that [sender_in](exec.snd.concepts#concept:sender_in "33.9.3Sender concepts[exec.snd.concepts]")<OutSndr, Env> is true[.](#13.sentence-2)
Let op be an lvalue referring to the operation state
that results from connecting out_sndr with out_rcvr[.](#13.sentence-3)
Calling start(op) shall
start sndr on the current execution agent and
execute completion operations on out_rcvr on an execution agent of the execution resource associated with sch[.](#13.sentence-4)
If scheduling onto sch fails,
an error completion on out_rcvr shall be executed
on an unspecified execution agent[.](#13.sentence-5)

460
cppdraft/exec/scope.md Normal file
View File

@@ -0,0 +1,460 @@
[exec.scope]
# 33 Execution control library [[exec]](./#exec)
## 33.14 Execution scope utilities [exec.scope]
### [33.14.1](#concepts) Execution scope concepts [[exec.scope.concepts]](exec.scope.concepts)
[1](#concepts-1)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L7711)
The [scope_token](#concept:scope_token "33.14.1Execution scope concepts[exec.scope.concepts]") concept defines the requirements on
a type Token that can be used to create associations
between senders and an async scope[.](#concepts-1.sentence-1)
[2](#concepts-2)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L7716)
Let *test-sender* and *test-env* be unspecified types such that[sender_in](exec.snd.concepts#concept:sender_in "33.9.3Sender concepts[exec.snd.concepts]")<*test-sender*, *test-env*> is modeled[.](#concepts-2.sentence-1)
namespace std::execution {template<class Token>concept [scope_token](#concept:scope_token "33.14.1Execution scope concepts[exec.scope.concepts]") =[copyable](concepts.object#concept:copyable "18.6Object concepts[concepts.object]")<Token> &&requires(const Token token) {{ token.try_associate() } -> [same_as](concept.same#concept:same_as "18.4.2Concept same_­as[concept.same]")<bool>; { token.disassociate() } noexcept -> [same_as](concept.same#concept:same_as "18.4.2Concept same_­as[concept.same]")<void>; { token.wrap(declval<*test-sender*>()) } -> [sender_in](exec.snd.concepts#concept:sender_in "33.9.3Sender concepts[exec.snd.concepts]")<*test-env*>; };}
[3](#concepts-3)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L7735)
A type Token models [scope_token](#concept:scope_token "33.14.1Execution scope concepts[exec.scope.concepts]") only if:
- [(3.1)](#concepts-3.1)
no exceptions are thrown from
copy construction,
move construction,
copy assignment, or
move assignment
of objects of type Token; and
- [(3.2)](#concepts-3.2)
given an lvalue token of type (possibly const) Token,
for all expressions sndr such thatdecltype((
sndr)) models [sender](exec.snd.concepts#concept:sender "33.9.3Sender concepts[exec.snd.concepts]"):
* [(3.2.1)](#concepts-3.2.1)
token.wrap(sndr) is a valid expression,
* [(3.2.2)](#concepts-3.2.2)
decltype(token.wrap(sndr)) models [sender](exec.snd.concepts#concept:sender "33.9.3Sender concepts[exec.snd.concepts]"), and
* [(3.2.3)](#concepts-3.2.3)
completion_signatures_of_t<decltype(token.wrap(sndr)), E> contains the same completion signatures as completion_signatures_of_t<decltype((sndr)), E> for all types E such that [sender_in](exec.snd.concepts#concept:sender_in "33.9.3Sender concepts[exec.snd.concepts]")<decltype((sndr)), E> is modeled.
### [33.14.2](#exec.counting.scopes) Counting Scopes [[exec.counting.scopes]](exec.counting.scopes)
#### [33.14.2.1](#exec.counting.scopes.general) General [[exec.counting.scopes.general]](exec.counting.scopes.general)
[1](#exec.counting.scopes.general-1)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L7769)
Scopes of type simple_counting_scope and counting_scope maintain counts of associations[.](#exec.counting.scopes.general-1.sentence-1)
Let:
- [(1.1)](#exec.counting.scopes.general-1.1)
Scope be either simple_counting_scope or counting_scope,
- [(1.2)](#exec.counting.scopes.general-1.2)
scope be an object of type Scope,
- [(1.3)](#exec.counting.scopes.general-1.3)
tkn be an object of type Scope::token obtained from scope.get_token(),
- [(1.4)](#exec.counting.scopes.general-1.4)
jsndr be a sender obtained from scope.join(), and
- [(1.5)](#exec.counting.scopes.general-1.5)
op be an operation state obtained from
connecting jsndr to a receiver[.](#exec.counting.scopes.general-1.sentence-2)
During its lifetime scope goes through different states
which govern what operations are allowed and the result of these operations:
- [(1.6)](#exec.counting.scopes.general-1.6)
*unused*:
a newly constructed object starts in the *unused* state[.](#exec.counting.scopes.general-1.6.sentence-1)
- [(1.7)](#exec.counting.scopes.general-1.7)
*open*:
when tkn.try_associate() is called
while scope is in the *unused* state,scope moves to the *open* state[.](#exec.counting.scopes.general-1.7.sentence-1)
- [(1.8)](#exec.counting.scopes.general-1.8)
*open-and-joining*:
when the operation state op is started
while scope is in the *unused* or *open* state,scope moves to the *open-and-joining* state[.](#exec.counting.scopes.general-1.8.sentence-1)
- [(1.9)](#exec.counting.scopes.general-1.9)
*closed*:
when scope.close() is called
while scope is in the *open* state,scope moves to the *closed* state[.](#exec.counting.scopes.general-1.9.sentence-1)
- [(1.10)](#exec.counting.scopes.general-1.10)
*unused-and-closed*:
when scope.close() is called
while scope is in the *unused* state,scope moves to the *unused-and-closed* state[.](#exec.counting.scopes.general-1.10.sentence-1)
- [(1.11)](#exec.counting.scopes.general-1.11)
*closed-and-joining*:
when scope.close() is called
while scope is in the *open-and-joining* state or
the operation state op is started
while scope is in
the *closed* or *unused-and-closed* state,scope moves to the *closed-and-joining* state[.](#exec.counting.scopes.general-1.11.sentence-1)
- [(1.12)](#exec.counting.scopes.general-1.12)
*joined*:
when the count of associations drops to zero
while scope is in
the *open-and-joining* or *closed-and-joining* state,scope moves to the *joined* state[.](#exec.counting.scopes.general-1.12.sentence-1)
[2](#exec.counting.scopes.general-2)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L7836)
*Recommended practice*: For simple_counting_scope and counting_scope,
implementations should store the state and the count of associations
in a single member of type size_t[.](#exec.counting.scopes.general-2.sentence-1)
[3](#exec.counting.scopes.general-3)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L7842)
Subclause [[exec.counting.scopes]](#exec.counting.scopes "33.14.2Counting Scopes") makes use of
the following exposition-only entities:
struct *scope-join-t* {}; // *exposition only*enum *scope-state-type* { // *exposition only**unused*, // *exposition only**open*, // *exposition only**closed*, // *exposition only**open-and-joining*, // *exposition only**closed-and-joining*, // *exposition only**unused-and-closed*, // *exposition only**joined*, // *exposition only*};
[4](#exec.counting.scopes.general-4)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L7860)
The exposition-only class template *impls-for* ([[exec.snd.expos]](exec.snd.expos "33.9.2Exposition-only entities"))
is specialized for *scope-join-t* as follows:
namespace std::execution {template<>struct *impls-for*<*scope-join-t*> : *default-impls* {template<class Scope, class Rcvr>struct *state* { // *exposition only*struct *rcvr-t* { // *exposition only*using receiver_concept = receiver_t;
Rcvr& *rcvr*; // *exposition only*void set_value() && noexcept { execution::set_value(std::move(*rcvr*)); }template<class E>void set_error(E&& e) && noexcept { execution::set_error(std::move(*rcvr*), std::forward<E>(e)); }void set_stopped() && noexcept { execution::set_stopped(std::move(*rcvr*)); }decltype(auto) get_env() const noexcept {return execution::get_env(*rcvr*); }}; using *sched-sender* = // *exposition only*decltype(schedule(get_scheduler(get_env(declval<Rcvr&>())))); using *op-t* = // *exposition only* connect_result_t<*sched-sender*, *rcvr-t*>;
Scope* *scope*; // *exposition only* Rcvr& *receiver*; // *exposition only**op-t* *op*; // *exposition only**state*(Scope* scope, Rcvr& rcvr) // *exposition only*noexcept([*nothrow-callable*](functional.syn#concept:nothrow-callable "22.10.2Header <functional> synopsis[functional.syn]")<connect_t, *sched-sender*, *rcvr-t*>): *scope*(scope), *receiver*(rcvr), *op*(connect(schedule(get_scheduler(get_env(rcvr))), *rcvr-t*(rcvr))) {}void *complete*() noexcept { // *exposition only* start(*op*); }void *complete-inline*() noexcept { // *exposition only* set_value(std::move(*receiver*)); }}; static constexpr auto *get-state* = // *exposition only*[]<class Rcvr>(auto&& sender, Rcvr& receiver)noexcept(is_nothrow_constructible_v<*state*<Rcvr>, *data-type*<decltype(sender)>, Rcvr&>) {auto[_, self] = sender; return *state*(self, receiver); }; static constexpr auto *start* = // *exposition only*[](auto& s, auto&) noexcept {if (s.*scope*->*start-join-sender*(s)) s.*complete-inline*(); }; };}
#### [33.14.2.2](#simple.counting) Simple Counting Scope [[exec.scope.simple.counting]](exec.scope.simple.counting)
#### [33.14.2.2.1](#simple.counting.general) General [[exec.scope.simple.counting.general]](exec.scope.simple.counting.general)
[🔗](#lib:execution::simple_counting_scope)
namespace std::execution {class simple_counting_scope {public:// [[exec.simple.counting.token]](#exec.simple.counting.token "33.14.2.2.4Token"), tokenstruct token; static constexpr size_t max_associations = *implementation-defined*; // [[exec.simple.counting.ctor]](#exec.simple.counting.ctor "33.14.2.2.2Constructor and Destructor"), constructor and destructor simple_counting_scope() noexcept;
simple_counting_scope(simple_counting_scope&&) = delete; ~simple_counting_scope(); // [[exec.simple.counting.mem]](#exec.simple.counting.mem "33.14.2.2.3Members"), members token get_token() noexcept; void close() noexcept; [sender](exec.snd.concepts#concept:sender "33.9.3Sender concepts[exec.snd.concepts]") auto join() noexcept; private: size_t *count*; // *exposition only**scope-state-type* *state*; // *exposition only*bool *try-associate*() noexcept; // *exposition only*void *disassociate*() noexcept; // *exposition only*template<class State>bool *start-join-sender*(State& state) noexcept; // *exposition only*};}
[1](#simple.counting.general-1)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L7971)
For purposes of determining the existence of a data race,get_token,close,join,*try-associate*,*disassociate*, and*start-join-sender* behave as atomic operations ([[intro.multithread]](intro.multithread "6.10.2Multi-threaded executions and data races"))[.](#simple.counting.general-1.sentence-1)
These operations on a single object of
type simple_counting_scope appear to occur in a single total order[.](#simple.counting.general-1.sentence-2)
#### [33.14.2.2.2](#exec.simple.counting.ctor) Constructor and Destructor [[exec.simple.counting.ctor]](exec.simple.counting.ctor)
[🔗](#lib:execution::simple_counting_scope,constructor)
`simple_counting_scope() noexcept;
`
[1](#exec.simple.counting.ctor-1)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L7991)
*Postconditions*: *count* is 0 and *state* is *unused*[.](#exec.simple.counting.ctor-1.sentence-1)
[🔗](#lib:execution::simple_counting_scope,destructor)
`~simple_counting_scope();
`
[2](#exec.simple.counting.ctor-2)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L8002)
*Effects*: If *state* is not one of*joined*, *unused*, or *unused-and-closed*,
invokes terminate ([[except.terminate]](except.terminate "14.6.2The std::terminate function"))[.](#exec.simple.counting.ctor-2.sentence-1)
Otherwise, has no effects[.](#exec.simple.counting.ctor-2.sentence-2)
#### [33.14.2.2.3](#exec.simple.counting.mem) Members [[exec.simple.counting.mem]](exec.simple.counting.mem)
[🔗](#lib:get_token,execution::simple_counting_scope)
`token get_token() noexcept;
`
[1](#exec.simple.counting.mem-1)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L8018)
*Returns*: An object t of type simple_counting_scope::token such thatt.*scope* == this is true[.](#exec.simple.counting.mem-1.sentence-1)
[🔗](#lib:close,execution::simple_counting_scope)
`void close() noexcept;
`
[2](#exec.simple.counting.mem-2)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L8030)
*Effects*: If *state* is
- [(2.1)](#exec.simple.counting.mem-2.1)
*unused*, then changes *state* to *unused-and-closed*;
- [(2.2)](#exec.simple.counting.mem-2.2)
*open*, then changes *state* to *closed*;
- [(2.3)](#exec.simple.counting.mem-2.3)
*open-and-joining*,
then changes *state* to *closed-and-joining*;
- [(2.4)](#exec.simple.counting.mem-2.4)
otherwise, no effects[.](#exec.simple.counting.mem-2.sentence-1)
[3](#exec.simple.counting.mem-3)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L8045)
*Postconditions*: Any subsequent call to *try-associate*() on *this returns false[.](#exec.simple.counting.mem-3.sentence-1)
[🔗](#lib:join,execution::simple_counting_scope)
`[sender](exec.snd.concepts#concept:sender "33.9.3Sender concepts[exec.snd.concepts]") auto join() noexcept;
`
[4](#exec.simple.counting.mem-4)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L8057)
*Returns*: *make-sender*(*scope-join-t*(), this)[.](#exec.simple.counting.mem-4.sentence-1)
[🔗](#lib:try-associate,execution::simple_counting_scope)
`bool try-associate() noexcept;
`
[5](#exec.simple.counting.mem-5)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L8068)
*Effects*: If *count* is equal to max_associations, then no effects[.](#exec.simple.counting.mem-5.sentence-1)
Otherwise, if *state* is
- [(5.1)](#exec.simple.counting.mem-5.1)
*unused*,
then increments *count* and changes *state* to *open*;
- [(5.2)](#exec.simple.counting.mem-5.2)
*open* or *open-and-joining*,
then increments *count*;
- [(5.3)](#exec.simple.counting.mem-5.3)
otherwise, no effects[.](#exec.simple.counting.mem-5.sentence-2)
[6](#exec.simple.counting.mem-6)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L8083)
*Returns*: true if *count* was incremented, false otherwise[.](#exec.simple.counting.mem-6.sentence-1)
[🔗](#lib:disassociate,execution::simple_counting_scope)
`void disassociate() noexcept;
`
[7](#exec.simple.counting.mem-7)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L8094)
*Preconditions*: *count* is greater than zero[.](#exec.simple.counting.mem-7.sentence-1)
[8](#exec.simple.counting.mem-8)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L8098)
*Effects*: Decrements *count*[.](#exec.simple.counting.mem-8.sentence-1)
If *count* is zero after decrementing and*state* is *open-and-joining* or *closed-and-joining*,
changes *state* to *joined* and
calls *complete*() on all objects registered with *this[.](#exec.simple.counting.mem-8.sentence-2)
[*Note [1](#exec.simple.counting.mem-note-1)*:
Calling *complete*() on any registered object
can cause *this to be destroyed[.](#exec.simple.counting.mem-8.sentence-3)
— *end note*]
[🔗](#lib:start-join-sender,execution::simple_counting_scope)
`template<class State>
bool start-join-sender(State& st) noexcept;
`
[9](#exec.simple.counting.mem-9)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L8118)
*Effects*: If *state* is
- [(9.1)](#exec.simple.counting.mem-9.1)
*unused*, *unused-and-closed*, or *joined*, then
changes *state* to *joined* and returns true;
- [(9.2)](#exec.simple.counting.mem-9.2)
*open* or *open-and-joining*, then
changes *state* to *open-and-joining*,
registers st with *this and returns false;
- [(9.3)](#exec.simple.counting.mem-9.3)
*closed* or *closed-and-joining*, then
changes *state* to *closed-and-joining*,
registers st with *this and returns false[.](#exec.simple.counting.mem-9.sentence-1)
#### [33.14.2.2.4](#exec.simple.counting.token) Token [[exec.simple.counting.token]](exec.simple.counting.token)
[🔗](#lib:execution::simple_counting_scope::token)
namespace std::execution {struct simple_counting_scope::token {template<[sender](exec.snd.concepts#concept:sender "33.9.3Sender concepts[exec.snd.concepts]") Sender> Sender&& wrap(Sender&& snd) const noexcept; bool try_associate() const noexcept; void disassociate() const noexcept; private: simple_counting_scope* *scope*; // *exposition only*};}
[🔗](#lib:wrap,execution::simple_counting_scope::token)
`template<[sender](exec.snd.concepts#concept:sender "33.9.3Sender concepts[exec.snd.concepts]") Sender>
Sender&& wrap(Sender&& snd) const noexcept;
`
[1](#exec.simple.counting.token-1)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L8160)
*Returns*: std::forward<Sender>(snd)[.](#exec.simple.counting.token-1.sentence-1)
[🔗](#lib:try_associate,execution::simple_counting_scope::token)
`bool try_associate() const noexcept;
`
[2](#exec.simple.counting.token-2)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L8171)
*Effects*: Equivalent to: return *scope*->*try-associate*();
[🔗](#lib:disassociate,execution::simple_counting_scope::token)
`void disassociate() const noexcept;
`
[3](#exec.simple.counting.token-3)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L8182)
*Effects*: Equivalent to *scope*->*disassociate*()[.](#exec.simple.counting.token-3.sentence-1)
#### [33.14.2.3](#counting) Counting Scope [[exec.scope.counting]](exec.scope.counting)
[🔗](#lib:execution::counting_scope)
namespace std::execution {class counting_scope {public:struct token {template<[sender](exec.snd.concepts#concept:sender "33.9.3Sender concepts[exec.snd.concepts]") Sender>[sender](exec.snd.concepts#concept:sender "33.9.3Sender concepts[exec.snd.concepts]") auto wrap(Sender&& snd) const noexcept(*see below*); bool try_associate() const noexcept; void disassociate() const noexcept; private: counting_scope* *scope*; // *exposition only*}; static constexpr size_t max_associations = *implementation-defined*;
counting_scope() noexcept;
counting_scope(counting_scope&&) = delete; ~counting_scope();
token get_token() noexcept; void close() noexcept; [sender](exec.snd.concepts#concept:sender "33.9.3Sender concepts[exec.snd.concepts]") auto join() noexcept; void request_stop() noexcept; private: size_t *count*; // *exposition only**scope-state-type* *state*; // *exposition only* inplace_stop_source *s_source*; // *exposition only*bool *try-associate*() noexcept; // *exposition only*void *disassociate*() noexcept; // *exposition only*template<class State>bool *start-join-sender*(State& state) noexcept; // *exposition only*};}
[1](#counting-1)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L8230)
counting_scope differs from simple_counting_scope by
adding support for cancellation[.](#counting-1.sentence-1)
Unless specified below, the semantics of members of counting_scope are the same as the corresponding members of simple_counting_scope[.](#counting-1.sentence-2)
[🔗](#lib:get_token,execution::counting_scope)
`token get_token() noexcept;
`
[2](#counting-2)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L8242)
*Returns*: An object t of type counting_scope::token such thatt.*scope* == this is true[.](#counting-2.sentence-1)
[🔗](#lib:request_stop,execution::counting_scope)
`void request_stop() noexcept;
`
[3](#counting-3)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L8254)
*Effects*: Equivalent to *s_source*.request_stop()[.](#counting-3.sentence-1)
[4](#counting-4)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L8258)
*Remarks*: Calls to request_stop do not introduce data races[.](#counting-4.sentence-1)
[🔗](#lib:wrap,execution::counting_scope::token)
`template<[sender](exec.snd.concepts#concept:sender "33.9.3Sender concepts[exec.snd.concepts]") Sender>
[sender](exec.snd.concepts#concept:sender "33.9.3Sender concepts[exec.snd.concepts]") auto counting_scope::token::wrap(Sender&& snd) const
noexcept(is_nothrow_constructible_v<remove_cvref_t<Sender>, Sender>);
`
[5](#counting-5)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L8271)
*Effects*: Equivalent to:return *stop-when*(std::forward<Sender>(snd), *scope*->*s_source*.get_token());

View File

@@ -0,0 +1,55 @@
[exec.scope.concepts]
# 33 Execution control library [[exec]](./#exec)
## 33.14 Execution scope utilities [[exec.scope]](exec.scope#concepts)
### 33.14.1 Execution scope concepts [exec.scope.concepts]
[1](#1)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L7711)
The [scope_token](#concept:scope_token "33.14.1Execution scope concepts[exec.scope.concepts]") concept defines the requirements on
a type Token that can be used to create associations
between senders and an async scope[.](#1.sentence-1)
[2](#2)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L7716)
Let *test-sender* and *test-env* be unspecified types such that[sender_in](exec.snd.concepts#concept:sender_in "33.9.3Sender concepts[exec.snd.concepts]")<*test-sender*, *test-env*> is modeled[.](#2.sentence-1)
namespace std::execution {template<class Token>concept [scope_token](#concept:scope_token "33.14.1Execution scope concepts[exec.scope.concepts]") =[copyable](concepts.object#concept:copyable "18.6Object concepts[concepts.object]")<Token> &&requires(const Token token) {{ token.try_associate() } -> [same_as](concept.same#concept:same_as "18.4.2Concept same_­as[concept.same]")<bool>; { token.disassociate() } noexcept -> [same_as](concept.same#concept:same_as "18.4.2Concept same_­as[concept.same]")<void>; { token.wrap(declval<*test-sender*>()) } -> [sender_in](exec.snd.concepts#concept:sender_in "33.9.3Sender concepts[exec.snd.concepts]")<*test-env*>; };}
[3](#3)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L7735)
A type Token models [scope_token](#concept:scope_token "33.14.1Execution scope concepts[exec.scope.concepts]") only if:
- [(3.1)](#3.1)
no exceptions are thrown from
copy construction,
move construction,
copy assignment, or
move assignment
of objects of type Token; and
- [(3.2)](#3.2)
given an lvalue token of type (possibly const) Token,
for all expressions sndr such thatdecltype((
sndr)) models [sender](exec.snd.concepts#concept:sender "33.9.3Sender concepts[exec.snd.concepts]"):
* [(3.2.1)](#3.2.1)
token.wrap(sndr) is a valid expression,
* [(3.2.2)](#3.2.2)
decltype(token.wrap(sndr)) models [sender](exec.snd.concepts#concept:sender "33.9.3Sender concepts[exec.snd.concepts]"), and
* [(3.2.3)](#3.2.3)
completion_signatures_of_t<decltype(token.wrap(sndr)), E> contains the same completion signatures as completion_signatures_of_t<decltype((sndr)), E> for all types E such that [sender_in](exec.snd.concepts#concept:sender_in "33.9.3Sender concepts[exec.snd.concepts]")<decltype((sndr)), E> is modeled.

View File

@@ -0,0 +1,68 @@
[exec.scope.counting]
# 33 Execution control library [[exec]](./#exec)
## 33.14 Execution scope utilities [[exec.scope]](exec.scope#counting)
### 33.14.2 Counting Scopes [[exec.counting.scopes]](exec.counting.scopes#exec.scope.counting)
#### 33.14.2.3 Counting Scope [exec.scope.counting]
[🔗](#lib:execution::counting_scope)
namespace std::execution {class counting_scope {public:struct token {template<[sender](exec.snd.concepts#concept:sender "33.9.3Sender concepts[exec.snd.concepts]") Sender>[sender](exec.snd.concepts#concept:sender "33.9.3Sender concepts[exec.snd.concepts]") auto wrap(Sender&& snd) const noexcept(*see below*); bool try_associate() const noexcept; void disassociate() const noexcept; private: counting_scope* *scope*; // *exposition only*}; static constexpr size_t max_associations = *implementation-defined*;
counting_scope() noexcept;
counting_scope(counting_scope&&) = delete; ~counting_scope();
token get_token() noexcept; void close() noexcept; [sender](exec.snd.concepts#concept:sender "33.9.3Sender concepts[exec.snd.concepts]") auto join() noexcept; void request_stop() noexcept; private: size_t *count*; // *exposition only**scope-state-type* *state*; // *exposition only* inplace_stop_source *s_source*; // *exposition only*bool *try-associate*() noexcept; // *exposition only*void *disassociate*() noexcept; // *exposition only*template<class State>bool *start-join-sender*(State& state) noexcept; // *exposition only*};}
[1](#1)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L8230)
counting_scope differs from simple_counting_scope by
adding support for cancellation[.](#1.sentence-1)
Unless specified below, the semantics of members of counting_scope are the same as the corresponding members of simple_counting_scope[.](#1.sentence-2)
[🔗](#lib:get_token,execution::counting_scope)
`token get_token() noexcept;
`
[2](#2)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L8242)
*Returns*: An object t of type counting_scope::token such thatt.*scope* == this is true[.](#2.sentence-1)
[🔗](#lib:request_stop,execution::counting_scope)
`void request_stop() noexcept;
`
[3](#3)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L8254)
*Effects*: Equivalent to *s_source*.request_stop()[.](#3.sentence-1)
[4](#4)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L8258)
*Remarks*: Calls to request_stop do not introduce data races[.](#4.sentence-1)
[🔗](#lib:wrap,execution::counting_scope::token)
`template<[sender](exec.snd.concepts#concept:sender "33.9.3Sender concepts[exec.snd.concepts]") Sender>
[sender](exec.snd.concepts#concept:sender "33.9.3Sender concepts[exec.snd.concepts]") auto counting_scope::token::wrap(Sender&& snd) const
noexcept(is_nothrow_constructible_v<remove_cvref_t<Sender>, Sender>);
`
[5](#5)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L8271)
*Effects*: Equivalent to:return *stop-when*(std::forward<Sender>(snd), *scope*->*s_source*.get_token());

View File

@@ -0,0 +1,240 @@
[exec.scope.simple.counting]
# 33 Execution control library [[exec]](./#exec)
## 33.14 Execution scope utilities [[exec.scope]](exec.scope#simple.counting)
### 33.14.2 Counting Scopes [[exec.counting.scopes]](exec.counting.scopes#exec.scope.simple.counting)
#### 33.14.2.2 Simple Counting Scope [exec.scope.simple.counting]
#### [33.14.2.2.1](#general) General [[exec.scope.simple.counting.general]](exec.scope.simple.counting.general)
[🔗](#lib:execution::simple_counting_scope)
namespace std::execution {class simple_counting_scope {public:// [[exec.simple.counting.token]](#exec.simple.counting.token "33.14.2.2.4Token"), tokenstruct token; static constexpr size_t max_associations = *implementation-defined*; // [[exec.simple.counting.ctor]](#exec.simple.counting.ctor "33.14.2.2.2Constructor and Destructor"), constructor and destructor simple_counting_scope() noexcept;
simple_counting_scope(simple_counting_scope&&) = delete; ~simple_counting_scope(); // [[exec.simple.counting.mem]](#exec.simple.counting.mem "33.14.2.2.3Members"), members token get_token() noexcept; void close() noexcept; [sender](exec.snd.concepts#concept:sender "33.9.3Sender concepts[exec.snd.concepts]") auto join() noexcept; private: size_t *count*; // *exposition only**scope-state-type* *state*; // *exposition only*bool *try-associate*() noexcept; // *exposition only*void *disassociate*() noexcept; // *exposition only*template<class State>bool *start-join-sender*(State& state) noexcept; // *exposition only*};}
[1](#general-1)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L7971)
For purposes of determining the existence of a data race,get_token,close,join,*try-associate*,*disassociate*, and*start-join-sender* behave as atomic operations ([[intro.multithread]](intro.multithread "6.10.2Multi-threaded executions and data races"))[.](#general-1.sentence-1)
These operations on a single object of
type simple_counting_scope appear to occur in a single total order[.](#general-1.sentence-2)
#### [33.14.2.2.2](#exec.simple.counting.ctor) Constructor and Destructor [[exec.simple.counting.ctor]](exec.simple.counting.ctor)
[🔗](#lib:execution::simple_counting_scope,constructor)
`simple_counting_scope() noexcept;
`
[1](#exec.simple.counting.ctor-1)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L7991)
*Postconditions*: *count* is 0 and *state* is *unused*[.](#exec.simple.counting.ctor-1.sentence-1)
[🔗](#lib:execution::simple_counting_scope,destructor)
`~simple_counting_scope();
`
[2](#exec.simple.counting.ctor-2)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L8002)
*Effects*: If *state* is not one of*joined*, *unused*, or *unused-and-closed*,
invokes terminate ([[except.terminate]](except.terminate "14.6.2The std::terminate function"))[.](#exec.simple.counting.ctor-2.sentence-1)
Otherwise, has no effects[.](#exec.simple.counting.ctor-2.sentence-2)
#### [33.14.2.2.3](#exec.simple.counting.mem) Members [[exec.simple.counting.mem]](exec.simple.counting.mem)
[🔗](#lib:get_token,execution::simple_counting_scope)
`token get_token() noexcept;
`
[1](#exec.simple.counting.mem-1)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L8018)
*Returns*: An object t of type simple_counting_scope::token such thatt.*scope* == this is true[.](#exec.simple.counting.mem-1.sentence-1)
[🔗](#lib:close,execution::simple_counting_scope)
`void close() noexcept;
`
[2](#exec.simple.counting.mem-2)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L8030)
*Effects*: If *state* is
- [(2.1)](#exec.simple.counting.mem-2.1)
*unused*, then changes *state* to *unused-and-closed*;
- [(2.2)](#exec.simple.counting.mem-2.2)
*open*, then changes *state* to *closed*;
- [(2.3)](#exec.simple.counting.mem-2.3)
*open-and-joining*,
then changes *state* to *closed-and-joining*;
- [(2.4)](#exec.simple.counting.mem-2.4)
otherwise, no effects[.](#exec.simple.counting.mem-2.sentence-1)
[3](#exec.simple.counting.mem-3)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L8045)
*Postconditions*: Any subsequent call to *try-associate*() on *this returns false[.](#exec.simple.counting.mem-3.sentence-1)
[🔗](#lib:join,execution::simple_counting_scope)
`[sender](exec.snd.concepts#concept:sender "33.9.3Sender concepts[exec.snd.concepts]") auto join() noexcept;
`
[4](#exec.simple.counting.mem-4)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L8057)
*Returns*: *make-sender*(*scope-join-t*(), this)[.](#exec.simple.counting.mem-4.sentence-1)
[🔗](#lib:try-associate,execution::simple_counting_scope)
`bool try-associate() noexcept;
`
[5](#exec.simple.counting.mem-5)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L8068)
*Effects*: If *count* is equal to max_associations, then no effects[.](#exec.simple.counting.mem-5.sentence-1)
Otherwise, if *state* is
- [(5.1)](#exec.simple.counting.mem-5.1)
*unused*,
then increments *count* and changes *state* to *open*;
- [(5.2)](#exec.simple.counting.mem-5.2)
*open* or *open-and-joining*,
then increments *count*;
- [(5.3)](#exec.simple.counting.mem-5.3)
otherwise, no effects[.](#exec.simple.counting.mem-5.sentence-2)
[6](#exec.simple.counting.mem-6)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L8083)
*Returns*: true if *count* was incremented, false otherwise[.](#exec.simple.counting.mem-6.sentence-1)
[🔗](#lib:disassociate,execution::simple_counting_scope)
`void disassociate() noexcept;
`
[7](#exec.simple.counting.mem-7)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L8094)
*Preconditions*: *count* is greater than zero[.](#exec.simple.counting.mem-7.sentence-1)
[8](#exec.simple.counting.mem-8)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L8098)
*Effects*: Decrements *count*[.](#exec.simple.counting.mem-8.sentence-1)
If *count* is zero after decrementing and*state* is *open-and-joining* or *closed-and-joining*,
changes *state* to *joined* and
calls *complete*() on all objects registered with *this[.](#exec.simple.counting.mem-8.sentence-2)
[*Note [1](#exec.simple.counting.mem-note-1)*:
Calling *complete*() on any registered object
can cause *this to be destroyed[.](#exec.simple.counting.mem-8.sentence-3)
— *end note*]
[🔗](#lib:start-join-sender,execution::simple_counting_scope)
`template<class State>
bool start-join-sender(State& st) noexcept;
`
[9](#exec.simple.counting.mem-9)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L8118)
*Effects*: If *state* is
- [(9.1)](#exec.simple.counting.mem-9.1)
*unused*, *unused-and-closed*, or *joined*, then
changes *state* to *joined* and returns true;
- [(9.2)](#exec.simple.counting.mem-9.2)
*open* or *open-and-joining*, then
changes *state* to *open-and-joining*,
registers st with *this and returns false;
- [(9.3)](#exec.simple.counting.mem-9.3)
*closed* or *closed-and-joining*, then
changes *state* to *closed-and-joining*,
registers st with *this and returns false[.](#exec.simple.counting.mem-9.sentence-1)
#### [33.14.2.2.4](#exec.simple.counting.token) Token [[exec.simple.counting.token]](exec.simple.counting.token)
[🔗](#lib:execution::simple_counting_scope::token)
namespace std::execution {struct simple_counting_scope::token {template<[sender](exec.snd.concepts#concept:sender "33.9.3Sender concepts[exec.snd.concepts]") Sender> Sender&& wrap(Sender&& snd) const noexcept; bool try_associate() const noexcept; void disassociate() const noexcept; private: simple_counting_scope* *scope*; // *exposition only*};}
[🔗](#lib:wrap,execution::simple_counting_scope::token)
`template<[sender](exec.snd.concepts#concept:sender "33.9.3Sender concepts[exec.snd.concepts]") Sender>
Sender&& wrap(Sender&& snd) const noexcept;
`
[1](#exec.simple.counting.token-1)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L8160)
*Returns*: std::forward<Sender>(snd)[.](#exec.simple.counting.token-1.sentence-1)
[🔗](#lib:try_associate,execution::simple_counting_scope::token)
`bool try_associate() const noexcept;
`
[2](#exec.simple.counting.token-2)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L8171)
*Effects*: Equivalent to: return *scope*->*try-associate*();
[🔗](#lib:disassociate,execution::simple_counting_scope::token)
`void disassociate() const noexcept;
`
[3](#exec.simple.counting.token-3)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L8182)
*Effects*: Equivalent to *scope*->*disassociate*()[.](#exec.simple.counting.token-3.sentence-1)

View File

@@ -0,0 +1,25 @@
[exec.scope.simple.counting.general]
# 33 Execution control library [[exec]](./#exec)
## 33.14 Execution scope utilities [[exec.scope]](exec.scope#simple.counting.general)
### 33.14.2 Counting Scopes [[exec.counting.scopes]](exec.counting.scopes#exec.scope.simple.counting.general)
#### 33.14.2.2 Simple Counting Scope [[exec.scope.simple.counting]](exec.scope.simple.counting#general)
#### 33.14.2.2.1 General [exec.scope.simple.counting.general]
[🔗](#lib:execution::simple_counting_scope)
namespace std::execution {class simple_counting_scope {public:// [[exec.simple.counting.token]](exec.simple.counting.token "33.14.2.2.4Token"), tokenstruct token; static constexpr size_t max_associations = *implementation-defined*; // [[exec.simple.counting.ctor]](exec.simple.counting.ctor "33.14.2.2.2Constructor and Destructor"), constructor and destructor simple_counting_scope() noexcept;
simple_counting_scope(simple_counting_scope&&) = delete; ~simple_counting_scope(); // [[exec.simple.counting.mem]](exec.simple.counting.mem "33.14.2.2.3Members"), members token get_token() noexcept; void close() noexcept; [sender](exec.snd.concepts#concept:sender "33.9.3Sender concepts[exec.snd.concepts]") auto join() noexcept; private: size_t *count*; // *exposition only**scope-state-type* *state*; // *exposition only*bool *try-associate*() noexcept; // *exposition only*void *disassociate*() noexcept; // *exposition only*template<class State>bool *start-join-sender*(State& state) noexcept; // *exposition only*};}
[1](#1)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L7971)
For purposes of determining the existence of a data race,get_token,close,join,*try-associate*,*disassociate*, and*start-join-sender* behave as atomic operations ([[intro.multithread]](intro.multithread "6.10.2Multi-threaded executions and data races"))[.](#1.sentence-1)
These operations on a single object of
type simple_counting_scope appear to occur in a single total order[.](#1.sentence-2)

View File

@@ -0,0 +1,20 @@
[exec.set.error]
# 33 Execution control library [[exec]](./#exec)
## 33.7 Receivers [[exec.recv]](exec.recv#exec.set.error)
### 33.7.3 execution::set_error [exec.set.error]
[1](#1)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L1249)
set_error is an error completion function ([[exec.async.ops]](exec.async.ops "33.3Asynchronous operations"))[.](#1.sentence-1)
Its associated completion tag is set_error_t[.](#1.sentence-2)
The expression set_error(rcvr, err) for some subexpressions rcvr and err is ill-formed
if rcvr is an lvalue or an rvalue of const type[.](#1.sentence-3)
Otherwise, it is expression-equivalent to*MANDATE-NOTHROW*(rcvr.set_error(err))[.](#1.sentence-4)

View File

@@ -0,0 +1,20 @@
[exec.set.stopped]
# 33 Execution control library [[exec]](./#exec)
## 33.7 Receivers [[exec.recv]](exec.recv#exec.set.stopped)
### 33.7.4 execution::set_stopped [exec.set.stopped]
[1](#1)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L1260)
set_stopped is a stopped completion function ([[exec.async.ops]](exec.async.ops "33.3Asynchronous operations"))[.](#1.sentence-1)
Its associated completion tag is set_stopped_t[.](#1.sentence-2)
The expression set_stopped(rcvr) for a subexpression rcvr is ill-formed
if rcvr is an lvalue or an rvalue of const type[.](#1.sentence-3)
Otherwise, it is expression-equivalent to*MANDATE-NOTHROW*(rcvr.set_stopped())[.](#1.sentence-4)

View File

@@ -0,0 +1,21 @@
[exec.set.value]
# 33 Execution control library [[exec]](./#exec)
## 33.7 Receivers [[exec.recv]](exec.recv#exec.set.value)
### 33.7.2 execution::set_value [exec.set.value]
[1](#1)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L1237)
set_value is a value completion function ([[exec.async.ops]](exec.async.ops "33.3Asynchronous operations"))[.](#1.sentence-1)
Its associated completion tag is set_value_t[.](#1.sentence-2)
The expression set_value(rcvr, vs...) for a subexpression rcvr and
pack of subexpressions vs is ill-formed
if rcvr is an lvalue or an rvalue of const type[.](#1.sentence-3)
Otherwise, it is expression-equivalent to*MANDATE-NOTHROW*(rcvr.set_value(vs...))[.](#1.sentence-4)

View File

@@ -0,0 +1,36 @@
[exec.simple.counting.ctor]
# 33 Execution control library [[exec]](./#exec)
## 33.14 Execution scope utilities [[exec.scope]](exec.scope#exec.simple.counting.ctor)
### 33.14.2 Counting Scopes [[exec.counting.scopes]](exec.counting.scopes#exec.simple.counting.ctor)
#### 33.14.2.2 Simple Counting Scope [[exec.scope.simple.counting]](exec.scope.simple.counting#exec.simple.counting.ctor)
#### 33.14.2.2.2 Constructor and Destructor [exec.simple.counting.ctor]
[🔗](#lib:execution::simple_counting_scope,constructor)
`simple_counting_scope() noexcept;
`
[1](#1)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L7991)
*Postconditions*: *count* is 0 and *state* is *unused*[.](#1.sentence-1)
[🔗](#lib:execution::simple_counting_scope,destructor)
`~simple_counting_scope();
`
[2](#2)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L8002)
*Effects*: If *state* is not one of*joined*, *unused*, or *unused-and-closed*,
invokes terminate ([[except.terminate]](except.terminate "14.6.2The std::terminate function"))[.](#2.sentence-1)
Otherwise, has no effects[.](#2.sentence-2)

View File

@@ -0,0 +1,157 @@
[exec.simple.counting.mem]
# 33 Execution control library [[exec]](./#exec)
## 33.14 Execution scope utilities [[exec.scope]](exec.scope#exec.simple.counting.mem)
### 33.14.2 Counting Scopes [[exec.counting.scopes]](exec.counting.scopes#exec.simple.counting.mem)
#### 33.14.2.2 Simple Counting Scope [[exec.scope.simple.counting]](exec.scope.simple.counting#exec.simple.counting.mem)
#### 33.14.2.2.3 Members [exec.simple.counting.mem]
[🔗](#lib:get_token,execution::simple_counting_scope)
`token get_token() noexcept;
`
[1](#1)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L8018)
*Returns*: An object t of type simple_counting_scope::token such thatt.*scope* == this is true[.](#1.sentence-1)
[🔗](#lib:close,execution::simple_counting_scope)
`void close() noexcept;
`
[2](#2)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L8030)
*Effects*: If *state* is
- [(2.1)](#2.1)
*unused*, then changes *state* to *unused-and-closed*;
- [(2.2)](#2.2)
*open*, then changes *state* to *closed*;
- [(2.3)](#2.3)
*open-and-joining*,
then changes *state* to *closed-and-joining*;
- [(2.4)](#2.4)
otherwise, no effects[.](#2.sentence-1)
[3](#3)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L8045)
*Postconditions*: Any subsequent call to *try-associate*() on *this returns false[.](#3.sentence-1)
[🔗](#lib:join,execution::simple_counting_scope)
`[sender](exec.snd.concepts#concept:sender "33.9.3Sender concepts[exec.snd.concepts]") auto join() noexcept;
`
[4](#4)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L8057)
*Returns*: *make-sender*(*scope-join-t*(), this)[.](#4.sentence-1)
[🔗](#lib:try-associate,execution::simple_counting_scope)
`bool try-associate() noexcept;
`
[5](#5)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L8068)
*Effects*: If *count* is equal to max_associations, then no effects[.](#5.sentence-1)
Otherwise, if *state* is
- [(5.1)](#5.1)
*unused*,
then increments *count* and changes *state* to *open*;
- [(5.2)](#5.2)
*open* or *open-and-joining*,
then increments *count*;
- [(5.3)](#5.3)
otherwise, no effects[.](#5.sentence-2)
[6](#6)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L8083)
*Returns*: true if *count* was incremented, false otherwise[.](#6.sentence-1)
[🔗](#lib:disassociate,execution::simple_counting_scope)
`void disassociate() noexcept;
`
[7](#7)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L8094)
*Preconditions*: *count* is greater than zero[.](#7.sentence-1)
[8](#8)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L8098)
*Effects*: Decrements *count*[.](#8.sentence-1)
If *count* is zero after decrementing and*state* is *open-and-joining* or *closed-and-joining*,
changes *state* to *joined* and
calls *complete*() on all objects registered with *this[.](#8.sentence-2)
[*Note [1](#note-1)*:
Calling *complete*() on any registered object
can cause *this to be destroyed[.](#8.sentence-3)
— *end note*]
[🔗](#lib:start-join-sender,execution::simple_counting_scope)
`template<class State>
bool start-join-sender(State& st) noexcept;
`
[9](#9)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L8118)
*Effects*: If *state* is
- [(9.1)](#9.1)
*unused*, *unused-and-closed*, or *joined*, then
changes *state* to *joined* and returns true;
- [(9.2)](#9.2)
*open* or *open-and-joining*, then
changes *state* to *open-and-joining*,
registers st with *this and returns false;
- [(9.3)](#9.3)
*closed* or *closed-and-joining*, then
changes *state* to *closed-and-joining*,
registers st with *this and returns false[.](#9.sentence-1)

View File

@@ -0,0 +1,49 @@
[exec.simple.counting.token]
# 33 Execution control library [[exec]](./#exec)
## 33.14 Execution scope utilities [[exec.scope]](exec.scope#exec.simple.counting.token)
### 33.14.2 Counting Scopes [[exec.counting.scopes]](exec.counting.scopes#exec.simple.counting.token)
#### 33.14.2.2 Simple Counting Scope [[exec.scope.simple.counting]](exec.scope.simple.counting#exec.simple.counting.token)
#### 33.14.2.2.4 Token [exec.simple.counting.token]
[🔗](#lib:execution::simple_counting_scope::token)
namespace std::execution {struct simple_counting_scope::token {template<[sender](exec.snd.concepts#concept:sender "33.9.3Sender concepts[exec.snd.concepts]") Sender> Sender&& wrap(Sender&& snd) const noexcept; bool try_associate() const noexcept; void disassociate() const noexcept; private: simple_counting_scope* *scope*; // *exposition only*};}
[🔗](#lib:wrap,execution::simple_counting_scope::token)
`template<[sender](exec.snd.concepts#concept:sender "33.9.3Sender concepts[exec.snd.concepts]") Sender>
Sender&& wrap(Sender&& snd) const noexcept;
`
[1](#1)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L8160)
*Returns*: std::forward<Sender>(snd)[.](#1.sentence-1)
[🔗](#lib:try_associate,execution::simple_counting_scope::token)
`bool try_associate() const noexcept;
`
[2](#2)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L8171)
*Effects*: Equivalent to: return *scope*->*try-associate*();
[🔗](#lib:disassociate,execution::simple_counting_scope::token)
`void disassociate() const noexcept;
`
[3](#3)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L8182)
*Effects*: Equivalent to *scope*->*disassociate*()[.](#3.sentence-1)

3837
cppdraft/exec/snd.md Normal file

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,40 @@
[exec.snd.apply]
# 33 Execution control library [[exec]](./#exec)
## 33.9 Senders [[exec.snd]](exec.snd#apply)
### 33.9.8 execution::apply_sender [exec.snd.apply]
[🔗](#lib:apply_sender)
`namespace std::execution {
template<class Domain, class Tag, [sender](exec.snd.concepts#concept:sender "33.9.3Sender concepts[exec.snd.concepts]") Sndr, class... Args>
constexpr decltype(auto) apply_sender(Domain dom, Tag, Sndr&& sndr, Args&&... args)
noexcept(see below);
}
`
[1](#1)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2601)
Let e be the expressiondom.apply_sender(Tag(), std::forward<Sndr>(sndr), std::forward<Args>(args)...) if that expression is well-formed; otherwise,default_domain().apply_sender(Tag(), std::forward<Sndr>(sndr), std::forward<Args>(args)...)
[2](#2)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2611)
*Constraints*: The expression e is well-formed[.](#2.sentence-1)
[3](#3)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2615)
*Returns*: e[.](#3.sentence-1)
[4](#4)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2619)
*Remarks*: The exception specification is equivalent to noexcept(e)[.](#4.sentence-1)

View File

@@ -0,0 +1,122 @@
[exec.snd.concepts]
# 33 Execution control library [[exec]](./#exec)
## 33.9 Senders [[exec.snd]](exec.snd#concepts)
### 33.9.3 Sender concepts [exec.snd.concepts]
[1](#1)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2127)
The [sender](#concept:sender "33.9.3Sender concepts[exec.snd.concepts]") concept defines
the requirements for a sender type ([[exec.async.ops]](exec.async.ops "33.3Asynchronous operations"))[.](#1.sentence-1)
The [sender_in](#concept:sender_in "33.9.3Sender concepts[exec.snd.concepts]") concept defines
the requirements for a sender type
that can create asynchronous operations given an associated environment type[.](#1.sentence-2)
The [sender_to](#concept:sender_to "33.9.3Sender concepts[exec.snd.concepts]") concept defines
the requirements for a sender type
that can connect with a specific receiver type[.](#1.sentence-3)
The get_env customization point object is used to access
a sender's associated attributes[.](#1.sentence-4)
The connect customization point object is used to connect ([[exec.async.ops]](exec.async.ops "33.3Asynchronous operations"))
a sender and a receiver to produce an operation state[.](#1.sentence-5)
[🔗](#lib:is-dependent-sender-helper)
namespace std::execution {template<auto>concept [*is-constant*](#concept:is-constant "33.9.3Sender concepts[exec.snd.concepts]") = true; // *exposition only*template<class Sndr>concept [*is-sender*](#concept:is-sender "33.9.3Sender concepts[exec.snd.concepts]") = // *exposition only*[derived_from](concept.derived#concept:derived_from "18.4.3Concept derived_­from[concept.derived]")<typename Sndr::sender_concept, sender_t>; template<class Sndr>concept [*enable-sender*](#concept:enable-sender "33.9.3Sender concepts[exec.snd.concepts]") = // *exposition only*[*is-sender*](#concept:is-sender "33.9.3Sender concepts[exec.snd.concepts]")<Sndr> ||[*is-awaitable*](exec.awaitable#concept:is-awaitable "33.9.4Awaitable helpers[exec.awaitable]")<Sndr, *env-promise*<env<>>>; // [[exec.awaitable]](exec.awaitable "33.9.4Awaitable helpers")template<class Sndr>inline constexpr bool enable_sender = [*enable-sender*](#concept:enable-sender "33.9.3Sender concepts[exec.snd.concepts]")<Sndr>; template<class Sndr>consteval bool *is-dependent-sender-helper*() try { // *exposition only* get_completion_signatures<Sndr>(); return false; } catch (dependent_sender_error&) {return true; }template<class Sndr>concept [sender](#concept:sender "33.9.3Sender concepts[exec.snd.concepts]") = enable_sender<remove_cvref_t<Sndr>> &&requires (const remove_cvref_t<Sndr>& sndr) {{ get_env(sndr) } -> [*queryable*](exec.queryable.concept#concept:queryable "33.2.2queryable concept[exec.queryable.concept]"); } &&[move_constructible](concept.moveconstructible#concept:move_constructible "18.4.13Concept move_­constructible[concept.moveconstructible]")<remove_cvref_t<Sndr>> &&[constructible_from](concept.constructible#concept:constructible_from "18.4.11Concept constructible_­from[concept.constructible]")<remove_cvref_t<Sndr>, Sndr>; template<class Sndr, class... Env>concept [sender_in](#concept:sender_in "33.9.3Sender concepts[exec.snd.concepts]") =[sender](#concept:sender "33.9.3Sender concepts[exec.snd.concepts]")<Sndr> &&(sizeof...(Env) <= 1) &&([*queryable*](exec.queryable.concept#concept:queryable "33.2.2queryable concept[exec.queryable.concept]")<Env> &&...) &&[*is-constant*](#concept:is-constant "33.9.3Sender concepts[exec.snd.concepts]")<get_completion_signatures<Sndr, Env...>()>; template<class Sndr>concept [dependent_sender](#concept:dependent_sender "33.9.3Sender concepts[exec.snd.concepts]") =[sender](#concept:sender "33.9.3Sender concepts[exec.snd.concepts]")<Sndr> && bool_constant<*is-dependent-sender-helper*<Sndr>()>::value; template<class Sndr, class Rcvr>concept [sender_to](#concept:sender_to "33.9.3Sender concepts[exec.snd.concepts]") =[sender_in](#concept:sender_in "33.9.3Sender concepts[exec.snd.concepts]")<Sndr, env_of_t<Rcvr>> &&[receiver_of](exec.recv.concepts#concept:receiver_of "33.7.1Receiver concepts[exec.recv.concepts]")<Rcvr, completion_signatures_of_t<Sndr, env_of_t<Rcvr>>> &&requires (Sndr&& sndr, Rcvr&& rcvr) { connect(std::forward<Sndr>(sndr), std::forward<Rcvr>(rcvr)); };}
[2](#2)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2197)
For a type Sndr, if[sender](#concept:sender "33.9.3Sender concepts[exec.snd.concepts]")<Sndr> is true and[dependent_sender](#concept:dependent_sender "33.9.3Sender concepts[exec.snd.concepts]")<Sndr> is false,
then Sndr is a non-dependent sender ([[exec.async.ops]](exec.async.ops "33.3Asynchronous operations"))[.](#2.sentence-1)
[3](#3)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2203)
Given a subexpression sndr,
let Sndr be decltype((sndr)) and
let rcvr be a receiver
with an associated environment whose type is Env[.](#3.sentence-1)
A completion operation is a [*permissible completion*](#def:completion,permissible "33.9.3Sender concepts[exec.snd.concepts]") for Sndr and Env if its completion signature appears in the argument list of the specialization of completion_signatures denoted bycompletion_signatures_of_t<Sndr, Env>[.](#3.sentence-2)
Sndr and Env model [sender_in](#concept:sender_in "33.9.3Sender concepts[exec.snd.concepts]")<Sndr, Env> if all the completion operations
that are potentially evaluated by connecting sndr to rcvr and
starting the resulting operation state
are permissible completions for Sndr and Env[.](#3.sentence-3)
[4](#4)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2218)
*Remarks*: Pursuant to [[namespace.std]](namespace.std "16.4.5.2.1Namespace std"),
users may specialize enable_sender totrue for cv-unqualified program-defined types that
model [sender](#concept:sender "33.9.3Sender concepts[exec.snd.concepts]"), andfalse for types that do not[.](#4.sentence-1)
Such specializations shall
be usable in constant expressions ([[expr.const]](expr.const "7.7Constant expressions")) and
have type const bool[.](#4.sentence-2)
[5](#5)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2229)
The exposition-only concepts[*sender-of*](#concept:sender-of "33.9.3Sender concepts[exec.snd.concepts]") and [*sender-in-of*](#concept:sender-in-of "33.9.3Sender concepts[exec.snd.concepts]") define the requirements for a sender type
that completes with a given unique set of value result types[.](#5.sentence-1)
namespace std::execution {template<class... As>using *value-signature* = set_value_t(As...); // *exposition only*template<class Sndr, class SetValue, class... Env>concept [*sender-in-of-impl*](#concept:sender-in-of-impl "33.9.3Sender concepts[exec.snd.concepts]") = // *exposition only*[sender_in](#concept:sender_in "33.9.3Sender concepts[exec.snd.concepts]")<Sndr, Env...> &&*MATCHING-SIG*(SetValue, // see [[exec.general]](exec.general "33.1General")*gather-signatures*<set_value_t, // see [[exec.cmplsig]](exec.cmplsig "33.10Completion signatures") completion_signatures_of_t<Sndr, Env...>, *value-signature*,
type_identity_t>); template<class Sndr, class Env, class... Values>concept [*sender-in-of*](#concept:sender-in-of "33.9.3Sender concepts[exec.snd.concepts]") = // *exposition only*[*sender-in-of-impl*](#concept:sender-in-of-impl "33.9.3Sender concepts[exec.snd.concepts]")<Sndr, set_value_t(Values...), Env>; template<class Sndr, class... Values>concept [*sender-of*](#concept:sender-of "33.9.3Sender concepts[exec.snd.concepts]") = // *exposition only*[*sender-in-of-impl*](#concept:sender-in-of-impl "33.9.3Sender concepts[exec.snd.concepts]")<Sndr, set_value_t(Values...)>;}
[6](#6)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2258)
Let sndr be an expression
such that decltype((sndr)) is Sndr[.](#6.sentence-1)
The type tag_of_t<Sndr> is as follows:
- [(6.1)](#6.1)
If the declarationauto&& [tag, data, ...children] = sndr; would be well-formed, tag_of_t<Sndr> is
an alias for decltype(auto(tag)).
- [(6.2)](#6.2)
Otherwise, tag_of_t<Sndr> is ill-formed.
[7](#7)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2274)
Let [*sender-for*](#concept:sender-for "33.9.3Sender concepts[exec.snd.concepts]") be an exposition-only concept defined as follows:namespace std::execution {template<class Sndr, class Tag>concept [*sender-for*](#concept:sender-for "33.9.3Sender concepts[exec.snd.concepts]") =[sender](#concept:sender "33.9.3Sender concepts[exec.snd.concepts]")<Sndr> &&[same_as](concept.same#concept:same_as "18.4.2Concept same_­as[concept.same]")<tag_of_t<Sndr>, Tag>;}
[8](#8)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2285)
For a type T,*SET-VALUE-SIG*(T) denotes the type set_value_t() if T is cv void;
otherwise, it denotes the type set_value_t(T)[.](#8.sentence-1)
[9](#9)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2291)
Library-provided sender types
- [(9.1)](#9.1)
always expose an overload of a member connect that accepts an rvalue sender and
- [(9.2)](#9.2)
only expose an overload of a member connect that accepts an lvalue sender if they model [copy_constructible](concept.copyconstructible#concept:copy_constructible "18.4.14Concept copy_­constructible[concept.copyconstructible]")[.](#9.sentence-1)

604
cppdraft/exec/snd/expos.md Normal file

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,59 @@
[exec.snd.general]
# 33 Execution control library [[exec]](./#exec)
## 33.9 Senders [[exec.snd]](exec.snd#general)
### 33.9.1 General [exec.snd.general]
[1](#1)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L1325)
Subclauses [[exec.factories]](exec.factories "33.9.11Sender factories") and [[exec.adapt]](exec.adapt "33.9.12Sender adaptors") define
customizable algorithms that return senders[.](#1.sentence-1)
Each algorithm has a default implementation[.](#1.sentence-2)
Let sndr be the result of an invocation of such an algorithm or
an object equal to the result ([[concepts.equality]](concepts.equality "18.2Equality preservation")), and
let Sndr be decltype((sndr))[.](#1.sentence-3)
Let rcvr be a receiver of type Rcvr with associated environment env of type Env such that [sender_to](exec.snd.concepts#concept:sender_to "33.9.3Sender concepts[exec.snd.concepts]")<Sndr, Rcvr> is true[.](#1.sentence-4)
For the default implementation of the algorithm that produced sndr,
connecting sndr to rcvr and
starting the resulting operation state ([[exec.async.ops]](exec.async.ops "33.3Asynchronous operations"))
necessarily results in the potential evaluation ([[basic.def.odr]](basic.def.odr "6.3One-definition rule")) of
a set of completion operations
whose first argument is a subexpression equal to rcvr[.](#1.sentence-5)
Let Sigs be a pack of completion signatures corresponding to
this set of completion operations, and
let CS be
the type of the expression get_completion_signatures<Sndr, Env>()[.](#1.sentence-6)
Then CS is
a specialization of
the class template completion_signatures ([[exec.cmplsig]](exec.cmplsig "33.10Completion signatures")),
the set of whose template arguments is Sigs[.](#1.sentence-7)
If none of the types in Sigs are dependent on the type Env, then
the expression get_completion_signatures<Sndr>() is well-formed and
its type is CS[.](#1.sentence-8)
If a user-provided implementation of the algorithm
that produced sndr is selected instead of the default:
- [(1.1)](#1.1)
Any completion signature
that is in the set of types
denoted by completion_signatures_of_t<Sndr, Env> and
that is not part of Sigs shall correspond to
error or stopped completion operations,
unless otherwise specified[.](#1.1.sentence-1)
- [(1.2)](#1.2)
If none of the types in Sigs are dependent on the type Env, thencompletion_signatures_of_t<Sndr> andcompletion_signatures_of_t<Sndr, Env> shall denote the same type[.](#1.2.sentence-1)

View File

@@ -0,0 +1,39 @@
[exec.snd.transform]
# 33 Execution control library [[exec]](./#exec)
## 33.9 Senders [[exec.snd]](exec.snd#transform)
### 33.9.6 execution::transform_sender [exec.snd.transform]
[🔗](#lib:transform_sender)
`namespace std::execution {
template<class Domain, [sender](exec.snd.concepts#concept:sender "33.9.3Sender concepts[exec.snd.concepts]") Sndr, [queryable](exec.queryable.concept#concept:queryable "33.2.2queryable concept[exec.queryable.concept]")... Env>
requires (sizeof...(Env) <= 1)
constexpr [sender](exec.snd.concepts#concept:sender "33.9.3Sender concepts[exec.snd.concepts]") decltype(auto) transform_sender(Domain dom, Sndr&& sndr, const Env&... env)
noexcept(see below);
}
`
[1](#1)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2534)
Let *transformed-sndr* be the expressiondom.transform_sender(std::forward<Sndr>(sndr), env...) if that expression is well-formed; otherwise,default_domain().transform_sender(std::forward<Sndr>(sndr), env...)
Let *final-sndr* be the expression *transformed-sndr* if *transformed-sndr* and *sndr* have the same type ignoring cv-qualifiers;
otherwise, it is
the expression transform_sender(dom, *transformed-sndr*, env...)[.](#1.sentence-2)
[2](#2)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2549)
*Returns*: *final-sndr*[.](#2.sentence-1)
[3](#3)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2553)
*Remarks*: The exception specification is equivalent tonoexcept(*final-sndr*)[.](#3.sentence-1)

View File

@@ -0,0 +1,33 @@
[exec.snd.transform.env]
# 33 Execution control library [[exec]](./#exec)
## 33.9 Senders [[exec.snd]](exec.snd#transform.env)
### 33.9.7 execution::transform_env [exec.snd.transform.env]
[🔗](#lib:transform_env)
`namespace std::execution {
template<class Domain, [sender](exec.snd.concepts#concept:sender "33.9.3Sender concepts[exec.snd.concepts]") Sndr, [queryable](exec.queryable.concept#concept:queryable "33.2.2queryable concept[exec.queryable.concept]") Env>
constexpr [queryable](exec.queryable.concept#concept:queryable "33.2.2queryable concept[exec.queryable.concept]") decltype(auto) transform_env(Domain dom, Sndr&& sndr, Env&& env) noexcept;
}
`
[1](#1)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2570)
Let e be the expressiondom.transform_env(std::forward<Sndr>(sndr), std::forward<Env>(env)) if that expression is well-formed; otherwise,default_domain().transform_env(std::forward<Sndr>(sndr), std::forward<Env>(env))
[2](#2)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2580)
*Mandates*: noexcept(e) is true[.](#2.sentence-1)
[3](#3)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2584)
*Returns*: e[.](#3.sentence-1)

161
cppdraft/exec/spawn.md Normal file
View File

@@ -0,0 +1,161 @@
[exec.spawn]
# 33 Execution control library [[exec]](./#exec)
## 33.9 Senders [[exec.snd]](exec.snd#exec.spawn)
### 33.9.13 Sender consumers [[exec.consumers]](exec.consumers#exec.spawn)
#### 33.9.13.3 execution::spawn [exec.spawn]
[1](#1)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5843)
spawn attempts to associate the given input sender with
the given token's async scope and, on success,
eagerly starts the input sender[.](#1.sentence-1)
[2](#2)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5848)
The name spawn denotes a customization point object[.](#2.sentence-1)
For subexpressions sndr, token, and env,
- [(2.1)](#2.1)
let Sndr be decltype((sndr)),
- [(2.2)](#2.2)
let Token be remove_cvref_t<decltype((token))>, and
- [(2.3)](#2.3)
let Env be remove_cvref_t<decltype((env))>[.](#2.sentence-2)
If any of[sender](exec.snd.concepts#concept:sender "33.9.3Sender concepts[exec.snd.concepts]")<Sndr>,[scope_token](exec.scope.concepts#concept:scope_token "33.14.1Execution scope concepts[exec.scope.concepts]")<Token>, or[*queryable*](exec.queryable.concept#concept:queryable "33.2.2queryable concept[exec.queryable.concept]")<Env> are not satisfied,
the expression spawn(sndr, token, env) is ill-formed[.](#2.sentence-3)
[3](#3)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5863)
Let *spawn-state-base* be the exposition-only class:
[🔗](#lib:execution::spawn-state-base)
namespace std::execution {struct *spawn-state-base* { // *exposition only*virtual void *complete*() noexcept = 0; // *exposition only*};}
[4](#4)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5875)
Let *spawn-receiver* be the exposition-only class:
[🔗](#lib:execution::spawn-receiver)
namespace std::execution {struct *spawn-receiver* { // *exposition only*using receiver_concept = receiver_t; *spawn-state-base** *state*; // *exposition only*void set_value() && noexcept { *state*->*complete*(); }void set_stopped() && noexcept { *state*->*complete*(); }};}
[5](#5)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5891)
Let *spawn-state* be the exposition-only class template:
[🔗](#lib:execution::spawn-state)
namespace std::execution {template<class Alloc, [scope_token](exec.scope.concepts#concept:scope_token "33.14.1Execution scope concepts[exec.scope.concepts]") Token, [sender](exec.snd.concepts#concept:sender "33.9.3Sender concepts[exec.snd.concepts]") Sender>struct *spawn-state* : *spawn-state-base* { // *exposition only*using *op-t* = connect_result_t<Sender, *spawn-receiver*>; // *exposition only**spawn-state*(Alloc alloc, Sender&& sndr, Token token); // *exposition only*void *complete*() noexcept override; // *exposition only*void *run*(); // *exposition only*private:using *alloc-t* = // *exposition only*typename allocator_traits<Alloc>::template rebind_alloc<*spawn-state*>; *alloc-t* *alloc*; // *exposition only**op-t* *op*; // *exposition only* Token *token*; // *exposition only*void *destroy*() noexcept; // *exposition only*};}
[🔗](#lib:execution::spawn-state,constructor)
`spawn-state(Alloc alloc, Sender&& sndr, Token token);
`
[6](#6)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5924)
*Effects*: Initializes*alloc* with alloc,*token* with token, and*op* with:connect(std::move(sndr), *spawn-receiver*(this))
[🔗](#lib:run,execution::spawn-state)
`void run();
`
[7](#7)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5941)
*Effects*: Equivalent to:if (*token*.try_associate()) start(*op*);else*destroy*();
[🔗](#lib:complete,execution::spawn-state)
`void complete() noexcept override;
`
[8](#8)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5958)
*Effects*: Equivalent to:auto token = std::move(this->*token*);
*destroy*();
token.disassociate();
[🔗](#lib:destroy,execution::spawn-state)
`void destroy() noexcept;
`
[9](#9)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5975)
*Effects*: Equivalent to:auto alloc = std::move(this->*alloc*);
allocator_traits<*alloc-t*>::destroy(alloc, this);
allocator_traits<*alloc-t*>::deallocate(alloc, this, 1);
[10](#10)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5986)
For the expression spawn(sndr, token, env) let new_sender be the expression token.wrap(sndr) and
let alloc and senv be defined as follows:
- [(10.1)](#10.1)
if the expression get_allocator(env) is well-formed, thenalloc is the result of get_allocator(env) andsenv is the expression env,
- [(10.2)](#10.2)
otherwise
if the expression get_allocator(get_env(new_sender)) is well-formed, thenalloc is the result of get_allocator(get_env(new_sender)) andsenv is the expression *JOIN-ENV*(prop(get_allocator, alloc), env),
- [(10.3)](#10.3)
otherwisealloc is allocator<void>() andsenv is the expression env[.](#10.sentence-1)
[11](#11)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6006)
The expression spawn(sndr, token, env) is of type void and
has the following effects:
- [(11.1)](#11.1)
Uses alloc to allocate and construct an object o of
type that is a specialization of *spawn-state* fromalloc, write_env(token.wrap(sndr), senv), and token and then
invokes o.*run*()[.](#11.sentence-1)
If an exception is thrown then
any constructed objects are destroyed and any allocated memory is deallocated[.](#11.1.sentence-2)
[12](#12)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6021)
The expression spawn(sndr, token) is expression-equivalent tospawn(sndr, token, execution::env<>())[.](#12.sentence-1)

View File

@@ -0,0 +1,276 @@
[exec.spawn.future]
# 33 Execution control library [[exec]](./#exec)
## 33.9 Senders [[exec.snd]](exec.snd#exec.spawn.future)
### 33.9.12 Sender adaptors [[exec.adapt]](exec.adapt#exec.spawn.future)
#### 33.9.12.18 execution::spawn_future [exec.spawn.future]
[1](#1)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5230)
spawn_future attempts to associate the given input sender
with the given token's async scope and, on success,
eagerly starts the input sender;
the return value is a sender that, when connected and started,
completes with either
the result of the eagerly-started input sender or withset_stopped if the input sender was not started[.](#1.sentence-1)
[2](#2)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5239)
The name spawn_future denotes a customization point object[.](#2.sentence-1)
For subexpressions sndr, token, and env,
- [(2.1)](#2.1)
let Sndr be decltype((sndr)),
- [(2.2)](#2.2)
let Token be remove_cvref_t<decltype((token))>, and
- [(2.3)](#2.3)
let Env be remove_cvref_t<decltype((env))>[.](#2.sentence-2)
If any of[sender](exec.snd.concepts#concept:sender "33.9.3Sender concepts[exec.snd.concepts]")<Sndr>,[scope_token](exec.scope.concepts#concept:scope_token "33.14.1Execution scope concepts[exec.scope.concepts]")<Token>, or[*queryable*](exec.queryable.concept#concept:queryable "33.2.2queryable concept[exec.queryable.concept]")<Env> are not satisfied,
the expression spawn_future(sndr, token, env) is ill-formed[.](#2.sentence-3)
[3](#3)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5254)
Let *spawn-future-state-base* be the exposition-only class template:
[🔗](#lib:execution::spawn-future-state-base)
namespace std::execution {template<class Completions>struct *spawn-future-state-base*; // *exposition only*template<class... Sigs>struct *spawn-future-state-base*<completion_signatures<Sigs...>> { // *exposition only*using *variant-t* = *see below*; // *exposition only**variant-t* *result*; // *exposition only*virtual void *complete*() noexcept = 0; // *exposition only*};}
[4](#4)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5272)
Let Sigs be the pack of arguments to
the completion_signatures specialization provided as
a parameter to the *spawn-future-state-base* class template[.](#4.sentence-1)
Let *as-tuple* be an alias template that
transforms a completion signature Tag(Args...) into the tuple specialization *decayed-tuple*<Tag, Args...>[.](#4.sentence-2)
- [(4.1)](#4.1)
If is_nothrow_constructible_v<decay_t<Arg>, Arg> is true for every type Arg in every parameter pack Args in every completion signature Tag(Args...) in Sigs then*variant-t* denotes the typevariant<monostate, tuple<set_stopped_t>, *as-tuple*<Sigs>...>,
except with duplicate types removed[.](#4.1.sentence-1)
- [(4.2)](#4.2)
Otherwise*variant-t* denotes the typevariant<monostate, tuple<set_stopped_t>, tuple<set_error_t, exception_ptr>, *as-tuple*<Sigs>...>,
except with duplicate types removed[.](#4.2.sentence-1)
[5](#5)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5298)
Let *spawn-future-receiver* be the exposition-only class template:
[🔗](#lib:execution::spawn-future-receiver)
namespace std::execution {template<class Completions>struct *spawn-future-receiver* { // *exposition only*using receiver_concept = receiver_t; *spawn-future-state-base*<Completions>* *state*; // *exposition only*template<class... T>void set_value(T&&... t) && noexcept {*set-complete*<set_value_t>(std::forward<T>(t)...); }template<class E>void set_error(E&& e) && noexcept {*set-complete*<set_error_t>(std::forward<E>(e)); }void set_stopped() && noexcept {*set-complete*<set_stopped_t>(); }private:template<class CPO, class... T>void *set-complete*(T&&... t) noexcept { // *exposition only*constexpr bool nothrow = (is_nothrow_constructible_v<decay_t<T>, T> && ...); try {*state*->*result*.template emplace<*decayed-tuple*<CPO, T...>>(CPO{},
std::forward<T>(t)...); }catch (...) {if constexpr (!nothrow) {using tuple_t = *decayed-tuple*<set_error_t, exception_ptr>; *state*->*result*.template emplace<tuple_t>(set_error_t{}, current_exception()); }}*state*->*complete*(); }};}
[6](#6)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5344)
Let *ssource-t* be an unspecified type
that models [*stoppable-source*](stoptoken.concepts#concept:stoppable-source "32.3.3Stop token concepts[stoptoken.concepts]") and
let ssource be an lvalue of type *ssource-t*[.](#6.sentence-1)
Let *stoken-t* be decltype(ssource.get_token())[.](#6.sentence-2)
Let *future-spawned-sender* be the alias template:
template<[sender](exec.snd.concepts#concept:sender "33.9.3Sender concepts[exec.snd.concepts]") Sender, class Env>using *future-spawned-sender* = // *exposition only*decltype(write_env(*stop-when*(declval<Sender>(), declval<*stoken-t*>()), declval<Env>()));
[7](#7)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5357)
Let *spawn-future-state* be the exposition-only class template:
[🔗](#lib:execution::spawn-future-state)
namespace std::execution {template<class Alloc, [scope_token](exec.scope.concepts#concept:scope_token "33.14.1Execution scope concepts[exec.scope.concepts]") Token, [sender](exec.snd.concepts#concept:sender "33.9.3Sender concepts[exec.snd.concepts]") Sender, class Env>struct *spawn-future-state* // *exposition only*: *spawn-future-state-base*<completion_signatures_of_t<*future-spawned-sender*<Sender, Env>>> {using *sigs-t* = // *exposition only* completion_signatures_of_t<*future-spawned-sender*<Sender, Env>>; using *receiver-t* = // *exposition only**spawn-future-receiver*<*sigs-t*>; using *op-t* = // *exposition only* connect_result_t<*future-spawned-sender*<Sender, Env>, *receiver-t*>; *spawn-future-state*(Alloc alloc, Sender&& sndr, Token token, Env env) // *exposition only*: *alloc*(std::move(alloc)), *op*(connect( write_env(*stop-when*(std::forward<Sender>(sndr), *ssource*.get_token()), std::move(env)), *receiver-t*(this))), *token*(std::move(token)), *associated*(token.try_associate()) {if (associated) start(*op*); else set_stopped(*receiver-t*(this)); }void *complete*() noexcept override; // *exposition only*void *consume*([receiver](exec.recv.concepts#concept:receiver "33.7.1Receiver concepts[exec.recv.concepts]") auto& rcvr) noexcept; // *exposition only*void *abandon*() noexcept; // *exposition only*private:using *alloc-t* = // *exposition only*typename allocator_traits<Alloc>::template rebind_alloc<*spawn-future-state*>; *alloc-t* *alloc*; // *exposition only**ssource-t* *ssource*; // *exposition only**op-t* *op*; // *exposition only* Token *token*; // *exposition only*bool *associated*; // *exposition only*void *destroy*() noexcept; // *exposition only*};}
[8](#8)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5405)
For purposes of determining the existence of a data race,*complete*, *consume*, and *abandon* behave as atomic operations ([[intro.multithread]](intro.multithread "6.10.2Multi-threaded executions and data races"))[.](#8.sentence-1)
These operations on a single object of a type
that is a specialization of *spawn-future-state* appear to occur in a single total order[.](#8.sentence-2)
[🔗](#lib:complete,execution::spawn-future-state)
`void complete() noexcept;
`
[9](#9)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5419)
*Effects*:
- [(9.1)](#9.1)
No effects if this invocation of *complete* happens before
an invocation of *consume* or *abandon* on *this;
- [(9.2)](#9.2)
otherwise,
if an invocation of *consume* on *this happens before
this invocation of *complete* then
there is a receiver, rcvr, registered and
that receiver is completed as if by *consume*(rcvr);
- [(9.3)](#9.3)
otherwise,*destroy* is invoked[.](#9.sentence-1)
[🔗](#lib:consume,execution::spawn-future-state)
`void consume([receiver](exec.recv.concepts#concept:receiver "33.7.1Receiver concepts[exec.recv.concepts]") auto& rcvr) noexcept;
`
[10](#10)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5443)
*Effects*:
- [(10.1)](#10.1)
If this invocation of *consume* happens before
an invocation of *complete* on *this thenrcvr is registered to be completed when*complete* is subsequently invoked on *this;
- [(10.2)](#10.2)
otherwise,rcvr is completed as if by:std::move(this->*result*).visit([&rcvr](auto&& tuple) noexcept {if constexpr (![same_as](concept.same#concept:same_as "18.4.2Concept same_­as[concept.same]")<remove_reference_t<decltype(tuple)>, monostate>) { apply([&rcvr](auto cpo, auto&&... vals) { cpo(std::move(rcvr), std::move(vals)...); }, std::move(tuple)); }});
[🔗](#lib:abandon,execution::spawn-future-state)
`void abandon() noexcept;
`
[11](#11)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5474)
*Effects*:
- [(11.1)](#11.1)
If this invocation of *abandon* happens before
an invocation of *complete* on *this then
equivalent to:*ssource*.request_stop();
- [(11.2)](#11.2)
otherwise,*destroy* is invoked[.](#11.sentence-1)
[🔗](#lib:destroy,execution::spawn-future-state)
`void destroy() noexcept;
`
[12](#12)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5496)
*Effects*: Equivalent to:auto token = std::move(this->*token*);bool associated = this->*associated*;
{auto alloc = std::move(this->*alloc*);
allocator_traits<*alloc-t*>::destroy(alloc, this);
allocator_traits<*alloc-t*>::deallocate(alloc, this, 1);}if (associated) token.disassociate();
[13](#13)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5515)
The exposition-only class template *impls-for* ([[exec.snd.expos]](exec.snd.expos "33.9.2Exposition-only entities"))
is specialized for spawn_future_t as follows:
[🔗](#lib:execution::impls-for%3cspawn_future_t%3e)
namespace std::execution {template<>struct *impls-for*<spawn_future_t> : *default-impls* {static constexpr auto *start* = *see below*; // *exposition only*};}
[14](#14)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5529)
The member *impls-for*<spawn_future_t>::*start* is initialized with a callable object equivalent to the following lambda:[](auto& state, auto& rcvr) noexcept -> void { state->*consume*(rcvr);}
[15](#15)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5538)
For the expression spawn_future(sndr, token, env) let new_sender be the expression token.wrap(sndr) and
let alloc and senv be defined as follows:
- [(15.1)](#15.1)
if the expression get_allocator(env) is well-formed, thenalloc is the result of get_allocator(env) andsenv is the expression env;
- [(15.2)](#15.2)
otherwise,
if the expression get_allocator(get_env(new_sender)) is well-formed, thenalloc is the result of get_allocator(get_env(new_sender)) andsenv is the expression*JOIN-ENV*(prop(get_allocator, alloc), env);
- [(15.3)](#15.3)
otherwise,alloc is allocator<void>() andsenv is the expression env[.](#15.sentence-1)
[16](#16)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5559)
The expression spawn_future(sndr, token, env) has the following effects:
- [(16.1)](#16.1)
Uses alloc to allocate and construct an object s of
a type that is a specialization of *spawn-future-state* from alloc, token.wrap(sndr), token, and senv[.](#16.1.sentence-1)
If an exception is thrown then
any constructed objects are destroyed and
any allocated memory is deallocated[.](#16.1.sentence-2)
- [(16.2)](#16.2)
Constructs an object u of
a type that is a specialization of unique_ptr such that:
* [(16.2.1)](#16.2.1)
u.get() is equal to the address of s, and
* [(16.2.2)](#16.2.2)
u.get_deleter()(u.release()) is equivalent to u.release()->*abandon*()[.](#16.2.sentence-1)
- [(16.3)](#16.3)
Returns *make-sender*(spawn_future, std::move(u))[.](#16.3.sentence-1)
[17](#17)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5587)
The expression spawn_future(sndr, token) is expression-equivalent tospawn_future(sndr, token, execution::env<>())[.](#17.sentence-1)

View File

@@ -0,0 +1,72 @@
[exec.starts.on]
# 33 Execution control library [[exec]](./#exec)
## 33.9 Senders [[exec.snd]](exec.snd#exec.starts.on)
### 33.9.12 Sender adaptors [[exec.adapt]](exec.adapt#exec.starts.on)
#### 33.9.12.5 execution::starts_on [exec.starts.on]
[1](#1)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3224)
starts_on adapts an input sender into a sender
that will start on an execution agent belonging to
a particular scheduler's associated execution resource[.](#1.sentence-1)
[2](#2)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3229)
The name starts_on denotes a customization point object[.](#2.sentence-1)
For subexpressions sch and sndr,
if decltype((
sch)) does not satisfy [scheduler](exec.sched#concept:scheduler "33.6Schedulers[exec.sched]"), ordecltype((sndr)) does not satisfy [sender](exec.snd.concepts#concept:sender "33.9.3Sender concepts[exec.snd.concepts]"),starts_on(sch, sndr) is ill-formed[.](#2.sentence-2)
[3](#3)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3236)
Otherwise,
the expression starts_on(sch, sndr) is expression-equivalent to:transform_sender(*query-with-default*(get_domain, sch, default_domain()), *make-sender*(starts_on, sch, sndr)) except that sch is evaluated only once[.](#3.sentence-1)
[4](#4)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3246)
Let out_sndr and env be subexpressions
such that OutSndr is decltype((out_sndr))[.](#4.sentence-1)
If [*sender-for*](exec.snd.concepts#concept:sender-for "33.9.3Sender concepts[exec.snd.concepts]")<OutSndr, starts_on_t> is false,
then the expressions starts_on.transform_env(out_sndr, env) andstarts_on.transform_sender(out_sndr, env) are ill-formed; otherwise
- [(4.1)](#4.1)
starts_on.transform_env(out_sndr, env) is equivalent to:auto&& [_, sch, _] = out_sndr;return *JOIN-ENV*(*SCHED-ENV*(sch), *FWD-ENV*(env));
- [(4.2)](#4.2)
starts_on.transform_sender(out_sndr, env) is equivalent to:auto&& [_, sch, sndr] = out_sndr;return let_value( schedule(sch), [sndr = std::forward_like<OutSndr>(sndr)]() mutablenoexcept(is_nothrow_move_constructible_v<decay_t<OutSndr>>) {return std::move(sndr); });
[5](#5)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3272)
Let out_sndr be a subexpression denoting
a sender returned from starts_on(sch, sndr) or one equal to such, and
let OutSndr be the type decltype((out_sndr))[.](#5.sentence-1)
Let out_rcvr be a subexpression denoting a receiver
that has an environment of type Env such that [sender_in](exec.snd.concepts#concept:sender_in "33.9.3Sender concepts[exec.snd.concepts]")<OutSndr, Env> is true[.](#5.sentence-2)
Let op be an lvalue referring to the operation state
that results from connecting out_sndr with out_rcvr[.](#5.sentence-3)
Calling start(op) shall start sndr on an execution agent of the associated execution resource of sch[.](#5.sentence-4)
If scheduling onto sch fails,
an error completion on out_rcvr shall be executed
on an unspecified execution agent[.](#5.sentence-5)

View File

@@ -0,0 +1,73 @@
[exec.stop.when]
# 33 Execution control library [[exec]](./#exec)
## 33.9 Senders [[exec.snd]](exec.snd#exec.stop.when)
### 33.9.12 Sender adaptors [[exec.adapt]](exec.adapt#exec.stop.when)
#### 33.9.12.17 Exposition-only execution::*stop-when* [exec.stop.when]
[1](#1)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5154)
*stop-when* fuses an additional stop token t into a sender so that, upon connecting to a receiver r,
the resulting operation state receives stop requests from botht and the token returned from get_stop_token(get_env(r))[.](#1.sentence-1)
[2](#2)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5161)
The name *stop-when* denotes an exposition-only sender adaptor[.](#2.sentence-1)
For subexpressions sndr and token:
- [(2.1)](#2.1)
If decltype((sndr)) does not satisfy [sender](exec.snd.concepts#concept:sender "33.9.3Sender concepts[exec.snd.concepts]"), orremove_cvref_t<decltype((token))> does not satisfy [stoppable_token](stoptoken.concepts#concept:stoppable_token "32.3.3Stop token concepts[stoptoken.concepts]"),
then *stop-when*(sndr, token) is ill-formed[.](#2.1.sentence-1)
- [(2.2)](#2.2)
Otherwise,
if remove_cvref_t<decltype((token))> models[unstoppable_token](stoptoken.concepts#concept:unstoppable_token "32.3.3Stop token concepts[stoptoken.concepts]") then*stop-when*(sndr, token) is expression-equivalent tosndr[.](#2.2.sentence-1)
- [(2.3)](#2.3)
Otherwise,*stop-when*(sndr, token) returns a sender osndr[.](#2.3.sentence-1)
If osndr is connected to a receiver r,
let rtoken be the result of get_stop_token(get_env(r))[.](#2.3.sentence-2)
* [(2.3.1)](#2.3.1)
If the type of rtoken models [unstoppable_token](stoptoken.concepts#concept:unstoppable_token "32.3.3Stop token concepts[stoptoken.concepts]") then
the effects of connecting osndr to r are equivalent toconnect(write_env(sndr, prop(get_stop_token, token)), r)[.](#2.3.1.sentence-1)
* [(2.3.2)](#2.3.2)
Otherwise,
the effects of connecting osndr to r are equivalent toconnect(write_env(sndr, prop(get_stop_token, stoken)), r) where stoken is an object of
an exposition-only type *stoken-t* such that:
+
[(2.3.2.1)](#2.3.2.1)
*stoken-t* models [stoppable_token](stoptoken.concepts#concept:stoppable_token "32.3.3Stop token concepts[stoptoken.concepts]");
+
[(2.3.2.2)](#2.3.2.2)
stoken.stop_requested() returns token.stop_requested() || rtoken.stop_reques-
ted();
+
[(2.3.2.3)](#2.3.2.3)
stoken.stop_possible() returns token.stop_possible() || rtoken.stop_possible(); and
+
[(2.3.2.4)](#2.3.2.4)
for types Fn and Init such that both [invocable](concept.invocable#concept:invocable "18.7.2Concept invocable[concept.invocable]")<Fn> and [constructible_from](concept.constructible#concept:constructible_from "18.4.11Concept constructible_­from[concept.constructible]")<Fn, Init> are modeled, *stoken-t*::callback_type<Fn> models [*stoppable-callback-for*](stoptoken.concepts#concept:stoppable-callback-for "32.3.3Stop token concepts[stoptoken.concepts]")<Fn, *stoken-t*, Init>[.](#2.3.2.sentence-1)
[*Note [1](#note-1)*:
For an object fn of type Fn constructed from a value, init, of type Init,
registering fn using *stoken-t*::callback_type<Fn>(stoken, init) results in an invocation of fn when
a callback registered with token or rtoken would be invoked[.](#2.3.2.4.sentence-2)
fn is invoked at most once[.](#2.3.2.4.sentence-3)
— *end note*]

View File

@@ -0,0 +1,45 @@
[exec.stopped.err]
# 33 Execution control library [[exec]](./#exec)
## 33.9 Senders [[exec.snd]](exec.snd#exec.stopped.err)
### 33.9.12 Sender adaptors [[exec.adapt]](exec.adapt#exec.stopped.err)
#### 33.9.12.15 execution::stopped_as_error [exec.stopped.err]
[1](#1)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4856)
stopped_as_error maps an input sender's stopped completion operation
into an error completion operation as a custom error type[.](#1.sentence-1)
The result is a sender that never completes with stopped,
reporting cancellation by completing with an error[.](#1.sentence-2)
[2](#2)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4862)
The name stopped_as_error denotes a pipeable sender adaptor object[.](#2.sentence-1)
For some subexpressions sndr and err,
let Sndr be decltype((sndr)) and
let Err be decltype((err))[.](#2.sentence-2)
If the type Sndr does not satisfy [sender](exec.snd.concepts#concept:sender "33.9.3Sender concepts[exec.snd.concepts]") or
if the type Err does not satisfy [*movable-value*](exec.general#concept:movable-value "33.1General[exec.general]"),stopped_as_error(sndr, err) is ill-formed[.](#2.sentence-3)
Otherwise, the expression stopped_as_error(sndr, err) is expression-equivalent to:transform_sender(*get-domain-early*(sndr), *make-sender*(stopped_as_error, err, sndr)) except that sndr is only evaluated once[.](#2.sentence-4)
[3](#3)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4877)
Let sndr and env be subexpressions
such that Sndr is decltype((sndr)) andEnv is decltype((env))[.](#3.sentence-1)
If [*sender-for*](exec.snd.concepts#concept:sender-for "33.9.3Sender concepts[exec.snd.concepts]")<Sndr, stopped_as_error_t> is false,
then the expression stopped_as_error.transform_sender(sndr, env) is ill-formed;
otherwise, it is equivalent to:auto&& [_, err, child] = sndr;using E = decltype(auto(err));return let_stopped( std::forward_like<Sndr>(child), [err = std::forward_like<Sndr>(err)]() mutable noexcept(is_nothrow_move_constructible_v<E>) {return just_error(std::move(err)); });

View File

@@ -0,0 +1,57 @@
[exec.stopped.opt]
# 33 Execution control library [[exec]](./#exec)
## 33.9 Senders [[exec.snd]](exec.snd#exec.stopped.opt)
### 33.9.12 Sender adaptors [[exec.adapt]](exec.adapt#exec.stopped.opt)
#### 33.9.12.14 execution::stopped_as_optional [exec.stopped.opt]
[1](#1)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4789)
stopped_as_optional maps a sender's stopped completion operation
into a value completion operation as a disengaged optional[.](#1.sentence-1)
The sender's value completion operation
is also converted into an optional[.](#1.sentence-2)
The result is a sender that never completes with stopped,
reporting cancellation by completing with a disengaged optional[.](#1.sentence-3)
[2](#2)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4797)
The name stopped_as_optional denotes a pipeable sender adaptor object[.](#2.sentence-1)
For a subexpression sndr, let Sndr be decltype((sndr))[.](#2.sentence-2)
The expression stopped_as_optional(sndr) is expression-equivalent to:transform_sender(*get-domain-early*(sndr), *make-sender*(stopped_as_optional, {}, sndr)) except that sndr is only evaluated once[.](#2.sentence-3)
[3](#3)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4806)
The exposition-only class template *impls-for* ([[exec.snd.expos]](exec.snd.expos "33.9.2Exposition-only entities"))
is specialized for stopped_as_optional_t as follows:
[🔗](#lib:impls-for%3cstopped_as_optional_t%3e)
namespace std::execution {template<>struct *impls-for*<stopped_as_optional_t> : *default-impls* {template<class Sndr, class... Env>static consteval void *check-types*() {*default-impls*::*check-types*<Sndr, Env...>(); if constexpr (!requires {requires (![same_as](concept.same#concept:same_as "18.4.2Concept same_­as[concept.same]")<void, *single-sender-value-type*<*child-type*<Sndr>, *FWD-ENV-T*(Env)...>>); })throw *unspecified-exception*(); }};} where *unspecified-exception* is
a type derived from exception[.](#3.sentence-1)
[4](#4)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4829)
Let sndr and env be subexpressions
such that Sndr is decltype((sndr)) andEnv is decltype((env))[.](#4.sentence-1)
If [*sender-for*](exec.snd.concepts#concept:sender-for "33.9.3Sender concepts[exec.snd.concepts]")<Sndr, stopped_as_optional_t> is false then the expression stopped_as_optional.transform_sender(sndr, env) is ill-formed;
otherwise,
if [sender_in](exec.snd.concepts#concept:sender_in "33.9.3Sender concepts[exec.snd.concepts]")<*child-type*<Sndr>, *FWD-ENV-T*(Env)> is false,
the expression stopped_as_optional.transform_sender(sndr, env) is equivalent to *not-a-sender*();
otherwise, it is equivalent to:auto&& [_, _, child] = sndr;using V = *single-sender-value-type*<*child-type*<Sndr>, *FWD-ENV-T*(Env)>;return let_stopped( then(std::forward_like<Sndr>(child), []<class... Ts>(Ts&&... ts) noexcept(is_nothrow_constructible_v<V, Ts...>) {return optional<V>(in_place, std::forward<Ts>(ts)...); }), []() noexcept { return just(optional<V>()); });

147
cppdraft/exec/sync/wait.md Normal file
View File

@@ -0,0 +1,147 @@
[exec.sync.wait]
# 33 Execution control library [[exec]](./#exec)
## 33.9 Senders [[exec.snd]](exec.snd#exec.sync.wait)
### 33.9.13 Sender consumers [[exec.consumers]](exec.consumers#exec.sync.wait)
#### 33.9.13.1 this_thread::sync_wait [exec.sync.wait]
[1](#1)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5595)
this_thread::sync_wait and this_thread::sync_wait_with_variant are used
to block the current thread of execution
until the specified sender completes and
to return its async result[.](#1.sentence-1)
sync_wait mandates
that the input sender has exactly one value completion signature[.](#1.sentence-2)
[2](#2)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5604)
Let *sync-wait-env* be the following exposition-only class type:namespace std::this_thread {struct *sync-wait-env* { execution::run_loop* *loop*; // *exposition only*auto query(execution::get_scheduler_t) const noexcept {return *loop*->get_scheduler(); }auto query(execution::get_delegation_scheduler_t) const noexcept {return *loop*->get_scheduler(); }};}
[3](#3)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5622)
Let *sync-wait-result-type* and*sync-wait-with-variant-result-type* be exposition-only alias templates defined as follows:namespace std::this_thread {template<execution::[sender_in](exec.snd.concepts#concept:sender_in "33.9.3Sender concepts[exec.snd.concepts]")<*sync-wait-env*> Sndr>using *sync-wait-result-type* = optional<execution::value_types_of_t<Sndr, *sync-wait-env*, *decayed-tuple*,
type_identity_t>>; template<execution::[sender_in](exec.snd.concepts#concept:sender_in "33.9.3Sender concepts[exec.snd.concepts]")<*sync-wait-env*> Sndr>using *sync-wait-with-variant-result-type* = optional<execution::value_types_of_t<Sndr, *sync-wait-env*>>;}
[4](#4)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5639)
The name this_thread::sync_wait denotes a customization point object[.](#4.sentence-1)
For a subexpression sndr, let Sndr be decltype((sndr))[.](#4.sentence-2)
The expression this_thread::sync_wait(sndr) is expression-equivalent to the following,
except that sndr is evaluated only once:apply_sender(*get-domain-early*(sndr), sync_wait, sndr)*Mandates*:
- [(4.1)](#4.1)
[sender_in](exec.snd.concepts#concept:sender_in "33.9.3Sender concepts[exec.snd.concepts]")<Sndr, *sync-wait-env*> is true[.](#4.1.sentence-1)
- [(4.2)](#4.2)
The type *sync-wait-result-type*<Sndr> is well-formed[.](#4.2.sentence-1)
- [(4.3)](#4.3)
[same_as](concept.same#concept:same_as "18.4.2Concept same_­as[concept.same]")<decltype(e), *sync-wait-result-type*<Sndr>> is true, where e is the apply_sender expression above[.](#4.3.sentence-1)
[5](#5)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5659)
Let *sync-wait-state* and *sync-wait-receiver* be the following exposition-only class templates:namespace std::this_thread {template<class Sndr>struct *sync-wait-state* { // *exposition only* execution::run_loop *loop*; // *exposition only* exception_ptr *error*; // *exposition only**sync-wait-result-type*<Sndr> *result*; // *exposition only*}; template<class Sndr>struct *sync-wait-receiver* { // *exposition only*using receiver_concept = execution::receiver_t; *sync-wait-state*<Sndr>* *state*; // *exposition only*template<class... Args>void set_value(Args&&... args) && noexcept; template<class Error>void set_error(Error&& err) && noexcept; void set_stopped() && noexcept; *sync-wait-env* get_env() const noexcept { return {&*state*->*loop*}; }};}
[🔗](#itemdecl:1)
`template<class... Args>
void set_value(Args&&... args) && noexcept;
`
[6](#6)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5695)
*Effects*: Equivalent to:try {*state*->*result*.emplace(std::forward<Args>(args)...);} catch (...) {*state*->*error* = current_exception();}*state*->*loop*.finish();
[🔗](#itemdecl:2)
`template<class Error>
void set_error(Error&& err) && noexcept;
`
[7](#7)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5714)
*Effects*: Equivalent to:*state*->*error* = *AS-EXCEPT-PTR*(std::forward<Error>(err)); // see [[exec.general]](exec.general "33.1General")*state*->*loop*.finish();
[🔗](#itemdecl:3)
`void set_stopped() && noexcept;
`
[8](#8)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5728)
*Effects*: Equivalent to *state*->*loop*.finish()[.](#8.sentence-1)
[9](#9)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5733)
For a subexpression sndr, let Sndr be decltype((sndr))[.](#9.sentence-1)
If [sender_to](exec.snd.concepts#concept:sender_to "33.9.3Sender concepts[exec.snd.concepts]")<Sndr, *sync-wait-receiver*<Sndr>> is false,
the expression sync_wait.apply_sender(sndr) is ill-formed;
otherwise, it is equivalent to:*sync-wait-state*<Sndr> state;auto op = connect(sndr, *sync-wait-receiver*<Sndr>{&state});
start(op);
state.*loop*.run();if (state.*error*) { rethrow_exception(std::move(state.*error*));}return std::move(state.*result*);
[10](#10)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5751)
The behavior of this_thread::sync_wait(sndr) is undefined unless:
- [(10.1)](#10.1)
It blocks the current thread of execution ([[defns.block]](defns.block "3.6block"))
with forward progress guarantee delegation ([[intro.progress]](intro.progress "6.10.2.3Forward progress"))
until the specified sender completes[.](#10.1.sentence-1)
[*Note [1](#note-1)*:
The default implementation of sync_wait achieves
forward progress guarantee delegation by providing a run_loop scheduler
via the get_delegation_scheduler query
on the *sync-wait-receiver*'s environment[.](#10.1.sentence-2)
The run_loop is driven by the current thread of execution[.](#10.1.sentence-3)
— *end note*]
- [(10.2)](#10.2)
It returns the specified sender's async results as follows:
* [(10.2.1)](#10.2.1)
For a value completion,
the result datums are returned in
a tuple in an engaged optional object[.](#10.2.1.sentence-1)
* [(10.2.2)](#10.2.2)
For an error completion, an exception is thrown[.](#10.2.2.sentence-1)
* [(10.2.3)](#10.2.3)
For a stopped completion, a disengaged optional object is returned[.](#10.2.3.sentence-1)

View File

@@ -0,0 +1,74 @@
[exec.sync.wait.var]
# 33 Execution control library [[exec]](./#exec)
## 33.9 Senders [[exec.snd]](exec.snd#exec.sync.wait.var)
### 33.9.13 Sender consumers [[exec.consumers]](exec.consumers#exec.sync.wait.var)
#### 33.9.13.2 this_thread::sync_wait_with_variant [exec.sync.wait.var]
[1](#1)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5781)
The name this_thread::sync_wait_with_variant denotes
a customization point object[.](#1.sentence-1)
For a subexpression sndr,
let Sndr be decltype(into_variant(sndr))[.](#1.sentence-2)
The expression this_thread::sync_wait_with_variant(sndr) is expression-equivalent to the following,
except sndr is evaluated only once:apply_sender(*get-domain-early*(sndr), sync_wait_with_variant, sndr)*Mandates*:
- [(1.1)](#1.1)
[sender_in](exec.snd.concepts#concept:sender_in "33.9.3Sender concepts[exec.snd.concepts]")<Sndr, *sync-wait-env*> is true[.](#1.1.sentence-1)
- [(1.2)](#1.2)
The type *sync-wait-with-variant-result-type*<Sndr> is well-formed[.](#1.2.sentence-1)
- [(1.3)](#1.3)
[same_as](concept.same#concept:same_as "18.4.2Concept same_­as[concept.same]")<decltype(e), *sync-wait-with-variant-result-type*<Sndr>> is true, where e is the apply_sender expression above[.](#1.3.sentence-1)
[2](#2)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5804)
The expression sync_wait_with_variant.apply_sender(sndr) is equivalent to:using result_type = *sync-wait-with-variant-result-type*<Sndr>;if (auto opt_value = sync_wait(into_variant(sndr))) {return result_type(std::move(get<0>(*opt_value)));}return result_type(nullopt);
[3](#3)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5814)
The behavior of this_thread::sync_wait_with_variant(sndr) is undefined unless:
- [(3.1)](#3.1)
It blocks the current thread of execution ([[defns.block]](defns.block "3.6block"))
with forward progress guarantee delegation ([[intro.progress]](intro.progress "6.10.2.3Forward progress"))
until the specified sender completes[.](#3.1.sentence-1)
[*Note [1](#note-1)*:
The default implementation of sync_wait_with_variant achieves
forward progress guarantee delegation by relying on
the forward progress guarantee delegation provided by sync_wait[.](#3.1.sentence-2)
— *end note*]
- [(3.2)](#3.2)
It returns the specified sender's async results as follows:
* [(3.2.1)](#3.2.1)
For a value completion,
the result datums are returned in an engaged optional object
that contains a variant of tuples[.](#3.2.1.sentence-1)
* [(3.2.2)](#3.2.2)
For an error completion, an exception is thrown[.](#3.2.2.sentence-1)
* [(3.2.3)](#3.2.3)
For a stopped completion, a disengaged optional object is returned[.](#3.2.3.sentence-1)

297
cppdraft/exec/sysctxrepl.md Normal file
View File

@@ -0,0 +1,297 @@
[exec.sysctxrepl]
# 33 Execution control library [[exec]](./#exec)
## 33.16 Namespace system_context_replaceability [exec.sysctxrepl]
### [33.16.1](#general) General [[exec.sysctxrepl.general]](exec.sysctxrepl.general)
[1](#general-1)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L8432)
Facilities in the system_context_replaceability namespace
allow users to replace the default implementation of parallel_scheduler[.](#general-1.sentence-1)
### [33.16.2](#query) query_parallel_scheduler_backend [[exec.sysctxrepl.query]](exec.sysctxrepl.query)
[🔗](#lib:query_parallel_scheduler_backend)
`shared_ptr<parallel_scheduler_backend> query_parallel_scheduler_backend();
`
[1](#query-1)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L8443)
query_parallel_scheduler_backend() returns
the implementation object for a parallel scheduler[.](#query-1.sentence-1)
[2](#query-2)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L8447)
*Returns*: A non-null shared pointer to an object
that implements the parallel_scheduler_backend interface[.](#query-2.sentence-1)
[3](#query-3)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L8452)
*Remarks*: This function is replaceable ([[dcl.fct.def.replace]](dcl.fct.def.replace#term.replaceable.function "9.6.5Replaceable function definitions"))[.](#query-3.sentence-1)
namespace std::execution::system_context_replaceability {struct [receiver_proxy](#lib:receiver_proxy "33.16.2query_­parallel_­scheduler_­backend[exec.sysctxrepl.query]") {virtual ~receiver_proxy() = default; protected:virtual bool *query-env*(*unspecified*) noexcept = 0; // *exposition only*public:virtual void set_value() noexcept = 0; virtual void set_error(exception_ptr) noexcept = 0; virtual void set_stopped() noexcept = 0; template<class P, [*class-type*](execution.syn#concept:class-type "33.4Header <execution> synopsis[execution.syn]") Query> optional<P> try_query(Query q) noexcept; }; struct [bulk_item_receiver_proxy](#lib:bulk_item_receiver_proxy "33.16.2query_­parallel_­scheduler_­backend[exec.sysctxrepl.query]") : receiver_proxy {virtual void execute(size_t, size_t) noexcept = 0; };}
[4](#query-4)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L8480)
receiver_proxy represents a receiver
that will be notified
by the implementations of parallel_scheduler_backend to trigger the completion operations[.](#query-4.sentence-1)
bulk_item_receiver_proxy is derived from receiver_proxy and
is used for bulk_chunked and bulk_unchunked customizations
that will also receive notifications
from implementations of parallel_scheduler_backend corresponding to different iterations[.](#query-4.sentence-2)
[🔗](#query-itemdecl:2)
`template<class P, [class-type](execution.syn#concept:class-type "33.4Header <execution> synopsis[execution.syn]") Query>
optional<P> [try_query](#lib:try_query "33.16.2query_­parallel_­scheduler_­backend[exec.sysctxrepl.query]")(Query q) noexcept;
`
[5](#query-5)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L8497)
*Mandates*: P is a cv-unqualified non-array object type[.](#query-5.sentence-1)
[6](#query-6)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L8501)
*Returns*: Let env be the environment of the receiver represented by *this[.](#query-6.sentence-1)
If
- [(6.1)](#query-6.1)
Query is not a member of an implementation-defined set
of supported queries; or
- [(6.2)](#query-6.2)
P is not a member of an implementation-defined set
of supported result types for Query; or
- [(6.3)](#query-6.3)
the expression q(env) is not well-formed or
does not have type cv P,
then returns nullopt[.](#query-6.sentence-2)
Otherwise, returns q(env)[.](#query-6.sentence-3)
[7](#query-7)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L8519)
*Remarks*: get_stop_token_t is
in the implementation-defined set of supported queries, andinplace_stop_token is a member
of the implementation-defined set of supported result types
for get_stop_token_t[.](#query-7.sentence-1)
### [33.16.3](#psb) Class parallel_scheduler_backend [[exec.sysctxrepl.psb]](exec.sysctxrepl.psb)
namespace std::execution::system_context_replaceability {struct [parallel_scheduler_backend](#lib:parallel_scheduler_backend "33.16.3Class parallel_­scheduler_­backend[exec.sysctxrepl.psb]") {virtual ~parallel_scheduler_backend() = default; virtual void schedule(receiver_proxy&, span<byte>) noexcept = 0; virtual void schedule_bulk_chunked(size_t, bulk_item_receiver_proxy&,
span<byte>) noexcept = 0; virtual void schedule_bulk_unchunked(size_t, bulk_item_receiver_proxy&,
span<byte>) noexcept = 0; };}
[🔗](#lib:schedule,parallel_scheduler_backend)
`virtual void schedule(receiver_proxy& r, span<byte> s) noexcept = 0;
`
[1](#psb-1)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L8550)
*Preconditions*: The ends of
the lifetimes of *this,
the object referred to by r, and
any storage referenced by s all happen after
the beginning of the evaluation of
the call to set_value, set_error, or set_done on r (see below)[.](#psb-1.sentence-1)
[2](#psb-2)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L8561)
*Effects*: A derived class shall implement this function such that:
- [(2.1)](#psb-2.1)
One of the following expressions is evaluated:
* [(2.1.1)](#psb-2.1.1)
r.set_value(), if no error occurs, and the work is successful;
* [(2.1.2)](#psb-2.1.2)
r.set_error(eptr), if an error occurs,
where eptr is an object of type exception_ptr;
* [(2.1.3)](#psb-2.1.3)
r.set_stopped(), if the work is canceled[.](#psb-2.1.sentence-1)
- [(2.2)](#psb-2.2)
Any call to r.set_value() happens on
an execution agent of the execution context represented by *this[.](#psb-2.2.sentence-1)
[3](#psb-3)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L8581)
*Remarks*: The storage referenced by s may be used by *this as temporary storage
for the duration of the operation launched by this call[.](#psb-3.sentence-1)
[🔗](#lib:schedule_bulk_chunked,parallel_scheduler_backend)
`virtual void schedule_bulk_chunked(size_t n, bulk_item_receiver_proxy& r,
span<byte> s) noexcept = 0;
`
[4](#psb-4)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L8595)
*Preconditions*: The ends of
the lifetimes of *this,
the object referred to by r, and
any storage referenced by s all happen after
the beginning of the evaluation of one of the expressions below[.](#psb-4.sentence-1)
[5](#psb-5)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L8604)
*Effects*: A derived class shall implement this function such that:
- [(5.1)](#psb-5.1)
Eventually, one of the following expressions is evaluated:
* [(5.1.1)](#psb-5.1.1)
r.set_value(), if no error occurs, and the work is successful;
* [(5.1.2)](#psb-5.1.2)
r.set_error(eptr), if an error occurs,
where eptr is an object of type exception_ptr;
* [(5.1.3)](#psb-5.1.3)
r.set_stopped(), if the work is canceled[.](#psb-5.1.sentence-1)
- [(5.2)](#psb-5.2)
If r.execute(b, e) is called,
then b and e are in the range [0, n] andb < e[.](#psb-5.2.sentence-1)
- [(5.3)](#psb-5.3)
For each i in [0, n),
there is at most one call to r.execute(b, e) such that i is in the range [b, e)[.](#psb-5.3.sentence-1)
- [(5.4)](#psb-5.4)
If r.set_value() is called,
then for each i in [0, n),
there is exactly one call to r.execute(b, e) such that i is in the range [b, e)[.](#psb-5.4.sentence-1)
- [(5.5)](#psb-5.5)
All calls to execute on r happen before
the call to either set_value, set_error, or set_stopped on r[.](#psb-5.5.sentence-1)
- [(5.6)](#psb-5.6)
All calls to execute and set_value on r are made
on execution agents of the execution context represented by *this[.](#psb-5.6.sentence-1)
[6](#psb-6)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L8641)
*Remarks*: The storage referenced by s may be used by *this as temporary storage for the duration of the operation launched by this call[.](#psb-6.sentence-1)
[🔗](#lib:schedule_bulk_unchunked,parallel_scheduler_backend)
`virtual void schedule_bulk_unchunked(size_t n, bulk_item_receiver_proxy& r,
span<byte> s) noexcept = 0;
`
[7](#psb-7)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L8654)
*Preconditions*: The ends of
the lifetimes of *this,
the object referred to by r, and
any storage referenced by s all happen after
the beginning of the evaluation of one of the expressions below[.](#psb-7.sentence-1)
[8](#psb-8)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L8663)
*Effects*: A derived class shall implement this function such that:
- [(8.1)](#psb-8.1)
Eventually, one of the following expressions is evaluated:
* [(8.1.1)](#psb-8.1.1)
r.set_value(), if no error occurs, and the work is successful;
* [(8.1.2)](#psb-8.1.2)
r.set_error(eptr), if an error occurs,
where eptr is an object of type exception_ptr;
* [(8.1.3)](#psb-8.1.3)
r.set_stopped(), if the work is canceled[.](#psb-8.1.sentence-1)
- [(8.2)](#psb-8.2)
If r.execute(b, e) is called,
then b is in the range [0, n) ande is equal to b + 1[.](#psb-8.2.sentence-1)
For each i in [0, n),
there is at most one call to r.execute(i, i + 1)[.](#psb-8.2.sentence-2)
- [(8.3)](#psb-8.3)
If r.set_value() is called,
then for each i in [0, n),
there is exactly one call to r.execute(i, i + 1)[.](#psb-8.3.sentence-1)
- [(8.4)](#psb-8.4)
All calls to execute on r happen before
the call to either set_value, set_error, or set_stopped on r[.](#psb-8.4.sentence-1)
- [(8.5)](#psb-8.5)
All calls to execute and set_value on r are made
on execution agents of the execution context represented by *this[.](#psb-8.5.sentence-1)
[9](#psb-9)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L8697)
*Remarks*: The storage referenced by s may be used by *this as temporary storage for the duration of the operation launched by this call[.](#psb-9.sentence-1)

View File

@@ -0,0 +1,14 @@
[exec.sysctxrepl.general]
# 33 Execution control library [[exec]](./#exec)
## 33.16 Namespace system_context_replaceability [[exec.sysctxrepl]](exec.sysctxrepl#general)
### 33.16.1 General [exec.sysctxrepl.general]
[1](#1)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L8432)
Facilities in the system_context_replaceability namespace
allow users to replace the default implementation of parallel_scheduler[.](#1.sentence-1)

View File

@@ -0,0 +1,198 @@
[exec.sysctxrepl.psb]
# 33 Execution control library [[exec]](./#exec)
## 33.16 Namespace system_context_replaceability [[exec.sysctxrepl]](exec.sysctxrepl#psb)
### 33.16.3 Class parallel_scheduler_backend [exec.sysctxrepl.psb]
namespace std::execution::system_context_replaceability {struct [parallel_scheduler_backend](#lib:parallel_scheduler_backend "33.16.3Class parallel_­scheduler_­backend[exec.sysctxrepl.psb]") {virtual ~parallel_scheduler_backend() = default; virtual void schedule(receiver_proxy&, span<byte>) noexcept = 0; virtual void schedule_bulk_chunked(size_t, bulk_item_receiver_proxy&,
span<byte>) noexcept = 0; virtual void schedule_bulk_unchunked(size_t, bulk_item_receiver_proxy&,
span<byte>) noexcept = 0; };}
[🔗](#lib:schedule,parallel_scheduler_backend)
`virtual void schedule(receiver_proxy& r, span<byte> s) noexcept = 0;
`
[1](#1)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L8550)
*Preconditions*: The ends of
the lifetimes of *this,
the object referred to by r, and
any storage referenced by s all happen after
the beginning of the evaluation of
the call to set_value, set_error, or set_done on r (see below)[.](#1.sentence-1)
[2](#2)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L8561)
*Effects*: A derived class shall implement this function such that:
- [(2.1)](#2.1)
One of the following expressions is evaluated:
* [(2.1.1)](#2.1.1)
r.set_value(), if no error occurs, and the work is successful;
* [(2.1.2)](#2.1.2)
r.set_error(eptr), if an error occurs,
where eptr is an object of type exception_ptr;
* [(2.1.3)](#2.1.3)
r.set_stopped(), if the work is canceled[.](#2.1.sentence-1)
- [(2.2)](#2.2)
Any call to r.set_value() happens on
an execution agent of the execution context represented by *this[.](#2.2.sentence-1)
[3](#3)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L8581)
*Remarks*: The storage referenced by s may be used by *this as temporary storage
for the duration of the operation launched by this call[.](#3.sentence-1)
[🔗](#lib:schedule_bulk_chunked,parallel_scheduler_backend)
`virtual void schedule_bulk_chunked(size_t n, bulk_item_receiver_proxy& r,
span<byte> s) noexcept = 0;
`
[4](#4)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L8595)
*Preconditions*: The ends of
the lifetimes of *this,
the object referred to by r, and
any storage referenced by s all happen after
the beginning of the evaluation of one of the expressions below[.](#4.sentence-1)
[5](#5)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L8604)
*Effects*: A derived class shall implement this function such that:
- [(5.1)](#5.1)
Eventually, one of the following expressions is evaluated:
* [(5.1.1)](#5.1.1)
r.set_value(), if no error occurs, and the work is successful;
* [(5.1.2)](#5.1.2)
r.set_error(eptr), if an error occurs,
where eptr is an object of type exception_ptr;
* [(5.1.3)](#5.1.3)
r.set_stopped(), if the work is canceled[.](#5.1.sentence-1)
- [(5.2)](#5.2)
If r.execute(b, e) is called,
then b and e are in the range [0, n] andb < e[.](#5.2.sentence-1)
- [(5.3)](#5.3)
For each i in [0, n),
there is at most one call to r.execute(b, e) such that i is in the range [b, e)[.](#5.3.sentence-1)
- [(5.4)](#5.4)
If r.set_value() is called,
then for each i in [0, n),
there is exactly one call to r.execute(b, e) such that i is in the range [b, e)[.](#5.4.sentence-1)
- [(5.5)](#5.5)
All calls to execute on r happen before
the call to either set_value, set_error, or set_stopped on r[.](#5.5.sentence-1)
- [(5.6)](#5.6)
All calls to execute and set_value on r are made
on execution agents of the execution context represented by *this[.](#5.6.sentence-1)
[6](#6)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L8641)
*Remarks*: The storage referenced by s may be used by *this as temporary storage for the duration of the operation launched by this call[.](#6.sentence-1)
[🔗](#lib:schedule_bulk_unchunked,parallel_scheduler_backend)
`virtual void schedule_bulk_unchunked(size_t n, bulk_item_receiver_proxy& r,
span<byte> s) noexcept = 0;
`
[7](#7)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L8654)
*Preconditions*: The ends of
the lifetimes of *this,
the object referred to by r, and
any storage referenced by s all happen after
the beginning of the evaluation of one of the expressions below[.](#7.sentence-1)
[8](#8)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L8663)
*Effects*: A derived class shall implement this function such that:
- [(8.1)](#8.1)
Eventually, one of the following expressions is evaluated:
* [(8.1.1)](#8.1.1)
r.set_value(), if no error occurs, and the work is successful;
* [(8.1.2)](#8.1.2)
r.set_error(eptr), if an error occurs,
where eptr is an object of type exception_ptr;
* [(8.1.3)](#8.1.3)
r.set_stopped(), if the work is canceled[.](#8.1.sentence-1)
- [(8.2)](#8.2)
If r.execute(b, e) is called,
then b is in the range [0, n) ande is equal to b + 1[.](#8.2.sentence-1)
For each i in [0, n),
there is at most one call to r.execute(i, i + 1)[.](#8.2.sentence-2)
- [(8.3)](#8.3)
If r.set_value() is called,
then for each i in [0, n),
there is exactly one call to r.execute(i, i + 1)[.](#8.3.sentence-1)
- [(8.4)](#8.4)
All calls to execute on r happen before
the call to either set_value, set_error, or set_stopped on r[.](#8.4.sentence-1)
- [(8.5)](#8.5)
All calls to execute and set_value on r are made
on execution agents of the execution context represented by *this[.](#8.5.sentence-1)
[9](#9)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L8697)
*Remarks*: The storage referenced by s may be used by *this as temporary storage for the duration of the operation launched by this call[.](#9.sentence-1)

View File

@@ -0,0 +1,95 @@
[exec.sysctxrepl.query]
# 33 Execution control library [[exec]](./#exec)
## 33.16 Namespace system_context_replaceability [[exec.sysctxrepl]](exec.sysctxrepl#query)
### 33.16.2 query_parallel_scheduler_backend [exec.sysctxrepl.query]
[🔗](#lib:query_parallel_scheduler_backend)
`shared_ptr<parallel_scheduler_backend> query_parallel_scheduler_backend();
`
[1](#1)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L8443)
query_parallel_scheduler_backend() returns
the implementation object for a parallel scheduler[.](#1.sentence-1)
[2](#2)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L8447)
*Returns*: A non-null shared pointer to an object
that implements the parallel_scheduler_backend interface[.](#2.sentence-1)
[3](#3)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L8452)
*Remarks*: This function is replaceable ([[dcl.fct.def.replace]](dcl.fct.def.replace#term.replaceable.function "9.6.5Replaceable function definitions"))[.](#3.sentence-1)
namespace std::execution::system_context_replaceability {struct [receiver_proxy](#lib:receiver_proxy "33.16.2query_­parallel_­scheduler_­backend[exec.sysctxrepl.query]") {virtual ~receiver_proxy() = default; protected:virtual bool *query-env*(*unspecified*) noexcept = 0; // *exposition only*public:virtual void set_value() noexcept = 0; virtual void set_error(exception_ptr) noexcept = 0; virtual void set_stopped() noexcept = 0; template<class P, [*class-type*](execution.syn#concept:class-type "33.4Header <execution> synopsis[execution.syn]") Query> optional<P> try_query(Query q) noexcept; }; struct [bulk_item_receiver_proxy](#lib:bulk_item_receiver_proxy "33.16.2query_­parallel_­scheduler_­backend[exec.sysctxrepl.query]") : receiver_proxy {virtual void execute(size_t, size_t) noexcept = 0; };}
[4](#4)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L8480)
receiver_proxy represents a receiver
that will be notified
by the implementations of parallel_scheduler_backend to trigger the completion operations[.](#4.sentence-1)
bulk_item_receiver_proxy is derived from receiver_proxy and
is used for bulk_chunked and bulk_unchunked customizations
that will also receive notifications
from implementations of parallel_scheduler_backend corresponding to different iterations[.](#4.sentence-2)
[🔗](#itemdecl:2)
`template<class P, [class-type](execution.syn#concept:class-type "33.4Header <execution> synopsis[execution.syn]") Query>
optional<P> [try_query](#lib:try_query "33.16.2query_­parallel_­scheduler_­backend[exec.sysctxrepl.query]")(Query q) noexcept;
`
[5](#5)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L8497)
*Mandates*: P is a cv-unqualified non-array object type[.](#5.sentence-1)
[6](#6)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L8501)
*Returns*: Let env be the environment of the receiver represented by *this[.](#6.sentence-1)
If
- [(6.1)](#6.1)
Query is not a member of an implementation-defined set
of supported queries; or
- [(6.2)](#6.2)
P is not a member of an implementation-defined set
of supported result types for Query; or
- [(6.3)](#6.3)
the expression q(env) is not well-formed or
does not have type cv P,
then returns nullopt[.](#6.sentence-2)
Otherwise, returns q(env)[.](#6.sentence-3)
[7](#7)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L8519)
*Remarks*: get_stop_token_t is
in the implementation-defined set of supported queries, andinplace_stop_token is a member
of the implementation-defined set of supported result types
for get_stop_token_t[.](#7.sentence-1)

530
cppdraft/exec/task.md Normal file
View File

@@ -0,0 +1,530 @@
[exec.task]
# 33 Execution control library [[exec]](./#exec)
## 33.13 Coroutine utilities [[exec.coro.util]](exec.coro.util#exec.task)
### 33.13.6 execution::task [exec.task]
#### [33.13.6.1](#task.overview) task overview [[task.overview]](task.overview)
[1](#task.overview-1)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L7171)
The task class template represents a sender that can
be used as the return type of coroutines[.](#task.overview-1.sentence-1)
The first template parameter T defines the type of the value
completion datum ([[exec.async.ops]](exec.async.ops "33.3Asynchronous operations")) if T is not void[.](#task.overview-1.sentence-2)
Otherwise, there are no value completion datums[.](#task.overview-1.sentence-3)
Inside coroutines returning task<T, E> the operand ofco_return (if any) becomes the argument of set_value[.](#task.overview-1.sentence-4)
The second template parameter Environment is used to customize
the behavior of task[.](#task.overview-1.sentence-5)
#### [33.13.6.2](#task.class) Class template task [[task.class]](task.class)
namespace std::execution {template<class T, class Environment>class [task](#lib:task "33.13.6.2Class template task[task.class]") {// [[task.state]](#task.state "33.13.6.4Class template task::state")template<[receiver](exec.recv.concepts#concept:receiver "33.7.1Receiver concepts[exec.recv.concepts]") Rcvr>class *state*; // *exposition only*public:using sender_concept = sender_t; using completion_signatures = *see below*; using allocator_type = *see below*; using scheduler_type = *see below*; using stop_source_type = *see below*; using stop_token_type = decltype(declval<stop_source_type>().get_token()); using error_types = *see below*; // [[task.promise]](#task.promise "33.13.6.5Class task::promise_­type")class promise_type;
task(task&&) noexcept; ~task(); template<[receiver](exec.recv.concepts#concept:receiver "33.7.1Receiver concepts[exec.recv.concepts]") Rcvr>*state*<Rcvr> connect(Rcvr&& rcvr); private: coroutine_handle<promise_type> *handle*; // *exposition only*};}
[1](#task.class-1)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L7216)
task<T, E> models [sender](exec.snd.concepts#concept:sender "33.9.3Sender concepts[exec.snd.concepts]") ([[exec.snd]](exec.snd "33.9Senders"))
if T is void, a reference type, or a cv-unqualified
non-array object type and E is a class type[.](#task.class-1.sentence-1)
Otherwise a program that instantiates the definition of task<T, E> is ill-formed[.](#task.class-1.sentence-2)
[2](#task.class-2)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L7223)
The nested types of task template specializations
are determined based on the Environment parameter:
- [(2.1)](#task.class-2.1)
allocator_type is Environment::allocator_type if that [*qualified-id*](expr.prim.id.qual#nt:qualified-id "7.5.5.3Qualified names[expr.prim.id.qual]") is valid and denotes a type,allocator<byte> otherwise[.](#task.class-2.1.sentence-1)
- [(2.2)](#task.class-2.2)
scheduler_type is Environment::scheduler_type if that [*qualified-id*](expr.prim.id.qual#nt:qualified-id "7.5.5.3Qualified names[expr.prim.id.qual]") is valid and denotes a type,task_scheduler otherwise[.](#task.class-2.2.sentence-1)
- [(2.3)](#task.class-2.3)
stop_source_type is Environment::stop_source_type if that [*qualified-id*](expr.prim.id.qual#nt:qualified-id "7.5.5.3Qualified names[expr.prim.id.qual]") is valid and denotes a type,inplace_stop_source otherwise[.](#task.class-2.3.sentence-1)
- [(2.4)](#task.class-2.4)
error_types is Environment::error_types if
that [*qualified-id*](expr.prim.id.qual#nt:qualified-id "7.5.5.3Qualified names[expr.prim.id.qual]") is valid and denotes a type,completion_signatures<set_error_t(exception_ptr)> otherwise[.](#task.class-2.4.sentence-1)
[3](#task.class-3)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L7241)
A program is ill-formed if error_types is not a
specialization of completion_signatures<ErrorSigs...> orErrorSigs contains an element which is not of the formset_error_t(E) for some type E[.](#task.class-3.sentence-1)
[4](#task.class-4)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L7247)
The type alias completion_signatures is a specialization
of execution::completion_signatures with the template
arguments (in unspecified order):
- [(4.1)](#task.class-4.1)
set_value_t() if T is void,
and set_value_t(T) otherwise;
- [(4.2)](#task.class-4.2)
template arguments of the specialization ofexecution::completion_signatures denoted by error_types;
and
- [(4.3)](#task.class-4.3)
set_stopped_t()[.](#task.class-4.sentence-1)
[5](#task.class-5)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L7260)
allocator_type shall meet the *Cpp17Allocator* requirements[.](#task.class-5.sentence-1)
#### [33.13.6.3](#task.members) task members [[task.members]](task.members)
[🔗](#lib:task,constructor)
`task(task&& other) noexcept;
`
[1](#task.members-1)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L7271)
*Effects*: Initializes *handle* with exchange(other.*handle*,{})[.](#task.members-1.sentence-1)
[🔗](#lib:task,destructor)
`~task();
`
[2](#task.members-2)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L7282)
*Effects*: Equivalent to:if (*handle*)*handle*.destroy();
[🔗](#lib:connect,task)
`template<[receiver](exec.recv.concepts#concept:receiver "33.7.1Receiver concepts[exec.recv.concepts]") Rcvr>
state<Rcvr> connect(Rcvr&& recv);
`
[3](#task.members-3)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L7297)
*Preconditions*: bool(*handle*) is true[.](#task.members-3.sentence-1)
[4](#task.members-4)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L7301)
*Effects*: Equivalent to:return *state*<Rcvr>(exchange(*handle*, {}), std::forward<Rcvr>(recv));
#### [33.13.6.4](#task.state) Class template task::*state* [[task.state]](task.state)
namespace std::execution {template<class T, class Environment>template<[receiver](exec.recv.concepts#concept:receiver "33.7.1Receiver concepts[exec.recv.concepts]") Rcvr>class task<T, Environment>::*state* { // *exposition only*public:using operation_state_concept = operation_state_t; template<class R>*state*(coroutine_handle<promise_type> h, R&& rr); ~*state*(); void start() & noexcept; private:using *own-env-t* = *see below*; // *exposition only* coroutine_handle<promise_type> *handle*; // *exposition only* remove_cvref_t<Rcvr> *rcvr*; // *exposition only**own-env-t* *own-env*; // *exposition only* Environment *environment*; // *exposition only*};}
[1](#task.state-1)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L7336)
The type *own-env-t* is Environment::template env_type<decltype(get_env(declval<Rcvr>()))> if that[*qualified-id*](expr.prim.id.qual#nt:qualified-id "7.5.5.3Qualified names[expr.prim.id.qual]") is valid and denotes a type, env<> otherwise[.](#task.state-1.sentence-1)
[🔗](#lib:task::state,constructor)
`template<class R>
state(coroutine_handle<promise_type> h, R&& rr);
`
[2](#task.state-2)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L7347)
*Effects*: Initializes
- [(2.1)](#task.state-2.1)
*handle* with std::move(h);
- [(2.2)](#task.state-2.2)
*rcvr* with std::forward<R>(rr);
- [(2.3)](#task.state-2.3)
*own-env* with *own-env-t*(get_env(*rcvr*)) if that expression
is valid and *own-env-t*() otherwise[.](#task.state-2.3.sentence-1)
If neither of these expressions is valid, the program is ill-formed[.](#task.state-2.3.sentence-2)
- [(2.4)](#task.state-2.4)
*environment* withEnvironment(*own-env*) if that expression is
valid, otherwise Environment(get_env(*rcvr*)) if this expression is valid, otherwise Environment()[.](#task.state-2.4.sentence-1)
If neither of these expressions is valid, the program is ill-formed[.](#task.state-2.4.sentence-2)
[🔗](#lib:task::state,destructor)
`~state();
`
[3](#task.state-3)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L7370)
*Effects*: Equivalent to:if (*handle*)*handle*.destroy();
[🔗](#lib:start,task::state)
`void start() & noexcept;
`
[4](#task.state-4)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L7384)
*Effects*: Let *prom* be the object *handle*.promise()[.](#task.state-4.sentence-1)
Associates *STATE*(*prom*), *RCVR*(*prom*), and *SCHED*(*prom*) with *this as follows:
- [(4.1)](#task.state-4.1)
*STATE*(*prom*) is *this[.](#task.state-4.1.sentence-1)
- [(4.2)](#task.state-4.2)
*RCVR*(*prom*) is *rcvr*[.](#task.state-4.2.sentence-1)
- [(4.3)](#task.state-4.3)
*SCHED*(*prom*) is the object initialized
with scheduler_type(get_scheduler(get_env(*rcvr*))) if that expression is valid and scheduler_type() otherwise[.](#task.state-4.3.sentence-1)
If neither of these expressions is valid, the program is ill-formed[.](#task.state-4.3.sentence-2)
Let *st* be get_stop_token(get_env(*rcvr*))[.](#task.state-4.sentence-3)
Initializes *prom*.*token* and*prom*.*source* such that
- [(4.4)](#task.state-4.4)
*prom*.*token*.stop_requested() returns*st*.stop_requested();
- [(4.5)](#task.state-4.5)
*prom*.*token*.stop_possible() returns*st*.stop_possible(); and
- [(4.6)](#task.state-4.6)
for types Fn and Init such that both[invocable](concept.invocable#concept:invocable "18.7.2Concept invocable[concept.invocable]")<Fn> and[constructible_from](concept.constructible#concept:constructible_from "18.4.11Concept constructible_­from[concept.constructible]")<Fn, Init> are modeled,stop_token_type::callback_type<Fn> models[*stoppable-callback-for*](stoptoken.concepts#concept:stoppable-callback-for "32.3.3Stop token concepts[stoptoken.concepts]")<Fn, stop_token_type, Init>[.](#task.state-4.sentence-4)
After that invokes *handle*.resume()[.](#task.state-4.sentence-5)
#### [33.13.6.5](#task.promise) Class task::promise_type [[task.promise]](task.promise)
namespace std::execution {template<class T, class Environment>class task<T, Environment>::promise_type {public:template<class... Args> promise_type(const Args&... args);
task get_return_object() noexcept; auto initial_suspend() noexcept; auto final_suspend() noexcept; void uncaught_exception();
coroutine_handle<> unhandled_stopped(); void return_void(); // present only if is_void_v<T> is truetemplate<class V>void return_value(V&& value); // present only if is_void_v<T> is falsetemplate<class E>*unspecified* yield_value(with_error<E> error); template<class A>auto await_transform(A&& a); template<class Sch>auto await_transform(change_coroutine_scheduler<Sch> sch); *unspecified* get_env() const noexcept; template<class... Args>void* operator new(size_t size, Args&&... args); void operator delete(void* pointer, size_t size) noexcept; private:using *error-variant* = *see below*; // *exposition only* allocator_type *alloc*; // *exposition only* stop_source_type *source*; // *exposition only* stop_token_type *token*; // *exposition only* optional<T> *result*; // *exposition only*; present only if is_void_v<T> is false*error-variant* *errors*; // *exposition only*};}
[1](#task.promise-1)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L7466)
Let *prom* be an object of promise_type and let *tsk* be the task object
created by *prom*.get_return_object()[.](#task.promise-1.sentence-1)
The description below
refers to objects *STATE*(*prom*),*RCVR*(*prom*),
and *SCHED*(*prom*) associated with *tsk* during evaluation of task::*state*<Rcvr>::start for some receiver Rcvr[.](#task.promise-1.sentence-2)
[2](#task.promise-2)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L7478)
*error-variant* is a variant<monostate,
remove_cvref_t<E>...>, with duplicate types removed, where E... are the parameter types of the template arguments of the specialization ofexecution::completion_signatures denoted byerror_types[.](#task.promise-2.sentence-1)
[🔗](#lib:task::promise_type,constructor)
`template<class... Args>
promise_type(const Args&... args);
`
[3](#task.promise-3)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L7491)
*Mandates*: The first parameter of type allocator_arg_t (if any) is not
the last parameter[.](#task.promise-3.sentence-1)
[4](#task.promise-4)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L7496)
*Effects*: If Args contains an element of type allocator_arg_t then *alloc* is initialized with the corresponding next
element of args[.](#task.promise-4.sentence-1)
Otherwise, *alloc* is initialized with allocator_type()[.](#task.promise-4.sentence-2)
[🔗](#lib:get_return_object,task::promise_type)
`task get_return_object() noexcept;
`
[5](#task.promise-5)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L7509)
*Returns*: A task object whose member *handle* iscoroutine_handle<promise_type>::from_promise(*this)[.](#task.promise-5.sentence-1)
[🔗](#lib:initial_suspend,task::promise_type)
`auto initial_suspend() noexcept;
`
[6](#task.promise-6)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L7520)
*Returns*: An awaitable object of unspecified type ([[expr.await]](expr.await "7.6.2.4Await")) whose
member functions arrange for
- [(6.1)](#task.promise-6.1)
the calling coroutine to be suspended,
- [(6.2)](#task.promise-6.2)
the coroutine to be resumed on an execution agent of the
execution resource associated with *SCHED*(*this)[.](#task.promise-6.sentence-1)
[🔗](#lib:final_suspend,task::promise_type)
`auto final_suspend() noexcept;
`
[7](#task.promise-7)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L7536)
*Returns*: An awaitable object of unspecified type ([[expr.await]](expr.await "7.6.2.4Await")) whose
member functions arrange for the completion of the asynchronous
operation associated with *STATE*(*this) by invoking:
- [(7.1)](#task.promise-7.1)
set_error(std::move(*RCVR*(*this)), std::move(e)) if *errors*.index() is greater than zero ande is the value held by *errors*, otherwise
- [(7.2)](#task.promise-7.2)
set_value(std::move(*RCVR*(*this))) if is_void<T> is true,
and otherwise
- [(7.3)](#task.promise-7.3)
set_value(std::move(*RCVR*(*this)), **result*)[.](#task.promise-7.sentence-1)
[🔗](#lib:yield_value,task::promise_type)
`template<class Err>
auto yield_value(with_error<Err> err);
`
[8](#task.promise-8)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L7560)
*Mandates*: std::move(err.error) is convertible to exactly one of theset_error_t argument types of error_types[.](#task.promise-8.sentence-1)
Let *Cerr* be that type[.](#task.promise-8.sentence-2)
[9](#task.promise-9)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L7566)
*Returns*: An awaitable object of unspecified type ([[expr.await]](expr.await "7.6.2.4Await")) whose
member functions arrange for the calling coroutine to be suspended
and then completes the asynchronous operation associated with*STATE*(*this) by invoking set_error(std::move(*RCVR*(*this)),*Cerr*(std::move(err.error)))[.](#task.promise-9.sentence-1)
[🔗](#lib:await_transform,task::promise_type)
`template<[sender](exec.snd.concepts#concept:sender "33.9.3Sender concepts[exec.snd.concepts]") Sender>
auto await_transform(Sender&& sndr) noexcept;
`
[10](#task.promise-10)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L7581)
*Returns*: If [same_as](concept.same#concept:same_as "18.4.2Concept same_­as[concept.same]")<inline_scheduler, scheduler_type> is true returns as_awaitable(std::forward<Sender>(sndr), *this);
otherwise returnsas_awaitable(affine_on(std::forward<Sender>(sndr), *SCHED*(*this)), *this)[.](#task.promise-10.sentence-1)
[🔗](#lib:await_transform,task::promise_type_)
`template<class Sch>
auto await_transform(change_coroutine_scheduler<Sch> sch) noexcept;
`
[11](#task.promise-11)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L7595)
*Effects*: Equivalent to:return await_transform(just(exchange(*SCHED*(*this), scheduler_type(sch.scheduler))), *this);
[🔗](#lib:uncaught_exception,task::promise_type)
`void uncaught_exception();
`
[12](#task.promise-12)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L7608)
*Effects*: If the signature set_error_t(exception_ptr) is not an element
of error_types, calls terminate() ([[except.terminate]](except.terminate "14.6.2The std::terminate function"))[.](#task.promise-12.sentence-1)
Otherwise, stores current_exception() into *errors*[.](#task.promise-12.sentence-2)
[🔗](#lib:unhandled_stopped,task::promise_type)
`coroutine_handle<> unhandled_stopped();
`
[13](#task.promise-13)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L7620)
*Effects*: Completes the asynchronous operation associated with *STATE*(*this) by invoking set_stopped(std::move(*RCVR*(*this)))[.](#task.promise-13.sentence-1)
[14](#task.promise-14)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L7626)
*Returns*: noop_coroutine()[.](#task.promise-14.sentence-1)
[🔗](#lib:get_env,task::promise_type)
`unspecified get_env() const noexcept;
`
[15](#task.promise-15)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L7636)
*Returns*: An object env such that queries are forwarded as follows:
- [(15.1)](#task.promise-15.1)
env.query(get_scheduler) returns scheduler_type(*SCHED*(*this))[.](#task.promise-15.1.sentence-1)
- [(15.2)](#task.promise-15.2)
env.query(get_allocator) returns *alloc*[.](#task.promise-15.2.sentence-1)
- [(15.3)](#task.promise-15.3)
env.query(get_stop_token) returns *token*[.](#task.promise-15.3.sentence-1)
- [(15.4)](#task.promise-15.4)
For any other query q and arguments a... a
call to env.query(q, a...) returns*STATE*(*this)[.](#task.promise-15.4.sentence-1)
environment.query(q, a...) if this expression
is well-formed and forwarding_query(q) is well-formed and is true[.](#task.promise-15.4.sentence-2)
Otherwise env.query(q, a...) is ill-formed[.](#task.promise-15.4.sentence-3)
[🔗](#lib:operator_new,task::promise_type)
`template<class... Args>
void* operator new(size_t size, const Args&... args);
`
[16](#task.promise-16)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L7657)
If there is no parameter with type allocator_arg_t then letalloc be allocator_type()[.](#task.promise-16.sentence-1)
Otherwise, let arg_next be the parameter
following the first allocator_arg_t parameter,
and let alloc be allocator_type(arg_next)[.](#task.promise-16.sentence-2)
Let PAlloc be allocator_traits<allocator_type>::template rebind_alloc<U>, where U is an unspecified type
whose size and alignment are both __STDCPP_DEFAULT_NEW_ALIGNMENT__[.](#task.promise-16.sentence-3)
[17](#task.promise-17)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L7667)
*Mandates*:
- [(17.1)](#task.promise-17.1)
The first parameter of type allocator_arg_t (if any) is not the last parameter[.](#task.promise-17.1.sentence-1)
- [(17.2)](#task.promise-17.2)
allocator_type(arg_next) is a valid expression if there is a parameter
of type allocator_arg_t[.](#task.promise-17.2.sentence-1)
- [(17.3)](#task.promise-17.3)
allocator_traits<PAlloc>::pointer is a pointer type[.](#task.promise-17.3.sentence-1)
[18](#task.promise-18)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L7676)
*Effects*: Initializes an allocator palloc of type PAlloc withalloc[.](#task.promise-18.sentence-1)
Uses palloc to allocate storage for the
smallest array of U sufficient to provide storage for a
coroutine state of size size, and unspecified additional
state necessary to ensure that operator delete can later
deallocate this memory block with an allocator equal to palloc[.](#task.promise-18.sentence-2)
[19](#task.promise-19)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L7686)
*Returns*: A pointer to the allocated storage[.](#task.promise-19.sentence-1)
[🔗](#lib:operator_delete,task::promise_type)
`void operator delete(void* pointer, size_t size) noexcept;
`
[20](#task.promise-20)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L7696)
*Preconditions*: pointer was returned from an invocation of the above overload
of operator new with a size argument equal to size[.](#task.promise-20.sentence-1)
[21](#task.promise-21)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L7701)
*Effects*: Deallocates the storage pointed to by pointer using an
allocator equal to that used to allocate it[.](#task.promise-21.sentence-1)

View File

@@ -0,0 +1,138 @@
[exec.task.scheduler]
# 33 Execution control library [[exec]](./#exec)
## 33.13 Coroutine utilities [[exec.coro.util]](exec.coro.util#exec.task.scheduler)
### 33.13.5 execution::task_scheduler [exec.task.scheduler]
namespace std::execution {class [task_scheduler](#lib:task_scheduler "33.13.5execution::task_­scheduler[exec.task.scheduler]") {class *ts-sender*; // *exposition only*template<[receiver](exec.recv.concepts#concept:receiver "33.7.1Receiver concepts[exec.recv.concepts]") R>class *state*; // *exposition only*public:using scheduler_concept = scheduler_t; template<class Sch, class Allocator = allocator<void>>requires (![same_as](concept.same#concept:same_as "18.4.2Concept same_­as[concept.same]")<task_scheduler, remove_cvref_t<Sch>>)&& [scheduler](exec.sched#concept:scheduler "33.6Schedulers[exec.sched]")<Sch>explicit task_scheduler(Sch&& sch, Allocator alloc = {}); *ts-sender* schedule(); friend bool operator==(const task_scheduler& lhs, const task_scheduler& rhs)noexcept; template<class Sch>requires (![same_as](concept.same#concept:same_as "18.4.2Concept same_­as[concept.same]")<task_scheduler, Sch>)&& [scheduler](exec.sched#concept:scheduler "33.6Schedulers[exec.sched]")<Sch>friend bool operator==(const task_scheduler& lhs, const Sch& rhs) noexcept; private: shared_ptr<void> *sch_*; // *exposition only*};}
[1](#1)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L7028)
task_scheduler is a class that models[scheduler](exec.sched#concept:scheduler "33.6Schedulers[exec.sched]") ([[exec.sched]](exec.sched "33.6Schedulers"))[.](#1.sentence-1)
Given an object s of type task_scheduler, let*SCHED*(s) be the object owned by s.*sch_*[.](#1.sentence-2)
[🔗](#lib:task_scheduler,constructor)
`template<class Sch, class Allocator = allocator<void>>
requires(![same_as](concept.same#concept:same_as "18.4.2Concept same_­as[concept.same]")<task_scheduler, remove_cvref_t<Sch>>) && [scheduler](exec.sched#concept:scheduler "33.6Schedulers[exec.sched]")<Sch>
explicit task_scheduler(Sch&& sch, Allocator alloc = {});
`
[2](#2)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L7041)
*Effects*: Initialize *sch_* withallocate_shared<remove_cvref_t<Sch>>(alloc, std::forward<Sch>(sch))[.](#2.sentence-1)
[3](#3)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L7046)
*Recommended practice*: Implementations should avoid the use of dynamically
allocated memory for small scheduler objects[.](#3.sentence-1)
[4](#4)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L7051)
*Remarks*: Any allocations performed by construction of *ts-sender* or*state* objects resulting from calls on *this are
performed using a copy of alloc[.](#4.sentence-1)
[🔗](#lib:scheduler,task_scheduler)
`ts-sender schedule();
`
[5](#5)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L7063)
*Effects*: Returns an object of type *ts-sender* containing a sender
initialized with schedule(*SCHED*(*this))[.](#5.sentence-1)
[🔗](#lib:operator==,task_scheduler)
`bool operator==(const task_scheduler& lhs, const task_scheduler& rhs) noexcept;
`
[6](#6)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L7074)
*Effects*: Equivalent to: return lhs == *SCHED*(rhs);
[🔗](#lib:operator==,task_scheduler_)
`template<class Sch>
requires (![same_as](concept.same#concept:same_as "18.4.2Concept same_­as[concept.same]")<task_scheduler, Sch>)
&& [scheduler](exec.sched#concept:scheduler "33.6Schedulers[exec.sched]")<Sch>
bool operator==(const task_scheduler& lhs, const Sch& rhs) noexcept;
`
[7](#7)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L7087)
*Returns*: false if the type of *SCHED*(lhs) is not Sch,
otherwise *SCHED*(lhs) == rhs[.](#7.sentence-1)
[8](#8)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L7093)
namespace std::execution {class task_scheduler::*ts-sender* { // *exposition only*public:using sender_concept = sender_t; template<[receiver](exec.recv.concepts#concept:receiver "33.7.1Receiver concepts[exec.recv.concepts]") Rcvr>*state*<Rcvr> connect(Rcvr&& rcvr); };}*ts-sender* is an exposition-only class that models[sender](exec.snd.concepts#concept:sender "33.9.3Sender concepts[exec.snd.concepts]") ([[exec.snd]](exec.snd "33.9Senders")) and for whichcompletion_signatures_of_t<*ts-sender*> denotes:completion_signatures< set_value_t(),
set_error_t(error_code),
set_error_t(exception_ptr),
set_stopped_t()>
[9](#9)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L7116)
Let *sch* be an object of type task_scheduler and let sndr be an object of type *ts-sender* obtained
from schedule(*sch*)[.](#9.sentence-1)
Then get_completion_scheduler<set_value_t>(get_env(sndr)) == *sch* is true[.](#9.sentence-2)
The object *SENDER*(sndr) is the sender object contained bysndr or an object move constructed from it[.](#9.sentence-3)
[🔗](#lib:connect,task_scheduler::ts-sender)
`template<[receiver](exec.recv.concepts#concept:receiver "33.7.1Receiver concepts[exec.recv.concepts]") Rcvr>
state<Rcvr> connect(Rcvr&& rcvr);
`
[10](#10)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L7131)
*Effects*: Let *r* be an object of a type that models [receiver](exec.recv.concepts#concept:receiver "33.7.1Receiver concepts[exec.recv.concepts]") and whose completion handlers result in invoking the corresponding
completion handlers of rcvr or copy thereof[.](#10.sentence-1)
Returns an object of type *state*<Rcvr> containing
an operation state object initialized with connect(*SENDER*(*this),
std::move(*r*))[.](#10.sentence-2)
[11](#11)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L7141)
namespace std::execution {template<[receiver](exec.recv.concepts#concept:receiver "33.7.1Receiver concepts[exec.recv.concepts]") R>class task_scheduler::*state* { // *exposition only*public:using operation_state_concept = operation_state_t; void start() & noexcept; };}*state* is an exposition-only class template whose
specializations model [operation_state](exec.opstate.general#concept:operation_state "33.8.1General[exec.opstate.general]") ([[exec.opstate]](exec.opstate "33.8Operation states"))[.](#11.sentence-1)
[🔗](#lib:start,task_scheduler::state)
`void start() & noexcept;
`
[12](#12)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L7161)
*Effects*: Equivalent to start(st) where st is the operation
state object contained by *this[.](#12.sentence-1)

84
cppdraft/exec/then.md Normal file
View File

@@ -0,0 +1,84 @@
[exec.then]
# 33 Execution control library [[exec]](./#exec)
## 33.9 Senders [[exec.snd]](exec.snd#exec.then)
### 33.9.12 Sender adaptors [[exec.adapt]](exec.adapt#exec.then)
#### 33.9.12.9 execution::then, execution::upon_error, execution::upon_stopped [exec.then]
[1](#1)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3757)
then attaches an invocable as a continuation
for an input sender's value completion operation[.](#1.sentence-1)
upon_error and upon_stopped do the same
for the error and stopped completion operations, respectively,
sending the result of the invocable as a value completion[.](#1.sentence-2)
[2](#2)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3764)
The names then, upon_error, and upon_stopped denote pipeable sender adaptor objects[.](#2.sentence-1)
Let the expression *then-cpo* be one ofthen, upon_error, or upon_stopped[.](#2.sentence-2)
For subexpressions sndr and f,
if decltype((sndr)) does not satisfy [sender](exec.snd.concepts#concept:sender "33.9.3Sender concepts[exec.snd.concepts]"), ordecltype((f)) does not satisfy [*movable-value*](exec.general#concept:movable-value "33.1General[exec.general]"),*then-cpo*(sndr, f) is ill-formed[.](#2.sentence-3)
[3](#3)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3774)
Otherwise,
the expression *then-cpo*(sndr, f) is expression-equivalent to:transform_sender(*get-domain-early*(sndr), *make-sender*(*then-cpo*, f, sndr)) except that sndr is evaluated only once[.](#3.sentence-1)
[4](#4)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3782)
For then, upon_error, and upon_stopped,
let *set-cpo* beset_value, set_error, and set_stopped, respectively[.](#4.sentence-1)
The exposition-only class template *impls-for* ([[exec.snd.expos]](exec.snd.expos "33.9.2Exposition-only entities"))
is specialized for *then-cpo* as follows:
[🔗](#lib:impls-for%3cdecayed-typeof%3cthen-cpo%3e%3e)
namespace std::execution {template<>struct *impls-for*<*decayed-typeof*<*then-cpo*>> : *default-impls* {static constexpr auto *complete* =[]<class Tag, class... Args>(auto, auto& fn, auto& rcvr, Tag, Args&&... args) noexcept -> void {if constexpr ([same_as](concept.same#concept:same_as "18.4.2Concept same_­as[concept.same]")<Tag, *decayed-typeof*<set-cpo>>) {*TRY-SET-VALUE*(rcvr,
invoke(std::move(fn), std::forward<Args>(args)...)); } else { Tag()(std::move(rcvr), std::forward<Args>(args)...); }}; template<class Sndr, class... Env>static consteval void *check-types*(); };}
[🔗](#lib:check-types,impls-for%3cdecayed-typeof%3cthen-cpo%3e%3e)
`template<class Sndr, class... Env>
static consteval void check-types();
`
[5](#5)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3817)
*Effects*: Equivalent to:auto cs = get_completion_signatures<*child-type*<Sndr>, *FWD-ENV-T*(Env)...>();auto fn = []<class... Ts>(set_value_t(*)(Ts...)) {if constexpr (![invocable](concept.invocable#concept:invocable "18.7.2Concept invocable[concept.invocable]")<remove_cvref_t<*data-type*<Sndr>>, Ts...>)throw *unspecified-exception*();};
cs.*for-each*(*overload-set*{fn, [](auto){}}); where *unspecified-exception* is
a type derived from exception[.](#5.sentence-1)
[6](#6)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3832)
The expression *then-cpo*(sndr, f) has undefined behavior
unless it returns a sender out_sndr that
- [(6.1)](#6.1)
invokes f or a copy of such
with the value, error, or stopped result datums of sndr for then, upon_error, and upon_stopped, respectively,
using the result value of f as out_sndr's value completion, and
- [(6.2)](#6.2)
forwards all other completion operations unchanged[.](#6.sentence-1)

View File

@@ -0,0 +1,24 @@
[exec.unstoppable]
# 33 Execution control library [[exec]](./#exec)
## 33.9 Senders [[exec.snd]](exec.snd#exec.unstoppable)
### 33.9.12 Sender adaptors [[exec.adapt]](exec.adapt#exec.unstoppable)
#### 33.9.12.4 execution::unstoppable [exec.unstoppable]
[1](#1)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3210)
unstoppable is a sender adaptor
that connects its inner sender
with a receiver that has the execution environment of the outer receiver
but with an object of type never_stop_token as the result of the get_stop_token query[.](#1.sentence-1)
[2](#2)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3217)
For a subexpression sndr,unstoppable(sndr) is expression-equivalent towrite_env(sndr, prop(get_stop_token, never_stop_token{}))[.](#2.sentence-1)

241
cppdraft/exec/when/all.md Normal file
View File

@@ -0,0 +1,241 @@
[exec.when.all]
# 33 Execution control library [[exec]](./#exec)
## 33.9 Senders [[exec.snd]](exec.snd#exec.when.all)
### 33.9.12 Sender adaptors [[exec.adapt]](exec.adapt#exec.when.all)
#### 33.9.12.12 execution::when_all [exec.when.all]
[1](#1)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4368)
when_all and when_all_with_variant both adapt multiple input senders into a sender
that completes when all input senders have completed[.](#1.sentence-1)
when_all only accepts senders
with a single value completion signature and
on success concatenates all the input senders' value result datums
into its own value completion operation[.](#1.sentence-2)
when_all_with_variant(sndrs...) is semantically equivalent to
when_all(into_variant(sndrs)...),
where sndrs is a pack of subexpressions
whose types model [sender](exec.snd.concepts#concept:sender "33.9.3Sender concepts[exec.snd.concepts]")[.](#1.sentence-3)
[2](#2)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4381)
The names when_all and when_all_with_variant denote
customization point objects[.](#2.sentence-1)
Let sndrs be a pack of subexpressions,
let Sndrs be a pack of the types decltype((sndrs))..., and
let CD be
the type common_type_t<decltype(*get-domain-early*(sndrs))...>[.](#2.sentence-2)
Let CD2 be CD if CD is well-formed, anddefault_domain otherwise[.](#2.sentence-3)
The expressions when_all(sndrs...) andwhen_all_with_variant(sndrs...) are ill-formed
if any of the following is true:
- [(2.1)](#2.1)
sizeof...(sndrs) is 0, or
- [(2.2)](#2.2)
([sender](exec.snd.concepts#concept:sender "33.9.3Sender concepts[exec.snd.concepts]")<Sndrs> && ...) is false[.](#2.sentence-4)
[3](#3)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4400)
The expression when_all(sndrs...) is expression-equivalent to:transform_sender(CD2(), *make-sender*(when_all, {}, sndrs...))
[4](#4)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4406)
The exposition-only class template *impls-for* ([[exec.snd.expos]](exec.snd.expos "33.9.2Exposition-only entities"))
is specialized for when_all_t as follows:
[🔗](#lib:impls-for%3cwhen_all_t%3e)
namespace std::execution {template<>struct *impls-for*<when_all_t> : *default-impls* {static constexpr auto *get-attrs* = *see below*; static constexpr auto *get-env* = *see below*; static constexpr auto *get-state* = *see below*; static constexpr auto *start* = *see below*; static constexpr auto *complete* = *see below*; template<class Sndr, class... Env>static consteval void *check-types*(); };}
[5](#5)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4426)
Let *make-when-all-env* be
the following exposition-only function template:template<class Env>constexpr auto *make-when-all-env*(inplace_stop_source& stop_src, // *exposition only* Env&& env) noexcept {return *see below*;}
Returns an object e such that
- [(5.1)](#5.1)
decltype(e) models [*queryable*](exec.queryable.concept#concept:queryable "33.2.2queryable concept[exec.queryable.concept]"), and
- [(5.2)](#5.2)
e.query(get_stop_token) is expression-equivalent tostate.*stop-src*.get_token(), and
- [(5.3)](#5.3)
given a query object q with type other than cv stop_token_t and
whose type satisfies *forwarding-query*,e.query(q) is expression-equivalent to get_env(rcvr).query(q)[.](#5.sentence-2)
[6](#6)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4452)
Let *when-all-env* be an alias template such that*when-all-env*<Env> denotes the typedecltype(*make-
when-all-env*(declval<inplace_stop_source&>(), declval<Env>()))[.](#6.sentence-1)
[🔗](#lib:check-types,impls-for%3cwhen_all_t%3e)
`template<class Sndr, class... Env>
static consteval void check-types();
`
[7](#7)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4464)
Let Is be the pack of integral template arguments of
the integer_sequence specialization denoted by*indices-for*<Sndr>[.](#7.sentence-1)
[8](#8)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4469)
*Effects*: Equivalent to:auto fn = []<class Child>() {auto cs = get_completion_signatures<Child, *when-all-env*<Env>...>(); if constexpr (cs.*count-of*(set_value) >= 2)throw *unspecified-exception*(); *decay-copyable-result-datums*(cs); // see [[exec.snd.expos]](exec.snd.expos "33.9.2Exposition-only entities")};(fn.template operator()<*child-type*<Sndr, Is>>(), ...); where *unspecified-exception* is
a type derived from exception[.](#8.sentence-1)
[9](#9)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4484)
*Throws*: Any exception thrown as a result of evaluating the *Effects*, or
an exception of an unspecified type
derived from exception when CD is ill-formed[.](#9.sentence-1)
[10](#10)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4491)
The member *impls-for*<when_all_t>::*get-attrs* is initialized with a callable object
equivalent to the following lambda expression:[](auto&&, auto&&... child) noexcept {if constexpr ([same_as](concept.same#concept:same_as "18.4.2Concept same_­as[concept.same]")<CD, default_domain>) {return env<>(); } else {return *MAKE-ENV*(get_domain, CD()); }}
[11](#11)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4505)
The member *impls-for*<when_all_t>::*get-env* is initialized with a callable object
equivalent to the following lambda expression:[]<class State, class Rcvr>(auto&&, State& state, const Receiver& rcvr) noexcept {return *make-when-all-env*(state.*stop-src*, get_env(rcvr));}
[12](#12)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4515)
The member *impls-for*<when_all_t>::*get-state* is initialized with a callable object
equivalent to the following lambda expression:[]<class Sndr, class Rcvr>(Sndr&& sndr, Rcvr& rcvr) noexcept(noexcept(e)) -> decltype(e) {return e;} where e is the expressionstd::forward<Sndr>(sndr).*apply*(*make-state*<Rcvr>()) and where *make-state* is the following exposition-only class template:enum class *disposition* { *started*, *error*, *stopped* }; // *exposition only*template<class Rcvr>struct *make-state* {template<class... Sndrs>auto operator()(auto, auto, Sndrs&&... sndrs) const {using values_tuple = *see below*; using errors_variant = *see below*; using stop_callback = stop_callback_for_t<stop_token_of_t<env_of_t<Rcvr>>, *on-stop-request*>; struct *state-type* {void *arrive*(Rcvr& rcvr) noexcept { // *exposition only*if (0 == --count) {*complete*(rcvr); }}void *complete*(Rcvr& rcvr) noexcept; // *exposition only* atomic<size_t> *count*{sizeof...(sndrs)}; // *exposition only* inplace_stop_source *stop_src*{}; // *exposition only* atomic<*disposition*> disp{*disposition*::*started*}; // *exposition only* errors_variant *errors*{}; // *exposition only* values_tuple *values*{}; // *exposition only* optional<stop_callback> *on_stop*{nullopt}; // *exposition only*}; return *state-type*{}; }};
[13](#13)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4562)
Let *copy-fail* be exception_ptr if decay-copying any of the child senders' result datums can potentially throw;
otherwise, *none-such*,
where *none-such* is an unspecified empty class type[.](#13.sentence-1)
[14](#14)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4568)
The alias values_tuple denotes the typetuple<value_types_of_t<Sndrs, *FWD-ENV-T*(env_of_t<Rcvr>), *decayed-tuple*, optional>...> if that type is well-formed; otherwise, tuple<>[.](#14.sentence-1)
[15](#15)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4575)
The alias errors_variant denotes
the type variant<*none-such*, *copy-fail*, Es...> with duplicate types removed,
where Es is the pack of the decayed types
of all the child senders' possible error result datums[.](#15.sentence-1)
[16](#16)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4582)
The membervoid *state-type*::*complete*(Rcvr& rcvr) noexcept behaves as follows:
- [(16.1)](#16.1)
If disp is equal to *disposition*::*started*,
evaluates:auto tie = []<class... T>(tuple<T...>& t) noexcept { return tuple<T&...>(t); };auto set = [&](auto&... t) noexcept { set_value(std::move(rcvr), std::move(t)...); };
*on_stop*.reset();
apply([&](auto&... opts) noexcept { apply(set, tuple_cat(tie(*opts)...)); },
values);
- [(16.2)](#16.2)
Otherwise,
if disp is equal to *disposition*::*error*,
evaluates:*on_stop*.reset();
visit([&]<class Error>(Error& error) noexcept {if constexpr (![same_as](concept.same#concept:same_as "18.4.2Concept same_­as[concept.same]")<Error, *none-such*>) { set_error(std::move(rcvr), std::move(error)); }},
errors);
- [(16.3)](#16.3)
Otherwise, evaluates:*on_stop*.reset();
set_stopped(std::move(rcvr));
[17](#17)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4623)
The member *impls-for*<when_all_t>::*start* is initialized with a callable object
equivalent to the following lambda expression:[]<class State, class Rcvr, class... Ops>( State& state, Rcvr& rcvr, Ops&... ops) noexcept -> void { state.*on_stop*.emplace( get_stop_token(get_env(rcvr)), *on-stop-request*{state.*stop_src*}); if (state.*stop_src*.stop_requested()) { state.*on_stop.*reset();
set_stopped(std::move(rcvr)); } else {(start(ops), ...); }}
[18](#18)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4642)
The member *impls-for<when_all_t>::*complete** is initialized with a callable object
equivalent to the following lambda expression:[]<class Index, class State, class Rcvr, class Set, class... Args>(this auto& complete, Index, State& state, Rcvr& rcvr, Set, Args&&... args) noexcept -> void {if constexpr ([same_as](concept.same#concept:same_as "18.4.2Concept same_­as[concept.same]")<Set, set_error_t>) {if (*disposition*::*error* != state.disp.exchange(*disposition*::*error*)) { state.*stop_src*.request_stop(); *TRY-EMPLACE-ERROR*(state.errors, std::forward<Args>(args)...); }} else if constexpr ([same_as](concept.same#concept:same_as "18.4.2Concept same_­as[concept.same]")<Set, set_stopped_t>) {auto expected = *disposition*::*started*; if (state.disp.compare_exchange_strong(expected, *disposition*::*stopped*)) { state.*stop_src*.request_stop(); }} else if constexpr (![same_as](concept.same#concept:same_as "18.4.2Concept same_­as[concept.same]")<decltype(State::values), tuple<>>) {if (state.disp == *disposition*::*started*) {auto& opt = get<Index::value>(state.values); *TRY-EMPLACE-VALUE*(complete, opt, std::forward<Args>(args)...); }} state.*arrive*(rcvr);} where *TRY-EMPLACE-ERROR*(v, e),
for subexpressions v and e, is equivalent to:try { v.template emplace<decltype(auto(e))>(e);} catch (...) { v.template emplace<exception_ptr>(current_exception());} if the expression decltype(auto(e))(e) is potentially throwing;
otherwise, v.template emplace<decltype(auto(e))>(e);
and where *TRY-EMPLACE-VALUE*(c, o, as...),
for subexpressions c, o, and pack of subexpressions as,
is equivalent to:try { o.emplace(as...);} catch (...) { c(Index(), state, rcvr, set_error, current_exception()); return;} if the expression *decayed-tuple*<decltype(as)...>{as...} is potentially throwing;
otherwise, o.emplace(as...)[.](#18.sentence-1)
[19](#19)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4694)
The expression when_all_with_variant(sndrs...) is expression-equivalent to:transform_sender(CD2(), *make-sender*(when_all_with_variant, {}, sndrs...));
[20](#20)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4701)
Given subexpressions sndr and env,
if[*sender-for*](exec.snd.concepts#concept:sender-for "33.9.3Sender concepts[exec.snd.concepts]")<decltype((sndr)), when_all_with_variant_t> is false,
then the expression when_all_with_variant.transform_sender(sndr, env) is ill-formed;
otherwise, it is equivalent to:auto&& [_, _, ...child] = sndr;return when_all(into_variant(std::forward_like<decltype((sndr))>(child))...);
[*Note [1](#note-1)*:
This causes the when_all_with_variant(sndrs...) sender
to become when_all(into_variant(sndrs)...) when it is connected with a receiver
whose execution domain does not customize when_all_with_variant[.](#20.sentence-1)
— *end note*]

View File

@@ -0,0 +1,55 @@
[exec.with.awaitable.senders]
# 33 Execution control library [[exec]](./#exec)
## 33.13 Coroutine utilities [[exec.coro.util]](exec.coro.util#exec.with.awaitable.senders)
### 33.13.2 execution::with_awaitable_senders [exec.with.awaitable.senders]
[1](#1)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6793)
with_awaitable_senders,
when used as the base class of a coroutine promise type,
makes senders awaitable in that coroutine type[.](#1.sentence-1)
In addition, it provides a default implementation of unhandled_stopped such that if a sender completes by calling set_stopped,
it is treated as if an uncatchable "stopped" exception were thrown
from the [*await-expression*](expr.await#nt:await-expression "7.6.2.4Await[expr.await]")[.](#1.sentence-2)
[*Note [1](#note-1)*:
The coroutine is never resumed, and
the unhandled_stopped of the coroutine caller's promise type is called[.](#1.sentence-3)
— *end note*]
namespace std::execution {template<[*class-type*](execution.syn#concept:class-type "33.4Header <execution> synopsis[execution.syn]") Promise>struct [with_awaitable_senders](#lib:with_awaitable_senders "33.13.2execution::with_­awaitable_­senders[exec.with.awaitable.senders]") {template<class OtherPromise>requires (![same_as](concept.same#concept:same_as "18.4.2Concept same_­as[concept.same]")<OtherPromise, void>)void set_continuation(coroutine_handle<OtherPromise> h) noexcept;
coroutine_handle<> [continuation](#lib:with_awaitable_senders,continuation "33.13.2execution::with_­awaitable_­senders[exec.with.awaitable.senders]")() const noexcept { return *continuation*; } coroutine_handle<> [unhandled_stopped](#lib:with_awaitable_senders,unhandled_stopped "33.13.2execution::with_­awaitable_­senders[exec.with.awaitable.senders]")() noexcept {return *stopped-handler*(*continuation*.address()); }template<class Value>*see below* await_transform(Value&& value); private:[[noreturn]] static coroutine_handle<>*default-unhandled-stopped*(void*) noexcept { // *exposition only* terminate(); } coroutine_handle<> *continuation*{}; // *exposition only* coroutine_handle<> (**stopped-handler*)(void*) noexcept = // *exposition only*&*default-unhandled-stopped*; };}
[🔗](#lib:set_continuation,with_awaitable_senders)
`template<class OtherPromise>
requires (![same_as](concept.same#concept:same_as "18.4.2Concept same_­as[concept.same]")<OtherPromise, void>)
void set_continuation(coroutine_handle<OtherPromise> h) noexcept;
`
[2](#2)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6844)
*Effects*: Equivalent to:*continuation* = h;if constexpr ( requires(OtherPromise& other) { other.unhandled_stopped(); } ) {*stopped-handler* = [](void* p) noexcept -> coroutine_handle<> {return coroutine_handle<OtherPromise>::from_address(p).promise().unhandled_stopped(); };} else {*stopped-handler* = &*default-unhandled-stopped*;}
[🔗](#lib:await_transform,with_awaitable_senders)
`template<class Value>
call-result-t<as_awaitable_t, Value, Promise&> await_transform(Value&& value);
`
[3](#3)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6867)
*Effects*: Equivalent to:return as_awaitable(std::forward<Value>(value), static_cast<Promise&>(*this));

View File

@@ -0,0 +1,77 @@
[exec.write.env]
# 33 Execution control library [[exec]](./#exec)
## 33.9 Senders [[exec.snd]](exec.snd#exec.write.env)
### 33.9.12 Sender adaptors [[exec.adapt]](exec.adapt#exec.write.env)
#### 33.9.12.3 execution::write_env [exec.write.env]
[1](#1)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3143)
write_env is a sender adaptor
that accepts a sender and a queryable object, and
that returns a sender that,
when connected with a receiver rcvr,
connects the adapted sender with a receiver
whose execution environment is the result of
joining the [*queryable*](exec.queryable.concept#concept:queryable "33.2.2queryable concept[exec.queryable.concept]") object
to the result of get_env(rcvr)[.](#1.sentence-1)
[2](#2)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3153)
write_env is a customization point object[.](#2.sentence-1)
For some subexpressions sndr and env,
if decltype((sndr)) does not satisfy [sender](exec.snd.concepts#concept:sender "33.9.3Sender concepts[exec.snd.concepts]") or
if decltype((env)) does not satisfy [*queryable*](exec.queryable.concept#concept:queryable "33.2.2queryable concept[exec.queryable.concept]"),
the expression write_env(sndr, env) is ill-formed[.](#2.sentence-2)
Otherwise, it is expression-equivalent to*make-sender*(write_env, env, sndr)[.](#2.sentence-3)
[3](#3)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3162)
Let *write-env-t* denote the type decltype(auto(write_env))[.](#3.sentence-1)
The exposition-only class template *impls-for* ([[exec.snd.expos]](exec.snd.expos "33.9.2Exposition-only entities"))
is specialized for *write-env-t* as follows:
[🔗](#lib:impls-for%3cwrite-env-t%3e)
template<>struct *impls-for*<*write-env-t*> : *default-impls* {static constexpr auto *join-env*(const auto& state, const auto& env) noexcept {return *see below*; }static constexpr auto *get-env* =[](auto, const auto& state, const auto& rcvr) noexcept {return *join-env*(state, *FWD-ENV*(get_env(rcvr))); }; template<class Sndr, class... Env>static consteval void *check-types*();};
[4](#4)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3184)
Invocation of*impls-for*<*write-env-t*>::*join-env* returns an object e such that
- [(4.1)](#4.1)
decltype(e) models [*queryable*](exec.queryable.concept#concept:queryable "33.2.2queryable concept[exec.queryable.concept]") and
- [(4.2)](#4.2)
given a query object q,
the expression e.query(q) is expression-equivalent
to state.query(q) if that expression is valid,
otherwise, e.query(q) is expression-equivalent
to env.query(q)[.](#4.sentence-1)
[5](#5)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3199)
For a type Sndr and a pack of types Env,
let State be *data-type*<Sndr> and
let JoinEnv be the packdecltype(*join-env*(declval<State>(), *FWD-ENV*(declval<Env>())))[.](#5.sentence-1)
Then *impls-for*<*write-env-t*>::*check-types*<Sndr, Env...>() is expression-equivalent toget_completion_signatures<*child-
type*<Sndr>, JoinEnv...>()[.](#5.sentence-2)