Files
cppdraft_translate/cppdraft/exec/consumers.md
2025-10-25 03:02:53 +03:00

16 KiB
Raw Blame History

[exec.consumers]

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]

1

#

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.

2

#

Let sync-wait-env be the following exposition-only class type:namespace std::this_thread {struct sync-wait-env { execution::run_loop* loop; // exposition onlyauto 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

#

Let sync-wait-result-type andsync-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>>;}

4

#

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:

  • (4.1)

    sender_in<Sndr, sync-wait-env> is true.

  • (4.2)

    The type sync-wait-result-type is well-formed.

  • (4.3)

    same_as<decltype(e), sync-wait-result-type> is true, where e is the apply_sender expression above.

5

#

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 onlyusing receiver_concept = execution::receiver_t; sync-wait-state* state; // exposition onlytemplate<class... Args>void 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}; }};}

🔗

template<class... Args> void set_value(Args&&... args) && noexcept;

6

#

Effects: Equivalent to:try {state->result.emplace(std::forward(args)...);} catch (...) {state->error = current_exception();}state->loop.finish();

🔗

template<class Error> void set_error(Error&& err) && noexcept;

7

#

Effects: Equivalent to:state->error = AS-EXCEPT-PTR(std::forward(err)); // see [exec.general]state->loop.finish();

🔗

void set_stopped() && noexcept;

8

#

Effects: Equivalent to state->loop.finish().

9

#

For a subexpression sndr, let Sndr be decltype((sndr)).

If sender_to<Sndr, sync-wait-receiver> 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

#

The behavior of this_thread::sync_wait(sndr) is undefined unless:

  • (10.1)

    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]

  • (10.2)

    It returns the specified sender's async results as follows:

    • (10.2.1)

      For a value completion, the result datums are returned in a tuple in an engaged optional object.

    • (10.2.2)

      For an error completion, an exception is thrown.

    • (10.2.3)

      For a stopped completion, a disengaged optional object is returned.

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

1

#

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:

  • (1.1)

    sender_in<Sndr, sync-wait-env> is true.

  • (1.2)

    The type sync-wait-with-variant-result-type is well-formed.

  • (1.3)

    same_as<decltype(e), sync-wait-with-variant-result-type> is true, where e is the apply_sender expression above.

2

#

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

#

The behavior of this_thread::sync_wait_with_variant(sndr) is undefined unless:

  • (3.1)

    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]

  • (3.2)

    It returns the specified sender's async results as follows:

    • (3.2.1)

      For a value completion, the result datums are returned in an engaged optional object that contains a variant of tuples.

    • (3.2.2)

      For an error completion, an exception is thrown.

    • (3.2.3)

      For a stopped completion, a disengaged optional object is returned.

33.9.13.3 execution::spawn [exec.spawn]

1

#

spawn attempts to associate the given input sender with the given token's async scope and, on success, eagerly starts the input sender.

2

#

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 ofsender,scope_token, orqueryable are not satisfied, the expression spawn(sndr, token, env) is ill-formed.

3

#

Let spawn-state-base be the exposition-only class:

🔗

namespace std::execution {struct spawn-state-base { // exposition onlyvirtual void complete() noexcept = 0; // exposition only};}

4

#

Let spawn-receiver be the exposition-only class:

🔗

namespace std::execution {struct spawn-receiver { // exposition onlyusing receiver_concept = receiver_t; spawn-state-base* state; // exposition onlyvoid set_value() && noexcept { state->complete(); }void set_stopped() && noexcept { state->complete(); }};}

5

#

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 onlyusing op-t = connect_result_t<Sender, spawn-receiver>; // exposition only**spawn-state(Alloc alloc, Sender&& sndr, Token token); // exposition onlyvoid complete() noexcept override; // exposition onlyvoid run(); // exposition onlyprivate:using alloc-t = // exposition onlytypename allocator_traits::template rebind_alloc<spawn-state>; alloc-t alloc; // exposition only**op-t op; // exposition only Token token; // exposition onlyvoid destroy() noexcept; // exposition only};}

🔗

spawn-state(Alloc alloc, Sender&& sndr, Token token);

6

#

Effects: Initializesalloc with alloc,token with token, andop with:connect(std::move(sndr), spawn-receiver(this))

🔗

void run();

7

#

Effects: Equivalent to:if (token.try_associate()) start(op);elsedestroy();

🔗

void complete() noexcept override;

8

#

Effects: Equivalent to:auto token = std::move(this->token);

destroy(); token.disassociate();

🔗

void destroy() noexcept;

9

#

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

#

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, thenalloc is the result of get_allocator(env) andsenv is the expression env,

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),

otherwisealloc is allocator() andsenv is the expression env.

11

#

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 fromalloc, 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.

12

#

The expression spawn(sndr, token) is expression-equivalent tospawn(sndr, token, execution::env<>()).