33 Execution control library [exec]

33.9 Senders [exec.snd]

33.9.12 Sender adaptors [exec.adapt]

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

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 with set_stopped if the input sender was not started.
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 of sender<Sndr>, scope_token<Token>, or queryable<Env> are not satisfied, the expression spawn_future(sndr, token, env) is ill-formed.
Let spawn-future-state-base be the exposition-only class template:
namespace std::execution { template<class Completions> struct spawn-future-state-base; // exposition only template<class... Sigs> struct spawn-future-state-base<completion_signatures<Sigs...>> { // exposition only using variant-t = see below; // exposition only variant-t result; // exposition only virtual void complete() noexcept = 0; // exposition only }; }
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...>.
  • If is_nothrow_constructible_v<decay_t<Arg>, 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 type variant<monostate, tuple<set_stopped_t>, as-tuple<Sigs>...>, except with duplicate types removed.
  • Otherwise variant-t denotes the type variant<monostate, tuple<set_stopped_t>, tuple<set_error_t, exception_ptr>, as-tuple<Sigs>...>, except with duplicate types removed.
Let spawn-future-receiver be the exposition-only class template:
namespace std::execution { template<class Completions> struct spawn-future-receiver { // exposition only using receiver_concept = receiver_t; spawn-future-state-base<Completions>* state; // exposition only template<class... T> void set_value(T&&... t) && noexcept { set-complete<set_value_t>(std::forward<T>(t)...); } template<class E> void set_error(E&& e) && noexcept { set-complete<set_error_t>(std::forward<E>(e)); } void set_stopped() && noexcept { set-complete<set_stopped_t>(); } private: template<class CPO, class... T> void set-complete(T&&... t) noexcept { // exposition only constexpr bool nothrow = (is_nothrow_constructible_v<decay_t<T>, T> && ...); try { state->result.template emplace<decayed-tuple<CPO, T...>>(CPO{}, std::forward<T>(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(); } }; }
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 only decltype(write_env(stop-when(declval<Sender>(), declval<stoken-t>()), declval<Env>()));
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<Sender>(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 auto& rcvr) noexcept; // exposition only void abandon() noexcept; // exposition only private: using alloc-t = // exposition only typename allocator_traits<Alloc>::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 }; }
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;
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 auto& rcvr) noexcept;
Effects:
  • If this invocation of consume happens before an invocation of complete on *this then rcvr is registered to be completed when complete 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;
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;
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();
The exposition-only class template impls-for ([exec.snd.general]) 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 }; }
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); }
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, 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_future(sndr, token, env) has the following effects:
  • 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.
  • 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().
  • Returns make-sender(spawn_future, std​::​move(u)).
The expression spawn_future(sndr, token) is expression-equivalent to spawn_future(sndr, token, execution​::​env<>()).