From 5ef734420ec45385d4d8fe0bcaa2e6bfb0a3a99b Mon Sep 17 00:00:00 2001 From: Jens Maurer Date: Sun, 16 Feb 2025 16:05:01 +0100 Subject: [PATCH 1/4] P3475R2 Defang and deprecate memory_order::consume --- source/basic.tex | 113 ++------------------------------------- source/compatibility.tex | 4 +- source/declarations.tex | 83 ++-------------------------- source/future.tex | 24 +++++++++ source/preprocessor.tex | 1 - source/threads.tex | 87 +++++++----------------------- source/xrefdelta.tex | 3 ++ 7 files changed, 56 insertions(+), 259 deletions(-) diff --git a/source/basic.tex b/source/basic.tex index 3f55c2294b..cbf4e6f66d 100644 --- a/source/basic.tex +++ b/source/basic.tex @@ -6306,122 +6306,17 @@ the value written'' by the last mutex release. \end{note} -\pnum -An evaluation $A$ \defn{carries a dependency} to an evaluation $B$ if -\begin{itemize} -\item -the value of $A$ is used as an operand of $B$, unless: -\begin{itemize} -\item -$B$ is an invocation of any specialization of -\tcode{std::kill_dependency}\iref{atomics.order}, or -\item -$A$ is the left operand of a built-in logical \logop{and} (\tcode{\&\&}, -see~\ref{expr.log.and}) or logical \logop{or} (\tcode{||}, see~\ref{expr.log.or}) -operator, or -\item -$A$ is the left operand of a conditional (\tcode{?:}, see~\ref{expr.cond}) -operator, or -\item -$A$ is the left operand of the built-in comma (\tcode{,}) -operator\iref{expr.comma}; \end{itemize} or -\item -$A$ writes a scalar object or bit-field $M$, $B$ reads the value -written by $A$ from $M$, and $A$ is sequenced before $B$, or -\item -for some evaluation $X$, $A$ carries a dependency to $X$, and -$X$ carries a dependency to $B$. -\end{itemize} -\begin{note} -``Carries a dependency to'' is a subset of ``is sequenced before'', -and is similarly strictly intra-thread. -\end{note} - -\pnum -An evaluation $A$ is \defn{dependency-ordered before} an evaluation -$B$ if -\begin{itemize} -\item -$A$ performs a release operation on an atomic object $M$, and, in -another thread, $B$ performs a consume operation on $M$ and reads -the value written by $A$, or - -\item -for some evaluation $X$, $A$ is dependency-ordered before $X$ and -$X$ carries a dependency to $B$. - -\end{itemize} -\begin{note} -The relation ``is dependency-ordered before'' is analogous to -``synchronizes with'', but uses release/consume in place of release/acquire. -\end{note} - -\pnum -An evaluation $A$ \defn{inter-thread happens before} an evaluation $B$ -if -\begin{itemize} -\item - $A$ synchronizes with $B$, or -\item - $A$ is dependency-ordered before $B$, or -\item - for some evaluation $X$ - \begin{itemize} - \item - $A$ synchronizes with $X$ and $X$ - is sequenced before $B$, or - \item - $A$ is sequenced before $X$ and $X$ - inter-thread happens before $B$, or - \item - $A$ inter-thread happens before $X$ and $X$ - inter-thread happens before $B$. - \end{itemize} -\end{itemize} -\begin{note} -The ``inter-thread happens before'' relation describes arbitrary -concatenations of ``sequenced before'', ``synchronizes with'' and -``dependency-ordered before'' relationships, with two exceptions. The first -exception is that a concatenation never ends with -``dependency-ordered before'' followed by ``sequenced before''. The reason for -this limitation is that a consume operation participating in a -``dependency-ordered before'' relationship provides ordering only with respect -to operations to which this consume operation actually carries a dependency. The -reason that this limitation applies only to the end of such a concatenation is -that any subsequent release operation will provide the required ordering for a -prior consume operation. The second exception is that a concatenation never -consist entirely of ``sequenced before''. The reasons for this -limitation are (1) to permit ``inter-thread happens before'' to be transitively -closed and (2) the ``happens before'' relation, defined below, provides for -relationships consisting entirely of ``sequenced before''. -\end{note} - \pnum An evaluation $A$ \defn{happens before} an evaluation $B$ -(or, equivalently, $B$ \defn{happens after} $A$) if -\begin{itemize} -\item $A$ is sequenced before $B$, or -\item $A$ inter-thread happens before $B$. -\end{itemize} -The implementation shall ensure that no program execution demonstrates a cycle -in the ``happens before'' relation. -\begin{note} -This cycle would otherwise be -possible only through the use of consume operations. -\end{note} - -\pnum -An evaluation $A$ \defn{simply happens before} an evaluation $B$ +(or, equivalently, $B$ happens after $A$) if either \begin{itemize} \item $A$ is sequenced before $B$, or \item $A$ synchronizes with $B$, or -\item $A$ simply happens before $X$ and -$X$ simply happens before $B$. +\item $A$ happens before $X$ and $X$ happens before $B$. \end{itemize} \begin{note} -In the absence of consume operations, -the happens before and simply happens before relations are identical. +An evaluation cannot happen before itself. \end{note} \pnum @@ -6443,7 +6338,7 @@ \begin{note} Informally, if $A$ strongly happens before $B$, then $A$ appears to be evaluated before $B$ -in all contexts. Strongly happens before excludes consume operations. +in all contexts. \end{note} \pnum diff --git a/source/compatibility.tex b/source/compatibility.tex index 40e123358b..393a551890 100644 --- a/source/compatibility.tex +++ b/source/compatibility.tex @@ -2368,8 +2368,8 @@ Avoid hard to diagnose or non-portable constructs. \effect Names of attribute identifiers may not be used as macro names. Valid \CppIII{} -code that defines \tcode{override}, \tcode{final}, -\tcode{carries_dependency}, or \tcode{noreturn} as macros is invalid in this +code that defines \tcode{override}, \tcode{final}, or +\tcode{noreturn} as macros is invalid in this revision of \Cpp{}. \rSec2[diff.cpp03.language.support]{\ref{support}: diff --git a/source/declarations.tex b/source/declarations.tex index a0c377be2d..782e118093 100644 --- a/source/declarations.tex +++ b/source/declarations.tex @@ -9140,86 +9140,6 @@ \end{codeblock} \end{example} -\rSec2[dcl.attr.depend]{Carries dependency attribute}% -\indextext{attribute!carries dependency} - -\pnum -The \grammarterm{attribute-token} \tcode{carries_dependency} specifies -dependency propagation into and out of functions. -No -\grammarterm{attribute-argument-clause} shall be present. The attribute may be -applied to a parameter of a function or lambda, in -which case it specifies that the initialization of the parameter carries a -dependency to\iref{intro.multithread} each lvalue-to-rvalue -conversion\iref{conv.lval} of that object. The attribute may also be applied -to a function or a lambda call operator, in which case it -specifies that the return value, if any, carries a dependency to the evaluation -of the function call expression. - -\pnum -The first declaration of a function shall specify the \tcode{carries_dependency} attribute for its -\grammarterm{declarator-id} if any declaration of the function specifies the -\tcode{carries_dependency} attribute. Furthermore, the first declaration of a function shall specify -the \tcode{carries_dependency} attribute for a parameter if any declaration of that function -specifies the \tcode{carries_dependency} attribute for that parameter. If a function or one of its -parameters is declared with the \tcode{carries_dependency} attribute in its first declaration in one -translation unit and the same function or one of its parameters is declared without the -\tcode{carries_dependency} attribute in its first declaration in another translation unit, the -program is ill-formed, no diagnostic required. - -\pnum -\begin{note} -The \tcode{carries_dependency} attribute does not change the meaning of the -program, but might result in generation of more efficient code. -\end{note} - -\pnum -\begin{example} -\begin{codeblock} -/* Translation unit A. */ - -struct foo { int* a; int* b; }; -std::atomic foo_head[10]; -int foo_array[10][10]; - -[[carries_dependency]] struct foo* f(int i) { - return foo_head[i].load(memory_order::consume); -} - -int g(int* x, int* y [[carries_dependency]]) { - return kill_dependency(foo_array[*x][*y]); -} - -/* Translation unit B. */ - -[[carries_dependency]] struct foo* f(int i); -int g(int* x, int* y [[carries_dependency]]); - -int c = 3; - -void h(int i) { - struct foo* p; - - p = f(i); - do_something_with(g(&c, p->a)); - do_something_with(g(p->a, &c)); -} -\end{codeblock} - -The \tcode{carries_dependency} attribute on function \tcode{f} means that the -return value carries a dependency out of \tcode{f}, so that the implementation -need not constrain ordering upon return from \tcode{f}. Implementations of -\tcode{f} and its caller may choose to preserve dependencies instead of emitting -hardware memory ordering instructions (a.k.a.\ fences). -Function \tcode{g}'s second parameter has a \tcode{carries_dependency} attribute, -but its first parameter does not. Therefore, function \tcode{h}'s first call to -\tcode{g} carries a dependency into \tcode{g}, but its second call does not. The -implementation might need to insert a fence prior to the second call to -\tcode{g}. -\end{example} -\indextext{attribute|)}% -\indextext{declaration|)} - \rSec2[dcl.attr.deprecated]{Deprecated attribute}% \indextext{attribute!deprecated} @@ -9695,3 +9615,6 @@ could have the same address as \tcode{buckets} if their respective types are all empty. \end{example} + +\indextext{attribute|)}% +\indextext{declaration|)} diff --git a/source/future.tex b/source/future.tex index 993bb7d34a..e9350626a7 100644 --- a/source/future.tex +++ b/source/future.tex @@ -854,6 +854,9 @@ void atomic_init(volatile atomic*, typename atomic::value_type) noexcept; template void atomic_init(atomic*, typename atomic::value_type) noexcept; + template + constexpr T kill_dependency(T y) noexcept; // freestanding + inline constexpr memory_order memory_order_consume = memory_order::consume; // freestanding #define @\libmacro{ATOMIC_VAR_INIT}@(value) @\seebelow@ } @@ -924,3 +927,24 @@ \end{codeblock} \end{example} \end{itemdescr} + +\rSec2[depr.atomics.order]{\tcode{memory_order::consume}} + +\indexlibrarymember{consume}{memory_order}% +\pnum +The memory_order enumeration contains an additional enumerator: +\begin{codeblock} +consume = 1 +\end{codeblock} +The \tcode{memory_order::consume} enumerator is allowed wherever +\tcode{memory_order::acquire} is allowed, and it has the same meaning. + +\begin{itemdecl} +template constexpr T kill_dependency(T y) noexcept; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\returns +\tcode{y}. +\end{itemdescr} diff --git a/source/preprocessor.tex b/source/preprocessor.tex index df3db03e80..198ccad793 100644 --- a/source/preprocessor.tex +++ b/source/preprocessor.tex @@ -400,7 +400,6 @@ \topline \lhdr{Attribute} & \rhdr{Value} \\ \rowsep \tcode{assume} & \tcode{202207L} \\ -\tcode{carries_dependency} & \tcode{200809L} \\ \tcode{deprecated} & \tcode{201309L} \\ \tcode{fallthrough} & \tcode{201603L} \\ \tcode{likely} & \tcode{201803L} \\ diff --git a/source/threads.tex b/source/threads.tex index 540575ade9..614bae78ef 100644 --- a/source/threads.tex +++ b/source/threads.tex @@ -2394,14 +2394,10 @@ // \ref{atomics.order}, order and consistency enum class memory_order : @\unspecnc@; // freestanding inline constexpr memory_order memory_order_relaxed = memory_order::relaxed; // freestanding - inline constexpr memory_order memory_order_consume = memory_order::consume; // freestanding inline constexpr memory_order memory_order_acquire = memory_order::acquire; // freestanding inline constexpr memory_order memory_order_release = memory_order::release; // freestanding inline constexpr memory_order memory_order_acq_rel = memory_order::acq_rel; // freestanding inline constexpr memory_order memory_order_seq_cst = memory_order::seq_cst; // freestanding - - template - constexpr T kill_dependency(T y) noexcept; // freestanding } // \ref{atomics.lockfree}, lock-free property @@ -2792,13 +2788,11 @@ \rSec2[atomics.order]{Order and consistency} \indexlibraryglobal{memory_order}% \indexlibrarymember{relaxed}{memory_order}% -\indexlibrarymember{consume}{memory_order}% \indexlibrarymember{acquire}{memory_order}% \indexlibrarymember{release}{memory_order}% \indexlibrarymember{acq_rel}{memory_order}% \indexlibrarymember{seq_cst}{memory_order}% \indexlibraryglobal{memory_order_relaxed}% -\indexlibraryglobal{memory_order_consume}% \indexlibraryglobal{memory_order_acquire}% \indexlibraryglobal{memory_order_release}% \indexlibraryglobal{memory_order_acq_rel}% @@ -2807,7 +2801,7 @@ \begin{codeblock} namespace std { enum class memory_order : @\unspec@ { - relaxed, consume, acquire, release, acq_rel, seq_cst + relaxed = 0, acquire = 2, release = 3, acq_rel = 4, seq_cst = 5 }; } \end{codeblock} @@ -2825,15 +2819,6 @@ \tcode{memory_order::seq_cst}: a store operation performs a release operation on the affected memory location. -\item \tcode{memory_order::consume}: a load operation performs a consume operation on the -affected memory location. -\begin{note} -Prefer \tcode{memory_order::acquire}, which provides stronger guarantees -than \tcode{memory_order::consume}. Implementations have found it infeasible -to provide performance better than that of \tcode{memory_order::acquire}. -Specification revisions are under consideration. -\end{note} - \item \tcode{memory_order::acquire}, \tcode{memory_order::acq_rel}, and \tcode{memory_order::seq_cst}: a load operation performs an acquire operation on the affected memory location. @@ -2990,24 +2975,6 @@ and atomic loads should observe atomic stores, within a reasonable amount of time. -\indexlibraryglobal{kill_dependency}% -\begin{itemdecl} -template - constexpr T kill_dependency(T y) noexcept; -\end{itemdecl} - -\begin{itemdescr} -\pnum -\effects -The argument does not carry a dependency to the return -value\iref{intro.multithread}. - -\pnum -\returns -\tcode{y}. -\end{itemdescr} - - \rSec2[atomics.lockfree]{Lock-free property} \indeximpldef{values of various \tcode{ATOMIC_..._LOCK_FREE} macros} @@ -3357,8 +3324,7 @@ \expects \tcode{order} is \tcode{memory_order::relaxed}, -\tcode{memory_order::consume}, -\tcode{memory_order::ac-\linebreak quire}, or +\tcode{memory_order::acquire}, or \tcode{memory_order::seq_cst}. \pnum @@ -3442,9 +3408,8 @@ \expects \tcode{failure} is \tcode{memory_order::relaxed}, -\tcode{memory_order::consume}, -\tcode{memory_order::ac\-quire}, or -\tcode{memory_order::seq_cst}. +\tcode{memory_order::acquire}, or +\tcode{memory_order::\linebreak seq_cst}. \pnum \effects @@ -3509,8 +3474,7 @@ \expects \tcode{order} is \tcode{memory_order::relaxed}, -\tcode{memory_order::consume}, -\tcode{memory_order::ac- \linebreak quire}, or +\tcode{memory_order::acquire}, or \tcode{memory_order::seq_cst}. \pnum @@ -4349,8 +4313,7 @@ \expects \tcode{order} is \tcode{memory_order::relaxed}, -\tcode{memory_order::consume}, -\tcode{memory_order::ac-\linebreak quire}, or +\tcode{memory_order::acquire}, or \tcode{memory_order::seq_cst}. \pnum @@ -4453,9 +4416,8 @@ \expects \tcode{failure} is \tcode{memory_order::relaxed}, -\tcode{memory_order::consume}, -\tcode{memory_order::ac\-quire}, or -\tcode{memory_order::seq_cst}. +\tcode{memory_order::acquire}, or +\tcode{memory_order::\linebreak seq_cst}. \pnum \effects @@ -4608,8 +4570,7 @@ \expects \tcode{order} is \tcode{memory_order::relaxed}, -\tcode{memory_order::consume}, -\tcode{memory_order::ac-\linebreak quire}, or +\tcode{memory_order::acquire}, or \tcode{memory_order::seq_cst}. \pnum @@ -5559,8 +5520,7 @@ \expects \tcode{order} is \tcode{memory_order::relaxed}, -\tcode{memory_order::consume}, -\tcode{memory_order::ac-\linebreak quire}, or +\tcode{memory_order::acquire}, or \tcode{memory_order::seq_cst}. \pnum @@ -5615,9 +5575,8 @@ \expects \tcode{failure} is \tcode{memory_order::relaxed}, -\tcode{memory_order::consume}, -\tcode{memory_order::ac\-quire}, or -\tcode{memory_order::seq_cst}. +\tcode{memory_order::acquire}, or +\tcode{memory_order::\linebreak seq_cst}. \pnum \effects @@ -5703,8 +5662,7 @@ \expects \tcode{order} is \tcode{memory_order::relaxed}, -\tcode{memory_order::consume}, -\tcode{memory_order::ac-\linebreak quire}, or +\tcode{memory_order::acquire}, or \tcode{memory_order::seq_cst}. \pnum @@ -5876,8 +5834,7 @@ \expects \tcode{order} is \tcode{memory_order::relaxed}, -\tcode{memory_order::consume}, -\tcode{memory_order::ac-\linebreak quire}, or +\tcode{memory_order::acquire}, or \tcode{memory_order::seq_cst}. \pnum @@ -5931,9 +5888,8 @@ \expects \tcode{failure} is \tcode{memory_order::relaxed}, -\tcode{memory_order::consume}, -\tcode{memory_order::ac\-quire}, or -\tcode{memory_order::seq_cst}. +\tcode{memory_order::acquire}, or +\tcode{memory_order::\linebreak seq_cst}. \pnum \effects @@ -6019,8 +5975,7 @@ \expects \tcode{order} is \tcode{memory_order::relaxed}, -\tcode{memory_order::consume}, -\tcode{memory_order::ac-\linebreak quire}, or +\tcode{memory_order::acquire}, or \tcode{memory_order::seq_cst}. \pnum @@ -6167,8 +6122,7 @@ \expects \tcode{order} is \tcode{memory_order::relaxed}, -\tcode{memory_order::consume}, -\tcode{memory_order::ac-\linebreak quire}, or +\tcode{memory_order::acquire}, or \tcode{memory_order::seq_cst}. \pnum @@ -6256,8 +6210,7 @@ \expects \tcode{order} is \tcode{memory_order::relaxed}, -\tcode{memory_order::consume}, -\tcode{memory_order::ac-\linebreak quire}, or +\tcode{memory_order::acquire}, or \tcode{memory_order::seq_cst}. \pnum @@ -6377,7 +6330,7 @@ \begin{itemize} \item has no effects, if \tcode{order == memory_order::relaxed}; -\item is an acquire fence, if \tcode{order == memory_order::acquire} or \tcode{order == memory_order::consume}; +\item is an acquire fence, if \tcode{order == memory_order::acquire}; \item is a release fence, if \tcode{order == memory_order::release}; diff --git a/source/xrefdelta.tex b/source/xrefdelta.tex index 8a55489e6a..45f98073b4 100644 --- a/source/xrefdelta.tex +++ b/source/xrefdelta.tex @@ -499,3 +499,6 @@ \removedxref{res.on.required} \deprxref{fs.path.factory} \movedxref{operators}{depr.relops} + +% P3475R2 Defang and deprecate memory_order::consume +\removedxref{dcl.attr.depend} From 1cb51f56909eda8dd5c49200c4baa676fead1333 Mon Sep 17 00:00:00 2001 From: Jens Maurer Date: Thu, 27 Feb 2025 00:28:55 +0100 Subject: [PATCH 2/4] fixup missed edit --- source/basic.tex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/basic.tex b/source/basic.tex index cbf4e6f66d..669a89e143 100644 --- a/source/basic.tex +++ b/source/basic.tex @@ -6329,7 +6329,7 @@ sequentially consistent atomic operations\iref{atomics.order}, or \item there are evaluations $B$ and $C$ such that $A$ is sequenced before $B$, -$B$ simply happens before $C$, and +$B$ happens before $C$, and $C$ is sequenced before $D$, or \item there is an evaluation $B$ such that $A$ strongly happens before $B$, and From 768a446bbfcc457fd291dd6c0b94df5824e3cff0 Mon Sep 17 00:00:00 2001 From: Jens Maurer Date: Sun, 16 Feb 2025 16:07:58 +0100 Subject: [PATCH 3/4] [intro.races] Remove mention of 'consume operation' --- source/basic.tex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/basic.tex b/source/basic.tex index 669a89e143..f357c89ea7 100644 --- a/source/basic.tex +++ b/source/basic.tex @@ -6250,7 +6250,7 @@ operations on mutexes\iref{thread} that are specially identified as synchronization operations. These operations play a special role in making assignments in one thread visible to another. A synchronization operation on one -or more memory locations is either a consume operation, an acquire operation, a +or more memory locations is either an acquire operation, a release operation, or both an acquire and release operation. A synchronization operation without an associated memory location is a fence and can be either an acquire fence, a release fence, or both an acquire and release fence. In From 8f7917e0db831862aaa1823c7bb41768d6f93ca8 Mon Sep 17 00:00:00 2001 From: Jens Maurer Date: Thu, 27 Feb 2025 00:28:15 +0100 Subject: [PATCH 4/4] [intro.races] Rephrase questionable phrasing in note --- source/basic.tex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/basic.tex b/source/basic.tex index f357c89ea7..8ecb612f04 100644 --- a/source/basic.tex +++ b/source/basic.tex @@ -6316,7 +6316,7 @@ \item $A$ happens before $X$ and $X$ happens before $B$. \end{itemize} \begin{note} -An evaluation cannot happen before itself. +An evaluation does not happen before itself. \end{note} \pnum