On Wed, 25 Feb 2026 at 13:01, Yuao Ma <[email protected]> wrote:
>
> On Wed, Feb 25, 2026 at 4:19 AM Jonathan Wakely <[email protected]> wrote:
> >
> > 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 <[email protected]>
> > >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.
> >
>
> Thanks for the detailed guidance about the commit msg! Current version
> is more detailed and has proper line wrap.
Thanks for the changes - OK for trunk now.
>
> > >---
> > > 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.
> >
>
> That makes sense. Done.
>
> > > __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).
> >
>
> Moved to atomic/ folder.
>
> > >@@ -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
> > >
> >