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

12 KiB
Raw Blame History

[exec.spawn.future]

33 Execution control library [exec]

33.9 Senders [exec.snd]

33.9.12 Sender adaptors [exec.adapt]

33.9.12.18 execution::spawn_future [exec.spawn.future]

1

#

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.

2

#

The name spawn_future 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_future(sndr, token, env) is ill-formed.

3

#

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

🔗

namespace std::execution {templatestruct spawn-future-state-base; // exposition onlytemplate<class... Sigs>struct spawn-future-state-base<completion_signatures<Sigs...>> { // exposition onlyusing variant-t = see below; // exposition only**variant-t result; // exposition onlyvirtual void complete() noexcept = 0; // exposition only};}

4

#

Let Sigs be the pack of arguments to the completion_signatures specialization provided as a parameter to the spawn-future-state-base class template.

Let as-tuple be an alias template that transforms a completion signature Tag(Args...) into the tuple specialization decayed-tuple<Tag, Args...>.

  • (4.1)

    If is_nothrow_constructible_v<decay_t, Arg> is true for every type Arg in every parameter pack Args in every completion signature Tag(Args...) in Sigs thenvariant-t denotes the typevariant<monostate, tuple<set_stopped_t>, as-tuple...>, except with duplicate types removed.

  • (4.2)

    Otherwisevariant-t denotes the typevariant<monostate, tuple<set_stopped_t>, tuple<set_error_t, exception_ptr>, as-tuple...>, except with duplicate types removed.

5

#

Let spawn-future-receiver be the exposition-only class template:

🔗

namespace std::execution {templatestruct spawn-future-receiver { // exposition onlyusing receiver_concept = receiver_t; spawn-future-state-base* state; // exposition onlytemplate<class... T>void set_value(T&&... t) && noexcept {set-complete<set_value_t>(std::forward(t)...); }templatevoid set_error(E&& e) && noexcept {set-complete<set_error_t>(std::forward(e)); }void set_stopped() && noexcept {set-complete<set_stopped_t>(); }private:template<class CPO, class... T>void set-complete(T&&... t) noexcept { // exposition onlyconstexpr bool nothrow = (is_nothrow_constructible_v<decay_t, T> && ...); try {state->result.template emplace<decayed-tuple<CPO, T...>>(CPO{}, std::forward(t)...); }catch (...) {if constexpr (!nothrow) {using tuple_t = decayed-tuple<set_error_t, exception_ptr>; state->result.template emplace<tuple_t>(set_error_t{}, current_exception()); }}state->complete(); }};}

6

#

Let ssource-t be an unspecified type that models stoppable-source and let ssource be an lvalue of type ssource-t.

Let stoken-t be decltype(ssource.get_token()).

Let future-spawned-sender be the alias template:

template<sender Sender, class Env>using future-spawned-sender = // exposition onlydecltype(write_env(stop-when(declval(), declval<stoken-t>()), declval()));

7

#

Let spawn-future-state be the exposition-only class template:

🔗

namespace std::execution {template<class Alloc, scope_token Token, sender Sender, class Env>struct spawn-future-state // exposition only: spawn-future-state-base<completion_signatures_of_t<future-spawned-sender<Sender, Env>>> {using sigs-t = // exposition only completion_signatures_of_t<future-spawned-sender<Sender, Env>>; using receiver-t = // exposition only**spawn-future-receiver<sigs-t>; using op-t = // exposition only connect_result_t<future-spawned-sender<Sender, Env>, 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 onlyvoid consume(receiver auto& rcvr) noexcept; // exposition onlyvoid abandon() noexcept; // exposition onlyprivate:using alloc-t = // exposition onlytypename 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 onlybool associated; // exposition onlyvoid destroy() noexcept; // exposition only};}

8

#

For purposes of determining the existence of a data race,complete, consume, and abandon behave as atomic operations ([intro.multithread]).

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.

🔗

void complete() noexcept;

9

#

Effects:

No effects if this invocation of complete happens before an invocation of consume or abandon on *this;

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

otherwise,destroy is invoked.

🔗

void consume([receiver](exec.recv.concepts#concept:receiver "33.7.1Receiver concepts[exec.recv.concepts]") auto& rcvr) noexcept;

10

#

Effects:

If this invocation of consume happens before an invocation of complete on this thenrcvr is registered to be completed whencomplete* is subsequently invoked on *this;

otherwise,rcvr is completed as if by:std::move(this->result).visit([&rcvr](auto&& tuple) noexcept {if constexpr (same_as<remove_reference_t<decltype(tuple)>, monostate>) { apply([&rcvr](auto cpo, auto&&... vals) { cpo(std::move(rcvr), std::move(vals)...); }, std::move(tuple)); }});

🔗

void abandon() noexcept;

11

#

Effects:

If this invocation of abandon happens before an invocation of complete on *this then equivalent to:ssource.request_stop();

otherwise,destroy is invoked.

🔗

void destroy() noexcept;

12

#

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

#

The exposition-only class template impls-for ([exec.snd.expos]) is specialized for spawn_future_t as follows:

🔗

namespace std::execution {template<>struct impls-for<spawn_future_t> : default-impls {static constexpr auto start = see below; // exposition only};}

14

#

The member impls-for<spawn_future_t>::start is initialized with a callable object equivalent to the following lambda:[](auto& state, auto& rcvr) noexcept -> void { state->consume(rcvr);}

15

#

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:

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 expressionJOIN-ENV(prop(get_allocator, alloc), env);

otherwise,alloc is allocator() andsenv is the expression env.

16

#

The expression spawn_future(sndr, token, env) has the following effects:

  • (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. If an exception is thrown then any constructed objects are destroyed and any allocated memory is deallocated.

  • (16.2)

    Constructs an object u of a type that is a specialization of unique_ptr such that:

u.get() is equal to the address of s, and

u.get_deleter()(u.release()) is equivalent to u.release()->abandon().

  • (16.3)

    Returns make-sender(spawn_future, std::move(u)).

17

#

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