[libcxx-commits] [libcxx] [libc++] cv-qualified types in atomic and atomic_ref (P3323R1) (PR #121414)
Damien L-G via libcxx-commits
libcxx-commits at lists.llvm.org
Tue Dec 31 13:10:04 PST 2024
https://github.com/dalg24 created https://github.com/llvm/llvm-project/pull/121414
Implement P3323R1 as a DR against C++11(?), respectively C++20, for `std::atomic` and `std::atomic_ref`.
The `std::atomic` changes are straightforward, adding a couple static assertions checking that the class template is only instantiated for cv-unqualified types, along with a test adapted from `libcxx/test/std/atomics/atomics.types.generic/trivially_copyable.verify.cpp`.
The `std::atomic_ref` changes are more involved. I started converting a subset of the tests to add coverage for volatile- and const-qualified types but I would like feedback before I process the rest.
>From 5afe474ddd2eab652ac24cddacc003957578d3f6 Mon Sep 17 00:00:00 2001
From: Damien L-G <dalg24 at gmail.com>
Date: Tue, 31 Dec 2024 14:38:33 -0500
Subject: [PATCH 1/4] [libc++] cv-qualified types in atomic_ref
Implement atomic_ref related fixes from P3323 as a DR against C++20
---
libcxx/include/__atomic/atomic_ref.h | 276 ++++++++++++++++++++-------
1 file changed, 212 insertions(+), 64 deletions(-)
diff --git a/libcxx/include/__atomic/atomic_ref.h b/libcxx/include/__atomic/atomic_ref.h
index eef15983b98331..f7ed626fed75da 100644
--- a/libcxx/include/__atomic/atomic_ref.h
+++ b/libcxx/include/__atomic/atomic_ref.h
@@ -29,7 +29,12 @@
#include <__cstddef/ptrdiff_t.h>
#include <__memory/addressof.h>
#include <__type_traits/has_unique_object_representation.h>
+#include <__type_traits/is_const.h>
+#include <__type_traits/is_pointer.h>
#include <__type_traits/is_trivially_copyable.h>
+#include <__type_traits/is_volatile.h>
+#include <__type_traits/remove_cv.h>
+#include <__type_traits/remove_pointer.h>
#include <cstdint>
#include <cstring>
@@ -110,7 +115,7 @@ struct __atomic_ref_base {
static constexpr size_t __min_alignment = (sizeof(_Tp) & (sizeof(_Tp) - 1)) || (sizeof(_Tp) > 16) ? 0 : sizeof(_Tp);
public:
- using value_type = _Tp;
+ using value_type = __remove_cv_t<_Tp>;
static constexpr size_t required_alignment = alignof(_Tp) > __min_alignment ? alignof(_Tp) : __min_alignment;
@@ -123,43 +128,50 @@ struct __atomic_ref_base {
_LIBCPP_HIDE_FROM_ABI bool is_lock_free() const noexcept { return __atomic_is_lock_free(sizeof(_Tp), __ptr_); }
- _LIBCPP_HIDE_FROM_ABI void store(_Tp __desired, memory_order __order = memory_order::seq_cst) const noexcept
- _LIBCPP_CHECK_STORE_MEMORY_ORDER(__order) {
+ _LIBCPP_HIDE_FROM_ABI void store(value_type __desired, memory_order __order = memory_order::seq_cst) const noexcept
+ requires(!is_const_v<_Tp>)
+ _LIBCPP_CHECK_STORE_MEMORY_ORDER(__order) {
_LIBCPP_ASSERT_ARGUMENT_WITHIN_DOMAIN(
__order == memory_order::relaxed || __order == memory_order::release || __order == memory_order::seq_cst,
"atomic_ref: memory order argument to atomic store operation is invalid");
__atomic_store(__ptr_, __clear_padding(__desired), std::__to_gcc_order(__order));
}
- _LIBCPP_HIDE_FROM_ABI _Tp operator=(_Tp __desired) const noexcept {
+ _LIBCPP_HIDE_FROM_ABI value_type operator=(value_type __desired) const noexcept
+ requires(!is_const_v<_Tp>)
+ {
store(__desired);
return __desired;
}
- _LIBCPP_HIDE_FROM_ABI _Tp load(memory_order __order = memory_order::seq_cst) const noexcept
+ _LIBCPP_HIDE_FROM_ABI value_type load(memory_order __order = memory_order::seq_cst) const noexcept
_LIBCPP_CHECK_LOAD_MEMORY_ORDER(__order) {
_LIBCPP_ASSERT_ARGUMENT_WITHIN_DOMAIN(
__order == memory_order::relaxed || __order == memory_order::consume || __order == memory_order::acquire ||
__order == memory_order::seq_cst,
"atomic_ref: memory order argument to atomic load operation is invalid");
- alignas(_Tp) byte __mem[sizeof(_Tp)];
- auto* __ret = reinterpret_cast<_Tp*>(__mem);
+ alignas(value_type) byte __mem[sizeof(value_type)];
+ auto* __ret = reinterpret_cast<value_type*>(__mem);
__atomic_load(__ptr_, __ret, std::__to_gcc_order(__order));
return *__ret;
}
- _LIBCPP_HIDE_FROM_ABI operator _Tp() const noexcept { return load(); }
+ _LIBCPP_HIDE_FROM_ABI operator value_type() const noexcept { return load(); }
- _LIBCPP_HIDE_FROM_ABI _Tp exchange(_Tp __desired, memory_order __order = memory_order::seq_cst) const noexcept {
- alignas(_Tp) byte __mem[sizeof(_Tp)];
- auto* __ret = reinterpret_cast<_Tp*>(__mem);
+ _LIBCPP_HIDE_FROM_ABI value_type
+ exchange(value_type __desired, memory_order __order = memory_order::seq_cst) const noexcept
+ requires(!is_const_v<_Tp>)
+ {
+ alignas(value_type) byte __mem[sizeof(value_type)];
+ auto* __ret = reinterpret_cast<value_type*>(__mem);
__atomic_exchange(__ptr_, __clear_padding(__desired), __ret, std::__to_gcc_order(__order));
return *__ret;
}
- _LIBCPP_HIDE_FROM_ABI bool
- compare_exchange_weak(_Tp& __expected, _Tp __desired, memory_order __success, memory_order __failure) const noexcept
- _LIBCPP_CHECK_EXCHANGE_MEMORY_ORDER(__success, __failure) {
+ _LIBCPP_HIDE_FROM_ABI bool compare_exchange_weak(
+ value_type& __expected, value_type __desired, memory_order __success, memory_order __failure) const noexcept
+ requires(!is_const_v<_Tp>)
+ _LIBCPP_CHECK_EXCHANGE_MEMORY_ORDER(__success, __failure) {
_LIBCPP_ASSERT_ARGUMENT_WITHIN_DOMAIN(
__failure == memory_order::relaxed || __failure == memory_order::consume ||
__failure == memory_order::acquire || __failure == memory_order::seq_cst,
@@ -172,9 +184,10 @@ struct __atomic_ref_base {
std::__to_gcc_order(__success),
std::__to_gcc_order(__failure));
}
- _LIBCPP_HIDE_FROM_ABI bool
- compare_exchange_strong(_Tp& __expected, _Tp __desired, memory_order __success, memory_order __failure) const noexcept
- _LIBCPP_CHECK_EXCHANGE_MEMORY_ORDER(__success, __failure) {
+ _LIBCPP_HIDE_FROM_ABI bool compare_exchange_strong(
+ value_type& __expected, value_type __desired, memory_order __success, memory_order __failure) const noexcept
+ requires(!is_const_v<_Tp>)
+ _LIBCPP_CHECK_EXCHANGE_MEMORY_ORDER(__success, __failure) {
_LIBCPP_ASSERT_ARGUMENT_WITHIN_DOMAIN(
__failure == memory_order::relaxed || __failure == memory_order::consume ||
__failure == memory_order::acquire || __failure == memory_order::seq_cst,
@@ -188,8 +201,10 @@ struct __atomic_ref_base {
std::__to_gcc_order(__failure));
}
- _LIBCPP_HIDE_FROM_ABI bool
- compare_exchange_weak(_Tp& __expected, _Tp __desired, memory_order __order = memory_order::seq_cst) const noexcept {
+ _LIBCPP_HIDE_FROM_ABI bool compare_exchange_weak(
+ value_type& __expected, value_type __desired, memory_order __order = memory_order::seq_cst) const noexcept
+ requires(!is_const_v<_Tp>)
+ {
return __compare_exchange(
__ptr_,
std::addressof(__expected),
@@ -198,8 +213,10 @@ struct __atomic_ref_base {
std::__to_gcc_order(__order),
std::__to_gcc_failure_order(__order));
}
- _LIBCPP_HIDE_FROM_ABI bool
- compare_exchange_strong(_Tp& __expected, _Tp __desired, memory_order __order = memory_order::seq_cst) const noexcept {
+ _LIBCPP_HIDE_FROM_ABI bool compare_exchange_strong(
+ value_type& __expected, value_type __desired, memory_order __order = memory_order::seq_cst) const noexcept
+ requires(!is_const_v<_Tp>)
+ {
return __compare_exchange(
__ptr_,
std::addressof(__expected),
@@ -209,7 +226,7 @@ struct __atomic_ref_base {
std::__to_gcc_failure_order(__order));
}
- _LIBCPP_HIDE_FROM_ABI void wait(_Tp __old, memory_order __order = memory_order::seq_cst) const noexcept
+ _LIBCPP_HIDE_FROM_ABI void wait(value_type __old, memory_order __order = memory_order::seq_cst) const noexcept
_LIBCPP_CHECK_WAIT_MEMORY_ORDER(__order) {
_LIBCPP_ASSERT_ARGUMENT_WITHIN_DOMAIN(
__order == memory_order::relaxed || __order == memory_order::consume || __order == memory_order::acquire ||
@@ -217,8 +234,16 @@ struct __atomic_ref_base {
"atomic_ref: memory order argument to atomic wait operation is invalid");
std::__atomic_wait(*this, __old, __order);
}
- _LIBCPP_HIDE_FROM_ABI void notify_one() const noexcept { std::__atomic_notify_one(*this); }
- _LIBCPP_HIDE_FROM_ABI void notify_all() const noexcept { std::__atomic_notify_all(*this); }
+ _LIBCPP_HIDE_FROM_ABI void notify_one() const noexcept
+ requires(!is_const_v<_Tp>)
+ {
+ std::__atomic_notify_one(*this);
+ }
+ _LIBCPP_HIDE_FROM_ABI void notify_all() const noexcept
+ requires(!is_const_v<_Tp>)
+ {
+ std::__atomic_notify_all(*this);
+ }
protected:
using _Aligned_Tp [[__gnu__::__aligned__(required_alignment)]] = _Tp;
@@ -243,6 +268,8 @@ struct atomic_ref : public __atomic_ref_base<_Tp> {
using __base = __atomic_ref_base<_Tp>;
+ static_assert(__base::is_always_lock_free || !is_volatile_v<_Tp>);
+
_LIBCPP_HIDE_FROM_ABI explicit atomic_ref(_Tp& __obj) : __base(__obj) {
_LIBCPP_ASSERT_ARGUMENT_WITHIN_DOMAIN(
reinterpret_cast<uintptr_t>(std::addressof(__obj)) % __base::required_alignment == 0,
@@ -251,17 +278,24 @@ struct atomic_ref : public __atomic_ref_base<_Tp> {
_LIBCPP_HIDE_FROM_ABI atomic_ref(const atomic_ref&) noexcept = default;
- _LIBCPP_HIDE_FROM_ABI _Tp operator=(_Tp __desired) const noexcept { return __base::operator=(__desired); }
+ _LIBCPP_HIDE_FROM_ABI __base::value_type operator=(__base::value_type __desired) const noexcept
+ requires(!is_const_v<_Tp>)
+ {
+ return __base::operator=(__desired);
+ }
atomic_ref& operator=(const atomic_ref&) = delete;
};
template <class _Tp>
- requires(std::integral<_Tp> && !std::same_as<bool, _Tp>)
+ requires(std::integral<_Tp> && !std::same_as<bool, __remove_cv_t<_Tp>>)
struct atomic_ref<_Tp> : public __atomic_ref_base<_Tp> {
using __base = __atomic_ref_base<_Tp>;
+ static_assert(__base::is_always_lock_free || !is_volatile_v<_Tp>);
+
using difference_type = __base::value_type;
+ using value_type = __base::value_type;
_LIBCPP_HIDE_FROM_ABI explicit atomic_ref(_Tp& __obj) : __base(__obj) {
_LIBCPP_ASSERT_ARGUMENT_WITHIN_DOMAIN(
@@ -271,35 +305,90 @@ struct atomic_ref<_Tp> : public __atomic_ref_base<_Tp> {
_LIBCPP_HIDE_FROM_ABI atomic_ref(const atomic_ref&) noexcept = default;
- _LIBCPP_HIDE_FROM_ABI _Tp operator=(_Tp __desired) const noexcept { return __base::operator=(__desired); }
+ _LIBCPP_HIDE_FROM_ABI value_type operator=(value_type __desired) const noexcept
+ requires(!is_const_v<_Tp>)
+ {
+ return __base::operator=(__desired);
+ }
atomic_ref& operator=(const atomic_ref&) = delete;
- _LIBCPP_HIDE_FROM_ABI _Tp fetch_add(_Tp __arg, memory_order __order = memory_order_seq_cst) const noexcept {
+ _LIBCPP_HIDE_FROM_ABI value_type
+ fetch_add(value_type __arg, memory_order __order = memory_order_seq_cst) const noexcept
+ requires(!is_const_v<value_type>)
+ {
return __atomic_fetch_add(this->__ptr_, __arg, std::__to_gcc_order(__order));
}
- _LIBCPP_HIDE_FROM_ABI _Tp fetch_sub(_Tp __arg, memory_order __order = memory_order_seq_cst) const noexcept {
+ _LIBCPP_HIDE_FROM_ABI value_type
+ fetch_sub(value_type __arg, memory_order __order = memory_order_seq_cst) const noexcept
+ requires(!is_const_v<value_type>)
+ {
return __atomic_fetch_sub(this->__ptr_, __arg, std::__to_gcc_order(__order));
}
- _LIBCPP_HIDE_FROM_ABI _Tp fetch_and(_Tp __arg, memory_order __order = memory_order_seq_cst) const noexcept {
+ _LIBCPP_HIDE_FROM_ABI value_type
+ fetch_and(value_type __arg, memory_order __order = memory_order_seq_cst) const noexcept
+ requires(!is_const_v<value_type>)
+ {
return __atomic_fetch_and(this->__ptr_, __arg, std::__to_gcc_order(__order));
}
- _LIBCPP_HIDE_FROM_ABI _Tp fetch_or(_Tp __arg, memory_order __order = memory_order_seq_cst) const noexcept {
+ _LIBCPP_HIDE_FROM_ABI value_type
+ fetch_or(value_type __arg, memory_order __order = memory_order_seq_cst) const noexcept
+ requires(!is_const_v<value_type>)
+ {
return __atomic_fetch_or(this->__ptr_, __arg, std::__to_gcc_order(__order));
}
- _LIBCPP_HIDE_FROM_ABI _Tp fetch_xor(_Tp __arg, memory_order __order = memory_order_seq_cst) const noexcept {
+ _LIBCPP_HIDE_FROM_ABI value_type
+ fetch_xor(value_type __arg, memory_order __order = memory_order_seq_cst) const noexcept
+ requires(!is_const_v<value_type>)
+ {
return __atomic_fetch_xor(this->__ptr_, __arg, std::__to_gcc_order(__order));
}
- _LIBCPP_HIDE_FROM_ABI _Tp operator++(int) const noexcept { return fetch_add(_Tp(1)); }
- _LIBCPP_HIDE_FROM_ABI _Tp operator--(int) const noexcept { return fetch_sub(_Tp(1)); }
- _LIBCPP_HIDE_FROM_ABI _Tp operator++() const noexcept { return fetch_add(_Tp(1)) + _Tp(1); }
- _LIBCPP_HIDE_FROM_ABI _Tp operator--() const noexcept { return fetch_sub(_Tp(1)) - _Tp(1); }
- _LIBCPP_HIDE_FROM_ABI _Tp operator+=(_Tp __arg) const noexcept { return fetch_add(__arg) + __arg; }
- _LIBCPP_HIDE_FROM_ABI _Tp operator-=(_Tp __arg) const noexcept { return fetch_sub(__arg) - __arg; }
- _LIBCPP_HIDE_FROM_ABI _Tp operator&=(_Tp __arg) const noexcept { return fetch_and(__arg) & __arg; }
- _LIBCPP_HIDE_FROM_ABI _Tp operator|=(_Tp __arg) const noexcept { return fetch_or(__arg) | __arg; }
- _LIBCPP_HIDE_FROM_ABI _Tp operator^=(_Tp __arg) const noexcept { return fetch_xor(__arg) ^ __arg; }
+ _LIBCPP_HIDE_FROM_ABI value_type operator++(int) const noexcept
+ requires(!is_const_v<_Tp>)
+ {
+ return fetch_add(value_type(1));
+ }
+ _LIBCPP_HIDE_FROM_ABI value_type operator--(int) const noexcept
+ requires(!is_const_v<_Tp>)
+ {
+ return fetch_sub(value_type(1));
+ }
+ _LIBCPP_HIDE_FROM_ABI value_type operator++() const noexcept
+ requires(!is_const_v<_Tp>)
+ {
+ return fetch_add(value_type(1)) + value_type(1);
+ }
+ _LIBCPP_HIDE_FROM_ABI value_type operator--() const noexcept
+ requires(!is_const_v<_Tp>)
+ {
+ return fetch_sub(value_type(1)) - value_type(1);
+ }
+ _LIBCPP_HIDE_FROM_ABI value_type operator+=(value_type __arg) const noexcept
+ requires(!is_const_v<_Tp>)
+ {
+ return fetch_add(__arg) + __arg;
+ }
+ _LIBCPP_HIDE_FROM_ABI value_type operator-=(value_type __arg) const noexcept
+ requires(!is_const_v<_Tp>)
+ {
+ return fetch_sub(__arg) - __arg;
+ }
+ _LIBCPP_HIDE_FROM_ABI value_type operator&=(value_type __arg) const noexcept
+ requires(!is_const_v<_Tp>)
+ {
+ return fetch_and(__arg) & __arg;
+ }
+ _LIBCPP_HIDE_FROM_ABI value_type operator|=(value_type __arg) const noexcept
+ requires(!is_const_v<_Tp>)
+ {
+ return fetch_or(__arg) | __arg;
+ }
+ _LIBCPP_HIDE_FROM_ABI value_type operator^=(value_type __arg) const noexcept
+ requires(!is_const_v<_Tp>)
+ {
+ return fetch_xor(__arg) ^ __arg;
+ }
};
template <class _Tp>
@@ -307,7 +396,10 @@ template <class _Tp>
struct atomic_ref<_Tp> : public __atomic_ref_base<_Tp> {
using __base = __atomic_ref_base<_Tp>;
+ static_assert(__base::is_always_lock_free || !is_volatile_v<_Tp>);
+
using difference_type = __base::value_type;
+ using value_type = __base::value_type;
_LIBCPP_HIDE_FROM_ABI explicit atomic_ref(_Tp& __obj) : __base(__obj) {
_LIBCPP_ASSERT_ARGUMENT_WITHIN_DOMAIN(
@@ -317,56 +409,112 @@ struct atomic_ref<_Tp> : public __atomic_ref_base<_Tp> {
_LIBCPP_HIDE_FROM_ABI atomic_ref(const atomic_ref&) noexcept = default;
- _LIBCPP_HIDE_FROM_ABI _Tp operator=(_Tp __desired) const noexcept { return __base::operator=(__desired); }
+ _LIBCPP_HIDE_FROM_ABI value_type operator=(value_type __desired) const noexcept
+ requires(!is_const_v<_Tp>)
+ {
+ return __base::operator=(__desired);
+ }
atomic_ref& operator=(const atomic_ref&) = delete;
- _LIBCPP_HIDE_FROM_ABI _Tp fetch_add(_Tp __arg, memory_order __order = memory_order_seq_cst) const noexcept {
- _Tp __old = this->load(memory_order_relaxed);
- _Tp __new = __old + __arg;
+ _LIBCPP_HIDE_FROM_ABI value_type
+ fetch_add(value_type __arg, memory_order __order = memory_order_seq_cst) const noexcept
+ requires(!is_const_v<_Tp>)
+ {
+ value_type __old = this->load(memory_order_relaxed);
+ value_type __new = __old + __arg;
while (!this->compare_exchange_weak(__old, __new, __order, memory_order_relaxed)) {
__new = __old + __arg;
}
return __old;
}
- _LIBCPP_HIDE_FROM_ABI _Tp fetch_sub(_Tp __arg, memory_order __order = memory_order_seq_cst) const noexcept {
- _Tp __old = this->load(memory_order_relaxed);
- _Tp __new = __old - __arg;
+ _LIBCPP_HIDE_FROM_ABI value_type
+ fetch_sub(value_type __arg, memory_order __order = memory_order_seq_cst) const noexcept
+ requires(!is_const_v<_Tp>)
+ {
+ value_type __old = this->load(memory_order_relaxed);
+ value_type __new = __old - __arg;
while (!this->compare_exchange_weak(__old, __new, __order, memory_order_relaxed)) {
__new = __old - __arg;
}
return __old;
}
- _LIBCPP_HIDE_FROM_ABI _Tp operator+=(_Tp __arg) const noexcept { return fetch_add(__arg) + __arg; }
- _LIBCPP_HIDE_FROM_ABI _Tp operator-=(_Tp __arg) const noexcept { return fetch_sub(__arg) - __arg; }
+ _LIBCPP_HIDE_FROM_ABI value_type operator+=(value_type __arg) const noexcept
+ requires(!is_const_v<_Tp>)
+ {
+ return fetch_add(__arg) + __arg;
+ }
+ _LIBCPP_HIDE_FROM_ABI value_type operator-=(value_type __arg) const noexcept
+ requires(!is_const_v<_Tp>)
+ {
+ return fetch_sub(__arg) - __arg;
+ }
};
template <class _Tp>
-struct atomic_ref<_Tp*> : public __atomic_ref_base<_Tp*> {
- using __base = __atomic_ref_base<_Tp*>;
+ requires(std::is_pointer_v<_Tp>)
+struct atomic_ref<_Tp> : public __atomic_ref_base<_Tp> {
+ using __base = __atomic_ref_base<_Tp>;
using difference_type = ptrdiff_t;
+ using value_type = typename __base::value_type;
- _LIBCPP_HIDE_FROM_ABI explicit atomic_ref(_Tp*& __ptr) : __base(__ptr) {}
+ _LIBCPP_HIDE_FROM_ABI explicit atomic_ref(_Tp& __ptr) : __base(__ptr) {}
- _LIBCPP_HIDE_FROM_ABI _Tp* operator=(_Tp* __desired) const noexcept { return __base::operator=(__desired); }
+ _LIBCPP_HIDE_FROM_ABI value_type operator=(value_type __desired) const noexcept
+ requires(!is_const_v<_Tp>)
+ {
+ return __base::operator=(__desired);
+ }
atomic_ref& operator=(const atomic_ref&) = delete;
- _LIBCPP_HIDE_FROM_ABI _Tp* fetch_add(ptrdiff_t __arg, memory_order __order = memory_order_seq_cst) const noexcept {
- return __atomic_fetch_add(this->__ptr_, __arg * sizeof(_Tp), std::__to_gcc_order(__order));
+ _LIBCPP_HIDE_FROM_ABI value_type
+ fetch_add(ptrdiff_t __arg, memory_order __order = memory_order_seq_cst) const noexcept
+ requires(!is_const_v<_Tp>)
+ {
+ return __atomic_fetch_add(
+ this->__ptr_, __arg * sizeof(__remove_pointer_t<value_type>), std::__to_gcc_order(__order));
}
- _LIBCPP_HIDE_FROM_ABI _Tp* fetch_sub(ptrdiff_t __arg, memory_order __order = memory_order_seq_cst) const noexcept {
- return __atomic_fetch_sub(this->__ptr_, __arg * sizeof(_Tp), std::__to_gcc_order(__order));
+ _LIBCPP_HIDE_FROM_ABI value_type
+ fetch_sub(ptrdiff_t __arg, memory_order __order = memory_order_seq_cst) const noexcept
+ requires(!is_const_v<_Tp>)
+ {
+ return __atomic_fetch_sub(
+ this->__ptr_, __arg * sizeof(__remove_pointer_t<value_type>), std::__to_gcc_order(__order));
}
- _LIBCPP_HIDE_FROM_ABI _Tp* operator++(int) const noexcept { return fetch_add(1); }
- _LIBCPP_HIDE_FROM_ABI _Tp* operator--(int) const noexcept { return fetch_sub(1); }
- _LIBCPP_HIDE_FROM_ABI _Tp* operator++() const noexcept { return fetch_add(1) + 1; }
- _LIBCPP_HIDE_FROM_ABI _Tp* operator--() const noexcept { return fetch_sub(1) - 1; }
- _LIBCPP_HIDE_FROM_ABI _Tp* operator+=(ptrdiff_t __arg) const noexcept { return fetch_add(__arg) + __arg; }
- _LIBCPP_HIDE_FROM_ABI _Tp* operator-=(ptrdiff_t __arg) const noexcept { return fetch_sub(__arg) - __arg; }
+ _LIBCPP_HIDE_FROM_ABI value_type operator++(int) const noexcept
+ requires(!is_const_v<_Tp>)
+ {
+ return fetch_add(1);
+ }
+ _LIBCPP_HIDE_FROM_ABI value_type operator--(int) const noexcept
+ requires(!is_const_v<_Tp>)
+ {
+ return fetch_sub(1);
+ }
+ _LIBCPP_HIDE_FROM_ABI value_type operator++() const noexcept
+ requires(!is_const_v<_Tp>)
+ {
+ return fetch_add(1) + 1;
+ }
+ _LIBCPP_HIDE_FROM_ABI value_type operator--() const noexcept
+ requires(!is_const_v<_Tp>)
+ {
+ return fetch_sub(1) - 1;
+ }
+ _LIBCPP_HIDE_FROM_ABI value_type operator+=(ptrdiff_t __arg) const noexcept
+ requires(!is_const_v<_Tp>)
+ {
+ return fetch_add(__arg) + __arg;
+ }
+ _LIBCPP_HIDE_FROM_ABI value_type operator-=(ptrdiff_t __arg) const noexcept
+ requires(!is_const_v<_Tp>)
+ {
+ return fetch_sub(__arg) - __arg;
+ }
};
_LIBCPP_CTAD_SUPPORTED_FOR_TYPE(atomic_ref);
>From fc886c3e9ac658474430a64c324cd7c14eb881ae Mon Sep 17 00:00:00 2001
From: Damien L-G <dalg24 at gmail.com>
Date: Tue, 31 Dec 2024 15:46:35 -0500
Subject: [PATCH 2/4] [libc++] check that std::atomic is only instantiated for
cv-unqualified types
---
libcxx/include/__atomic/support.h | 4 +++
.../__cxx03/__atomic/cxx_atomic_impl.h | 4 +++
.../cv_unqualified.verify.cpp | 25 +++++++++++++++++++
3 files changed, 33 insertions(+)
create mode 100644 libcxx/test/std/atomics/atomics.types.generic/cv_unqualified.verify.cpp
diff --git a/libcxx/include/__atomic/support.h b/libcxx/include/__atomic/support.h
index 4b555ab483ca0e..e0e1d3b008a393 100644
--- a/libcxx/include/__atomic/support.h
+++ b/libcxx/include/__atomic/support.h
@@ -10,7 +10,9 @@
#define _LIBCPP___ATOMIC_SUPPORT_H
#include <__config>
+#include <__type_traits/is_same.h>
#include <__type_traits/is_trivially_copyable.h>
+#include <__type_traits/remove_cv.h>
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
# pragma GCC system_header
@@ -114,6 +116,8 @@ _LIBCPP_BEGIN_NAMESPACE_STD
template <typename _Tp, typename _Base = __cxx_atomic_base_impl<_Tp> >
struct __cxx_atomic_impl : public _Base {
static_assert(is_trivially_copyable<_Tp>::value, "std::atomic<T> requires that 'T' be a trivially copyable type");
+ static_assert(is_same<_Tp, typename remove_cv<_Tp>::type>::value,
+ "std::atomic<T> requires that 'T' be a cv-unqualified type");
_LIBCPP_HIDE_FROM_ABI __cxx_atomic_impl() _NOEXCEPT = default;
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR explicit __cxx_atomic_impl(_Tp __value) _NOEXCEPT : _Base(__value) {}
diff --git a/libcxx/include/__cxx03/__atomic/cxx_atomic_impl.h b/libcxx/include/__cxx03/__atomic/cxx_atomic_impl.h
index 990d283c62d1a5..7d428591e33758 100644
--- a/libcxx/include/__cxx03/__atomic/cxx_atomic_impl.h
+++ b/libcxx/include/__cxx03/__atomic/cxx_atomic_impl.h
@@ -14,8 +14,10 @@
#include <__cxx03/__config>
#include <__cxx03/__memory/addressof.h>
#include <__cxx03/__type_traits/is_assignable.h>
+#include <__cxx03/__type_traits/is_same.h>
#include <__cxx03/__type_traits/is_trivially_copyable.h>
#include <__cxx03/__type_traits/remove_const.h>
+#include <__cxx03/__type_traits/remove_cv.h>
#include <__cxx03/cstddef>
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
@@ -500,6 +502,8 @@ __cxx_atomic_fetch_xor(__cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern, memory_o
template <typename _Tp, typename _Base = __cxx_atomic_base_impl<_Tp> >
struct __cxx_atomic_impl : public _Base {
static_assert(is_trivially_copyable<_Tp>::value, "std::atomic<T> requires that 'T' be a trivially copyable type");
+ static_assert(is_same<_Tp, typename remove_cv<_Tp>::type>::value,
+ "std::atomic<T> requires that 'T' be a cv-unqualified type");
_LIBCPP_HIDE_FROM_ABI __cxx_atomic_impl() _NOEXCEPT = default;
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR explicit __cxx_atomic_impl(_Tp __value) _NOEXCEPT : _Base(__value) {}
diff --git a/libcxx/test/std/atomics/atomics.types.generic/cv_unqualified.verify.cpp b/libcxx/test/std/atomics/atomics.types.generic/cv_unqualified.verify.cpp
new file mode 100644
index 00000000000000..c13c246cfd4eed
--- /dev/null
+++ b/libcxx/test/std/atomics/atomics.types.generic/cv_unqualified.verify.cpp
@@ -0,0 +1,25 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// <atomic>
+
+// template <class T>
+// struct atomic;
+
+// This test checks that we static_assert inside std::atomic<T> when T
+// is cv-qualified, however Clang will sometimes emit additional
+// errors while trying to instantiate the rest of std::atomic<T>.
+// We silence those to make the test more robust.
+// ADDITIONAL_COMPILE_FLAGS: -Xclang -verify-ignore-unexpected=error
+
+#include <atomic>
+
+void f() {
+ std::atomic<const int> a; // expected-error@*:* {{std::atomic<T> requires that 'T' be a cv-unqualified type}}
+}
+
>From 69d92fa1adcaed6e321fee1920bdad85e98c30ce Mon Sep 17 00:00:00 2001
From: Damien L-G <dalg24 at gmail.com>
Date: Tue, 31 Dec 2024 15:50:07 -0500
Subject: [PATCH 3/4] [libc++] Mark P3323R1 as complete
---
libcxx/docs/Status/Cxx2cPapers.csv | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/libcxx/docs/Status/Cxx2cPapers.csv b/libcxx/docs/Status/Cxx2cPapers.csv
index 3a8a794ca4ea1e..990c65a4993a3a 100644
--- a/libcxx/docs/Status/Cxx2cPapers.csv
+++ b/libcxx/docs/Status/Cxx2cPapers.csv
@@ -87,7 +87,7 @@
"`P3050R2 <https://wg21.link/P3050R2>`__","Fix C++26 by optimizing ``linalg::conjugated`` for noncomplex value types","2024-11 (Wrocław)","","",""
"`P3396R1 <https://wg21.link/P3396R1>`__","``std::execution`` wording fixes","2024-11 (Wrocław)","","",""
"`P2835R7 <https://wg21.link/P2835R7>`__","Expose ``std::atomic_ref``'s object address","2024-11 (Wrocław)","","",""
-"`P3323R1 <https://wg21.link/P3323R1>`__","cv-qualified types in ``atomic`` and ``atomic_ref``","2024-11 (Wrocław)","","",""
+"`P3323R1 <https://wg21.link/P3323R1>`__","cv-qualified types in ``atomic`` and ``atomic_ref``","2024-11 (Wrocław)","|Complete|","20","Implemented as DR against C++20."
"`P3508R0 <https://wg21.link/P3508R0>`__","Wording for ""constexpr for specialized memory algorithms""","2024-11 (Wrocław)","","",""
"`P3369R0 <https://wg21.link/P3369R0>`__","constexpr for ``uninitialized_default_construct``","2024-11 (Wrocław)","","",""
"`P3370R1 <https://wg21.link/P3370R1>`__","Add new library headers from C23","2024-11 (Wrocław)","","",""
>From 9c6dd660ebeb1014ba1fe6eefcd32c08cbe9f08b Mon Sep 17 00:00:00 2001
From: Damien L-G <dalg24 at gmail.com>
Date: Tue, 31 Dec 2024 15:54:48 -0500
Subject: [PATCH 4/4] [libc++] Add testing for atomic_ref store/load for
cv-qualified types
---
.../std/atomics/atomics.ref/assign.pass.cpp | 42 +++++++++++++------
.../std/atomics/atomics.ref/convert.pass.cpp | 34 +++++++++++----
.../atomics/atomics.ref/deduction.pass.cpp | 13 +++++-
.../std/atomics/atomics.ref/load.pass.cpp | 35 ++++++++++++----
.../std/atomics/atomics.ref/store.pass.cpp | 40 ++++++++++++++----
.../std/atomics/atomics.ref/test_helper.h | 39 ++++++++---------
6 files changed, 146 insertions(+), 57 deletions(-)
diff --git a/libcxx/test/std/atomics/atomics.ref/assign.pass.cpp b/libcxx/test/std/atomics/atomics.ref/assign.pass.cpp
index 9b2f9042e9836f..f9dcb3c37e6ab7 100644
--- a/libcxx/test/std/atomics/atomics.ref/assign.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.ref/assign.pass.cpp
@@ -20,26 +20,42 @@
#include "test_helper.h"
#include "test_macros.h"
-template <typename T>
+template <typename U>
struct TestAssign {
void operator()() const {
- {
- T x(T(1));
- std::atomic_ref<T> const a(x);
+ static_assert(std::is_nothrow_assignable_v<std::atomic_ref<U>, U>);
+ do_test<U>();
+ do_test_atomic<U>();
+ static_assert(!std::is_assignable_v<std::atomic_ref<U const>, U>);
+ if constexpr (std::atomic_ref<U>::is_always_lock_free) {
+ static_assert(std::is_nothrow_assignable_v<std::atomic_ref<U volatile>, U>);
+ do_test<U volatile>();
+ do_test_atomic<U volatile>();
+ static_assert(!std::is_assignable_v<std::atomic_ref<U const volatile>, U>);
+ }
+ }
- std::same_as<T> decltype(auto) y = (a = T(2));
- assert(y == T(2));
- assert(x == T(2));
+ template <typename T>
+ void do_test() const {
+ T x(T(1));
+ std::atomic_ref<T> const a(x);
- ASSERT_NOEXCEPT(a = T(0));
- static_assert(std::is_nothrow_assignable_v<std::atomic_ref<T>, T>);
+ std::same_as<std::remove_cv_t<T>> decltype(auto) y = (a = T(2));
+ assert(y == std::remove_cv_t<T>(2));
+ assert(const_cast<std::remove_cv_t<T> const&>(x) == std::remove_cv_t<T>(2));
- static_assert(!std::is_copy_assignable_v<std::atomic_ref<T>>);
- }
+ ASSERT_NOEXCEPT(a = T(0));
+
+ static_assert(!std::is_copy_assignable_v<std::atomic_ref<T>>);
+ }
+ template <typename T>
+ void do_test_atomic() const {
{
- auto assign = [](std::atomic_ref<T> const& y, T, T new_val) { y = new_val; };
- auto load = [](std::atomic_ref<T> const& y) { return y.load(); };
+ auto assign = [](std::atomic_ref<T> const& y, T const&, T const& new_val) {
+ y = const_cast<std::remove_cv_t<T> const&>(new_val);
+ };
+ auto load = [](std::atomic_ref<T> const& y) { return y.load(); };
test_seq_cst<T>(assign, load);
}
}
diff --git a/libcxx/test/std/atomics/atomics.ref/convert.pass.cpp b/libcxx/test/std/atomics/atomics.ref/convert.pass.cpp
index 2a58a5ea6ae2ab..754065753331d1 100644
--- a/libcxx/test/std/atomics/atomics.ref/convert.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.ref/convert.pass.cpp
@@ -19,22 +19,42 @@
#include "test_helper.h"
#include "test_macros.h"
-template <typename T>
+template <typename U>
struct TestConvert {
void operator()() const {
+ static_assert(std::is_nothrow_convertible_v<std::atomic_ref<U>, U>);
+ do_test<U>();
+ do_test_atomic<U>();
+ static_assert(std::is_nothrow_convertible_v<std::atomic_ref<U const>, U>);
+ do_test<U const>();
+ if constexpr (std::atomic_ref<U>::is_always_lock_free) {
+ static_assert(std::is_nothrow_convertible_v<std::atomic_ref<U volatile>, U>);
+ do_test<U volatile>();
+ do_test_atomic<U volatile>();
+ static_assert(std::is_nothrow_convertible_v<std::atomic_ref<U const volatile>, U>);
+ do_test<U const volatile>();
+ }
+ }
+
+ template <class T>
+ void do_test() const {
T x(T(1));
- T copy = x;
+ T copy = const_cast<std::remove_cv_t<T> const&>(x);
std::atomic_ref<T> const a(copy);
- T converted = a;
- assert(converted == x);
+ std::remove_cv_t<T> converted = a;
+ assert(converted == const_cast<std::remove_cv_t<T> const&>(x));
ASSERT_NOEXCEPT(T(a));
- static_assert(std::is_nothrow_convertible_v<std::atomic_ref<T>, T>);
+ }
- auto store = [](std::atomic_ref<T> const& y, T, T new_val) { y.store(new_val); };
- auto load = [](std::atomic_ref<T> const& y) { return static_cast<T>(y); };
+ template <class T>
+ void do_test_atomic() const {
+ auto store = [](std::atomic_ref<T> const& y, T const&, T const& new_val) {
+ y.store(const_cast<std::remove_cv_t<T> const&>(new_val));
+ };
+ auto load = [](std::atomic_ref<T> const& y) { return static_cast<std::remove_cv_t<T>>(y); };
test_seq_cst<T>(store, load);
}
};
diff --git a/libcxx/test/std/atomics/atomics.ref/deduction.pass.cpp b/libcxx/test/std/atomics/atomics.ref/deduction.pass.cpp
index 24a399ac4711e1..397ac88d0bf9c6 100644
--- a/libcxx/test/std/atomics/atomics.ref/deduction.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.ref/deduction.pass.cpp
@@ -21,9 +21,18 @@
template <typename T>
struct TestDeduction {
void operator()() const {
- T x(T(0));
+ do_test<T>();
+ do_test<T const>();
+ if constexpr (std::atomic_ref<T>::is_always_lock_free) {
+ do_test<T volatile>();
+ do_test<T const volatile>();
+ }
+ }
+ template <class U>
+ void do_test() const {
+ U x(U(0));
std::atomic_ref a(x);
- ASSERT_SAME_TYPE(decltype(a), std::atomic_ref<T>);
+ ASSERT_SAME_TYPE(decltype(a), std::atomic_ref<U>);
}
};
diff --git a/libcxx/test/std/atomics/atomics.ref/load.pass.cpp b/libcxx/test/std/atomics/atomics.ref/load.pass.cpp
index feed0fbaed8428..ae702e3d5fc666 100644
--- a/libcxx/test/std/atomics/atomics.ref/load.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.ref/load.pass.cpp
@@ -20,27 +20,44 @@
#include "test_helper.h"
#include "test_macros.h"
-template <typename T>
+template <typename U>
struct TestLoad {
void operator()() const {
+ do_test<U>();
+ do_test_atomic<U>();
+ do_test<U const>();
+ if constexpr (std::atomic_ref<U>::is_always_lock_free) {
+ do_test<U volatile>();
+ do_test_atomic<U volatile>();
+ do_test<U const volatile>();
+ }
+ }
+
+ template <class T>
+ void do_test() const {
T x(T(1));
std::atomic_ref<T> const a(x);
{
- std::same_as<T> decltype(auto) y = a.load();
- assert(y == T(1));
+ std::same_as<std::remove_cv_t<T>> decltype(auto) y = a.load();
+ assert(y == std::remove_cv_t<T>(1));
ASSERT_NOEXCEPT(a.load());
}
{
- std::same_as<T> decltype(auto) y = a.load(std::memory_order_seq_cst);
- assert(y == T(1));
+ std::same_as<std::remove_cv_t<T>> decltype(auto) y = a.load(std::memory_order_seq_cst);
+ assert(y == std::remove_cv_t<T>(1));
ASSERT_NOEXCEPT(a.load(std::memory_order_seq_cst));
}
+ }
+ template <class T>
+ void do_test_atomic() const {
// memory_order::seq_cst
{
- auto store = [](std::atomic_ref<T> const& y, T, T new_val) { y.store(new_val); };
+ auto store = [](std::atomic_ref<T> const& y, T const&, T const& new_val) {
+ y.store(const_cast<std::remove_cv_t<T> const&>(new_val));
+ };
auto load_no_arg = [](std::atomic_ref<T> const& y) { return y.load(); };
auto load_with_order = [](std::atomic_ref<T> const& y) { return y.load(std::memory_order::seq_cst); };
test_seq_cst<T>(store, load_no_arg);
@@ -49,8 +66,10 @@ struct TestLoad {
// memory_order::release
{
- auto store = [](std::atomic_ref<T> const& y, T, T new_val) { y.store(new_val, std::memory_order::release); };
- auto load = [](std::atomic_ref<T> const& y) { return y.load(std::memory_order::acquire); };
+ auto store = [](std::atomic_ref<T> const& y, T const&, T const& new_val) {
+ y.store(const_cast<std::remove_cv_t<T> const&>(new_val), std::memory_order::release);
+ };
+ auto load = [](std::atomic_ref<T> const& y) { return y.load(std::memory_order::acquire); };
test_acquire_release<T>(store, load);
}
}
diff --git a/libcxx/test/std/atomics/atomics.ref/store.pass.cpp b/libcxx/test/std/atomics/atomics.ref/store.pass.cpp
index ea01a3d02a34f9..7b9a4036878929 100644
--- a/libcxx/test/std/atomics/atomics.ref/store.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.ref/store.pass.cpp
@@ -19,27 +19,49 @@
#include "test_helper.h"
#include "test_macros.h"
-template <typename T>
+template <typename T, typename U>
+concept has_store = requires { std::declval<T>().store(std::declval<U>()); };
+
+template <typename U>
struct TestStore {
void operator()() const {
+ static_assert(has_store<std::atomic_ref<U>, U>);
+ do_test<U>();
+ do_test_atomic<U>();
+ static_assert(!has_store<std::atomic_ref<U const>, U>);
+ if constexpr (std::atomic_ref<U>::is_always_lock_free) {
+ static_assert(has_store<std::atomic_ref<U volatile>, U>);
+ do_test<U volatile>();
+ do_test_atomic<U volatile>();
+ static_assert(!has_store<std::atomic_ref<U const volatile>, U>);
+ }
+ }
+
+ template <typename T>
+ void do_test() const {
T x(T(1));
std::atomic_ref<T> const a(x);
a.store(T(2));
- assert(x == T(2));
+ assert(const_cast<std::remove_cv_t<T> const&>(x) == std::remove_cv_t<T>(2));
ASSERT_NOEXCEPT(a.store(T(1)));
a.store(T(3), std::memory_order_seq_cst);
- assert(x == T(3));
+ assert(const_cast<std::remove_cv_t<T> const&>(x) == std::remove_cv_t<T>(3));
ASSERT_NOEXCEPT(a.store(T(0), std::memory_order_seq_cst));
+ }
+ template <typename T>
+ void do_test_atomic() const {
// TODO memory_order::relaxed
// memory_order::seq_cst
{
- auto store_no_arg = [](std::atomic_ref<T> const& y, T, T new_val) { y.store(new_val); };
- auto store_with_order = [](std::atomic_ref<T> const& y, T, T new_val) {
- y.store(new_val, std::memory_order::seq_cst);
+ auto store_no_arg = [](std::atomic_ref<T> const& y, T const&, T const& new_val) {
+ y.store(const_cast<std::remove_cv_t<T> const&>(new_val));
+ };
+ auto store_with_order = [](std::atomic_ref<T> const& y, T const&, T const& new_val) {
+ y.store(const_cast<std::remove_cv_t<T> const&>(new_val), std::memory_order::seq_cst);
};
auto load = [](std::atomic_ref<T> const& y) { return y.load(); };
test_seq_cst<T>(store_no_arg, load);
@@ -48,8 +70,10 @@ struct TestStore {
// memory_order::release
{
- auto store = [](std::atomic_ref<T> const& y, T, T new_val) { y.store(new_val, std::memory_order::release); };
- auto load = [](std::atomic_ref<T> const& y) { return y.load(std::memory_order::acquire); };
+ auto store = [](std::atomic_ref<T> const& y, T const&, T const& new_val) {
+ y.store(const_cast<std::remove_cv_t<T> const&>(new_val), std::memory_order::release);
+ };
+ auto load = [](std::atomic_ref<T> const& y) { return y.load(std::memory_order::acquire); };
test_acquire_release<T>(store, load);
}
}
diff --git a/libcxx/test/std/atomics/atomics.ref/test_helper.h b/libcxx/test/std/atomics/atomics.ref/test_helper.h
index 225a70c5a16ca8..4f6c6b16b3dd1f 100644
--- a/libcxx/test/std/atomics/atomics.ref/test_helper.h
+++ b/libcxx/test/std/atomics/atomics.ref/test_helper.h
@@ -45,12 +45,12 @@ template <class T, class StoreOp, class LoadOp>
void test_seq_cst(StoreOp store_op, LoadOp load_op) {
#ifndef TEST_HAS_NO_THREADS
for (int i = 0; i < 100; ++i) {
- T old_value(make_value<T>(0));
- T new_value(make_value<T>(1));
+ T old_value(make_value<std::remove_cv_t<T>>(0));
+ T new_value(make_value<std::remove_cv_t<T>>(1));
- T copy_x = old_value;
+ T copy_x = const_cast<std::remove_cv_t<T> const&>(old_value);
std::atomic_ref<T> const x(copy_x);
- T copy_y = old_value;
+ T copy_y = const_cast<std::remove_cv_t<T> const&>(old_value);
std::atomic_ref<T> const y(copy_y);
std::atomic_bool x_updated_first(false);
@@ -61,19 +61,19 @@ void test_seq_cst(StoreOp store_op, LoadOp load_op) {
auto t2 = support::make_test_thread([&] { store_op(y, old_value, new_value); });
auto t3 = support::make_test_thread([&] {
- while (!equals(load_op(x), new_value)) {
+ while (!equals(load_op(x), const_cast<std::remove_cv_t<T> const&>(new_value))) {
std::this_thread::yield();
}
- if (!equals(load_op(y), new_value)) {
+ if (!equals(load_op(y), const_cast<std::remove_cv_t<T> const&>(new_value))) {
x_updated_first.store(true, std::memory_order_relaxed);
}
});
auto t4 = support::make_test_thread([&] {
- while (!equals(load_op(y), new_value)) {
+ while (!equals(load_op(y), const_cast<std::remove_cv_t<T> const&>(new_value))) {
std::this_thread::yield();
}
- if (!equals(load_op(x), new_value)) {
+ if (!equals(load_op(x), const_cast<std::remove_cv_t<T> const&>(new_value))) {
y_updated_first.store(true, std::memory_order_relaxed);
}
});
@@ -98,10 +98,10 @@ template <class T, class StoreOp, class LoadOp>
void test_acquire_release(StoreOp store_op, LoadOp load_op) {
#ifndef TEST_HAS_NO_THREADS
for (auto i = 0; i < 100; ++i) {
- T old_value(make_value<T>(0));
- T new_value(make_value<T>(1));
+ T old_value(make_value<std::remove_cv_t<T>>(0));
+ T new_value(make_value<std::remove_cv_t<T>>(1));
- T copy = old_value;
+ T copy = const_cast<std::remove_cv_t<T> const&>(old_value);
std::atomic_ref<T> const at(copy);
int non_atomic = 5;
@@ -110,14 +110,15 @@ void test_acquire_release(StoreOp store_op, LoadOp load_op) {
threads.reserve(number_of_threads);
for (auto j = 0; j < number_of_threads; ++j) {
- threads.push_back(support::make_test_thread([&at, &non_atomic, load_op, new_value] {
- while (!equals(load_op(at), new_value)) {
- std::this_thread::yield();
- }
- // Other thread's writes before the release store are visible
- // in this thread's read after the acquire load
- assert(non_atomic == 6);
- }));
+ threads.push_back(support::make_test_thread(
+ [&at, &non_atomic, load_op, new_value = const_cast<std::remove_cv_t<T> const&>(new_value)] {
+ while (!equals(load_op(at), new_value)) {
+ std::this_thread::yield();
+ }
+ // Other thread's writes before the release store are visible
+ // in this thread's read after the acquire load
+ assert(non_atomic == 6);
+ }));
}
non_atomic = 6;
More information about the libcxx-commits
mailing list