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.
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}. * include/std/atomic: Extend pointer support for nonmember funcs. * 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, __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 @@ -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 @@ -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
