33 Execution control library [exec]

33.9 Senders [exec.snd]

33.9.13 Sender consumers [exec.consumers]

33.9.13.1 this_thread​::​sync_wait [exec.sync.wait]

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.
sync_wait mandates that the input sender has exactly one value completion signature.
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(); } }; }
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<execution::sender_in<sync-wait-env> Sndr> using sync-wait-result-type = optional<execution::value_types_of_t<Sndr, sync-wait-env, decayed-tuple, type_identity_t>>; template<execution::sender_in<sync-wait-env> Sndr> using sync-wait-with-variant-result-type = optional<execution::value_types_of_t<Sndr, sync-wait-env>>; }
The name this_thread​::​sync_wait denotes a customization point object.
For a subexpression sndr, let Sndr be decltype((sndr)).
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:
  • sender_in<Sndr, sync-wait-env> is true.
  • The type sync-wait-result-type<Sndr> is well-formed.
  • same_as<decltype(e), sync-wait-result-type<Sndr>> is true, where e is the apply_sender expression above.
Let sync-wait-state and sync-wait-receiver be the following exposition-only class templates: namespace std::this_thread { template<class Sndr> struct sync-wait-state { // exposition only execution::run_loop loop; // exposition only exception_ptr error; // exposition only sync-wait-result-type<Sndr> result; // exposition only }; template<class Sndr> struct sync-wait-receiver { // exposition only using receiver_concept = execution::receiver_t; sync-wait-state<Sndr>* state; // exposition only template<class... Args> void set_value(Args&&... args) && noexcept; template<class Error> void set_error(Error&& err) && noexcept; void set_stopped() && noexcept; sync-wait-env get_env() const noexcept { return {&state->loop}; } }; }
template<class... Args> void set_value(Args&&... args) && noexcept;
Effects: Equivalent to: try { state->result.emplace(std::forward<Args>(args)...); } catch (...) { state->error = current_exception(); } state->loop.finish();
template<class Error> void set_error(Error&& err) && noexcept;
Effects: Equivalent to: state->error = AS-EXCEPT-PTR(std::forward<Error>(err)); // see [exec.general] state->loop.finish();
void set_stopped() && noexcept;
Effects: Equivalent to state->loop.finish().
For a subexpression sndr, let Sndr be decltype((sndr)).
If sender_to<Sndr, sync-wait-receiver<Sndr>> is false, the expression sync_wait.apply_sender(sndr) is ill-formed; otherwise, it is equivalent to: sync-wait-state<Sndr> state; auto op = connect(sndr, sync-wait-receiver<Sndr>{&state}); start(op); state.loop.run(); if (state.error) { rethrow_exception(std::move(state.error)); } return std::move(state.result);
The behavior of this_thread​::​sync_wait(sndr) is undefined unless:
  • It blocks the current thread of execution ([defns.block]) with forward progress guarantee delegation ([intro.progress]) until the specified sender completes.
    [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.
    The run_loop is driven by the current thread of execution.
    — end note]
  • It returns the specified sender's async results as follows:
    • For a value completion, the result datums are returned in a tuple in an engaged optional object.
    • For an error completion, an exception is thrown.
    • For a stopped completion, a disengaged optional object is returned.

33.9.13.2 this_thread​::​sync_wait_with_variant [exec.sync.wait.var]

The name this_thread​::​sync_wait_with_variant denotes a customization point object.
For a subexpression sndr, let Sndr be decltype(into_variant(sndr)).
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:
  • sender_in<Sndr, sync-wait-env> is true.
  • The type sync-wait-with-variant-result-type<Sndr> is well-formed.
  • same_as<decltype(e), sync-wait-with-variant-result-type<Sndr>> is true, where e is the apply_sender expression above.
The expression sync_wait_with_variant.apply_sender(sndr) is ill-formed.
Otherwise, it is equivalent to: using result_type = sync-wait-with-variant-result-type<Sndr>; if (auto opt_value = sync_wait(into_variant(sndr))) { return result_type(std::move(get<0>(*opt_value))); } return result_type(nullopt);
The behavior of this_thread​::​sync_wait_with_variant(sndr) is undefined unless:
  • It blocks the current thread of execution ([defns.block]) with forward progress guarantee delegation ([intro.progress]) until the specified sender completes.
    [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.
    — end note]
  • It returns the specified sender's async results as follows:
    • For a value completion, the result datums are returned in an engaged optional object that contains a variant of tuples.
    • For an error completion, an exception is thrown.
    • For a stopped completion, a disengaged optional object is returned.

33.9.13.3 std​::​execution​::​spawn [exec.spawn]

spawn attempts to associate the given input sender with the given token's async scope and, on success, eagerly starts the input sender.
The name spawn denotes a customization point object.
For subexpressions sndr, token, and env,
  • let Sndr be decltype((sndr)),
  • let Token be remove_cvref_t<decltype((token))>, and
  • let Env be remove_cvref_t<decltype((env))>.
If any of sender<Sndr>, scope_token<Token>, or queryable<Env> are not satisfied, the expression spawn(​sndr, token, env) is ill-formed.
Let spawn-state-base be the exposition-only class:
namespace std::execution { struct spawn-state-base { // exposition only virtual void complete() noexcept = 0; // exposition only }; }
Let spawn-receiver be the exposition-only class:
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(); } }; }
Let spawn-state be the exposition-only class template:
namespace std::execution { template<class Alloc, scope_token Token, sender Sender> struct spawn-state : spawn-state-base { // exposition only using op-t = connect_result_t<Sender, spawn-receiver>; // 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<Alloc>::template rebind_alloc<spawn-state>; alloc-t alloc; // exposition only op-t op; // exposition only Token token; // exposition only void destroy() noexcept; // exposition only }; }
spawn-state(Alloc alloc, Sender&& sndr, Token token);
Effects: Initializes alloc with alloc, token with token, and op with: connect(std::move(sndr), spawn-receiver(this))
void run();
Effects: Equivalent to: if (token.try_associate()) start(op); else destroy();
void complete() noexcept override;
Effects: Equivalent to: auto token = std::move(this->token); destroy(); token.disassociate();
void destroy() noexcept;
Effects: Equivalent to: auto alloc = std::move(this->alloc); allocator_traits<alloc-t>::destroy(alloc, this); allocator_traits<alloc-t>::deallocate(alloc, this, 1);
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:
  • if the expression get_allocator(env) is well-formed, then alloc is the result of get_allocator(env) and senv is the expression env,
  • otherwise if the expression get_allocator(get_env(new_sender)) is well-formed, then alloc is the result of get_allocator(get_env(new_sender)) and senv is the expression JOIN-ENV(prop(get_allocator, alloc), env),
  • otherwise alloc is allocator<void>() and senv is the expression env.
The expression spawn(sndr, token, env) is of type void and has the following effects:
  • Uses alloc to allocate and construct an object o of type that is a specialization of spawn-state from alloc, write_env(token.wrap(sndr), senv), and token and then invokes o.run().
    If an exception is thrown then any constructed objects are destroyed and any allocated memory is deallocated.
The expression spawn(sndr, token) is expression-equivalent to spawn(sndr, token, execution​::​env<>()).