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