[exec.adapt] # 33 Execution control library [[exec]](./#exec) ## 33.9 Senders [[exec.snd]](exec.snd#exec.adapt) ### 33.9.12 Sender adaptors [exec.adapt] #### [33.9.12.1](#general) General [[exec.adapt.general]](exec.adapt.general) [1](#general-1) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3002) Subclause [exec.adapt] specifies a set of sender adaptors[.](#general-1.sentence-1) [2](#general-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[.](#general-2.sentence-1) The adaptors also support function call syntax with equivalent semantics[.](#general-2.sentence-2) [3](#general-3) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3010) Unless otherwise specified: - [(3.1)](#general-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[.](#general-3.1.sentence-1) - [(3.2)](#general-3.2) A parent sender ([[exec.async.ops]](exec.async.ops "33.3 Asynchronous 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.1 forwarding_­query"))[.](#general-3.2.sentence-1) - [(3.3)](#general-3.3) A parent sender with more than one child sender has an associated attributes object equal to env<>{}[.](#general-3.3.sentence-1) - [(3.4)](#general-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))[.](#general-3.4.sentence-1) - [(3.5)](#general-3.5) An adaptor whose child senders are all non-dependent ([[exec.async.ops]](exec.async.ops "33.3 Asynchronous operations")) is itself non-dependent[.](#general-3.5.sentence-1) - [(3.6)](#general-3.6) These requirements apply to any function that is selected by the implementation of the sender adaptor[.](#general-3.6.sentence-1) - [(3.7)](#general-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[.](#general-3.7.sentence-1) [4](#general-4) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3043) If a sender returned from a sender adaptor specified in [exec.adapt] is specified to include set_error_t(Err) among its set of completion signatures where decay_t 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[.](#general-4.sentence-1) #### [33.9.12.2](#obj) Closure objects [[exec.adapt.obj]](exec.adapt.obj) [1](#obj-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.2 Closure objects [exec.adapt.obj]") is a function object that accepts one or more [sender](exec.snd.concepts#concept:sender "33.9.3 Sender concepts [exec.snd.concepts]") arguments and returns a [sender](exec.snd.concepts#concept:sender "33.9.3 Sender concepts [exec.snd.concepts]")[.](#obj-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.3 Sender concepts [exec.snd.concepts]"), the following expressions are equivalent and yield a [sender](exec.snd.concepts#concept:sender "33.9.3 Sender 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.4 Requirements")) with the following properties: - [(1.1)](#obj-1.1) Its target object is an object d2 of type decltype(auto(d)) direct-non-list-initialized with d[.](#obj-1.1.sentence-1) - [(1.2)](#obj-1.2) It has one bound argument entity, an object c2 of type decltype(auto(c)) direct-non-list-initialized with c[.](#obj-1.2.sentence-1) - [(1.3)](#obj-1.3) Its call pattern is d2(c2(arg)), where arg is the argument used in a function call expression of e[.](#obj-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.3 Definitions")) of e are all well-formed[.](#obj-1.sentence-5) [2](#obj-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.3 Concept derived_­from [concept.derived]")>,T has no other base classes of type sender_adaptor_closure for any other type U, andT does not satisfy [sender](exec.snd.concepts#concept:sender "33.9.3 Sender concepts [exec.snd.concepts]")[.](#obj-2.sentence-1) [3](#obj-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[.](#obj-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.3 Concept derived_­from [concept.derived]")>[.](#obj-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[.](#obj-3.sentence-3) [4](#obj-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.2 Closure objects [exec.adapt.obj]") is a customization point object that accepts a [sender](exec.snd.concepts#concept:sender "33.9.3 Sender concepts [exec.snd.concepts]") as its first argument and returns a [sender](exec.snd.concepts#concept:sender "33.9.3 Sender concepts [exec.snd.concepts]")[.](#obj-4.sentence-1) If a pipeable sender adaptor object accepts only one argument, then it is a pipeable sender adaptor closure object[.](#obj-4.sentence-2) [5](#obj-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.3 Sender 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))...[.](#obj-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)](#obj-5.1) Its target object is a copy of adaptor[.](#obj-5.1.sentence-1) - [(5.2)](#obj-5.2) Its bound argument entities bound_args consist of objects of types BoundArgs... direct-non-list-initialized withstd​::​forward(args)..., respectively[.](#obj-5.2.sentence-1) - [(5.3)](#obj-5.3) Its call pattern is adaptor(rcvr, bound_args...), where rcvr is the argument used in a function call expression of f[.](#obj-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[.](#obj-5.sentence-3) #### [33.9.12.3](#exec.write.env) execution​::​write_env [[exec.write.env]](exec.write.env) [1](#exec.write.env-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.2 queryable concept [exec.queryable.concept]") object to the result of get_env(rcvr)[.](#exec.write.env-1.sentence-1) [2](#exec.write.env-2) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3153) write_env is a customization point object[.](#exec.write.env-2.sentence-1) For some subexpressions sndr and env, if decltype((sndr)) does not satisfy [sender](exec.snd.concepts#concept:sender "33.9.3 Sender concepts [exec.snd.concepts]") or if decltype((env)) does not satisfy [*queryable*](exec.queryable.concept#concept:queryable "33.2.2 queryable concept [exec.queryable.concept]"), the expression write_env(sndr, env) is ill-formed[.](#exec.write.env-2.sentence-2) Otherwise, it is expression-equivalent to*make-sender*(write_env, env, sndr)[.](#exec.write.env-2.sentence-3) [3](#exec.write.env-3) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3162) Let *write-env-t* denote the type decltype(auto(write_env))[.](#exec.write.env-3.sentence-1) The exposition-only class template *impls-for* ([[exec.snd.expos]](exec.snd.expos "33.9.2 Exposition-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))); }; templatestatic consteval void *check-types*();}; [4](#exec.write.env-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)](#exec.write.env-4.1) decltype(e) models [*queryable*](exec.queryable.concept#concept:queryable "33.2.2 queryable concept [exec.queryable.concept]") and - [(4.2)](#exec.write.env-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)[.](#exec.write.env-4.sentence-1) [5](#exec.write.env-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* and let JoinEnv be the packdecltype(*join-env*(declval(), *FWD-ENV*(declval())))[.](#exec.write.env-5.sentence-1) Then *impls-for*<*write-env-​t*>​::​*check-types*() is expression-equivalent toget_completion_signatures<*child- type*, JoinEnv...>()[.](#exec.write.env-5.sentence-2) #### [33.9.12.4](#exec.unstoppable) execution​::​unstoppable [[exec.unstoppable]](exec.unstoppable) [1](#exec.unstoppable-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[.](#exec.unstoppable-1.sentence-1) [2](#exec.unstoppable-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{}))[.](#exec.unstoppable-2.sentence-1) #### [33.9.12.5](#exec.starts.on) execution​::​starts_on [[exec.starts.on]](exec.starts.on) [1](#exec.starts.on-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[.](#exec.starts.on-1.sentence-1) [2](#exec.starts.on-2) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3229) The name starts_on denotes a customization point object[.](#exec.starts.on-2.sentence-1) For subexpressions sch and sndr, if decltype(( sch)) does not satisfy [scheduler](exec.sched#concept:scheduler "33.6 Schedulers [exec.sched]"), ordecltype((sndr)) does not satisfy [sender](exec.snd.concepts#concept:sender "33.9.3 Sender concepts [exec.snd.concepts]"),starts_on(sch, sndr) is ill-formed[.](#exec.starts.on-2.sentence-2) [3](#exec.starts.on-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[.](#exec.starts.on-3.sentence-1) [4](#exec.starts.on-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))[.](#exec.starts.on-4.sentence-1) If [*sender-for*](exec.snd.concepts#concept:sender-for "33.9.3 Sender concepts [exec.snd.concepts]") 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)](#exec.starts.on-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)](#exec.starts.on-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(sndr)]() mutablenoexcept(is_nothrow_move_constructible_v>) {return std::move(sndr); }); [5](#exec.starts.on-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))[.](#exec.starts.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.3 Sender concepts [exec.snd.concepts]") is true[.](#exec.starts.on-5.sentence-2) Let op be an lvalue referring to the operation state that results from connecting out_sndr with out_rcvr[.](#exec.starts.on-5.sentence-3) Calling start(op) shall start sndr on an execution agent of the associated execution resource of sch[.](#exec.starts.on-5.sentence-4) If scheduling onto sch fails, an error completion on out_rcvr shall be executed on an unspecified execution agent[.](#exec.starts.on-5.sentence-5) #### [33.9.12.6](#exec.continues.on) execution​::​continues_on [[exec.continues.on]](exec.continues.on) [1](#exec.continues.on-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[.](#exec.continues.on-1.sentence-1) [2](#exec.continues.on-2) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3293) The name continues_on denotes a pipeable sender adaptor object[.](#exec.continues.on-2.sentence-1) For subexpressions sch and sndr, if decltype((sch)) does not satisfy [scheduler](exec.sched#concept:scheduler "33.6 Schedulers [exec.sched]"), ordecltype((sndr)) does not satisfy [sender](exec.snd.concepts#concept:sender "33.9.3 Sender concepts [exec.snd.concepts]"),continues_on(sndr, sch) is ill-formed[.](#exec.continues.on-2.sentence-2) [3](#exec.continues.on-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[.](#exec.continues.on-3.sentence-1) [4](#exec.continues.on-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.2 Exposition-only entities")) is specialized for continues_on_t as follows:namespace std::execution {template<>struct *impls-for* : *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.continues.on-5) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3323) Let sndr and env be subexpressions such that Sndr is decltype((sndr))[.](#exec.continues.on-5.sentence-1) If [*sender-for*](exec.snd.concepts#concept:sender-for "33.9.3 Sender concepts [exec.snd.concepts]") 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](#exec.continues.on-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[.](#exec.continues.on-5.sentence-2) — *end note*] [6](#exec.continues.on-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))[.](#exec.continues.on-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.3 Sender concepts [exec.snd.concepts]") is true[.](#exec.continues.on-6.sentence-2) Let op be an lvalue referring to the operation state that results from connecting out_sndr with out_rcvr[.](#exec.continues.on-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[.](#exec.continues.on-6.sentence-4) If scheduling onto sch fails, an error completion on out_rcvr shall be executed on an unspecified execution agent[.](#exec.continues.on-6.sentence-5) #### [33.9.12.7](#exec.schedule.from) execution​::​schedule_from [[exec.schedule.from]](exec.schedule.from) [1](#exec.schedule.from-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[.](#exec.schedule.from-1.sentence-1) [*Note [1](#exec.schedule.from-note-1)*: schedule_from is not meant to be used in user code; it is used in the implementation of continues_on[.](#exec.schedule.from-1.sentence-2) — *end note*] [2](#exec.schedule.from-2) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3367) The name schedule_from denotes a customization point object[.](#exec.schedule.from-2.sentence-1) For some subexpressions sch and sndr, let Sch be decltype((sch)) andSndr be decltype((sndr))[.](#exec.schedule.from-2.sentence-2) If Sch does not satisfy [scheduler](exec.sched#concept:scheduler "33.6 Schedulers [exec.sched]"), orSndr does not satisfy [sender](exec.snd.concepts#concept:sender "33.9.3 Sender concepts [exec.snd.concepts]"),schedule_from(sch, sndr) is ill-formed[.](#exec.schedule.from-2.sentence-3) [3](#exec.schedule.from-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[.](#exec.schedule.from-3.sentence-1) [4](#exec.schedule.from-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.2 Exposition-only entities")) is specialized for schedule_from_t as follows: [🔗](#lib:impls-for%3cschedule_from_t%3e) namespace std::execution {template<>struct *impls-for* : *default-impls* {static constexpr auto *get-attrs* = *see below*; static constexpr auto *get-state* = *see below*; static constexpr auto *complete* = *see below*; templatestatic consteval void *check-types*(); };} [5](#exec.schedule.from-5) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3404) The member *impls-for*​::​*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](#exec.schedule.from-6) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3413) The member *impls-for*​::​*get-state* is initialized with a callable object equivalent to the following lambda:[](Sndr&& sndr, Rcvr& rcvr) noexcept(*see below*)requires [sender_in](exec.snd.concepts#concept:sender_in "33.9.3 Sender concepts [exec.snd.concepts]")<*child-type*, *FWD-ENV-T*(env_of_t)> {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, 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 static consteval void check-types(); ` [7](#exec.schedule.from-7) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3448) *Effects*: Equivalent to:get_completion_signatures>, *FWD-ENV-T*(Env)...>();auto cs = get_completion_signatures<*child-type*, *FWD-ENV-T*(Env)...>();*decay-copyable-result-datums*(cs); // see [[exec.snd.expos]](exec.snd.expos "33.9.2 Exposition-only entities") [8](#exec.schedule.from-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[.](#exec.schedule.from-8.sentence-1) [9](#exec.schedule.from-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*, *FWD-ENV-T*(env_of_t)>[.](#exec.schedule.from-9.sentence-1) Let *as-tuple* be an alias template such that*as-tuple* denotes the type *decayed-tuple*, and let *is-nothrow-decay-copy-sig* be a variable template such thatauto(*is-nothrow-decay-copy-sig*) is a constant expression of type bool and equal to (is_nothrow_constructible_v, Args> && ...)[.](#exec.schedule.from-9.sentence-2) Let *error-completion* be a pack consisting of the type set_error_t(exception_ptr) if (*is-nothrow-decay-copy-sig* &&...) is false, and an empty pack otherwise[.](#exec.schedule.from-9.sentence-3) Then variant_t denotes the type variant..., *error-completion*...>, except with duplicate types removed[.](#exec.schedule.from-9.sentence-4) [10](#exec.schedule.from-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](Tuple& result) noexcept -> void {if constexpr (![same_as](concept.same#concept:same_as "18.4.2 Concept same_­as [concept.same]")) {auto& [tag, ...args] = result; tag(std::move(*state*->*rcvr*), std::move(args)...); }}, *state*->*async-result*); }templatevoid set_error(Error&& err) && noexcept { execution::set_error(std::move(*state*->*rcvr*), std::forward(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](#exec.schedule.from-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[.](#exec.schedule.from-11.sentence-1) [12](#exec.schedule.from-12) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3522) The member *impls-for*​::​*complete* is initialized with a callable object equivalent to the following lambda:[](auto, auto& state, auto& rcvr, Tag, Args&&... args) noexcept-> void {using result_t = *decayed-tuple*; constexpr bool nothrow = (is_nothrow_constructible_v, Args> && ...); try { state.*async-result*.template emplace(Tag(), std::forward(args)...); } catch (...) {if constexpr (!nothrow) state.*async-result*.template emplace>(set_error, current_exception()); } start(state.*op-state*);}; [13](#exec.schedule.from-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))[.](#exec.schedule.from-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.3 Sender concepts [exec.snd.concepts]") is true[.](#exec.schedule.from-13.sentence-2) Let op be an lvalue referring to the operation state that results from connecting out_sndr with out_rcvr[.](#exec.schedule.from-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[.](#exec.schedule.from-13.sentence-4) If scheduling onto sch fails, an error completion on out_rcvr shall be executed on an unspecified execution agent[.](#exec.schedule.from-13.sentence-5) #### [33.9.12.8](#exec.on) execution​::​on [[exec.on]](exec.on) [1](#exec.on-1) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3561) The on sender adaptor has two forms: - [(1.1)](#exec.on-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[.](#exec.on-1.1.sentence-1) - [(1.2)](#exec.on-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[.](#exec.on-1.2.sentence-1) [2](#exec.on-2) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3582) The name on denotes a pipeable sender adaptor object[.](#exec.on-2.sentence-1) For subexpressions sch and sndr,on(sch, sndr) is ill-formed if any of the following is true: - [(2.1)](#exec.on-2.1) decltype((sch)) does not satisfy [scheduler](exec.sched#concept:scheduler "33.6 Schedulers [exec.sched]"), or - [(2.2)](#exec.on-2.2) decltype((sndr)) does not satisfy [sender](exec.snd.concepts#concept:sender "33.9.3 Sender concepts [exec.snd.concepts]") andsndr is not a pipeable sender adaptor closure object ([[exec.adapt.obj]](#obj "33.9.12.2 Closure objects")), or - [(2.3)](#exec.on-2.3) decltype((sndr)) satisfies [sender](exec.snd.concepts#concept:sender "33.9.3 Sender concepts [exec.snd.concepts]") andsndr is also a pipeable sender adaptor closure object[.](#exec.on-2.sentence-2) [3](#exec.on-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.3 Sender 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[.](#exec.on-3.sentence-1) [4](#exec.on-4) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3608) For subexpressions sndr, sch, and closure, if - [(4.1)](#exec.on-4.1) decltype((sch)) does not satisfy [scheduler](exec.sched#concept:scheduler "33.6 Schedulers [exec.sched]"), or - [(4.2)](#exec.on-4.2) decltype((sndr)) does not satisfy [sender](exec.snd.concepts#concept:sender "33.9.3 Sender concepts [exec.snd.concepts]"), or - [(4.3)](#exec.on-4.3) closure is not a pipeable sender adaptor closure object ([[exec.adapt.obj]](#obj "33.9.12.2 Closure 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[.](#exec.on-4.sentence-1) [5](#exec.on-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))[.](#exec.on-5.sentence-1) If [*sender-for*](exec.snd.concepts#concept:sender-for "33.9.3 Sender concepts [exec.snd.concepts]") is false, then the expressions on.transform_env(out_sndr, env) andon.transform_sender(out_sndr, env) are ill-formed[.](#exec.on-5.sentence-2) [6](#exec.on-6) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3635) Otherwise: Let *not-a-scheduler* be an unspecified empty class type[.](#exec.on-6.sentence-1) [7](#exec.on-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.6 Schedulers [exec.sched]")) {return *JOIN-ENV*(*SCHED-ENV*(std::forward_like(data)), *FWD-ENV*(std::forward(env)));} else {return std::forward(env);} [8](#exec.on-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.6 Schedulers [exec.sched]")) {auto orig_sch =*query-with-default*(get_scheduler, env, *not-a-scheduler*()); if constexpr ([same_as](concept.same#concept:same_as "18.4.2 Concept same_­as [concept.same]")) {return *not-a-sender*{}; } else {return continues_on( starts_on(std::forward_like(data), std::forward_like(child)), std::move(orig_sch)); }} else {auto& [sch, closure] = data; auto orig_sch = *query-with-default*( get_completion_scheduler, get_env(child), *query-with-default*(get_scheduler, env, *not-a-scheduler*())); if constexpr ([same_as](concept.same#concept:same_as "18.4.2 Concept same_­as [concept.same]")) {return *not-a-sender*{}; } else {return write_env( continues_on( std::forward_like(closure)( continues_on( write_env(std::forward_like(child), *SCHED-ENV*(orig_sch)), sch)), orig_sch), *SCHED-ENV*(sch)); }} [9](#exec.on-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))[.](#exec.on-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.3 Sender concepts [exec.snd.concepts]") is true[.](#exec.on-9.sentence-2) Let op be an lvalue referring to the operation state that results from connecting out_sndr with out_rcvr[.](#exec.on-9.sentence-3) Calling start(op) shall - [(9.1)](#exec.on-9.1) remember the current scheduler, get_scheduler(get_env(rcvr)); - [(9.2)](#exec.on-9.2) start sndr on an execution agent belonging tosch's associated execution resource; - [(9.3)](#exec.on-9.3) upon sndr's completion, transfer execution back to the execution resource associated with the scheduler remembered in step 1; and - [(9.4)](#exec.on-9.4) forward sndr's async result to out_rcvr[.](#exec.on-9.sentence-4) If any scheduling operation fails, an error completion on out_rcvr shall be executed on an unspecified execution agent[.](#exec.on-9.sentence-5) [10](#exec.on-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))[.](#exec.on-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.3 Sender concepts [exec.snd.concepts]") is true[.](#exec.on-10.sentence-2) Let op be an lvalue referring to the operation state that results from connecting out_sndr with out_rcvr[.](#exec.on-10.sentence-3) Calling start(op) shall - [(10.1)](#exec.on-10.1) remember the current scheduler, which is the first of the following expressions that is well-formed: * [(10.1.1)](#exec.on-10.1.1) get_completion_scheduler(get_env(sndr)) * [(10.1.2)](#exec.on-10.1.2) get_scheduler(get_env(rcvr)); - [(10.2)](#exec.on-10.2) start sndr on the current execution agent; - [(10.3)](#exec.on-10.3) upon sndr's completion, transfer execution to an agent owned by sch's associated execution resource; - [(10.4)](#exec.on-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)](#exec.on-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[.](#exec.on-10.sentence-4) If any scheduling operation fails, an error completion on out_rcvr shall be executed on an unspecified execution agent[.](#exec.on-10.sentence-5) #### [33.9.12.9](#exec.then) execution​::​then, execution​::​upon_error, execution​::​upon_stopped [[exec.then]](exec.then) [1](#exec.then-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[.](#exec.then-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[.](#exec.then-1.sentence-2) [2](#exec.then-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[.](#exec.then-2.sentence-1) Let the expression *then-cpo* be one ofthen, upon_error, or upon_stopped[.](#exec.then-2.sentence-2) For subexpressions sndr and f, if decltype((sndr)) does not satisfy [sender](exec.snd.concepts#concept:sender "33.9.3 Sender concepts [exec.snd.concepts]"), ordecltype((f)) does not satisfy [*movable-value*](exec.general#concept:movable-value "33.1 General [exec.general]"),*then-cpo*(sndr, f) is ill-formed[.](#exec.then-2.sentence-3) [3](#exec.then-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[.](#exec.then-3.sentence-1) [4](#exec.then-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[.](#exec.then-4.sentence-1) The exposition-only class template *impls-for* ([[exec.snd.expos]](exec.snd.expos "33.9.2 Exposition-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* =[](auto, auto& fn, auto& rcvr, Tag, Args&&... args) noexcept -> void {if constexpr ([same_as](concept.same#concept:same_as "18.4.2 Concept same_­as [concept.same]")>) {*TRY-SET-VALUE*(rcvr, invoke(std::move(fn), std::forward(args)...)); } else { Tag()(std::move(rcvr), std::forward(args)...); }}; templatestatic consteval void *check-types*(); };} [🔗](#lib:check-types,impls-for%3cdecayed-typeof%3cthen-cpo%3e%3e) `template static consteval void check-types(); ` [5](#exec.then-5) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3817) *Effects*: Equivalent to:auto cs = get_completion_signatures<*child-type*, *FWD-ENV-T*(Env)...>();auto fn = [](set_value_t(*)(Ts...)) {if constexpr (![invocable](concept.invocable#concept:invocable "18.7.2 Concept invocable [concept.invocable]")>, Ts...>)throw *unspecified-exception*();}; cs.*for-each*(*overload-set*{fn, [](auto){}}); where *unspecified-exception* is a type derived from exception[.](#exec.then-5.sentence-1) [6](#exec.then-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)](#exec.then-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)](#exec.then-6.2) forwards all other completion operations unchanged[.](#exec.then-6.sentence-1) #### [33.9.12.10](#exec.let) execution​::​let_value, execution​::​let_error, execution​::​let_stopped [[exec.let]](exec.let) [1](#exec.let-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[.](#exec.let-1.sentence-1) [2](#exec.let-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[.](#exec.let-2.sentence-1) Let the expression *let-cpo* be one oflet_value, let_error, or let_stopped[.](#exec.let-2.sentence-2) For a subexpression sndr, let *let-env*(sndr) be expression-equivalent to the first well-formed expression below: - [(2.1)](#exec.let-2.1) *SCHED-ENV*(get_completion_scheduler<*decayed-typeof*<*set-cpo*>>(get_env(sndr))) - [(2.2)](#exec.let-2.2) *MAKE-ENV*(get_domain, get_domain(get_env(sndr))) - [(2.3)](#exec.let-2.3) (void(sndr), env<>{}) [3](#exec.let-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[.](#exec.let-3.sentence-1) For subexpressions sndr and f, let F be the decayed type of f[.](#exec.let-3.sentence-2) If decltype((sndr)) does not satisfy [sender](exec.snd.concepts#concept:sender "33.9.3 Sender concepts [exec.snd.concepts]") or if decltype((f)) does not satisfy [*movable-value*](exec.general#concept:movable-value "33.1 General [exec.general]"), the expression *let-cpo*(sndr, f) is ill-formed[.](#exec.let-3.sentence-3) If F does not satisfy [invocable](concept.invocable#concept:invocable "18.7.2 Concept invocable [concept.invocable]"), the expression let_stopped(sndr, f) is ill-formed[.](#exec.let-3.sentence-4) [4](#exec.let-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[.](#exec.let-4.sentence-1) [5](#exec.let-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.2 Exposition-only entities")) is specialized for *let-cpo* as follows: [🔗](#lib:impls-for%3cdecayed-typeof%3clet-cpo%3e%3e) namespace std::execution {templatevoid *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*; templatestatic consteval void *check-types*(); };} [6](#exec.let-6) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3911) Let *receiver2* denote the following exposition-only class template:namespace std::execution {templatestruct *receiver2* {using receiver_concept = receiver_t; templatevoid set_value(Args&&... args) && noexcept { execution::set_value(std::move(*rcvr*), std::forward(args)...); }templatevoid set_error(Error&& err) && noexcept { execution::set_error(std::move(*rcvr*), std::forward(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)](#exec.let-6.1) decltype(e) models [*queryable*](exec.queryable.concept#concept:queryable "33.2.2 queryable concept [exec.queryable.concept]") and - [(6.2)](#exec.let-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.4 Header synopsis [execution.syn]"),e.query(q) is expression-equivalent to get_env(*rcvr*).query(q); otherwise,e.query(q) is ill-formed[.](#exec.let-6.sentence-2) [🔗](#lib:check-types,impls-for%3cdecayed-typeof%3clet-cpo%3e%3e) `template static consteval void check-types(); ` [7](#exec.let-7) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3966) *Effects*: Equivalent to:using LetFn = remove_cvref_t<*data-type*>;auto cs = get_completion_signatures<*child-type*, *FWD-ENV-T*(Env)...>();auto fn = [](*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)](#exec.let-7.1) ([constructible_from](concept.constructible#concept:constructible_from "18.4.11 Concept constructible_­from [concept.constructible]"), Ts> &&...) - [(7.2)](#exec.let-7.2) [invocable](concept.invocable#concept:invocable "18.7.2 Concept invocable [concept.invocable]")&...> - [(7.3)](#exec.let-7.3) [sender](exec.snd.concepts#concept:sender "33.9.3 Sender concepts [exec.snd.concepts]")&...>> - [(7.4)](#exec.let-7.4) sizeof...(Env) == 0 || [sender_in](exec.snd.concepts#concept:sender_in "33.9.3 Sender concepts [exec.snd.concepts]")&...>, *env-t* ...> where *env-t* is the packdecltype(*let-cpo*.transform_env(declval(), declval()))[.](#exec.let-7.sentence-1) [8](#exec.let-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:[](Sndr&& sndr, Rcvr& rcvr) requires *see below* {auto& [_, fn, child] = sndr; using fn_t = decay_t; 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(fn), rcvr), *let-env*(child), {}, {}};} [9](#exec.let-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*, *FWD-ENV-T*(env_of_t)>[.](#exec.let-9.sentence-1) Let LetSigs be a pack of those types in Sigs with a return type of *decayed-typeof*<*set-cpo*>[.](#exec.let-9.sentence-2) Let *as-tuple* be an alias template such that *as-tuple* denotes the type *decayed-tuple*[.](#exec.let-9.sentence-3) Then args_variant_t denotes the type variant...> except with duplicate types removed[.](#exec.let-9.sentence-4) [10](#exec.let-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* denotes the type *call-result-t*&...>[.](#exec.let-10.sentence-1) Then ops2_variant_t denotes the typevariant, *receiver2*>...> except with duplicate types removed[.](#exec.let-10.sentence-2) [11](#exec.let-11) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4040) The [*requires-clause*](temp.pre#nt:requires-clause "13.1 Preamble [temp.pre]") constraining the above lambda is satisfied if and only if the types args_variant_t and ops2_variant_t are well-formed[.](#exec.let-11.sentence-1) [12](#exec.let-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*;auto mkop2 = [&] {return connect( apply(std::move(state.fn), state.args.template emplace(std::forward(args)...)), *receiver2*{rcvr, std::move(state.env)});}; start(state.ops2.template emplace(*emplace-from*{mkop2})); [13](#exec.let-13) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4059) *impls-for*<*decayed-typeof*>​::​*complete* is initialized with a callable object equivalent to the following:[](auto, auto& state, auto& rcvr, Tag, Args&&... args) noexcept -> void {if constexpr ([same_as](concept.same#concept:same_as "18.4.2 Concept same_­as [concept.same]")>) {*TRY-EVAL*(rcvr, *let-bind*(state, rcvr, std::forward(args)...)); } else { Tag()(std::move(rcvr), std::forward(args)...); }} [14](#exec.let-14) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4073) Let sndr and env be subexpressions, and let Sndr be decltype((sndr))[.](#exec.let-14.sentence-1) If[*sender-for*](exec.snd.concepts#concept:sender-for "33.9.3 Sender concepts [exec.snd.concepts]")> is false, then the expression *let-cpo*.transform_env(sndr, env) is ill-formed[.](#exec.let-14.sentence-2) Otherwise, it is equal to:auto& [_, _, child] = sndr;return *JOIN-ENV*(*let-env*(child), *FWD-ENV*(env)); [15](#exec.let-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[.](#exec.let-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.3 Asynchronous operations")) that, when started: - [(15.1)](#exec.let-15.1) invokes f when *set-cpo* is called with sndr's result datums, - [(15.2)](#exec.let-15.2) makes its completion dependent on the completion of a sender returned by f, and - [(15.3)](#exec.let-15.3) propagates the other completion operations sent by sndr[.](#exec.let-15.sentence-2) #### [33.9.12.11](#exec.bulk) execution​::​bulk, execution​::​bulk_chunked, and execution​::​bulk_unchunked [[exec.bulk]](exec.bulk) [1](#exec.bulk-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[.](#exec.bulk-1.sentence-1) [2](#exec.bulk-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[.](#exec.bulk-2.sentence-1) Let *bulk-algo* be eitherbulk, bulk_chunked, or bulk_unchunked[.](#exec.bulk-2.sentence-2) For subexpressions sndr, policy, shape, and f, letPolicy be remove_cvref_t,Shape be decltype(auto(shape)), andFunc be decay_t[.](#exec.bulk-2.sentence-3) If - [(2.1)](#exec.bulk-2.1) decltype((sndr)) does not satisfy [sender](exec.snd.concepts#concept:sender "33.9.3 Sender concepts [exec.snd.concepts]"), or - [(2.2)](#exec.bulk-2.2) is_execution_policy_v is false, or - [(2.3)](#exec.bulk-2.3) Shape does not satisfy [integral](concepts.arithmetic#concept:integral "18.4.7 Arithmetic concepts [concepts.arithmetic]"), or - [(2.4)](#exec.bulk-2.4) Func does not model [copy_constructible](concept.copyconstructible#concept:copy_constructible "18.4.14 Concept copy_­constructible [concept.copyconstructible]"), *bulk-algo*(sndr, policy, shape, f) is ill-formed[.](#exec.bulk-2.sentence-4) [3](#exec.bulk-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[.](#exec.bulk-3.sentence-2) The first template argument of *product-type* is Policy if Policy models [copy_constructible](concept.copyconstructible#concept:copy_constructible "18.4.14 Concept copy_­constructible [concept.copyconstructible]"), andconst Policy& otherwise[.](#exec.bulk-3.sentence-3) [4](#exec.bulk-4) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4150) Let sndr and env be subexpressions such thatSndr is decltype((sndr))[.](#exec.bulk-4.sentence-1) If [*sender-for*](exec.snd.concepts#concept:sender-for "33.9.3 Sender concepts [exec.snd.concepts]") 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](#exec.bulk-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[.](#exec.bulk-4.sentence-2) — *end note*] [5](#exec.bulk-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.2 Exposition-only entities")) is specialized for bulk_chunked_t as follows: [🔗](#lib:impls-for%3cbulk_chunked_t%3e) namespace std::execution {template<>struct *impls-for* : *default-impls* {static constexpr auto *complete* = *see below*; templatestatic consteval void *check-types*(); };} The member *impls-for*​::​*complete* is initialized with a callable object equivalent to the following lambda:[](Index, State& state, Rcvr& rcvr, Tag, Args&&... args) noexcept-> void requires *see below* {if constexpr ([same_as](concept.same#concept:same_as "18.4.2 Concept same_­as [concept.same]")) {auto& [policy, shape, f] = state; constexpr bool nothrow = noexcept(f(auto(shape), auto(shape), args...)); *TRY-EVAL*(rcvr, [&]() noexcept(nothrow) { f(static_cast(0), auto(shape), args...); Tag()(std::move(rcvr), std::forward(args)...); }()); } else { Tag()(std::move(rcvr), std::forward(args)...); }} The expression in the [*requires-clause*](temp.pre#nt:requires-clause "13.1 Preamble [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[.](#exec.bulk-5.sentence-3) [6](#exec.bulk-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.2 Exposition-only entities")) is specialized for bulk_unchunked_t as follows:namespace std::execution {template<>struct *impls-for* : *default-impls* {static constexpr auto *complete* = *see below*; };} The member *impls-for*​::​*complete* is initialized with a callable object equivalent to the following lambda:[](Index, State& state, Rcvr& rcvr, Tag, Args&&... args) noexcept-> void requires *see below* {if constexpr ([same_as](concept.same#concept:same_as "18.4.2 Concept same_­as [concept.same]")) {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)...); }()); } else { Tag()(std::move(rcvr), std::forward(args)...); }} The expression in the [*requires-clause*](temp.pre#nt:requires-clause "13.1 Preamble [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[.](#exec.bulk-6.sentence-3) [🔗](#lib:check-types,impls-for%3cbulk_t%3e) `template static consteval void check-types(); ` [7](#exec.bulk-7) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4253) *Effects*: Equivalent to:auto cs = get_completion_signatures<*child-type*, *FWD-ENV-T*(Env)...>();auto fn = [](set_value_t(*)(Ts...)) {if constexpr (![invocable](concept.invocable#concept:invocable "18.7.2 Concept invocable [concept.invocable]")>, Ts&...>)throw *unspecified-exception*();}; cs.*for-each*(*overload-set*(fn, [](auto){})); where *unspecified-exception* is a type derived from exception[.](#exec.bulk-7.sentence-1) [8](#exec.bulk-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[.](#exec.bulk-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.3 Asynchronous operations")) that, when started: - [(8.1)](#exec.bulk-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.14 Concept copy_­constructible [concept.copyconstructible]"), then: * [(8.1.1)](#exec.bulk-8.1.1) If out_sndr also completes successfully, then: + [(8.1.1.1)](#exec.bulk-8.1.1.1) for bulk, invokes f(i, args...) for every i of type Shape from 0 to shape; + [(8.1.1.2)](#exec.bulk-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[.](#exec.bulk-8.1.1.2.sentence-2) + [(8.1.1.3)](#exec.bulk-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[.](#exec.when.all-2.sentence-2) Let CD2 be CD if CD is well-formed, anddefault_domain otherwise[.](#exec.when.all-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)](#exec.when.all-2.1) sizeof...(sndrs) is 0, or - [(2.2)](#exec.when.all-2.2) ([sender](exec.snd.concepts#concept:sender "33.9.3 Sender concepts [exec.snd.concepts]") && ...) is false[.](#exec.when.all-2.sentence-4) [3](#exec.when.all-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](#exec.when.all-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](#exec.when.all-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)](#exec.when.all-5.1) decltype(e) models [*queryable*](exec.queryable.concept#concept:queryable "33.2.2 queryable concept [exec.queryable.concept]"), and - [(5.2)](#exec.when.all-5.2) e.query(get_stop_token) is expression-equivalent tostate.*stop-src*.get_token(), and - [(5.3)](#exec.when.all-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)[.](#exec.when.all-5.sentence-2) [6](#exec.when.all-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()))[.](#exec.when.all-6.sentence-1) [🔗](#lib:check-types,impls-for%3cwhen_all_t%3e) `template static consteval void check-types(); ` [7](#exec.when.all-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*[.](#exec.when.all-7.sentence-1) [8](#exec.when.all-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[.](#exec.when.all-8.sentence-1) [9](#exec.when.all-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[.](#exec.when.all-9.sentence-1) [10](#exec.when.all-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](#exec.when.all-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](#exec.when.all-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](#exec.when.all-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[.](#exec.when.all-13.sentence-1) [14](#exec.when.all-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<>[.](#exec.when.all-14.sentence-1) [15](#exec.when.all-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[.](#exec.when.all-15.sentence-1) [16](#exec.when.all-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)](#exec.when.all-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)](#exec.when.all-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)](#exec.when.all-16.3) Otherwise, evaluates:*on_stop*.reset(); set_stopped(std::move(rcvr)); [17](#exec.when.all-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](#exec.when.all-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...)[.](#exec.when.all-18.sentence-1) [19](#exec.when.all-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](#exec.when.all-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](#exec.when.all-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[.](#exec.when.all-20.sentence-1) — *end note*] #### [33.9.12.13](#exec.into.variant) execution​::​into_variant [[exec.into.variant]](exec.into.variant) [1](#exec.into.variant-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[.](#exec.into.variant-1.sentence-1) [2](#exec.into.variant-2) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4727) The name into_variant denotes a pipeable sender adaptor object[.](#exec.into.variant-2.sentence-1) For a subexpression sndr, let Sndr be decltype((sndr))[.](#exec.into.variant-2.sentence-2) If Sndr does not satisfy [sender](exec.snd.concepts#concept:sender "33.9.3 Sender concepts [exec.snd.concepts]"),into_variant(sndr) is ill-formed[.](#exec.into.variant-2.sentence-3) [3](#exec.into.variant-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[.](#exec.into.variant-3.sentence-1) [4](#exec.into.variant-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.2 Exposition-only entities")) is specialized for into_variant as follows: [🔗](#lib:impls-for%3cinto_variant_t%3e) namespace std::execution {template<>struct *impls-for* : *default-impls* {static constexpr auto *get-state* = *see below*; static constexpr auto *complete* = *see below*; templatestatic consteval void *check-types*() {auto cs = get_completion_signatures<*child-type*, *FWD-ENV-T*(Env)...>(); *decay-copyable-result-datums*(cs); // see [[exec.snd.expos]](exec.snd.expos "33.9.2 Exposition-only entities")}};} [5](#exec.into.variant-5) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4762) The member *impls-for*​::​*get-state* is initialized with a callable object equivalent to the following lambda:[](Sndr&& sndr, Rcvr& rcvr) noexcept-> type_identity, *FWD-ENV-T*(env_of_t)>> {return {};} [6](#exec.into.variant-6) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4772) The member *impls-for*​::​*complete* is initialized with a callable object equivalent to the following lambda:[](auto, State, Rcvr& rcvr, Tag, Args&&... args) noexcept -> void {if constexpr ([same_as](concept.same#concept:same_as "18.4.2 Concept same_­as [concept.same]")) {using variant_type = typename State::type; *TRY-SET-VALUE*(rcvr, variant_type(*decayed-tuple*{std::forward(args)...})); } else { Tag()(std::move(rcvr), std::forward(args)...); }} #### [33.9.12.14](#exec.stopped.opt) execution​::​stopped_as_optional [[exec.stopped.opt]](exec.stopped.opt) [1](#exec.stopped.opt-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[.](#exec.stopped.opt-1.sentence-1) The sender's value completion operation is also converted into an optional[.](#exec.stopped.opt-1.sentence-2) The result is a sender that never completes with stopped, reporting cancellation by completing with a disengaged optional[.](#exec.stopped.opt-1.sentence-3) [2](#exec.stopped.opt-2) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4797) The name stopped_as_optional denotes a pipeable sender adaptor object[.](#exec.stopped.opt-2.sentence-1) For a subexpression sndr, let Sndr be decltype((sndr))[.](#exec.stopped.opt-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[.](#exec.stopped.opt-2.sentence-3) [3](#exec.stopped.opt-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.2 Exposition-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* : *default-impls* {templatestatic consteval void *check-types*() {*default-impls*::*check-types*(); if constexpr (!requires {requires (![same_as](concept.same#concept:same_as "18.4.2 Concept same_­as [concept.same]"), *FWD-ENV-T*(Env)...>>); })throw *unspecified-exception*(); }};} where *unspecified-exception* is a type derived from exception[.](#exec.stopped.opt-3.sentence-1) [4](#exec.stopped.opt-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))[.](#exec.stopped.opt-4.sentence-1) If [*sender-for*](exec.snd.concepts#concept:sender-for "33.9.3 Sender concepts [exec.snd.concepts]") 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.3 Sender concepts [exec.snd.concepts]")<*child-type*, *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*, *FWD-ENV-T*(Env)>;return let_stopped( then(std::forward_like(child), [](Ts&&... ts) noexcept(is_nothrow_constructible_v) {return optional(in_place, std::forward(ts)...); }), []() noexcept { return just(optional()); }); #### [33.9.12.15](#exec.stopped.err) execution​::​stopped_as_error [[exec.stopped.err]](exec.stopped.err) [1](#exec.stopped.err-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[.](#exec.stopped.err-1.sentence-1) The result is a sender that never completes with stopped, reporting cancellation by completing with an error[.](#exec.stopped.err-1.sentence-2) [2](#exec.stopped.err-2) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4862) The name stopped_as_error denotes a pipeable sender adaptor object[.](#exec.stopped.err-2.sentence-1) For some subexpressions sndr and err, let Sndr be decltype((sndr)) and let Err be decltype((err))[.](#exec.stopped.err-2.sentence-2) If the type Sndr does not satisfy [sender](exec.snd.concepts#concept:sender "33.9.3 Sender concepts [exec.snd.concepts]") or if the type Err does not satisfy [*movable-value*](exec.general#concept:movable-value "33.1 General [exec.general]"),stopped_as_error(sndr, err) is ill-formed[.](#exec.stopped.err-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[.](#exec.stopped.err-2.sentence-4) [3](#exec.stopped.err-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))[.](#exec.stopped.err-3.sentence-1) If [*sender-for*](exec.snd.concepts#concept:sender-for "33.9.3 Sender concepts [exec.snd.concepts]") 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(child), [err = std::forward_like(err)]() mutable noexcept(is_nothrow_move_constructible_v) {return just_error(std::move(err)); }); #### [33.9.12.16](#exec.associate) execution​::​associate [[exec.associate]](exec.associate) [1](#exec.associate-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[.](#exec.associate-1.sentence-1) [2](#exec.associate-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.1 Execution scope concepts [exec.scope.concepts]") Token, [sender](exec.snd.concepts#concept:sender "33.9.3 Sender concepts [exec.snd.concepts]") Sender>struct *associate-data* { // *exposition only*using *wrap-sender* = // *exposition only* remove_cvref_t().wrap(declval()))>; explicit *associate-data*(Token t, Sender&& s): *sndr*(t.wrap(std::forward(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> 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.1 Execution scope concepts [exec.scope.concepts]") Token, [sender](exec.snd.concepts#concept:sender "33.9.3 Sender concepts [exec.snd.concepts]") Sender>*associate-data*(Token, Sender&&) -> *associate-data*;} [3](#exec.associate-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[.](#exec.associate-3.sentence-1) [🔗](#lib:execution::associate-data,constructor) `associate-data(const associate-data& other) noexcept(is_nothrow_copy_constructible_v && noexcept(other.token.try_associate())); ` [4](#exec.associate-4) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4957) *Constraints*: [copy_constructible](concept.copyconstructible#concept:copy_constructible "18.4.14 Concept copy_­constructible [concept.copyconstructible]")<*wrap-sender*> is true[.](#exec.associate-4.sentence-1) [5](#exec.associate-5) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4961) *Effects*: Value-initializes *sndr* and initializes *token* with other.*token*[.](#exec.associate-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[.](#exec.associate-5.sentence-2) [🔗](#lib:execution::associate-data,constructor_) `associate-data(associate-data&& other) noexcept(is_nothrow_move_constructible_v); ` [6](#exec.associate-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()[.](#exec.associate-6.sentence-1) [🔗](#lib:execution::associate-data,destructor) `~associate-data(); ` [7](#exec.associate-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()[.](#exec.associate-7.sentence-1) [🔗](#lib:release,execution::associate-data) `optional> release() && noexcept(is_nothrow_move_constructible_v); ` [8](#exec.associate-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 as if by:return optional(pair(*token*, std::move(**sndr*))); [9](#exec.associate-9) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5020) *Postconditions*: *sndr* does not contain a value[.](#exec.associate-9.sentence-1) [10](#exec.associate-10) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5025) The name associate denotes a pipeable sender adaptor object[.](#exec.associate-10.sentence-1) For subexpressions sndr and token: - [(10.1)](#exec.associate-10.1) If decltype((sndr)) does not satisfy [sender](exec.snd.concepts#concept:sender "33.9.3 Sender concepts [exec.snd.concepts]"), orremove_cvref_t does not satisfy [scope_token](exec.scope.concepts#concept:scope_token "33.14.1 Execution scope concepts [exec.scope.concepts]"), thenassociate(sndr, token) is ill-formed. - [(10.2)](#exec.associate-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](#exec.associate-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.2 Exposition-only entities")) is specialized for associate_t as follows: [🔗](#lib:execution::impls-for%3cassociate_t%3e) namespace std::execution {template<>struct *impls-for* : *default-impls* {static constexpr auto *get-state* = *see below*; // *exposition only*static constexpr auto *start* = *see below*; // *exposition only*templatestatic consteval void *check-types*() { // *exposition only*using associate_data_t = remove_cvref_t<*data-type*>; using child_type_t = typename associate_data_t::*wrap-sender*; (void)get_completion_signatures(); }};} [12](#exec.associate-12) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5066) The member *impls-for*​::​*get-state* is initialized with a callable object equivalent to the following lambda: [](Sndr&& sndr, Rcvr& rcvr) noexcept(*see below*) {auto [_, data] = std::forward(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; 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](#exec.associate-13) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5126) The expression in the noexcept clause of*impls-for*​::​*get-state* isis_nothrow_constructible_v, Sndr> && is_nothrow_move_constructible_v<*wrap-sender*> &&[*nothrow-callable*](functional.syn#concept:nothrow-callable "22.10.2 Header synopsis [functional.syn]") where *wrap-sender* is the typeremove_cvref_t<*data-type*>​::​*wrap-sender*[.](#exec.associate-13.sentence-1) [14](#exec.associate-14) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5137) The member *impls-for*​::​*start* is initialized with a callable object equivalent to the following lambda:[](auto& state, auto&) noexcept -> void { state.*run*();} [15](#exec.associate-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[.](#exec.associate-15.sentence-1) #### [33.9.12.17](#exec.stop.when) Exposition-only execution​::​*stop-when* [[exec.stop.when]](exec.stop.when) [1](#exec.stop.when-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))[.](#exec.stop.when-1.sentence-1) [2](#exec.stop.when-2) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5161) The name *stop-when* denotes an exposition-only sender adaptor[.](#exec.stop.when-2.sentence-1) For subexpressions sndr and token: - [(2.1)](#exec.stop.when-2.1) If decltype((sndr)) does not satisfy [sender](exec.snd.concepts#concept:sender "33.9.3 Sender concepts [exec.snd.concepts]"), orremove_cvref_t does not satisfy [stoppable_token](stoptoken.concepts#concept:stoppable_token "32.3.3 Stop token concepts [stoptoken.concepts]"), then *stop-when*(sndr, token) is ill-formed[.](#exec.stop.when-2.1.sentence-1) - [(2.2)](#exec.stop.when-2.2) Otherwise, if remove_cvref_t models[unstoppable_token](stoptoken.concepts#concept:unstoppable_token "32.3.3 Stop token concepts [stoptoken.concepts]") then*stop-when*(​sndr, token) is expression-equivalent tosndr[.](#exec.stop.when-2.2.sentence-1) - [(2.3)](#exec.stop.when-2.3) Otherwise,*stop-when*(sndr, token) returns a sender osndr[.](#exec.stop.when-2.3.sentence-1) If osndr is connected to a receiver r, let rtoken be the result of get_stop_token(get_env(r))[.](#exec.stop.when-2.3.sentence-2) * [(2.3.1)](#exec.stop.when-2.3.1) If the type of rtoken models [unstoppable_token](stoptoken.concepts#concept:unstoppable_token "32.3.3 Stop token concepts [stoptoken.concepts]") then the effects of connecting osndr to r are equivalent toconnect(write_env(sndr, prop(get_stop_token, token)), r)[.](#exec.stop.when-2.3.1.sentence-1) * [(2.3.2)](#exec.stop.when-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)](#exec.stop.when-2.3.2.1) *stoken-t* models [stoppable_token](stoptoken.concepts#concept:stoppable_token "32.3.3 Stop token concepts [stoptoken.concepts]"); + [(2.3.2.2)](#exec.stop.when-2.3.2.2) stoken.stop_requested() returns token.stop_requested() || rtoken.stop_reques- ted(); + [(2.3.2.3)](#exec.stop.when-2.3.2.3) stoken.stop_possible() returns token.stop_possible() || rtoken.stop_possible(); and + [(2.3.2.4)](#exec.stop.when-2.3.2.4) for types Fn and Init such that both [invocable](concept.invocable#concept:invocable "18.7.2 Concept invocable [concept.invocable]") and [constructible_from](concept.constructible#concept:constructible_from "18.4.11 Concept constructible_­from [concept.constructible]") are modeled, *stoken-t*​::​callback_type models [*stoppable-callback-for*](stoptoken.concepts#concept:stoppable-callback-for "32.3.3 Stop token concepts [stoptoken.concepts]")[.](#exec.stop.when-2.3.2.sentence-1) [*Note [1](#exec.stop.when-note-1)*: For an object fn of type Fn constructed from a value, init, of type Init, registering fn using *stoken-t*​::​callback_type(stoken, init) results in an invocation of fn when a callback registered with token or rtoken would be invoked[.](#exec.stop.when-2.3.2.4.sentence-2) fn is invoked at most once[.](#exec.stop.when-2.3.2.4.sentence-3) — *end note*] #### [33.9.12.18](#exec.spawn.future) execution​::​spawn_future [[exec.spawn.future]](exec.spawn.future) [1](#exec.spawn.future-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[.](#exec.spawn.future-1.sentence-1) [2](#exec.spawn.future-2) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5239) The name spawn_future denotes a customization point object[.](#exec.spawn.future-2.sentence-1) For subexpressions sndr, token, and env, - [(2.1)](#exec.spawn.future-2.1) let Sndr be decltype((sndr)), - [(2.2)](#exec.spawn.future-2.2) let Token be remove_cvref_t, and - [(2.3)](#exec.spawn.future-2.3) let Env be remove_cvref_t[.](#exec.spawn.future-2.sentence-2) If any of[sender](exec.snd.concepts#concept:sender "33.9.3 Sender concepts [exec.snd.concepts]"),[scope_token](exec.scope.concepts#concept:scope_token "33.14.1 Execution scope concepts [exec.scope.concepts]"), or[*queryable*](exec.queryable.concept#concept:queryable "33.2.2 queryable concept [exec.queryable.concept]") are not satisfied, the expression spawn_future(sndr, token, env) is ill-formed[.](#exec.spawn.future-2.sentence-3) [3](#exec.spawn.future-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 {templatestruct *spawn-future-state-base*; // *exposition only*templatestruct *spawn-future-state-base*> { // *exposition only*using *variant-t* = *see below*; // *exposition only**variant-t* *result*; // *exposition only*virtual void *complete*() noexcept = 0; // *exposition only*};} [4](#exec.spawn.future-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[.](#exec.spawn.future-4.sentence-1) Let *as-tuple* be an alias template that transforms a completion signature Tag(Args...) into the tuple specialization *decayed-tuple*[.](#exec.spawn.future-4.sentence-2) - [(4.1)](#exec.spawn.future-4.1) If is_nothrow_constructible_v, 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, *as-tuple*...>, except with duplicate types removed[.](#exec.spawn.future-4.1.sentence-1) - [(4.2)](#exec.spawn.future-4.2) Otherwise*variant-t* denotes the typevariant, tuple, *as-tuple*...>, except with duplicate types removed[.](#exec.spawn.future-4.2.sentence-1) [5](#exec.spawn.future-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 {templatestruct *spawn-future-receiver* { // *exposition only*using receiver_concept = receiver_t; *spawn-future-state-base** *state*; // *exposition only*templatevoid set_value(T&&... t) && noexcept {*set-complete*(std::forward(t)...); }templatevoid set_error(E&& e) && noexcept {*set-complete*(std::forward(e)); }void set_stopped() && noexcept {*set-complete*(); }private:templatevoid *set-complete*(T&&... t) noexcept { // *exposition only*constexpr bool nothrow = (is_nothrow_constructible_v, T> && ...); try {*state*->*result*.template emplace<*decayed-tuple*>(CPO{}, std::forward(t)...); }catch (...) {if constexpr (!nothrow) {using tuple_t = *decayed-tuple*; *state*->*result*.template emplace(set_error_t{}, current_exception()); }}*state*->*complete*(); }};} [6](#exec.spawn.future-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.3 Stop token concepts [stoptoken.concepts]") and let ssource be an lvalue of type *ssource-t*[.](#exec.spawn.future-6.sentence-1) Let *stoken-t* be decltype(ssource.get_token())[.](#exec.spawn.future-6.sentence-2) Let *future-spawned-sender* be the alias template: template<[sender](exec.snd.concepts#concept:sender "33.9.3 Sender concepts [exec.snd.concepts]") Sender, class Env>using *future-spawned-sender* = // *exposition only*decltype(write_env(*stop-when*(declval(), declval<*stoken-t*>()), declval())); [7](#exec.spawn.future-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 {templatestruct *spawn-future-state* // *exposition only*: *spawn-future-state-base*>> {using *sigs-t* = // *exposition only* completion_signatures_of_t<*future-spawned-sender*>; using *receiver-t* = // *exposition only**spawn-future-receiver*<*sigs-t*>; using *op-t* = // *exposition only* connect_result_t<*future-spawned-sender*, *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(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.1 Receiver concepts [exec.recv.concepts]") auto& rcvr) noexcept; // *exposition only*void *abandon*() noexcept; // *exposition only*private:using *alloc-t* = // *exposition only*typename allocator_traits::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](#exec.spawn.future-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.2 Multi-threaded executions and data races"))[.](#exec.spawn.future-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[.](#exec.spawn.future-8.sentence-2) [🔗](#lib:complete,execution::spawn-future-state) `void complete() noexcept; ` [9](#exec.spawn.future-9) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5419) *Effects*: - [(9.1)](#exec.spawn.future-9.1) No effects if this invocation of *complete* happens before an invocation of *consume* or *abandon* on *this; - [(9.2)](#exec.spawn.future-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)](#exec.spawn.future-9.3) otherwise,*destroy* is invoked[.](#exec.spawn.future-9.sentence-1) [🔗](#lib:consume,execution::spawn-future-state) `void consume([receiver](exec.recv.concepts#concept:receiver "33.7.1 Receiver concepts [exec.recv.concepts]") auto& rcvr) noexcept; ` [10](#exec.spawn.future-10) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5443) *Effects*: - [(10.1)](#exec.spawn.future-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)](#exec.spawn.future-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.2 Concept same_­as [concept.same]"), 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](#exec.spawn.future-11) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5474) *Effects*: - [(11.1)](#exec.spawn.future-11.1) If this invocation of *abandon* happens before an invocation of *complete* on *this then equivalent to:*ssource*.request_stop(); - [(11.2)](#exec.spawn.future-11.2) otherwise,*destroy* is invoked[.](#exec.spawn.future-11.sentence-1) [🔗](#lib:destroy,execution::spawn-future-state) `void destroy() noexcept; ` [12](#exec.spawn.future-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](#exec.spawn.future-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.2 Exposition-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* : *default-impls* {static constexpr auto *start* = *see below*; // *exposition only*};} [14](#exec.spawn.future-14) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5529) The member *impls-for*​::​*start* is initialized with a callable object equivalent to the following lambda:[](auto& state, auto& rcvr) noexcept -> void { state->*consume*(rcvr);} [15](#exec.spawn.future-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)](#exec.spawn.future-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)](#exec.spawn.future-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)](#exec.spawn.future-15.3) otherwise,alloc is allocator() andsenv is the expression env[.](#exec.spawn.future-15.sentence-1) [16](#exec.spawn.future-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)](#exec.spawn.future-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[.](#exec.spawn.future-16.1.sentence-1) If an exception is thrown then any constructed objects are destroyed and any allocated memory is deallocated[.](#exec.spawn.future-16.1.sentence-2) - [(16.2)](#exec.spawn.future-16.2) Constructs an object u of a type that is a specialization of unique_ptr such that: * [(16.2.1)](#exec.spawn.future-16.2.1) u.get() is equal to the address of s, and * [(16.2.2)](#exec.spawn.future-16.2.2) u.get_deleter()(u.release()) is equivalent to u.release()->*abandon*()[.](#exec.spawn.future-16.2.sentence-1) - [(16.3)](#exec.spawn.future-16.3) Returns *make-sender*(spawn_future, std​::​move(u))[.](#exec.spawn.future-16.3.sentence-1) [17](#exec.spawn.future-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<>())[.](#exec.spawn.future-17.sentence-1)