[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 (![same_as](concept.same#concept:same_as "18.4.2 Concept 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.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 (![same_as](concept.same#concept:same_as "18.4.2 Concept 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.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*]
