#
Scopes of type simple_counting_scope and counting_scope maintain counts of associations.
Let:
During its lifetime scope goes through different states which govern what operations are allowed and the result of these operations:
2
#
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