[PATCH] libstdc++: complete P0493R5 with pointer support
Jonathan Wakely
jwakely@redhat.com
Tue Feb 24 20:19:41 GMT 2026
On Mon, 23 Feb 2026 at 15:26 +0800, Yuao Ma wrote:
>Hi,
>
>Previous implementations of fetch_min/max(r16-7097-g68a1218c189cce)
>only supported integrals, not handling pointers. To complete the paper
>P0493R5, we need to implement support for pointers as well. This patch
>adds the missing functionality and test cases.
Oh well spotted.
>Regression tested on x86_64-linux, ok for trunk?
>Thanks,
>Yuao
>From 1fc8439ab78a6695fd67263ce31f452e1e12a356 Mon Sep 17 00:00:00 2001
>From: Yuao Ma <c8ef@outlook.com>
>Date: Mon, 23 Feb 2026 15:15:56 +0800
>Subject: [PATCH] libstdc++: complete P0493R5 with pointer support
>
>Previous implementations of fetch_min/max only supported integers, not handling
>pointers. To complete the paper, we need to implement support for pointers as
>well. This patch adds the missing functionality and test cases.
>
>libstdc++-v3/ChangeLog:
>
> * include/bits/atomic_base.h: Extend pointer support for atomic{, _ref}.
Please say what you actually changed. The GNU ChangeLog format is
documented here:
https://www.gnu.org/prep/standards/html_node/Style-of-Change-Logs.html
For this change it should be something like:
* include/bits/atomic_base.h (__atomic_base<T*>::fetch_min)
(__atomic_base<T*>::fetch_max): Define new functions.
(__atomic_ref<P, false, false, true>::fetch_min): Likewise.
(__atomic_ref<P, false, false, true>::fetch_max): Likewise.
> * include/std/atomic: Extend pointer support for nonmember funcs.
Again, this doesn't tell us what actually changed.
* include/std/atomic (atomic_fetch_min_explicit): Change
parameter from __atomic_base<I>* to atomic<I>*.
(atomic_fetch_max_explicit, atomic_fetch_min)
(atomic_fetch_max): Likewise.
> * testsuite/29_atomics/atomic_integral/pointer_fetch_minmax.cc: New test.
> * testsuite/29_atomics/atomic_ref/pointer_fetch_minmax.cc: New test.
These lines are too long, please add line breaks to keep them to
around 72 chars or less (when that's possible):
* testsuite/29_atomics/atomic_integral/pointer_fetch_minmax.cc:
New test.
* testsuite/29_atomics/atomic_ref/pointer_fetch_minmax.cc: New
test.
>---
> libstdc++-v3/include/bits/atomic_base.h | 34 +++++++++
> libstdc++-v3/include/std/atomic | 38 +++++++---
> .../atomic_integral/pointer_fetch_minmax.cc | 53 ++++++++++++++
> .../atomic_ref/pointer_fetch_minmax.cc | 71 +++++++++++++++++++
> 4 files changed, 188 insertions(+), 8 deletions(-)
> create mode 100644 libstdc++-v3/testsuite/29_atomics/atomic_integral/pointer_fetch_minmax.cc
> create mode 100644 libstdc++-v3/testsuite/29_atomics/atomic_ref/pointer_fetch_minmax.cc
>
>diff --git a/libstdc++-v3/include/bits/atomic_base.h b/libstdc++-v3/include/bits/atomic_base.h
>index 79c1b31ccc4..6c5b83a0818 100644
>--- a/libstdc++-v3/include/bits/atomic_base.h
>+++ b/libstdc++-v3/include/bits/atomic_base.h
>@@ -983,6 +983,28 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> fetch_sub(ptrdiff_t __d,
> memory_order __m = memory_order_seq_cst) volatile noexcept
> { return __atomic_fetch_sub(&_M_p, _S_type_size(__d), int(__m)); }
>+
>+#if __glibcxx_atomic_min_max
>+ _GLIBCXX_ALWAYS_INLINE __pointer_type
>+ fetch_min(__pointer_type __p,
>+ memory_order __m = memory_order_seq_cst) noexcept
>+ { return __atomic_impl::__fetch_min(&_M_p, __p, __m); }
>+
>+ _GLIBCXX_ALWAYS_INLINE __pointer_type
>+ fetch_min(__pointer_type __p,
>+ memory_order __m = memory_order_seq_cst) volatile noexcept
>+ { return __atomic_impl::__fetch_min(&_M_p, __p, __m); }
>+
>+ _GLIBCXX_ALWAYS_INLINE __pointer_type
>+ fetch_max(__pointer_type __p,
>+ memory_order __m = memory_order_seq_cst) noexcept
>+ { return __atomic_impl::__fetch_max(&_M_p, __p, __m); }
>+
>+ _GLIBCXX_ALWAYS_INLINE __pointer_type
>+ fetch_max(__pointer_type __p,
>+ memory_order __m = memory_order_seq_cst) volatile noexcept
>+ { return __atomic_impl::__fetch_max(&_M_p, __p, __m); }
>+#endif
> };
>
> namespace __atomic_impl
>@@ -1976,6 +1998,18 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> memory_order __m = memory_order_seq_cst) const noexcept
> { return __atomic_impl::fetch_sub(this->_M_ptr, _S_type_size(__d), __m); }
>
>+#if __glibcxx_atomic_min_max
>+ _GLIBCXX_ALWAYS_INLINE value_type
>+ fetch_min(value_type __i,
>+ memory_order __m = memory_order_seq_cst) const noexcept
>+ { return __atomic_impl::__fetch_min(this->_M_ptr, __i, __m); }
>+
>+ _GLIBCXX_ALWAYS_INLINE value_type
>+ fetch_max(value_type __i,
>+ memory_order __m = memory_order_seq_cst) const noexcept
>+ { return __atomic_impl::__fetch_max(this->_M_ptr, __i, __m); }
>+#endif
>+
> value_type
> operator++(int) const noexcept
> { return fetch_add(1); }
>diff --git a/libstdc++-v3/include/std/atomic b/libstdc++-v3/include/std/atomic
>index 306667e3c63..07c422d6e82 100644
>--- a/libstdc++-v3/include/std/atomic
>+++ b/libstdc++-v3/include/std/atomic
>@@ -718,6 +718,28 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> #endif
> return _M_b.fetch_sub(__d, __m);
> }
>+
>+#if __glibcxx_atomic_min_max
>+ __pointer_type
>+ fetch_min(__pointer_type __p,
>+ memory_order __m = memory_order_seq_cst) noexcept
>+ { return _M_b.fetch_min(__p, __m); }
>+
>+ __pointer_type
>+ fetch_min(__pointer_type __p,
>+ memory_order __m = memory_order_seq_cst) volatile noexcept
>+ { return _M_b.fetch_min(__p, __m); }
>+
>+ __pointer_type
>+ fetch_max(__pointer_type __p,
>+ memory_order __m = memory_order_seq_cst) noexcept
>+ { return _M_b.fetch_max(__p, __m); }
>+
>+ __pointer_type
>+ fetch_max(__pointer_type __p,
>+ memory_order __m = memory_order_seq_cst) volatile noexcept
>+ { return _M_b.fetch_max(__p, __m); }
>+#endif
> };
>
>
>@@ -1576,28 +1598,28 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> #ifdef __cpp_lib_atomic_min_max
> template<typename _ITp>
> inline _ITp
>- atomic_fetch_min_explicit(__atomic_base<_ITp>* __a,
>+ atomic_fetch_min_explicit(atomic<_ITp>* __a,
I thought that maybe we need to constrain this to only be valid for
integer and pointer types, but actually the standard just says "If no
such member function exists, the program is ill-formed." in
[atomics.nonmembers].
Could you please change the _ITp parameter to _Tp in all these
atomic_fetch_{min,max}{,_explicit} functions though? Now that they're
not specific to integer types, _ITp is misleading.
> __atomic_val_t<_ITp> __i,
> memory_order __m) noexcept
> { return __a->fetch_min(__i, __m); }
>
> template<typename _ITp>
> inline _ITp
>- atomic_fetch_min_explicit(volatile __atomic_base<_ITp>* __a,
>+ atomic_fetch_min_explicit(volatile atomic<_ITp>* __a,
> __atomic_val_t<_ITp> __i,
> memory_order __m) noexcept
> { return __a->fetch_min(__i, __m); }
>
> template<typename _ITp>
> inline _ITp
>- atomic_fetch_max_explicit(__atomic_base<_ITp>* __a,
>+ atomic_fetch_max_explicit(atomic<_ITp>* __a,
> __atomic_val_t<_ITp> __i,
> memory_order __m) noexcept
> { return __a->fetch_max(__i, __m); }
>
> template<typename _ITp>
> inline _ITp
>- atomic_fetch_max_explicit(volatile __atomic_base<_ITp>* __a,
>+ atomic_fetch_max_explicit(volatile atomic<_ITp>* __a,
> __atomic_val_t<_ITp> __i,
> memory_order __m) noexcept
> { return __a->fetch_max(__i, __m); }
>@@ -1666,25 +1688,25 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> #ifdef __cpp_lib_atomic_min_max
> template<typename _ITp>
> inline _ITp
>- atomic_fetch_min(__atomic_base<_ITp>* __a,
>+ atomic_fetch_min(atomic<_ITp>* __a,
> __atomic_val_t<_ITp> __i) noexcept
> { return atomic_fetch_min_explicit(__a, __i, memory_order_seq_cst); }
>
> template<typename _ITp>
> inline _ITp
>- atomic_fetch_min(volatile __atomic_base<_ITp>* __a,
>+ atomic_fetch_min(volatile atomic<_ITp>* __a,
> __atomic_val_t<_ITp> __i) noexcept
> { return atomic_fetch_min_explicit(__a, __i, memory_order_seq_cst); }
>
> template<typename _ITp>
> inline _ITp
>- atomic_fetch_max(__atomic_base<_ITp>* __a,
>+ atomic_fetch_max(atomic<_ITp>* __a,
> __atomic_val_t<_ITp> __i) noexcept
> { return atomic_fetch_max_explicit(__a, __i, memory_order_seq_cst); }
>
> template<typename _ITp>
> inline _ITp
>- atomic_fetch_max(volatile __atomic_base<_ITp>* __a,
>+ atomic_fetch_max(volatile atomic<_ITp>* __a,
> __atomic_val_t<_ITp> __i) noexcept
> { return atomic_fetch_max_explicit(__a, __i, memory_order_seq_cst); }
> #endif
>diff --git a/libstdc++-v3/testsuite/29_atomics/atomic_integral/pointer_fetch_minmax.cc b/libstdc++-v3/testsuite/29_atomics/atomic_integral/pointer_fetch_minmax.cc
>new file mode 100644
>index 00000000000..aca0c3157e0
>--- /dev/null
>+++ b/libstdc++-v3/testsuite/29_atomics/atomic_integral/pointer_fetch_minmax.cc
The 29_atomics/atomic_integral directory is for the integer
specializations, please put this new test file in 29_atomics/atomic
instead (we don't have a directory for atomic<T*> specializations,
maybe we should).
>@@ -0,0 +1,53 @@
>+// { dg-do run { target c++26 } }
>+// { dg-require-atomic-cmpxchg-word "" }
>+// { dg-add-options libatomic }
>+
>+#include <atomic>
>+#include <testsuite_hooks.h>
>+
>+void test01() {
>+ long arr[10] = {};
>+
>+ const auto mo = std::memory_order_relaxed;
>+ std::atomic<long *> a(arr);
>+
>+ auto v = atomic_fetch_max(&a, arr + 5);
>+ VERIFY(v == arr);
>+ VERIFY(a == arr + 5);
>+ v = atomic_fetch_max_explicit(&a, arr + 2, mo);
>+ VERIFY(v == arr + 5);
>+ VERIFY(a == arr + 5);
>+
>+ v = atomic_fetch_min(&a, arr + 3);
>+ VERIFY(v == arr + 5);
>+ VERIFY(a == arr + 3);
>+ v = atomic_fetch_min_explicit(&a, arr + 5, mo);
>+ VERIFY(v == arr + 3);
>+ VERIFY(a == arr + 3);
>+}
>+
>+void test02() {
>+ char arr[10] = {};
>+
>+ const auto mo = std::memory_order_relaxed;
>+ std::atomic<char *> a(arr);
>+
>+ auto v = atomic_fetch_max(&a, arr + 5);
>+ VERIFY(v == arr);
>+ VERIFY(a == arr + 5);
>+ v = atomic_fetch_max_explicit(&a, arr + 2, mo);
>+ VERIFY(v == arr + 5);
>+ VERIFY(a == arr + 5);
>+
>+ v = atomic_fetch_min(&a, arr + 3);
>+ VERIFY(v == arr + 5);
>+ VERIFY(a == arr + 3);
>+ v = atomic_fetch_min_explicit(&a, arr + 5, mo);
>+ VERIFY(v == arr + 3);
>+ VERIFY(a == arr + 3);
>+}
>+
>+int main() {
>+ test01();
>+ test02();
>+}
>diff --git a/libstdc++-v3/testsuite/29_atomics/atomic_ref/pointer_fetch_minmax.cc b/libstdc++-v3/testsuite/29_atomics/atomic_ref/pointer_fetch_minmax.cc
>new file mode 100644
>index 00000000000..25eef63ba99
>--- /dev/null
>+++ b/libstdc++-v3/testsuite/29_atomics/atomic_ref/pointer_fetch_minmax.cc
This directory is fine though.
>@@ -0,0 +1,71 @@
>+// { dg-do run { target c++26 } }
>+// { dg-require-atomic-cmpxchg-word "" }
>+// { dg-add-options libatomic }
>+
>+#include <atomic>
>+#include <testsuite_hooks.h>
>+
>+void test01() {
>+ long arr[10] = {};
>+ long *value;
>+
>+ {
>+ const auto mo = std::memory_order_relaxed;
>+ std::atomic_ref<long *> a(value);
>+ bool ok = a.is_lock_free();
>+ if constexpr (std::atomic_ref<long *>::is_always_lock_free)
>+ VERIFY(ok);
>+ a = arr;
>+
>+ auto v = a.fetch_max(arr + 5);
>+ VERIFY(v == arr);
>+ VERIFY(a == arr + 5);
>+ v = a.fetch_max(arr + 2, mo);
>+ VERIFY(v == arr + 5);
>+ VERIFY(a == arr + 5);
>+
>+ v = a.fetch_min(arr + 3);
>+ VERIFY(v == arr + 5);
>+ VERIFY(a == arr + 3);
>+ v = a.fetch_min(arr + 5, mo);
>+ VERIFY(v == arr + 3);
>+ VERIFY(a == arr + 3);
>+ }
>+
>+ VERIFY(value == arr + 3);
>+}
>+
>+void test02() {
>+ char arr[10] = {};
>+ char *value;
>+
>+ {
>+ const auto mo = std::memory_order_relaxed;
>+ std::atomic_ref<char *> a(value);
>+ bool ok = a.is_lock_free();
>+ if constexpr (std::atomic_ref<char *>::is_always_lock_free)
>+ VERIFY(ok);
>+ a = arr;
>+
>+ auto v = a.fetch_max(arr + 5);
>+ VERIFY(v == arr);
>+ VERIFY(a == arr + 5);
>+ v = a.fetch_max(arr + 2, mo);
>+ VERIFY(v == arr + 5);
>+ VERIFY(a == arr + 5);
>+
>+ v = a.fetch_min(arr + 3);
>+ VERIFY(v == arr + 5);
>+ VERIFY(a == arr + 3);
>+ v = a.fetch_min(arr + 5, mo);
>+ VERIFY(v == arr + 3);
>+ VERIFY(a == arr + 3);
>+ }
>+
>+ VERIFY(value == arr + 3);
>+}
>+
>+int main() {
>+ test01();
>+ test02();
>+}
>--
>2.53.0
>
More information about the Libstdc++
mailing list