From 333e544370081eb04bc4e91baf2d54fb79bd7ec7 Mon Sep 17 00:00:00 2001 From: Jens Maurer Date: Thu, 26 Jun 2025 19:44:45 +0200 Subject: [PATCH 1/2] P3111R8 Atomic Reduction Operations --- source/algorithms.tex | 3 +- source/basic.tex | 10 +- source/support.tex | 1 + source/threads.tex | 597 +++++++++++++++++++++++++++++++++++++++++- 4 files changed, 604 insertions(+), 7 deletions(-) diff --git a/source/algorithms.tex b/source/algorithms.tex index 3a138a9f43..59a656ec5e 100644 --- a/source/algorithms.tex +++ b/source/algorithms.tex @@ -315,7 +315,8 @@ A standard library function is \defn{vectorization-unsafe} if it is specified to synchronize with another function invocation, or another function invocation is specified to synchronize with it, -and if it is not a memory allocation or deallocation function. +and if it is not a memory allocation or deallocation function +or lock-free atomic modify-write operation\iref{atomics.order}. \begin{note} Implementations must ensure that internal synchronization inside standard library functions does not prevent forward progress diff --git a/source/basic.tex b/source/basic.tex index 8178129463..c5fd8f9878 100644 --- a/source/basic.tex +++ b/source/basic.tex @@ -6618,7 +6618,8 @@ \item invoke the function \tcode{std::this_thread::yield}\iref{thread.thread.this}, \item make a call to a library I/O function, \item perform an access through a volatile glvalue, -\item perform a synchronization operation or an atomic operation, or +\item perform an atomic or synchronization operation +other than an atomic modify-write operation\iref{atomics.order}, or \item continue execution of a trivial infinite loop\iref{stmt.iter.general}. \end{itemize} \begin{note} @@ -6675,9 +6676,10 @@ an \defn{execution step}: \begin{itemize} \item termination of the thread of execution, -\item performing an access through a volatile glvalue, or -\item completion of a call to a library I/O function, a - synchronization operation, or an atomic operation. +\item performing an access through a volatile glvalue, +\item completion of a call to a library I/O function, or +\item completion of an atomic or synchronization operation +other than an atomic modify-write operation\iref{atomics.order}. \end{itemize} \pnum diff --git a/source/support.tex b/source/support.tex index 95d674aafb..d4d57fab5e 100644 --- a/source/support.tex +++ b/source/support.tex @@ -579,6 +579,7 @@ #define @\defnlibxname{cpp_lib_atomic_is_always_lock_free}@ 201603L // freestanding, also in \libheader{atomic} #define @\defnlibxname{cpp_lib_atomic_lock_free_type_aliases}@ 201907L // also in \libheader{atomic} #define @\defnlibxname{cpp_lib_atomic_min_max}@ 202403L // freestanding, also in \libheader{atomic} +#define @\defnlibxname{cpp_lib_atomic_reductions}@ 202506L // freestanding, also in \libheader{atomic} #define @\defnlibxname{cpp_lib_atomic_ref}@ 202411L // freestanding, also in \libheader{atomic} #define @\defnlibxname{cpp_lib_atomic_shared_ptr}@ 201711L // also in \libheader{memory} #define @\defnlibxname{cpp_lib_atomic_value_initialization}@ 201911L // freestanding, also in \libheader{atomic}, \libheader{memory} diff --git a/source/threads.tex b/source/threads.tex index 313a925e70..c85127254a 100644 --- a/source/threads.tex +++ b/source/threads.tex @@ -2600,6 +2600,99 @@ constexpr T atomic_fetch_min_explicit(atomic*, // freestanding typename atomic::value_type, memory_order) noexcept; + + template + void atomic_store_add(volatile atomic*, // freestanding + typename atomic::difference_type) noexcept; + template + constexpr void atomic_store_add(atomic*, // freestanding + typename atomic::difference_type) noexcept; + template + void atomic_store_add_explicit(volatile atomic*, // freestanding + typename atomic::difference_type, memory_order) noexcept; + template + constexpr void atomic_store_add_explicit(atomic*, // freestanding + typename atomic::difference_type, + memory_order) noexcept; + template + void atomic_store_sub(volatile atomic*, // freestanding + typename atomic::difference_type) noexcept; + template + constexpr void atomic_store_sub(atomic*, // freestanding + typename atomic::difference_type) noexcept; + template + void atomic_store_sub_explicit(volatile atomic*, // freestanding + typename atomic::difference_type, memory_order) noexcept; + template + constexpr void atomic_store_sub_explicit(atomic*, // freestanding + typename atomic::difference_type, + memory_order) noexcept; + template + void atomic_store_and(volatile atomic*, // freestanding + typename atomic::value_type) noexcept; + template + constexpr void atomic_store_and(atomic*, // freestanding + typename atomic::value_type) noexcept; + template + void atomic_store_and_explicit(volatile atomic*, // freestanding + typename atomic::value_type, memory_order) noexcept; + template + constexpr void atomic_store_and_explicit(atomic*, // freestanding + typename atomic::value_type, + memory_order) noexcept; + template + void atomic_store_or(volatile atomic*, // freestanding + typename atomic::value_type) noexcept; + template + constexpr void atomic_store_or(atomic*, // freestanding + typename atomic::value_type) noexcept; + template + void atomic_store_or_explicit(volatile atomic*, // freestanding + typename atomic::value_type, memory_order) noexcept; + template + constexpr void atomic_store_or_explicit(atomic*, // freestanding + typename atomic::value_type, + memory_order) noexcept; + template + void atomic_store_xor(volatile atomic*, // freestanding + typename atomic::value_type) noexcept; + template + constexpr void atomic_store_xor(atomic*, // freestanding + typename atomic::value_type) noexcept; + template + void atomic_store_xor_explicit(volatile atomic*, // freestanding + typename atomic::value_type, memory_order) noexcept; + template + constexpr void atomic_store_xor_explicit(atomic*, // freestanding + typename atomic::value_type, + memory_order) noexcept; + template + void atomic_store_max(volatile atomic*, // freestanding + typename atomic::value_type) noexcept; + template + constexpr void atomic_store_max(atomic*, // freestanding + typename atomic::value_type) noexcept; + template + void atomic_store_max_explicit(volatile atomic*, // freestanding + typename atomic::value_type, memory_order) noexcept; + template + constexpr void atomic_store_max_explicit(atomic*, // freestanding + typename atomic::value_type, + memory_order) noexcept; + template + void atomic_store_min(volatile atomic*, // freestanding + typename atomic::value_type) noexcept; + template + constexpr void atomic_store_min(atomic*, // freestanding + typename atomic::value_type) noexcept; + template + void atomic_store_min_explicit(volatile atomic*, // freestanding + typename atomic::value_type, memory_order) noexcept; + template + constexpr void atomic_store_min_explicit(atomic*, // freestanding + typename atomic::value_type, + memory_order) noexcept; + template void atomic_wait(const volatile atomic*, // freestanding typename atomic::value_type) noexcept; @@ -2972,6 +3065,18 @@ (in the modification order) written before the write associated with the read-modify-write operation. +\pnum +An \defnadj{atomic}{modify-write operation} is +an atomic read-modify-write operation +with weaker synchronization requirements as specified in\iref{atomics.fences}. +\begin{note} +The intent is for atomic modify-write operations +to be implemented using mechanisms that are not ordered, in hardware, +by the implementation of acquire fences. +No other semantic or hardware property +(e.g., that the mechanism is a far atomic operation) is implied. +\end{note} + \pnum \recommended The implementation should make atomic stores visible to atomic loads, @@ -3622,6 +3727,21 @@ constexpr value_type fetch_min(value_type, memory_order = memory_order::seq_cst) const noexcept; + constexpr void store_add(value_type, + memory_order = memory_order::seq_cst) const noexcept; + constexpr void store_sub(value_type, + memory_order = memory_order::seq_cst) const noexcept; + constexpr void store_and(value_type, + memory_order = memory_order::seq_cst) const noexcept; + constexpr void store_or(value_type, + memory_order = memory_order::seq_cst) const noexcept; + constexpr void store_xor(value_type, + memory_order = memory_order::seq_cst) const noexcept; + constexpr void store_max(value_type, + memory_order = memory_order::seq_cst) const noexcept; + constexpr void store_min(value_type, + memory_order = memory_order::seq_cst) const noexcept; + constexpr value_type operator++(int) const noexcept; constexpr value_type operator--(int) const noexcept; constexpr value_type operator++() const noexcept; @@ -3698,6 +3818,49 @@ parameter as the arguments. \end{itemdescr} +\indexlibrarymember{store_add}{atomic_ref<\placeholder{integral-type}>}% +\indexlibrarymember{store_and}{atomic_ref<\placeholder{integral-type}>}% +\indexlibrarymember{store_max}{atomic_ref<\placeholder{integral-type}>}% +\indexlibrarymember{store_min}{atomic_ref<\placeholder{integral-type}>}% +\indexlibrarymember{store_or}{atomic_ref<\placeholder{integral-type}>}% +\indexlibrarymember{store_sub}{atomic_ref<\placeholder{integral-type}>}% +\begin{itemdecl} +constexpr void store_@\placeholdernc{key}@(value_type operand, + memory_order order = memory_order::seq_cst) const noexcept; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\expects +\tcode{order} is \tcode{memory_order_relaxed}, +\tcode{memory_order_release}, or +\tcode{memory_order_seq_cst}. + +\pnum +\effects +Atomically replaces the value referenced by \tcode{*ptr} +with the result of the computation applied to +the value referenced by \tcode{*ptr} and the given \tcode{operand}. +Memory is affected according to the value of \tcode{order}. +These operations are atomic modify-write operations\iref{atomics.order}. + +\pnum +\remarks +Except for \tcode{store_max} and \tcode{store_min}, +for signed integer types, +the result is as if \tcode{*ptr} and parameters +were converted to their corresponding unsigned types, +the computation performed on those types, and +the result converted back to the signed type. +\begin{note} +There are no undefined results arising from the computation. +\end{note} +For \tcode{store_max} and \tcode{store_min}, +the maximum and minimum computation is performed +as if by \tcode{max} and \tcode{min} algorithms\iref{alg.min.max}, respectively, +with \tcode{*ptr} and the first parameter as the arguments. +\end{itemdescr} + \indexlibrarymember{operator+=}{atomic_ref<\placeholder{integral-type}>}% \indexlibrarymember{operator-=}{atomic_ref<\placeholder{integral-type}>}% \indexlibrarymember{operator\&=}{atomic_ref<\placeholder{integral-type}>}% @@ -3773,6 +3936,19 @@ constexpr value_type fetch_sub(value_type, memory_order = memory_order::seq_cst) const noexcept; + constexpr void store_add(value_type, memory_order = memory_order::seq_cst) const noexcept; + constexpr void store_sub(value_type, memory_order = memory_order::seq_cst) const noexcept; + constexpr void store_max(value_type, memory_order = memory_order::seq_cst) const noexcept; + constexpr void store_min(value_type, memory_order = memory_order::seq_cst) const noexcept; + constexpr void store_fmaximum(value_type, + memory_order = memory_order::seq_cst) const noexcept; + constexpr void store_fminimum(value_type, + memory_order = memory_order::seq_cst) const noexcept; + constexpr void store_fmaximum_num(value_type, + memory_order = memory_order::seq_cst) const noexcept; + constexpr void store_fminimum_num(value_type, + memory_order = memory_order::seq_cst) const noexcept; + constexpr value_type operator+=(value_type) const noexcept; constexpr value_type operator-=(value_type) const noexcept; @@ -3828,8 +4004,90 @@ the \tcode{std::numeric_limits} traits associated with the floating-point type\iref{limits.syn}. The floating-point environment\iref{cfenv} -for atomic arithmetic operations on \tcode{\placeholder{floating-\newline point-type}} +for atomic arithmetic operations on \tcode{\placeholder{floating-\brk{}point-type}} +may be different than the calling thread's floating-point environment. +\end{itemdescr} + +\indexlibrarymember{store_add}{atomic_ref<\placeholder{floating-point-type}>}% +\indexlibrarymember{store_sub}{atomic_ref<\placeholder{floating-point-type}>}% +\indexlibrarymember{store_min}{atomic_ref<\placeholder{floating-point-type}>}% +\indexlibrarymember{store_max}{atomic_ref<\placeholder{floating-point-type}>}% +\indexlibrarymember{store_fminimum}{atomic_ref<\placeholder{floating-point-type}>}% +\indexlibrarymember{store_fmaximum}{atomic_ref<\placeholder{floating-point-type}>}% +\indexlibrarymember{store_fminimum_num}{atomic_ref<\placeholder{floating-point-type}>}% +\indexlibrarymember{store_fmaximum_num}{atomic_ref<\placeholder{floating-point-type}>}% +\begin{itemdecl} +constexpr void store_@\placeholdernc{key}@(value_type operand, + memory_order order = memory_order::seq_cst) const noexcept; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\expects +\tcode{order} is \tcode{memory_order_relaxed}, +\tcode{memory_order_release}, or +\tcode{memory_order_seq_cst}. + +\pnum +\effects +Atomically replaces the value referenced by \tcode{*ptr} +with the result of the computation applied to +the value referenced by \tcode{*ptr} and the given \tcode{operand}. +Memory is affected according to the value of \tcode{order}. +These operations are atomic modify-write operations\iref{atomics.order}. + +\pnum +\remarks +If the result is not a representable value for its type\iref{expr.pre}, +the result is unspecified, +but the operations otherwise have no undefined behavior. +Atomic arithmetic operations on \placeholder{floating-point-type} +should conform to the \tcode{numeric_limits<\placeholder{floating-point-type}>} +traits associated with the floating-point type\iref{limits.syn}. +The floating-point environment\iref{cfenv} +for atomic arithmetic operations on \placeholder{floating-point-type} may be different than the calling thread's floating-point environment. +The arithmetic rules of floating-point atomic modify-write operations +may be different from operations on floating-point types or +atomic floating-point types. +\begin{note} +Tree reductions are permitted for atomic modify-write operations. +\end{note} + +\pnum +\begin{itemize} +\item +For \tcode{store_fmaximum} and \tcode{store_fminimum}, +the maximum and minimum computation is performed +as if by \tcode{fmaximum} and \tcode{fminimum}, respectively, +with \tcode{*ptr} and the first parameter as the arguments. +\item +For \tcode{store_fmaximum_num} and \tcode{store_fminimum_num}, +the maximum and minimum computation is performed +as if by \tcode{fmaximum_num }and \tcode{fminimum_num}, respectively, +with \tcode{*ptr} and the first parameter as the arguments. +\item +For \tcode{store_max} and \tcode{store_min}, +the maximum and minimum computation is performed +as if by \tcode{fmaximum_num} and \tcode{fminimum_num}, respectively, +with \tcode{*ptr} and the first parameter as the arguments, except that: +\begin{itemize} +\item +If both arguments are NaN, an unspecified NaN value is stored at \tcode{*ptr}. +\item +If exactly one argument is a NaN, +either the other argument or an unspecified NaN value is stored at \tcode{*ptr}, +it is unspecified which. +\item +If the arguments are differently signed zeros, +which of these values is stored at \tcode{*ptr} is unspecified. +\end{itemize} +\end{itemize} + +\pnum +\recommended +The implementation of \tcode{store_max} and \tcode{store_min} +should treat negative zero as smaller than positive zero. \end{itemdescr} \indexlibrarymember{operator+=}{atomic_ref<\placeholder{floating-point-type}>}% @@ -3907,6 +4165,15 @@ constexpr value_type fetch_min(value_type, memory_order = memory_order::seq_cst) const noexcept; + constexpr void store_add(difference_type, + memory_order = memory_order::seq_cst) const noexcept; + constexpr void store_sub(difference_type, + memory_order = memory_order::seq_cst) const noexcept; + constexpr void store_max(value_type, + memory_order = memory_order::seq_cst) const noexcept; + constexpr void store_min(value_type, + memory_order = memory_order::seq_cst) const noexcept; + constexpr value_type operator++(int) const noexcept; constexpr value_type operator--(int) const noexcept; constexpr value_type operator++() const noexcept; @@ -3980,6 +4247,49 @@ \end{note} \end{itemdescr} +\indexlibrarymember{store_add}{atomic_ref}% +\indexlibrarymember{store_sub}{atomic_ref}% +\indexlibrarymember{store_max}{atomic_ref}% +\indexlibrarymember{store_min}{atomic_ref}% +\begin{itemdecl} +constexpr void store_@\placeholdernc{key}@(@\UNSP{see above}@ operand, + memory_order order = memory_order::seq_cst) const noexcept; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\mandates +\tcode{T} is a complete object type. + +\pnum +\expects +\tcode{order} is \tcode{memory_order_relaxed}, +\tcode{memory_order_release}, or +\tcode{memory_order_seq_cst}. + +\pnum +\effects +Atomically replaces the value referenced by \tcode{*ptr} +with the result of the computation applied to +the value referenced by \tcode{*ptr} and the given \tcode{operand}. +Memory is affected according to the value of \tcode{order}. +These operations are atomic modify-write operations\iref{atomics.order}. + +\pnum +\remarks +The result may be an undefined address, +but the operations otherwise have no undefined behavior. +For \tcode{store_max} and \tcode{store_min}, +the \tcode{maximum} and \tcode{minimum} computation is performed +as if by \tcode{max} and \tcode{min} algorithms\iref{alg.min.max}, respectively, +with \tcode{*ptr} and the first parameter as the arguments. +\begin{note} +If the pointers point to different complete objects (or subobjects thereof), +the \tcode{<} operator does not establish +a strict weak ordering~(\tref{cpp17.lessthancomparable}, \ref{expr.rel}). +\end{note} +\end{itemdescr} + \indexlibrarymember{operator+=}{atomic_ref}% \indexlibrarymember{operator-=}{atomic_ref}% \begin{itemdecl} @@ -4740,6 +5050,35 @@ constexpr @\placeholdernc{integral-type}@ fetch_min( @\placeholdernc{integral-type}@, memory_order = memory_order::seq_cst) noexcept; + void store_add(@\placeholdernc{integral-type}@, + memory_order = memory_order::seq_cst) volatile noexcept; + constexpr void store_add(@\placeholdernc{integral-type}@, + memory_order = memory_order::seq_cst) noexcept; + void store_sub(@\placeholdernc{integral-type}@, + memory_order = memory_order::seq_cst) volatile noexcept; + constexpr void store_sub(@\placeholdernc{integral-type}@, + memory_order = memory_order::seq_cst) noexcept; + void store_and(@\placeholdernc{integral-type}@, + memory_order = memory_order::seq_cst) volatile noexcept; + constexpr void store_and(@\placeholdernc{integral-type}@, + memory_order = memory_order::seq_cst) noexcept; + void store_or(@\placeholdernc{integral-type}@, + memory_order = memory_order::seq_cst) volatile noexcept; + constexpr void store_or(@\placeholdernc{integral-type}@, + memory_order = memory_order::seq_cst) noexcept; + void store_xor(@\placeholdernc{integral-type}@, + memory_order = memory_order::seq_cst) volatile noexcept; + constexpr void store_xor(@\placeholdernc{integral-type}@, + memory_order = memory_order::seq_cst) noexcept; + void store_max(@\placeholdernc{integral-type}@, + memory_order = memory_order::seq_cst) volatile noexcept; + constexpr void store_max(@\placeholdernc{integral-type}@, + memory_order = memory_order::seq_cst) noexcept; + void store_min(@\placeholdernc{integral-type}@, + memory_order = memory_order::seq_cst) volatile noexcept; + constexpr void store_min(@\placeholdernc{integral-type}@, + memory_order = memory_order::seq_cst) noexcept; + @\placeholdernc{integral-type}@ operator++(int) volatile noexcept; constexpr @\placeholdernc{integral-type}@ operator++(int) noexcept; @\placeholdernc{integral-type}@ operator--(int) volatile noexcept; @@ -4878,6 +5217,70 @@ as the arguments. \end{itemdescr} +\indexlibraryglobal{atomic_store_add}% +\indexlibraryglobal{atomic_store_and}% +\indexlibraryglobal{atomic_store_max}% +\indexlibraryglobal{atomic_store_min}% +\indexlibraryglobal{atomic_store_or}% +\indexlibraryglobal{atomic_store_sub}% +\indexlibraryglobal{atomic_store_add_explicit}% +\indexlibraryglobal{atomic_store_and_explicit}% +\indexlibraryglobal{atomic_store_max_explicit}% +\indexlibraryglobal{atomic_store_min_explicit}% +\indexlibraryglobal{atomic_store_or_explicit}% +\indexlibraryglobal{atomic_store_sub_explicit}% +\indexlibrarymember{store_add}{atomic<\placeholder{integral-type}>}% +\indexlibrarymember{store_and}{atomic<\placeholder{integral-type}>}% +\indexlibrarymember{store_max}{atomic<\placeholder{integral-type}>}% +\indexlibrarymember{store_min}{atomic<\placeholder{integral-type}>}% +\indexlibrarymember{store_or}{atomic<\placeholder{integral-type}>}% +\indexlibrarymember{store_sub}{atomic<\placeholder{integral-type}>}% +\begin{itemdecl} +void store_@\placeholdernc{key}@(@\placeholder{integral-type}@ operand, + memory_order order = memory_order::seq_cst) volatile noexcept; +constexpr void store_@\placeholdernc{key}@(@\placeholder{integral-type}@ operand, + memory_order order = memory_order::seq_cst) noexcept; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +For the \tcode{volatile} overload of this function, +\tcode{is_always_lock_free} is \tcode{true}. + +\pnum +\expects +\tcode{order} is +\tcode{memory_order_relaxed}, +\tcode{memory_order_release}, or +\tcode{memory_order_seq_cst}. + +\pnum +\effects +Atomically replaces the value pointed to by \tcode{this} +with the result of the computation applied to +the value pointed to by \tcode{this} and the given \tcode{operand}. +Memory is affected according to the value of \tcode{order}. +These operations are atomic modify-write operations\iref{atomics.order}. + +\pnum +\remarks +Except for \tcode{store_max} and \tcode{store_min}, +for signed integer types the result is as if +the value pointed to by \tcode{this} and parameters +were converted to their corresponding unsigned types, +the computation performed on those types, and +the result converted back to the signed type. +\begin{note} +There are no undefined results arising from the computation. +\end{note} +For \tcode{store_max} and \tcode{store_min}, +the maximum and minimum computation is performed +as if by \tcode{max} and \tcode{min} algorithms\iref{alg.min.max}, respectively, +with the value pointed to by \tcode{this} and the first parameter as the arguments. +\end{itemdescr} + + \indexlibrarymember{operator+=}{atomic}% \indexlibrarymember{operator-=}{atomic}% \indexlibrarymember{operator+=}{atomic<\placeholder{integral-type}>}% @@ -4966,6 +5369,39 @@ constexpr @\placeholdernc{floating-point-type}@ fetch_sub(@\placeholdernc{floating-point-type}@, memory_order = memory_order::seq_cst) noexcept; + void store_add(@\placeholdernc{floating-point-type}@, + memory_order = memory_order::seq_cst) volatile noexcept; + constexpr void store_add(@\placeholdernc{floating-point-type}@, + memory_order = memory_order::seq_cst) noexcept; + void store_sub(@\placeholdernc{floating-point-type}@, + memory_order = memory_order::seq_cst) volatile noexcept; + constexpr void store_sub(@\placeholdernc{floating-point-type}@, + memory_order = memory_order::seq_cst) noexcept; + void store_max(@\placeholdernc{floating-point-type}@, + memory_order = memory_order::seq_cst) volatile noexcept; + constexpr void store_max(@\placeholdernc{floating-point-type}@, + memory_order = memory_order::seq_cst) noexcept; + void store_min(@\placeholdernc{floating-point-type}@, + memory_order = memory_order::seq_cst) volatile noexcept; + constexpr void store_min(@\placeholdernc{floating-point-type}@, + memory_order = memory_order::seq_cst) noexcept; + void store_fmaximum(@\placeholdernc{floating-point-type}@, + memory_order = memory_order::seq_cst) volatile noexcept; + constexpr void store_fmaximum(@\placeholdernc{floating-point-type}@, + memory_order = memory_order::seq_cst) noexcept; + void store_fminimum(@\placeholdernc{floating-point-type}@, + memory_order = memory_order::seq_cst) volatile noexcept; + constexpr void store_fminimum(@\placeholdernc{floating-point-type}@, + memory_order = memory_order::seq_cst) noexcept; + void store_fmaximum_num(@\placeholdernc{floating-point-type}@, + memory_order = memory_order::seq_cst) volatile noexcept; + constexpr void store_fmaximum_num(@\placeholdernc{floating-point-type}@, + memory_order = memory_order::seq_cst) noexcept; + void store_fminimum_num(@\placeholdernc{floating-point-type}@, + memory_order = memory_order::seq_cst) volatile noexcept; + constexpr void store_fminimum_num(@\placeholdernc{floating-point-type}@, + memory_order = memory_order::seq_cst) noexcept; + @\placeholdernc{floating-point-type}@ operator+=(@\placeholder{floating-point-type}@) volatile noexcept; constexpr @\placeholdernc{floating-point-type}@ operator+=(@\placeholder{floating-point-type}@) noexcept; @\placeholdernc{floating-point-type}@ operator-=(@\placeholder{floating-point-type}@) volatile noexcept; @@ -5037,6 +5473,98 @@ calling thread's floating-point environment. \end{itemdescr} +\indexlibrarymember{store_max}{atomic<\placeholder{floating-point-type}>}% +\indexlibrarymember{store_min}{atomic<\placeholder{floating-point-type}>}% +\indexlibrarymember{store_fmaximum}{atomic<\placeholder{floating-point-type}>}% +\indexlibrarymember{store_fminimum}{atomic<\placeholder{floating-point-type}>}% +\indexlibrarymember{store_fmaximum_num}{atomic<\placeholder{floating-point-type}>}% +\indexlibrarymember{store_fminimum_num}{atomic<\placeholder{floating-point-type}>}% +\begin{itemdecl} +void store_@\placeholdernc{key}@(@\placeholdernc{floating-point-type}@ operand, + memory_order order = memory_order::seq_cst) volatile noexcept; +constexpr void store_@\placeholdernc{key}@(@\placeholdernc{floating-point-type}@ operand, + memory_order order = memory_order::seq_cst) noexcept; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +For the \tcode{volatile} overload of this function, +\tcode{is_always_lock_free} is \tcode{true}. + +\pnum +\expects +\tcode{order} is +\tcode{memory_order_relaxed}, +\tcode{memory_order_release}, or +\tcode{memory_order_seq_cst}. + +\pnum +\effects +Atomically replaces the value pointed to by \tcode{this} +with the result of the computation applied to +the value pointed to by \tcode{this} and the given operand. +Memory is affected according to the value of \tcode{order}. +These operations are atomic modify-write operations\iref{atomics.order}. + +\pnum +\remarks +If the result is not a representable value for its type\iref{expr.pre} +the result is unspecified, +but the operations otherwise have no undefined behavior. +Atomic arithmetic operations on \placeholder{floating-point-type} +should conform to the \tcode{numeric_limits<\placeholder{floating-point-type}>} +traits associated with the floating-point type\iref{limits.syn}. +The floating-point environment\iref{cfenv} for +atomic arithmetic operations on \placeholder{floating-point-type} +may be different than the calling thread's floating-point environment. +The arithmetic rules of floating-point atomic modify-write operations +may be different from operations on +floating-point types or atomic floating-point types. +\begin{note} +Tree reductions are permitted for atomic modify-write operations. +\end{note} + +\pnum +\begin{itemize} +\item +For \tcode{store_fmaximum} and \tcode{store_fminimum}, +the maximum and minimum computation is performed +as if by \tcode{fmaximum} and \tcode{fminimum}, respectively, +with the value pointed to by \tcode{this} and +the first parameter as the arguments. +\item +For \tcode{store_fmaximum_num} and \tcode{store_fminimum_num}, +the maximum and minimum computation is performed +as if by \tcode{fmaximum_num} and \tcode{fminimum_num}, respectively, +with the value pointed to by \tcode{this} and +the first parameter as the arguments. +\item +For \tcode{store_max} and \tcode{store_min}, +the maximum and minimum computation is performed +as if by \tcode{fmaximum_num} and \tcode{fminimum_num}, respectively, +with the value pointed to by \tcode{this} and +the first parameter as the arguments, except that: +\begin{itemize} +\item +If both arguments are NaN, +an unspecified NaN value replaces the value pointed to by \tcode{this.} +\item +If exactly one argument is a NaN, +either the other argument or an unspecified NaN value replaces +the value pointed to by \tcode{this}, it is unspecified which. +\item +If the arguments are differently signed zeros, +which of these values replaces the value pointed to by \tcode{this} is unspecified. +\end{itemize} +\end{itemize} + +\pnum +\recommended +The implementation of \tcode{store_max} and \tcode{store_min} +should treat negative zero as smaller than positive zero. +\end{itemdescr} + \indexlibrarymember{operator+=}{atomic}% \indexlibrarymember{operator-=}{atomic}% \indexlibrarymember{operator+=}{atomic<\placeholder{floating-point-type}>}% @@ -5120,6 +5648,15 @@ T* fetch_min(T*, memory_order = memory_order::seq_cst) volatile noexcept; constexpr T* fetch_min(T*, memory_order = memory_order::seq_cst) noexcept; + void store_add(ptrdiff_t, memory_order = memory_order::seq_cst) volatile noexcept; + constexpr void store_add(ptrdiff_t, memory_order = memory_order::seq_cst) noexcept; + void store_sub(ptrdiff_t, memory_order = memory_order::seq_cst) volatile noexcept; + constexpr void store_sub(ptrdiff_t, memory_order = memory_order::seq_cst) noexcept; + void store_max(T*, memory_order = memory_order::seq_cst) volatile noexcept; + constexpr void store_max(T*, memory_order = memory_order::seq_cst) noexcept; + void store_min(T*, memory_order = memory_order::seq_cst) volatile noexcept; + constexpr void store_min(T*, memory_order = memory_order::seq_cst) noexcept; + T* operator++(int) volatile noexcept; constexpr T* operator++(int) noexcept; T* operator--(int) volatile noexcept; @@ -5240,6 +5777,60 @@ \end{note} \end{itemdescr} +\indexlibraryglobal{atomic_store_add}% +\indexlibraryglobal{atomic_store_max}% +\indexlibraryglobal{atomic_store_min}% +\indexlibraryglobal{atomic_store_sub}% +\indexlibraryglobal{atomic_store_add_explicit}% +\indexlibraryglobal{atomic_store_max_explicit}% +\indexlibraryglobal{atomic_store_min_explicit}% +\indexlibraryglobal{atomic_store_sub_explicit}% +\indexlibrarymember{store_add}{atomic}% +\indexlibrarymember{store_max}{atomic}% +\indexlibrarymember{store_min}{atomic}% +\indexlibrarymember{store_sub}{atomic}% +\begin{itemdecl} +void store_@\placeholdernc{key}@(ptrdiff_t operand, memory_order order = memory_order::seq_cst) volatile noexcept; +constexpr void store_@\placeholdernc{key}@(ptrdiff_t operand, memory_order order = memory_order::seq_cst) noexcept; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +For the \tcode{volatile} overload of this function, +\tcode{is_always_lock_free} is \tcode{true}. + +\pnum +\mandates +\tcode{T} is a complete object type. +\begin{note} +Pointer arithmetic on \tcode{void*} or function pointers is ill-formed. +\end{note} + +\pnum +\effects +Atomically replaces the value pointed to by \tcode{this} +with the result of the computation applied to +the value pointed to by \tcode{this} and the given \tcode{operand}. +Memory is affected according to the value of \tcode{order}. +These operations are atomic modify-write operations\iref{atomics.order}. + +\pnum +\remarks +The result may be an undefined address, +but the operations otherwise have no undefined behavior. +For \tcode{store_max} and \tcode{store_min}, +the maximum and minimum computation is performed +as if by \tcode{max} and \tcode{min} algorithms\iref{alg.min.max}, respectively, +with the value pointed to by \tcode{this} and +the first parameter as the arguments. +\begin{note} +If the pointers point to different complete objects (or subobjects thereof), +the \tcode{<} operator does not establish +a strict weak ordering~(\tref{cpp17.lessthancomparable}, \ref{expr.rel}). +\end{note} +\end{itemdescr} + \indexlibrarymember{operator+=}{atomic}% \indexlibrarymember{operator-=}{atomic}% \begin{itemdecl} @@ -6300,7 +6891,9 @@ \pnum A release fence $A$ synchronizes with an acquire fence $B$ if there exist -atomic operations $X$ and $Y$, both operating on some atomic object +atomic operations $X$ and $Y$, +where $Y$ is not an atomic modify-write operation\iref{atomics.order}, +both operating on some atomic object $M$, such that $A$ is sequenced before $X$, $X$ modifies $M$, $Y$ is sequenced before $B$, and $Y$ reads the value written by $X$ or a value written by any side effect in the hypothetical release From f5fab81b12178613ea4c6a74b5833321d2fe8d6f Mon Sep 17 00:00:00 2001 From: Jens Maurer Date: Fri, 27 Jun 2025 08:47:05 +0200 Subject: [PATCH 2/2] [atomics] Use memory_order::foo for store_key functions --- source/threads.tex | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/source/threads.tex b/source/threads.tex index c85127254a..740fea9cfc 100644 --- a/source/threads.tex +++ b/source/threads.tex @@ -3832,9 +3832,9 @@ \begin{itemdescr} \pnum \expects -\tcode{order} is \tcode{memory_order_relaxed}, -\tcode{memory_order_release}, or -\tcode{memory_order_seq_cst}. +\tcode{order} is \tcode{memory_order::relaxed}, +\tcode{memory_order::release}, or +\tcode{memory_order::seq_cst}. \pnum \effects @@ -4024,9 +4024,9 @@ \begin{itemdescr} \pnum \expects -\tcode{order} is \tcode{memory_order_relaxed}, -\tcode{memory_order_release}, or -\tcode{memory_order_seq_cst}. +\tcode{order} is \tcode{memory_order::relaxed}, +\tcode{memory_order::release}, or +\tcode{memory_order::seq_cst}. \pnum \effects @@ -4263,9 +4263,9 @@ \pnum \expects -\tcode{order} is \tcode{memory_order_relaxed}, -\tcode{memory_order_release}, or -\tcode{memory_order_seq_cst}. +\tcode{order} is \tcode{memory_order::relaxed}, +\tcode{memory_order::release}, or +\tcode{memory_order::seq_cst}. \pnum \effects @@ -5251,9 +5251,9 @@ \pnum \expects \tcode{order} is -\tcode{memory_order_relaxed}, -\tcode{memory_order_release}, or -\tcode{memory_order_seq_cst}. +\tcode{memory_order::relaxed}, +\tcode{memory_order::release}, or +\tcode{memory_order::seq_cst}. \pnum \effects @@ -5495,9 +5495,9 @@ \pnum \expects \tcode{order} is -\tcode{memory_order_relaxed}, -\tcode{memory_order_release}, or -\tcode{memory_order_seq_cst}. +\tcode{memory_order::relaxed}, +\tcode{memory_order::release}, or +\tcode{memory_order::seq_cst}. \pnum \effects