#
The scope_association concept defines the requirements on a type Assoc.
An object of type Assoc is engaged if and only if it owns an association with an async scope, referred to as its associated scope.
namespace std::execution { template<class Assoc> concept scope_association = movable<Assoc> && is_nothrow_move_constructible_v<Assoc> && is_nothrow_move_assignable_v<Assoc> && default_initializable<Assoc> && requires(const Assoc assoc) { { static_cast<bool>(assoc) } noexcept; { assoc.try_associate() } -> same_as<Assoc>; }; }
2
#
A type Assoc models scope_association only if:
  • a default constructed object of type Assoc is not engaged;
  • for an object assoc of type Assoc, static_cast<bool>(assoc) is true if and only if assoc is engaged;
  • no two distinct objects of type Assoc own the same assocation;
  • for an object assoc of type Assoc, destroying assoc releases the assocation owned by assoc, if any;
  • for an object assoc of type Assoc, after it is used as the source operand of a move constructor, the assoc is not engaged;
  • for distinct objects assoc1 and assoc2 of type Assoc, after evaluating assoc1 = std​::​move(assoc2), the association owned by assoc1, if any, is released and assoc2 is not engaged;
  • for an object assoc of type Assoc that is engaged, assoc.try_associate() either returns an object that is not engaged or acquires a new association with assoc's associated scope and returns an engaged object that owns that association;
  • for an object assoc of type Assoc that is not engaged, assoc.try_associate() returns an object that is not engaged.
The scope_token concept defines the requirements on a type Token that can be used to create associations between senders and an async scope.
Every object of type Token is associated with an async scope that is referred to as its associated scope.
Let test-sender and test-env be unspecified types such that sender_in<test-sender, test-env> is modeled.
namespace std::execution { template<class Token> concept scope_token = copyable<Token> && requires(const Token token) { { token.try_associate() } -> scope_association; { 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;
  • for an object token of type Token, token.try_associate() either returns an object that is not engaged or acquires a new association with token's associated scope and returns an engaged object that owns that association; and
  • given an lvalue token of type (possibly const) Token, for all expressions sndr such that decltype(
    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.
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 only enum 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 }; template<class Scope> struct association-t; / 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 only struct rcvr-t { / exposition only using receiver_concept = receiver_t; Rcvr& rcvr; / exposition only void set_value() && noexcept { execution::set_value(std::move(rcvr)); } template<class E> void set_error(E&& e) && noexcept { execution::set_error(std::move(rcvr), std::forward<E>(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 only decltype(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 only noexcept(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 []<class Rcvr>(auto& sender, Rcvr& receiver) noexcept(is_nothrow_constructible_v<state<Rcvr>, 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(); }; }; }
association-t is a class template, specializations of which model scope_association and contain an exposition-only member scope of type Scope*.
For a class type Scope and an object assoc of type association-t<Scope>:
  • assoc.scope points to its associated scope,
  • assoc is engaged when assoc.scope != nullptr is true,
  • if assoc is engaged, then assoc.try_associate() is equivalent to assoc.scope->try-associate(), and
  • the association owned by assoc is released by invoking assoc.scope->disassociate().
namespace std::execution { class simple_counting_scope { public: / [exec.simple.counting.token], token struct token; using assoc-t = association-t<simple_counting_scope>; / exposition only 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 only assoc-t try-associate() noexcept; / exposition only void disassociate() noexcept; / exposition only template<class State> bool 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, and start-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 of joined, unused, or unused-and-closed, invokes terminate ([except.terminate]).
Otherwise, has no effects.
token get_token() noexcept;
Returns: An object t of type simple_counting_scope​::​token such that t.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 auto join() noexcept;
Returns: make-sender(scope-join-t(), this).
assoc-t 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: If count was incremented, an object of type assoc-t that is engaged and associated with *this, and assoc-t() otherwise.
void disassociate() noexcept;
Effects: Decrements count.
If count is zero after decrementing and state 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.
namespace std::execution { struct simple_counting_scope::token { template<sender Sender> Sender&& wrap(Sender&& snd) const noexcept; assoc-t try_associate() const noexcept; private: simple_counting_scope* scope; / exposition only }; }
template<sender Sender> Sender&& wrap(Sender&& snd) const noexcept;
Returns: std​::​forward<Sender>(snd).
assoc-t try_associate() const noexcept;
Effects: Equivalent to: return scope->try-associate();
namespace std::execution { class counting_scope { public: using assoc-t = association-t<counting_scope>; / exposition only struct token { template<sender Sender> sender auto wrap(Sender&& snd) const noexcept(see below); assoc-t try_associate() 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 only assoc-t try-associate() noexcept; / exposition only void disassociate() noexcept; / exposition only template<class State> bool 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 that t.scope == this is true.
void request_stop() noexcept;
Remarks: Calls to request_stop do not introduce data races.
assoc-t 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: If count was incremented, an object of type assoc-t that is engaged and associated with *this, and assoc-t() otherwise.
template<sender Sender> sender 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<Sender>(snd), scope->s_source.get_token();
assoc-t counting_scope::token::try_associate() const noexcept;
Effects: Equivalent to: return scope->try-associate();

Follow Lee on X/Twitter - Father, Husband, Serial builder creating AI, crypto, games & web tools. We are friends :) AI Will Come To Life!

Check out: eBank.nz (Art Generator) | Netwrck.com (AI Tools) | Text-Generator.io (AI API) | BitBank.nz (Crypto AI) | ReadingTime (Kids Reading) | RewordGame | BigMultiplayerChess | WebFiddle | How.nz | Helix AI Assistant