20 KiB
[exec.scope]
33 Execution control library [exec]
33.14 Execution scope utilities [exec.scope]
33.14.1 Execution scope concepts [exec.scope.concepts]
The scope_token concept defines the requirements on a type Token that can be used to create associations between senders and an async scope.
Let test-sender and test-env be unspecified types such thatsender_in<test-sender, test-env> is modeled.
namespace std::execution {templateconcept scope_token =copyable &&requires(const Token token) {{ token.try_associate() } -> same_as; { token.disassociate() } noexcept -> same_as; { token.wrap(declval<test-sender>()) } -> sender_in<test-env>; };}
A type Token models scope_token only if:
no exceptions are thrown from copy construction, move construction, copy assignment, or move assignment of objects of type Token; and
given an lvalue token of type (possibly const) Token,
for all expressions sndr such thatdecltype((
sndr)) models sender:
token.wrap(sndr) is a valid expression,
decltype(token.wrap(sndr)) models sender, and
completion_signatures_of_t<decltype(token.wrap(sndr)), E> contains the same completion signatures as completion_signatures_of_t<decltype((sndr)), E> for all types E such that sender_in<decltype((sndr)), E> is modeled.
33.14.2 Counting Scopes [exec.counting.scopes]
33.14.2.1 General [exec.counting.scopes.general]
Scopes of type simple_counting_scope and counting_scope maintain counts of associations.
Let:
Scope be either simple_counting_scope or counting_scope,
scope be an object of type Scope,
tkn be an object of type Scope::token obtained from scope.get_token(),
jsndr be a sender obtained from scope.join(), and
op be an operation state obtained from connecting jsndr to a receiver.
During its lifetime scope goes through different states which govern what operations are allowed and the result of these operations:
-
unused: a newly constructed object starts in the unused state.
-
open: when tkn.try_associate() is called while scope is in the unused state,scope moves to the open state.
-
open-and-joining: when the operation state op is started while scope is in the unused or open state,scope moves to the open-and-joining state.
-
closed: when scope.close() is called while scope is in the open state,scope moves to the closed state.
-
unused-and-closed: when scope.close() is called while scope is in the unused state,scope moves to the unused-and-closed state.
-
closed-and-joining: when scope.close() is called while scope is in the open-and-joining state or the operation state op is started while scope is in the closed or unused-and-closed state,scope moves to the closed-and-joining state.
-
joined: when the count of associations drops to zero while scope is in the open-and-joining or closed-and-joining state,scope moves to the joined state.
Recommended practice: For simple_counting_scope and counting_scope, implementations should store the state and the count of associations in a single member of type size_t.
Subclause [exec.counting.scopes] makes use of the following exposition-only entities:
struct scope-join-t {}; // exposition onlyenum scope-state-type { // exposition only**unused, // exposition only**open, // exposition only**closed, // exposition only**open-and-joining, // exposition only**closed-and-joining, // exposition only**unused-and-closed, // exposition only**joined, // exposition only};
The exposition-only class template impls-for ([exec.snd.expos]) is specialized for scope-join-t as follows:
namespace std::execution {template<>struct impls-for<scope-join-t> : default-impls {template<class Scope, class Rcvr>struct state { // exposition onlystruct rcvr-t { // exposition onlyusing receiver_concept = receiver_t;
Rcvr& rcvr; // exposition onlyvoid set_value() && noexcept { execution::set_value(std::move(rcvr)); }templatevoid set_error(E&& e) && noexcept { execution::set_error(std::move(rcvr), std::forward(e)); }void set_stopped() && noexcept { execution::set_stopped(std::move(rcvr)); }decltype(auto) get_env() const noexcept {return execution::get_env(rcvr); }}; using sched-sender = // exposition onlydecltype(schedule(get_scheduler(get_env(declval<Rcvr&>())))); using op-t = // exposition only connect_result_t<sched-sender, rcvr-t>;
Scope* scope; // exposition only Rcvr& receiver; // exposition only**op-t op; // exposition only**state(Scope* scope, Rcvr& rcvr) // exposition onlynoexcept(nothrow-callable<connect_t, sched-sender, rcvr-t>): scope(scope), receiver(rcvr), op(connect(schedule(get_scheduler(get_env(rcvr))), rcvr-t(rcvr))) {}void complete() noexcept { // exposition only start(op); }void complete-inline() noexcept { // exposition only set_value(std::move(receiver)); }}; static constexpr auto get-state = // exposition only[](auto&& sender, Rcvr& receiver)noexcept(is_nothrow_constructible_v<state, data-type<decltype(sender)>, Rcvr&>) {auto[_, self] = sender; return state(self, receiver); }; static constexpr auto start = // exposition only[](auto& s, auto&) noexcept {if (s.scope->start-join-sender(s)) s.complete-inline(); }; };}
33.14.2.2 Simple Counting Scope [exec.scope.simple.counting]
33.14.2.2.1 General [exec.scope.simple.counting.general]
namespace std::execution {class simple_counting_scope {public:// [exec.simple.counting.token], tokenstruct token; static constexpr size_t max_associations = implementation-defined; // [exec.simple.counting.ctor], constructor and destructor simple_counting_scope() noexcept; simple_counting_scope(simple_counting_scope&&) = delete; ~simple_counting_scope(); // [exec.simple.counting.mem], members token get_token() noexcept; void close() noexcept; sender auto join() noexcept; private: size_t count; // exposition only**scope-state-type state; // exposition onlybool try-associate() noexcept; // exposition onlyvoid disassociate() noexcept; // exposition onlytemplatebool start-join-sender(State& state) noexcept; // exposition only};}
For purposes of determining the existence of a data race,get_token,close,join,try-associate,disassociate, andstart-join-sender behave as atomic operations ([intro.multithread]).
These operations on a single object of type simple_counting_scope appear to occur in a single total order.
33.14.2.2.2 Constructor and Destructor [exec.simple.counting.ctor]
simple_counting_scope() noexcept;
Postconditions: count is 0 and state is unused.
~simple_counting_scope();
Effects: If state is not one ofjoined, unused, or unused-and-closed, invokes terminate ([except.terminate]).
Otherwise, has no effects.
33.14.2.2.3 Members [exec.simple.counting.mem]
token get_token() noexcept;
Returns: An object t of type simple_counting_scope::token such thatt.scope == this is true.
void close() noexcept;
Effects: If state is
unused, then changes state to unused-and-closed;
open, then changes state to closed;
open-and-joining, then changes state to closed-and-joining;
otherwise, no effects.
Postconditions: Any subsequent call to try-associate() on *this returns false.
[sender](exec.snd.concepts#concept:sender "33.9.3 Sender concepts [exec.snd.concepts]") auto join() noexcept;
Returns: make-sender(scope-join-t(), this).
bool try-associate() noexcept;
Effects: If count is equal to max_associations, then no effects.
Otherwise, if state is
unused, then increments count and changes state to open;
open or open-and-joining, then increments count;
otherwise, no effects.
Returns: true if count was incremented, false otherwise.
void disassociate() noexcept;
Preconditions: count is greater than zero.
Effects: Decrements count.
If count is zero after decrementing andstate is open-and-joining or closed-and-joining, changes state to joined and calls complete() on all objects registered with *this.
[Note 1:
Calling complete() on any registered object can cause *this to be destroyed.
â end note]
template<class State> bool start-join-sender(State& st) noexcept;
Effects: If state is
unused, unused-and-closed, or joined, then changes state to joined and returns true;
open or open-and-joining, then changes state to open-and-joining, registers st with *this and returns false;
closed or closed-and-joining, then changes state to closed-and-joining, registers st with *this and returns false.
33.14.2.2.4 Token [exec.simple.counting.token]
namespace std::execution {struct simple_counting_scope::token {template<sender Sender> Sender&& wrap(Sender&& snd) const noexcept; bool try_associate() const noexcept; void disassociate() const noexcept; private: simple_counting_scope* scope; // exposition only};}
template<[sender](exec.snd.concepts#concept:sender "33.9.3 Sender concepts [exec.snd.concepts]") Sender> Sender&& wrap(Sender&& snd) const noexcept;
Returns: std::forward(snd).
bool try_associate() const noexcept;
Effects: Equivalent to: return scope->try-associate();
void disassociate() const noexcept;
Effects: Equivalent to scope->disassociate().
33.14.2.3 Counting Scope [exec.scope.counting]
namespace std::execution {class counting_scope {public:struct token {template<sender Sender>sender auto wrap(Sender&& snd) const noexcept(see below); bool try_associate() const noexcept; void disassociate() const noexcept; private: counting_scope* scope; // exposition only}; static constexpr size_t max_associations = implementation-defined;
counting_scope() noexcept; counting_scope(counting_scope&&) = delete; ~counting_scope();
token get_token() noexcept; void close() noexcept; sender auto join() noexcept; void request_stop() noexcept; private: size_t count; // exposition only**scope-state-type state; // exposition only inplace_stop_source s_source; // exposition onlybool try-associate() noexcept; // exposition onlyvoid disassociate() noexcept; // exposition onlytemplatebool start-join-sender(State& state) noexcept; // exposition only};}
counting_scope differs from simple_counting_scope by adding support for cancellation.
Unless specified below, the semantics of members of counting_scope are the same as the corresponding members of simple_counting_scope.
token get_token() noexcept;
Returns: An object t of type counting_scope::token such thatt.scope == this is true.
void request_stop() noexcept;
Effects: Equivalent to s_source.request_stop().
Remarks: Calls to request_stop do not introduce data races.
template<[sender](exec.snd.concepts#concept:sender "33.9.3 Sender concepts [exec.snd.concepts]") Sender> [sender](exec.snd.concepts#concept:sender "33.9.3 Sender concepts [exec.snd.concepts]") auto counting_scope::token::wrap(Sender&& snd) const noexcept(is_nothrow_constructible_v<remove_cvref_t<Sender>, Sender>);
Effects: Equivalent to:return stop-when(std::forward(snd), scope->s_source.get_token());