From 9340209510ec7fcfb1055c8f24477866e4973ee9 Mon Sep 17 00:00:00 2001 From: Jens Maurer Date: Sat, 15 Nov 2025 20:26:04 +0100 Subject: [PATCH 1/4] P3815R1 Add scope_association concept to P3149 Fixes NB FI-392, CA-393 (C++26 CD). --- source/exec.tex | 378 +++++++++++++++++++++++++++++------------------- 1 file changed, 230 insertions(+), 148 deletions(-) diff --git a/source/exec.tex b/source/exec.tex index 6e99784a93..3c067c607e 100644 --- a/source/exec.tex +++ b/source/exec.tex @@ -742,6 +742,8 @@ class @\libglobal{task}@; // \ref{exec.scope.concepts}, scope concepts + template + concept @\libconcept{scope_association}@ = @\seebelow@; template concept @\libconcept{scope_token}@ = @\seebelow@; @@ -4904,32 +4906,42 @@ \begin{codeblock} namespace std::execution { template<@\libconcept{scope_token}@ Token, @\libconcept{sender}@ Sender> - struct @\exposid{associate-data}@ { // \expos - using @\exposid{wrap-sender}@ = // \expos + struct @\exposid{associate-data}@ { // \expos + using @\exposid{wrap-sender}@ = // \expos remove_cvref_t().wrap(declval()))>; + using @\exposid{assoc-t}@ = decltype(declval().try_associate()); // \expos + using @\exposid{sender-ref}@ = // \expos + unique_ptr<@\exposid{wrap-sender}@, decltype([](auto* p) noexcept { destroy_at(p); })>; explicit @\exposid{associate-data}@(Token t, Sender&& s) : @\exposid{sndr}@(t.wrap(std::forward(s))), - @\exposid{token}@(t) { - if (!@\exposid{token}@.try_associate()) - @\exposid{sndr}@.reset(); + @\exposid{assoc}@([&] { + @\exposid{sender-ref}@ guard{addressof(@\exposid{sndr}@)}; + auto assoc = t.try_associate(); + if (assoc) + guard.release(); + return assoc; + }()) { } @\exposid{associate-data}@(const @\exposid{associate-data}@& other) noexcept(is_nothrow_copy_constructible_v<@\exposid{wrap-sender}@> && - noexcept(other.@\exposid{token}@.try_associate())); + noexcept(other.@\exposid{assoc}@.try_associate())); @\exposid{associate-data}@(@\exposid{associate-data}@&& other) - noexcept(is_nothrow_move_constructible_v<@\exposid{wrap-sender}@>); + noexcept(is_nothrow_move_constructible_v<@\exposid{wrap-sender}@>) + : @\exposid{associate-data}@(std::move(other).release()) {} ~@\exposid{associate-data}@(); - optional> - release() && noexcept(is_nothrow_move_constructible_v<@\exposid{wrap-sender}@>); + pair<@\exposid{assoc-t}@, @\exposid{sender-ref}@> release() && noexcept; private: - optional<@\exposid{wrap-sender}@> @\exposid{sndr}@; // \expos - Token @\exposid{token}@; // \expos + @\exposid{associate-data}@(pair<@\exposid{assoc-t}@, @\exposid{sender-ref}@> parts); // \expos + union { + @\exposid{wrap-sender}@ @\exposid{sndr}@; // \expos + }; + @\exposid{assoc-t}@ @\exposid{assoc}@; // \expos }; template<@\libconcept{scope_token}@ Token, @\libconcept{sender}@ Sender> @@ -4939,7 +4951,7 @@ \pnum For an \exposid{associate-data} object \tcode{a}, -\tcode{a.\exposid{sndr}.has_value()} is \tcode{true} +\tcode{bool(a.\exposid{assoc})} is \tcode{true} if and only if an association was successfully made and is owned by \tcode{a}. @@ -4947,40 +4959,32 @@ \begin{itemdecl} @\exposid{associate-data}@(const @\exposid{associate-data}@& other) noexcept(is_nothrow_copy_constructible_v<@\exposid{wrap-sender}@> && - noexcept(other.@\exposid{token}@.try_associate())); + noexcept(other.@\exposid{assoc}@.try_associate())); \end{itemdecl} \begin{itemdescr} \pnum \constraints -\tcode{\libconcept{copy_constructible}<\exposid{wrap-sender}>} is \tcode{true}. +\exposid{wrap-sender} models \libconcept{copy_constructible}. \pnum \effects -Value-initializes \exposid{sndr} and -initializes \exposid{token} with \tcode{other.\exposid{token}}. -If \tcode{other.\exposid{sndr}.has_value()} is \tcode{false}, -no further effects; -otherwise, -calls \tcode{\exposid{token}.try_associate()} and, -if that returns \tcode{true}, -calls \tcode{\exposid{sndr}.emplace(*other.\exposid{sndr})} and, -if that exits with an exception, -calls \tcode{\exposid{token}.disassociate()} before propagating the exception. +Initializes \exposid{assoc} with \tcode{other.\exposid{assoc}.try_associate()}. +If \tcode{bool(\exposid{assoc})} is \tcode{true}, +initializes \exposid{sndr} with \tcode{other.\exposid{sndr}}. \end{itemdescr} \indexlibraryctor{execution::\exposid{associate-data}}% \begin{itemdecl} -@\exposid{associate-data}@(@\exposid{associate-data}@&& other) - noexcept(is_nothrow_move_constructible_v<@\exposid{wrap-sender}@>); +@\exposid{associate-data}@(pair<@\exposid{assoc-t}@, @\exposid{sender-ref}@> parts); \end{itemdecl} \begin{itemdescr} \pnum \effects -Initializes \exposid{sndr} with \tcode{std::move(other.\exposid{sndr})} and -initializes \exposid{token} with \tcode{std::move(other.\brk{}\exposid{token})} and -then calls \tcode{other.\exposid{sndr}.reset()}. +Initializes \exposid{assoc} with \tcode{std::move(parts.first)}. +If \tcode{bool(\exposid{assoc})} is \tcode{true}, +initializes \exposid{sndr} with \tcode{std::move(*parts.second)}. \end{itemdescr} \indexlibrarydtor{execution::\exposid{associate-data}}% @@ -4991,32 +4995,22 @@ \begin{itemdescr} \pnum \effects -If \tcode{\exposid{sndr}.has_value()} returns \tcode{false} then no effect; -otherwise, invokes \tcode{\exposid{sndr}.reset()} -before invoking \tcode{\exposid{token}.disassociate()}. +If \tcode{bool(\exposid{assoc})} is \tcode{true}, destroys \exposid{sndr}. \end{itemdescr} \indexlibrarymember{release}{execution::\exposid{associate-data}}% \begin{itemdecl} -optional> - release() && noexcept(is_nothrow_move_constructible_v<@\exposid{wrap-sender}@>); +pair<@\exposid{assoc-t}@, @\exposid{sender-ref}@> release() && noexcept; \end{itemdecl} \begin{itemdescr} \pnum \effects -If \tcode{\exposid{sndr}.has_value()} returns \tcode{false} then -returns an \tcode{optional} that does not contain a value; -otherwise returns an \tcode{optional} -containing a value of type \tcode{pair} -as if by: -\begin{codeblock} -return optional(pair(@\exposid{token}@, std::move(*@\exposid{sndr}@))); -\end{codeblock} - -\pnum -\ensures -\exposid{sndr} does not contain a value. +Constructs an object \tcode{u} of type \exposid{sender-ref} +that is initialized with \tcode{addressof(\exposid{sndr})} +if \tcode{bool(\exposid{assoc})} is \tcode{true} and +with \tcode{nullptr} otherwise, +then returns \tcode{pair{std::move(\exposid{assoc}), std::move(u)}}. \end{itemdescr} \pnum @@ -5066,57 +5060,53 @@ \begin{codeblock} [](Sndr&& sndr, Rcvr& rcvr) noexcept(@\seebelow@) { - auto [_, data] = std::forward(sndr); - auto dataParts = std::move(data).release(); + auto&& [_, data] = std::forward(sndr); - using scope_tkn = decltype(dataParts->first); - using wrap_sender = decltype(dataParts->second); - using op_t = connect_result_t; + using associate_data_t = remove_cvref_t; + using assoc_t = associate_data_t::assoc-t; + using sender_ref_t = associate_data_t::sender-ref; + + using op_t = connect_result_t; struct op_state { - bool @\exposid{associated}@ = false; // \expos + assoc_t @\exposid{assoc}@; // \expos union { Rcvr* @\exposid{rcvr}@; // \expos - struct { - scope_tkn @\exposid{token}@; // \expos - op_t @\exposid{op}@; // \expos - } @\exposid{assoc}@; // \expos + op_t @\exposid{op}@; // \expos }; - explicit op_state(Rcvr& r) noexcept - : @\exposid{rcvr}@(addressof(r)) {} - - explicit op_state(scope_tkn tkn, wrap_sender&& sndr, Rcvr& r) try - : @\exposid{associated}@(true), - @\exposid{assoc}@(tkn, connect(std::move(sndr), std::move(r))) { - } - catch (...) { - tkn.disassociate(); - throw; + explicit op_state(pair parts, Rcvr& r) + : assoc(std::move(parts.first)) { + if (assoc) + ::new (voidify(op)) op_t(connect(std::move(*parts.second), std::move(r))); + else + rcvr = addressof(r); } + explicit op_state(associate_data_t&& ad, Rcvr& r) + : op_state(std::move(ad).release(), r) {} + + explicit op_state(const associate_data_t& ad, Rcvr& r) + requires @\libconcept{copy_constructible}@ + : op_state(associate_data_t(ad).release(), r) {} + op_state(op_state&&) = delete; ~op_state() { - if (@\exposid{associated}@) { - @\exposid{assoc}@.@\exposid{op}@.~op_t(); - @\exposid{assoc}@.@\exposid{token}@.disassociate(); - @\exposid{assoc}@.@\exposid{token}@.~scope_tkn(); + if (@\exposid{assoc}@) { + @\exposid{op}@.~op_t(); } } void @\exposid{run}@() noexcept { // \expos - if (@\exposid{associated}@) - start(@\exposid{assoc}@.@\exposid{op}@); + if (@\exposid{assoc}@) + start(@\exposid{op}@); else set_stopped(std::move(*@\exposid{rcvr}@)); } }; - if (dataParts) - return op_state{std::move(dataParts->first), std::move(dataParts->second), rcvr}; - else - return op_state{rcvr}; + return op_state{std::forward_like(data), @\exposid{rcvr}@}; } \end{codeblock} @@ -5124,8 +5114,8 @@ The expression in the \tcode{noexcept} clause of \tcode{\exposid{impls-for}::\exposid{get-state}} is \begin{codeblock} -is_nothrow_constructible_v, Sndr> && -is_nothrow_move_constructible_v<@\exposid{wrap-sender}@> && +(is_same_v || + is_nothrow_constructible_v, Sndr>) && @\exposconcept{nothrow-callable}@ \end{codeblock} where \exposid{wrap-sender} is the type @@ -5372,9 +5362,8 @@ @\exposid{op}@(connect( write_env(@\exposid{stop-when}@(std::forward(sndr), @\exposid{ssource}@.get_token()), std::move(env)), @\exposid{receiver-t}@(this))), - @\exposid{token}@(std::move(token)), - @\exposid{associated}@(token.try_associate()) { - if (associated) + @\exposid{assoc}@(token.try_associate()) { + if (@\exposid{assoc}@) start(@\exposid{op}@); else set_stopped(@\exposid{receiver-t}@(this)); @@ -5387,12 +5376,13 @@ private: using @\exposid{alloc-t}@ = // \expos allocator_traits::template rebind_alloc<@\exposid{spawn-future-state}@>; + using @\exposid{assoc-t}@ = remove_cvref_t().try_associate())>; // \expos + @\exposid{alloc-t}@ @\exposid{alloc}@; // \expos @\exposid{ssource-t}@ @\exposid{ssource}@; // \expos @\exposid{op-t}@ @\exposid{op}@; // \expos - Token @\exposid{token}@; // \expos - bool @\exposid{associated}@; // \expos + @\exposid{assoc-t}@ @\exposid{assoc}@; // \expos void @\exposid{destroy}@() noexcept; // \expos }; @@ -5494,18 +5484,10 @@ \effects Equivalent to: \begin{codeblock} -auto token = std::move(this->@\exposid{token}@); -bool associated = this->@\exposid{associated}@; - -{ - auto alloc = std::move(this->@\exposid{alloc}@); - - allocator_traits<@\exposid{alloc-t}@>::destroy(alloc, this); - allocator_traits<@\exposid{alloc-t}@>::deallocate(alloc, this, 1); -} - -if (associated) - token.disassociate(); +auto assoc = std::move(this->@\exposid{assoc}@); +auto alloc = std::move(this->@\exposid{alloc}@); +allocator_traits<@\exposid{alloc-t}@>::destroy(alloc, this); +allocator_traits<@\exposid{alloc-t}@>::deallocate(alloc, this, 1); \end{codeblock} \end{itemdescr} @@ -5902,12 +5884,11 @@ private: using @\exposid{alloc-t}@ = // \expos allocator_traits::template rebind_alloc<@\exposid{spawn-state}@>; + using @\exposid{assoc-t}@ = remove_cvref_t().try_associate())>; // \expos @\exposid{alloc-t}@ @\exposid{alloc}@; // \expos @\exposid{op-t}@ @\exposid{op}@; // \expos - Token @\exposid{token}@; // \expos - - void @\exposid{destroy}@() noexcept; // \expos + @\exposid{assoc-t}@ @\exposid{assoc}@; // \expos }; } \end{codeblock} @@ -5921,17 +5902,14 @@ \pnum \effects Initializes -\exposid{alloc} with \tcode{alloc}, -\exposid{token} with \tcode{token}, and -\exposid{op} with: -\begin{codeblock} -connect(std::move(sndr), @\exposid{spawn-receiver}@(this)) -\end{codeblock} +\exposid{alloc} with \tcode{std::move(alloc)}, +\exposid{op} with \tcode{connect(std::move(sndr), \exposid{spawn-receiver}(this))}, and +\exposid{assoc} with \tcode{token.try_associate()}. \end{itemdescr} \indexlibrarymember{\exposid{run}}{execution::\exposid{spawn-state}}% \begin{itemdecl} -void @\exposid{run}@(); +void @\exposid{run}@() noexcept; \end{itemdecl} \begin{itemdescr} @@ -5939,10 +5917,10 @@ \effects Equivalent to: \begin{codeblock} -if (@\exposid{token}@.try_associate()) +if (@\exposid{assoc}) start(@\exposid{op}@); else - @\exposid{destroy}@(); + @\exposid{complete}@(); \end{codeblock} \end{itemdescr} @@ -5956,25 +5934,8 @@ \effects Equivalent to: \begin{codeblock} -auto token = std::move(this->@\exposid{token}@); - -@\exposid{destroy}@(); -token.disassociate(); -\end{codeblock} -\end{itemdescr} - -\indexlibrarymember{\exposid{destroy}}{execution::\exposid{spawn-state}}% -\begin{itemdecl} -void @\exposid{destroy}@() noexcept; -\end{itemdecl} - -\begin{itemdescr} -\pnum -\effects -Equivalent to: -\begin{codeblock} +auto assoc = std::move(this->@\exposid{assoc}@); auto alloc = std::move(this->@\exposid{alloc}@); - allocator_traits<@\exposid{alloc-t}@>::destroy(alloc, this); allocator_traits<@\exposid{alloc-t}@>::deallocate(alloc, this, 1); \end{codeblock} @@ -7709,10 +7670,68 @@ \rSec2[exec.scope.concepts]{Execution scope concepts} +\pnum +The \libconcept{scope_assocation} concept defines +the requirements on a type \tcode{Assoc}. +An object of type \tcode{Assoc} is \defn{engaged} +if and only if it owns an association with an async scope, +referred to as its \defnadj{associated}{scope}. + +\begin{codeblock} +namespace std::execution { + template + concept @\deflibconcept{scope_association}@ = + movable && + is_nothrow_move_constructible_v && + is_nothrow_move_assignable_v && + default_initializable && + requires(const Assoc assoc) { + { static_cast(assoc) } noexcept; + { assoc.try_associate() } -> same_as; + }; +} +\end{codeblock} + +\pnum +A type \tcode{Assoc} models \libconcept{scope_association} only if: +\begin{itemize} +\item +a default constructed object of type \tcode{Assoc} is not engaged; +\item +for an object assoc of type \tcode{Assoc}, +\tcode{static_cast(assoc)} is \tcode{true} +if and only if \tcode{assoc} is engaged; +\item +no two distinct objects of type \tcode{Assoc} own the same assocation; +\item +for an object \tcode{assoc} of type \tcode{Assoc}, +destroying \tcode{assoc} releases the assocation owned by \tcode{assoc}, if any; +\item +for an object \tcode{assoc} of type \tcode{Assoc}, +after it is used as the source operand of a move constructor, +the \tcode{assoc} is not engaged; +\item +for distinct objects \tcode{assoc1} and \tcode{assoc2} of type \tcode{Assoc}, +after evaluating \tcode{assoc1 = std::move(assoc2)}, +the association owned by \tcode{assoc1}, if any, is released and +\tcode{assoc2} is not engaged; +\item +for an object \tcode{assoc} of type \tcode{Assoc} that is engaged, +\tcode{assoc.try_associate()} either +returns an object that is not engaged or +acquires a new association with \tcode{assoc}'s associated scope and +returns an engaged object that owns that association; +\item +for an object \tcode{assoc} of type \tcode{Assoc} that is not engaged, +\tcode{assoc.try_associate()} returns an object that is not engaged. +\end{itemize} + \pnum The \libconcept{scope_token} concept defines the requirements on a type \tcode{Token} that can be used to create associations between senders and an async scope. +Every object of type \tcode{Token} is associated with an async scope +that is referred to as its \term{associated scope}. \pnum Let \placeholder{test-sender} and \placeholder{test-env} @@ -7726,8 +7745,7 @@ concept @\deflibconcept{scope_token}@ = @\libconcept{copyable}@ && requires(const Token token) { - { token.try_associate() } -> @\libconcept{same_as}@; - { token.disassociate() } noexcept -> @\libconcept{same_as}@; + { token.try_associate() } -> @\libconcept{scope_association}@; { token.wrap(declval<@\placeholder{test-sender}@>()) } -> @\libconcept{sender_in}@<@\placeholder{test-env}@>; }; } @@ -7743,7 +7761,14 @@ move construction, copy assignment, or move assignment -of objects of type \tcode{Token}; and +of objects of type \tcode{Token}; + +\item +for an object \tcode{token} of type \tcode{Token}, +\tcode{token.try_associate()} either +returns an object that is not engaged or +acquires a new association with \tcode{token}'s associated scope and +returns an engaged object that owns that association; and \item given an lvalue \tcode{token} of type (possibly const) \tcode{Token}, @@ -7856,6 +7881,9 @@ @\exposid{unused-and-closed}@, // \expos @\exposid{joined}@, // \expos }; + +template + @\exposid{struct association-t}@; // \expos \end{codeblock} \pnum @@ -7933,6 +7961,26 @@ } \end{codeblock} +\pnum +\exposid{association-t} is a class template, +specializations of which model \libconcept{scope_association} and +contain an exposition-only member scope of type \tcode{Scope*}. +For a class type \tcode{Scope} and +an object \tcode{assoc} of type \tcode{\exposid{association-t}}: +\begin{itemize} +\item +\tcode{assoc.\exposid{scope}} points to its associated scope, +\item +\tcode{assoc} is engaged when \tcode{assoc.\exposid{scope} != nullptr} is \tcode{true}, +\item +if \tcode{assoc} is engaged, +then \tcode{assoc.try_associate()} is equivalent to +\tcode{assoc.scope->try-associate()}, and +\item +the association owned by \tcode{assoc} +is released by invoking \tcode{assoc.scope->disassociate()}. +\end{itemize} + \rSec3[exec.scope.simple.counting]{Simple Counting Scope} \rSec4[exec.scope.simple.counting.general]{General} @@ -7945,6 +7993,9 @@ // \ref{exec.simple.counting.token}, token struct token; + // \ref{exec.simple.counting.assoc}, assoc + using @\exposid{assoc-t}@ = @\exposid{association-t}@; // \expos + static constexpr size_t max_associations = @\UNSP{\impldef{value of \tcode{std::execution::simple_counting_scope::max_associations}}}@; // \ref{exec.simple.counting.ctor}, constructor and destructor @@ -7961,7 +8012,7 @@ size_t @\exposid{count}@; // \expos @\exposid{scope-state-type}@ @\exposid{state}@; // \expos - bool @\exposid{try-associate}@() noexcept; // \expos + @\exposid{assoc-t}@ @\exposid{try-associate}@() noexcept; // \expos void @\exposid{disassociate}@() noexcept; // \expos template bool @\exposid{start-join-sender}@(State& state) noexcept; // \expos @@ -8062,7 +8113,7 @@ \indexlibrarymember{\exposid{try-associate}}{execution::simple_counting_scope}% \begin{itemdecl} -bool @\exposid{try-associate}@() noexcept; +@\exposid{assoc-t}@ @\exposid{try-associate}@() noexcept; \end{itemdecl} \begin{itemdescr} @@ -8083,7 +8134,10 @@ \pnum \returns -\tcode{true} if \exposid{count} was incremented, \tcode{false} otherwise. +If \exposid{count} was incremented, +an object of type \exposid{assoc-t} +that is engaged and associated with \tcode{*this}, and +\tcode{\exposid{assoc-t}()} otherwise. \end{itemdescr} \indexlibrarymember{\exposid{disassociate}}{execution::simple_counting_scope}% @@ -8142,8 +8196,7 @@ struct simple_counting_scope::token { template<@\libconcept{sender}@ Sender> Sender&& wrap(Sender&& snd) const noexcept; - bool try_associate() const noexcept; - void disassociate() const noexcept; + @\exposid{assoc-t}@ try_associate() const noexcept; private: simple_counting_scope* @\exposid{scope}@; // \expos @@ -8165,7 +8218,7 @@ \indexlibrarymember{try_associate}{execution::simple_counting_scope::token}% \begin{itemdecl} -bool try_associate() const noexcept; +@\exposid{assoc-t}@ try_associate() const noexcept; \end{itemdecl} \begin{itemdescr} @@ -8174,17 +8227,6 @@ Equivalent to: \tcode{return \exposid{scope}->\exposid{try-associate}();} \end{itemdescr} -\indexlibrarymember{disassociate}{execution::simple_counting_scope::token}% -\begin{itemdecl} -void disassociate() const noexcept; -\end{itemdecl} - -\begin{itemdescr} -\pnum -\effects -Equivalent to \tcode{\exposid{scope}->\exposid{disassociate}()}. -\end{itemdescr} - \rSec3[exec.scope.counting]{Counting Scope} \indexlibraryglobal{execution::counting_scope}% @@ -8193,11 +8235,12 @@ namespace std::execution { class counting_scope { public: + using @\exposid{assoc-t}@ = @\exposid{association-t}@; // \expos + struct token { template<@\libconcept{sender}@ Sender> @\libconcept{sender}@ auto wrap(Sender&& snd) const noexcept(@\seebelow@); - bool try_associate() const noexcept; - void disassociate() const noexcept; + @\exposid{assoc-t}@ try_associate() const noexcept; private: counting_scope* @\exposid{scope}@; // \expos @@ -8219,7 +8262,7 @@ @\exposid{scope-state-type}@ @\exposid{state}@; // \expos inplace_stop_source @\exposid{s_source}@; // \expos - bool @\exposid{try-associate}@() noexcept; // \expos + @\exposid{assoc-t}@ @\exposid{try-associate}@() noexcept; // \expos void @\exposid{disassociate}@() noexcept; // \expos template @@ -8261,6 +8304,34 @@ Calls to \tcode{request_stop} do not introduce data races. \end{itemdescr} +\indexlibrarymember{\exposid{try-associate}}{execution::counting_scope}% +\begin{itemdecl} +@\exposid{assoc-t}@ @\exposid{try-associate}@() noexcept; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\effects +If \exposid{count} is equal to \tcode{max_associations}, +then no effects. Otherwise, if \exposid{state} is +\begin{itemize} +\item +\exposid{unused}, then increments \exposid{count} and +changes \exposid{state} to \exposid{open}; +\item +\exposid{open} or \exposid{open-and-joining}, then increments \exposid{count}; +\item +otherwise, no effects. +\end{itemize} + +\pnum +\returns +If \exposid{count} was incremented, +an object of type \exposid{assoc-t} +that is engaged and associated with \tcode{*this}, and +\tcode{\exposid{assoc-t}()} otherwise. +\end{itemdescr} + \indexlibrarymember{wrap}{execution::counting_scope::token}% \begin{itemdecl} template<@\libconcept{sender}@ Sender> @@ -8277,6 +8348,17 @@ \end{codeblock} \end{itemdescr} +\indexlibrarymember{wrap}{execution::counting_scope::token}% +\begin{itemdecl} +@\exposid{assoc-t}@ counting_scope::token::try_associate() const noexcept; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\effects +Equivalent to: \tcode{return \exposid{scope}->\exposid{try-associate}();} +\end{itemdescr} + \rSec1[exec.par.scheduler]{Parallel scheduler} \begin{codeblock} From bf5e1f968fbacaa88eddcb48e1f234b74491b380 Mon Sep 17 00:00:00 2001 From: Jens Maurer Date: Sun, 16 Nov 2025 00:49:58 +0100 Subject: [PATCH 2/4] fixup: overfull hbox --- source/exec.tex | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/source/exec.tex b/source/exec.tex index 3c067c607e..643d81d7ad 100644 --- a/source/exec.tex +++ b/source/exec.tex @@ -5010,7 +5010,7 @@ that is initialized with \tcode{addressof(\exposid{sndr})} if \tcode{bool(\exposid{assoc})} is \tcode{true} and with \tcode{nullptr} otherwise, -then returns \tcode{pair{std::move(\exposid{assoc}), std::move(u)}}. +then returns \tcode{pair\{std::move(\exposid{assoc}), std::\brk move(u)\}}. \end{itemdescr} \pnum @@ -5884,7 +5884,8 @@ private: using @\exposid{alloc-t}@ = // \expos allocator_traits::template rebind_alloc<@\exposid{spawn-state}@>; - using @\exposid{assoc-t}@ = remove_cvref_t().try_associate())>; // \expos + using @\exposid{assoc-t}@ = // \expos + remove_cvref_t().try_associate())>; @\exposid{alloc-t}@ @\exposid{alloc}@; // \expos @\exposid{op-t}@ @\exposid{op}@; // \expos @@ -5903,7 +5904,7 @@ \effects Initializes \exposid{alloc} with \tcode{std::move(alloc)}, -\exposid{op} with \tcode{connect(std::move(sndr), \exposid{spawn-receiver}(this))}, and +\exposid{op} with \tcode{connect(std::move(sndr), \exposid{spawn-re\-ceiv\-er}(this))}, and \exposid{assoc} with \tcode{token.try_associate()}. \end{itemdescr} @@ -7671,7 +7672,7 @@ \rSec2[exec.scope.concepts]{Execution scope concepts} \pnum -The \libconcept{scope_assocation} concept defines +The \libconcept{scope_association} concept defines the requirements on a type \tcode{Assoc}. An object of type \tcode{Assoc} is \defn{engaged} if and only if it owns an association with an async scope, @@ -7681,13 +7682,13 @@ namespace std::execution { template concept @\deflibconcept{scope_association}@ = - movable && + @\libconcept{movable}@ && is_nothrow_move_constructible_v && is_nothrow_move_assignable_v && - default_initializable && + @\libconcept{default_initializable}@ && requires(const Assoc assoc) { { static_cast(assoc) } noexcept; - { assoc.try_associate() } -> same_as; + { assoc.try_associate() } -> @\libconcept{same_as}@; }; } \end{codeblock} @@ -7993,7 +7994,6 @@ // \ref{exec.simple.counting.token}, token struct token; - // \ref{exec.simple.counting.assoc}, assoc using @\exposid{assoc-t}@ = @\exposid{association-t}@; // \expos static constexpr size_t max_associations = @\UNSP{\impldef{value of \tcode{std::execution::simple_counting_scope::max_associations}}}@; From 49f079716a18b75d1a11b6470c2f0c0ccb2dc32a Mon Sep 17 00:00:00 2001 From: Jens Maurer Date: Sun, 16 Nov 2025 15:20:56 +0100 Subject: [PATCH 3/4] fixup: markup issues --- source/exec.tex | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/source/exec.tex b/source/exec.tex index 643d81d7ad..291f07099c 100644 --- a/source/exec.tex +++ b/source/exec.tex @@ -5063,8 +5063,8 @@ auto&& [_, data] = std::forward(sndr); using associate_data_t = remove_cvref_t; - using assoc_t = associate_data_t::assoc-t; - using sender_ref_t = associate_data_t::sender-ref; + using assoc_t = associate_data_t::@\exposid{assoc-t}@; + using sender_ref_t = associate_data_t::@\exposid{sender-ref}@; using op_t = connect_result_t; @@ -5078,7 +5078,7 @@ explicit op_state(pair parts, Rcvr& r) : assoc(std::move(parts.first)) { if (assoc) - ::new (voidify(op)) op_t(connect(std::move(*parts.second), std::move(r))); + ::new (@\placeholdernc{voidify}@(op)) op_t(connect(std::move(*parts.second), std::move(r))); else rcvr = addressof(r); } @@ -5879,7 +5879,7 @@ @\exposid{spawn-state}@(Alloc alloc, Sender&& sndr, Token token); // \expos void @\exposid{complete}@() noexcept override; // \expos - void @\exposid{run}@(); // \expos + void @\exposid{run}@() noexcept; // \expos private: using @\exposid{alloc-t}@ = // \expos @@ -5918,7 +5918,7 @@ \effects Equivalent to: \begin{codeblock} -if (@\exposid{assoc}) +if (@\exposid{assoc}@) start(@\exposid{op}@); else @\exposid{complete}@(); @@ -7699,7 +7699,7 @@ \item a default constructed object of type \tcode{Assoc} is not engaged; \item -for an object assoc of type \tcode{Assoc}, +for an object \exposid{assoc} of type \tcode{Assoc}, \tcode{static_cast(assoc)} is \tcode{true} if and only if \tcode{assoc} is engaged; \item @@ -7746,7 +7746,7 @@ concept @\deflibconcept{scope_token}@ = @\libconcept{copyable}@ && requires(const Token token) { - { token.try_associate() } -> @\libconcept{scope_association}@; + { token.try_associate() } -> @\libconcept{scope_association}@; { token.wrap(declval<@\placeholder{test-sender}@>()) } -> @\libconcept{sender_in}@<@\placeholder{test-env}@>; }; } @@ -7884,7 +7884,7 @@ }; template - @\exposid{struct association-t}@; // \expos + struct @\exposid{association-t}@; // \expos \end{codeblock} \pnum @@ -7975,11 +7975,11 @@ \tcode{assoc} is engaged when \tcode{assoc.\exposid{scope} != nullptr} is \tcode{true}, \item if \tcode{assoc} is engaged, -then \tcode{assoc.try_associate()} is equivalent to +then \tcode{assoc.\exposid{try_associate}()} is equivalent to \tcode{assoc.scope->try-associate()}, and \item the association owned by \tcode{assoc} -is released by invoking \tcode{assoc.scope->disassociate()}. +is released by invoking \tcode{assoc.scope->\exposid{disassociate}()}. \end{itemize} \rSec3[exec.scope.simple.counting]{Simple Counting Scope} @@ -8313,7 +8313,8 @@ \pnum \effects If \exposid{count} is equal to \tcode{max_associations}, -then no effects. Otherwise, if \exposid{state} is +then no effects. +Otherwise, if \exposid{state} is \begin{itemize} \item \exposid{unused}, then increments \exposid{count} and From 6742202c64493338713677f325dd8b7b6f421c10 Mon Sep 17 00:00:00 2001 From: Jens Maurer Date: Sun, 16 Nov 2025 15:21:40 +0100 Subject: [PATCH 4/4] [exec.associate] Fix missing template argument --- source/exec.tex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/exec.tex b/source/exec.tex index 291f07099c..0b2d8c992f 100644 --- a/source/exec.tex +++ b/source/exec.tex @@ -5114,7 +5114,7 @@ The expression in the \tcode{noexcept} clause of \tcode{\exposid{impls-for}::\exposid{get-state}} is \begin{codeblock} -(is_same_v || +(is_same_v> || is_nothrow_constructible_v, Sndr>) && @\exposconcept{nothrow-callable}@ \end{codeblock}