242 lines
13 KiB
Markdown
242 lines
13 KiB
Markdown
[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.3 Sender 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.3 Sender 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.2 Exposition-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.2 queryable 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.2 Exposition-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.2 Concept 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 (<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.2 Concept 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.2 Concept 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 (<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.3 Sender 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*]
|