[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[.](#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]") && ...) 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* : *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*; templatestatic 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:templateconstexpr 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* denotes the typedecltype(*make- when-all-env*(declval(), declval()))[.](#6.sentence-1) [🔗](#lib:check-types,impls-for%3cwhen_all_t%3e) `template 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*[.](#7.sentence-1) [8](#8) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4469) *Effects*: Equivalent to:auto fn = []() {auto cs = get_completion_signatures...>(); 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*>(), ...); 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*​::​*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]")) {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*​::​*get-env* is initialized with a callable object equivalent to the following lambda expression:[](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*​::​*get-state* is initialized with a callable object equivalent to the following lambda expression:[](Sndr&& sndr, Rcvr& rcvr) noexcept(noexcept(e)) -> decltype(e) {return e;} where e is the expressionstd::forward(sndr).*apply*(*make-state*()) and where *make-state* is the following exposition-only class template:enum class *disposition* { *started*, *error*, *stopped* }; // *exposition only*templatestruct *make-state* {templateauto operator()(auto, auto, Sndrs&&... sndrs) const {using values_tuple = *see below*; using errors_variant = *see below*; using stop_callback = stop_callback_for_t>, *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 *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 *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), *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 = [](tuple& t) noexcept { return tuple(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([&](Error& error) noexcept {if constexpr (![same_as](concept.same#concept:same_as "18.4.2 Concept same_­as [concept.same]")) { 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*​::​*start* is initialized with a callable object equivalent to the following lambda expression:[]( 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​::​*complete** is initialized with a callable object equivalent to the following lambda expression:[](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]")) {if (*disposition*::*error* != state.disp.exchange(*disposition*::*error*)) { state.*stop_src*.request_stop(); *TRY-EMPLACE-ERROR*(state.errors, std::forward(args)...); }} else if constexpr ([same_as](concept.same#concept:same_as "18.4.2 Concept same_­as [concept.same]")) {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.2 Concept same_­as [concept.same]")>) {if (state.disp == *disposition*::*started*) {auto& opt = get(state.values); *TRY-EMPLACE-VALUE*(complete, opt, std::forward(args)...); }} state.*arrive*(rcvr);} where *TRY-EMPLACE-ERROR*(v, e), for subexpressions v and e, is equivalent to:try { v.template emplace(e);} catch (...) { v.template emplace(current_exception());} if the expression decltype(auto(e))(e) is potentially throwing; otherwise, v.template emplace(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*{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]") 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(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*]