[libcxx-commits] [libcxx] [libc++][In progress] Floating Point Atomic (PR #67799)
via libcxx-commits
libcxx-commits at lists.llvm.org
Fri Oct 13 12:08:55 PDT 2023
https://github.com/huixie90 updated https://github.com/llvm/llvm-project/pull/67799
>From 3391961d064b498115b565cd80fdba9aac5b239f Mon Sep 17 00:00:00 2001
From: Hui <hui.xie0621 at gmail.com>
Date: Wed, 28 Jun 2023 15:47:39 +0100
Subject: [PATCH 1/8] [libc++][In progress] Floating Point Atomic
- implement P0020R6 Floating Point Atomic
Differential Revision: https://reviews.llvm.org/D153981
---
libcxx/include/__atomic/atomic.h | 118 ++++++++++++++++++
.../atomics.types.float/copy.compile.pass.cpp | 29 +++++
.../atomics.types.float/ctor.pass.cpp | 39 ++++++
.../atomics.types.float/fetch_add.pass.cpp | 81 ++++++++++++
.../atomics.types.float/fetch_sub.pass.cpp | 50 ++++++++
.../atomics.types.float/lockfree.pass.cpp | 52 ++++++++
.../typedef.compile.pass.cpp | 25 ++++
7 files changed, 394 insertions(+)
create mode 100644 libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/copy.compile.pass.cpp
create mode 100644 libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/ctor.pass.cpp
create mode 100644 libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/fetch_add.pass.cpp
create mode 100644 libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/fetch_sub.pass.cpp
create mode 100644 libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/lockfree.pass.cpp
create mode 100644 libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/typedef.compile.pass.cpp
diff --git a/libcxx/include/__atomic/atomic.h b/libcxx/include/__atomic/atomic.h
index 47de6b958a96c1b..7b17c1cbbd98640 100644
--- a/libcxx/include/__atomic/atomic.h
+++ b/libcxx/include/__atomic/atomic.h
@@ -14,11 +14,15 @@
#include <__atomic/cxx_atomic_impl.h>
#include <__atomic/memory_order.h>
#include <__config>
+#include <__functional/operations.h>
#include <__memory/addressof.h>
+#include <__type_traits/is_floating_point.h>
#include <__type_traits/is_function.h>
#include <__type_traits/is_same.h>
#include <__type_traits/remove_pointer.h>
+#include <__utility/forward.h>
#include <cstddef>
+#include <cstdio>
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
# pragma GCC system_header
@@ -136,6 +140,120 @@ struct atomic<_Tp*>
atomic& operator=(const atomic&) volatile = delete;
};
+#if _LIBCPP_STD_VER >= 20
+template <class _Tp> requires is_floating_point_v<_Tp>
+struct atomic<_Tp>
+ : public __atomic_base<_Tp>
+{
+ private:
+ // see lib/Sema/SemaChecking.cpp approx. line 6748, function IsAllowedValueType
+ // // LLVM Parser does not allow atomicrmw with x86_fp80 type.
+ //if (ValType->isSpecificBuiltinType(BuiltinType::LongDouble) &&
+ // &Context.getTargetInfo().getLongDoubleFormat() ==
+ // &llvm::APFloat::x87DoubleExtended())
+
+ // this is platform dependent. How can we get the correct answer?
+ _LIBCPP_HIDE_FROM_ABI static constexpr bool __has_rmw_builtin = !std::is_same_v<_Tp, long double>;
+
+ template <class _This, class _BuiltinOp, class _Operation>
+ _LIBCPP_HIDE_FROM_ABI static _Tp __rmw_op(_This&& __self, _Tp __operand, memory_order __m, _BuiltinOp __builtin_op, _Operation __operation) {
+ if constexpr (__has_rmw_builtin) {
+ return __builtin_op(std::addressof(std::forward<_This>(__self).__a_), __operand, __m);
+ } else {
+ _Tp __old = __self.load(memory_order_relaxed);
+ _Tp __new = __operation(__old, __operand);
+ while(!__self.compare_exchange_weak(__old, __new, __m, memory_order_relaxed)) {
+ if constexpr (std::is_same_v<_Tp, long double>){
+ // https://reviews.llvm.org/D53965
+ // https://bugs.llvm.org/show_bug.cgi?id=48634
+ // clang bug: __old is not updated on failure for atomic<long double>
+ __old = __self.load(memory_order_relaxed);
+ }
+ __new = __operation(__old, __operand);
+ }
+ return __old;
+ }
+ }
+
+ template <class _This>
+ _LIBCPP_HIDE_FROM_ABI static _Tp __fetch_add(_This&& __self, _Tp __operand, memory_order __m) {
+ auto __builtin_op = [](auto __a, auto __operand, auto __order){
+ return std::__cxx_atomic_fetch_add(__a, __operand, __order);
+ };
+ return __rmw_op(std::forward<_This>(__self), __operand, __m, __builtin_op, std::plus<>{});
+ }
+
+ template <class _This>
+ _LIBCPP_HIDE_FROM_ABI static _Tp __fetch_sub(_This&& __self, _Tp __operand, memory_order __m) {
+ auto __builtin_op = [](auto __a, auto __operand, auto __order){
+ return std::__cxx_atomic_fetch_sub(__a, __operand, __order);
+ };
+ return __rmw_op(std::forward<_This>(__self), __operand, __m, __builtin_op, std::minus<>{});
+ }
+
+ public:
+ using __base = __atomic_base<_Tp>;
+ using value_type = _Tp;
+ using difference_type = value_type;
+
+ _LIBCPP_HIDE_FROM_ABI constexpr atomic() noexcept = default;
+ _LIBCPP_HIDE_FROM_ABI constexpr atomic(_Tp __d) noexcept : __base(__d) {}
+
+ atomic(const atomic&) = delete;
+ atomic& operator=(const atomic&) = delete;
+ atomic& operator=(const atomic&) volatile = delete;
+
+ _LIBCPP_HIDE_FROM_ABI
+ _Tp operator=(_Tp __d) volatile noexcept
+ {__base::store(__d); return __d;}
+ _LIBCPP_HIDE_FROM_ABI
+ _Tp operator=(_Tp __d) noexcept
+ {__base::store(__d); return __d;}
+
+ _LIBCPP_HIDE_FROM_ABI
+ _Tp fetch_add(_Tp __op, memory_order __m = memory_order_seq_cst) volatile noexcept
+ requires __base::is_always_lock_free {
+ return __fetch_add(*this, __op, __m);
+ }
+
+ _LIBCPP_HIDE_FROM_ABI
+ _Tp fetch_add(_Tp __op, memory_order __m = memory_order_seq_cst) noexcept {
+ return __fetch_add(*this, __op, __m);
+ }
+
+ _LIBCPP_HIDE_FROM_ABI
+ _Tp fetch_sub(_Tp __op, memory_order __m = memory_order_seq_cst) volatile noexcept
+ requires __base::is_always_lock_free {
+ return __fetch_sub(*this, __op, __m);
+ }
+
+ _LIBCPP_HIDE_FROM_ABI
+ _Tp fetch_sub(_Tp __op, memory_order __m = memory_order_seq_cst) noexcept {
+ return __fetch_sub(*this, __op, __m);
+ }
+
+ _LIBCPP_HIDE_FROM_ABI _Tp operator+=(_Tp __op) volatile noexcept
+ requires __base::is_always_lock_free {
+ return fetch_add(__op) + __op;
+ }
+
+ _LIBCPP_HIDE_FROM_ABI _Tp operator+=(_Tp __op) noexcept {
+ return fetch_add(__op) + __op;
+ }
+
+ _LIBCPP_HIDE_FROM_ABI _Tp operator-=(_Tp __op) volatile noexcept
+ requires __base::is_always_lock_free {
+ return fetch_sub(__op) - __op;
+ }
+
+ _LIBCPP_HIDE_FROM_ABI _Tp operator-=(_Tp __op) noexcept {
+ return fetch_sub(__op) - __op;
+ }
+
+};
+
+#endif // _LIBCPP_STD_VER >= 20
+
// atomic_is_lock_free
template <class _Tp>
diff --git a/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/copy.compile.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/copy.compile.pass.cpp
new file mode 100644
index 000000000000000..3984d621ad3ab04
--- /dev/null
+++ b/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/copy.compile.pass.cpp
@@ -0,0 +1,29 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+//
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+
+// atomic(const atomic&) = delete;
+// atomic& operator=(const atomic&) = delete;
+// atomic& operator=(const atomic&) volatile = delete;
+
+#include <atomic>
+#include <type_traits>
+
+template <class T>
+void test() {
+ static_assert(!std::is_copy_assignable_v<std::atomic<T>>);
+ static_assert(!std::is_copy_constructible_v<std::atomic<T>>);
+ static_assert(!std::is_move_constructible_v<std::atomic<T>>);
+ static_assert(!std::is_move_assignable_v<std::atomic<T>>);
+ static_assert(!std::is_assignable_v<volatile std::atomic<T>&, const std::atomic<T>&>);
+}
+
+template void test<float>();
+template void test<double>();
+template void test<long double>();
diff --git a/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/ctor.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/ctor.pass.cpp
new file mode 100644
index 000000000000000..8fc6ac0f34cf1ed
--- /dev/null
+++ b/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/ctor.pass.cpp
@@ -0,0 +1,39 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+
+// constexpr atomic() noexcept;
+// constexpr atomic(floating-point-type) noexcept;
+
+#include <atomic>
+#include <cassert>
+#include <concepts>
+
+#include "test_macros.h"
+
+template <class T>
+void test() {
+ // constexpr atomic() noexcept;
+ {
+ constexpr std::atomic<T> a = {};
+ assert(a.load() == T(0));
+ }
+
+ // constexpr atomic(floating-point-type) noexcept;
+ {
+ constexpr std::atomic<T> a = T(5.2);
+ assert(a.load() == T(5.2));
+ }
+}
+
+int main(int, char**) {
+ test<float>();
+ test<double>();
+ test<long double>();
+ return 0;
+}
diff --git a/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/fetch_add.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/fetch_add.pass.cpp
new file mode 100644
index 000000000000000..32f5f4097165ae4
--- /dev/null
+++ b/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/fetch_add.pass.cpp
@@ -0,0 +1,81 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+
+// floating-point-type fetch_add(floating-point-type,
+// memory_order = memory_order::seq_cst) volatile noexcept;
+// floating-point-type fetch_add(floating-point-type,
+// memory_order = memory_order::seq_cst) noexcept;
+
+#include <atomic>
+#include <cassert>
+#include <concepts>
+#include <thread>
+#include <vector>
+
+#include "test_macros.h"
+#include "make_test_thread.h"
+
+template <class T>
+void test() {
+ // fetch_add
+ {
+ std::atomic<T> a(3.1);
+ std::same_as<T> decltype(auto) r = a.fetch_add(1.2);
+ assert(r == T(3.1));
+ assert(a.load() == T(3.1) + T(1.2));
+ }
+
+ // fetch_add volatile
+ {
+ volatile std::atomic<T> a(3.1);
+ std::same_as<T> decltype(auto) r = a.fetch_add(1.2);
+ assert(r == T(3.1));
+ assert(a.load() == T(3.1) + T(1.2));
+ }
+
+ // fetch_add concurrent
+ {
+ constexpr auto numberOfThreads = 4;
+ constexpr auto loop = 1000;
+
+ std::atomic<T> at;
+
+ std::vector<std::thread> threads;
+ threads.reserve(numberOfThreads);
+ for (auto i = 0; i < numberOfThreads; ++i) {
+ threads.emplace_back([&at]() {
+ for (auto j = 0; j < loop; ++j) {
+ at.fetch_add(T(1.234));
+ }
+ });
+ }
+
+ for (auto& thread : threads) {
+ thread.join();
+ }
+
+ const auto times = [](T t, int n) {
+ T res(0);
+ for (auto i = 0; i < n; ++i) {
+ res += t;
+ }
+ return res;
+ };
+
+ assert(at.load() == times(1.234, numberOfThreads * loop));
+ }
+}
+
+int main(int, char**) {
+ test<float>();
+ test<double>();
+ test<long double>();
+
+ return 0;
+}
diff --git a/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/fetch_sub.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/fetch_sub.pass.cpp
new file mode 100644
index 000000000000000..ce3aa57e506acfd
--- /dev/null
+++ b/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/fetch_sub.pass.cpp
@@ -0,0 +1,50 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+
+// floating-point-type fetch_add(floating-point-type,
+// memory_order = memory_order::seq_cst) volatile noexcept;
+// floating-point-type fetch_add(floating-point-type,
+// memory_order = memory_order::seq_cst) noexcept;
+// floating-point-type fetch_sub(floating-point-type,
+// memory_order = memory_order::seq_cst) volatile noexcept;
+// floating-point-type fetch_sub(floating-point-type,
+// memory_order = memory_order::seq_cst) noexcept;
+
+#include <atomic>
+#include <cassert>
+#include <concepts>
+
+#include "test_macros.h"
+
+template <class T>
+void test() {
+ // fetch_add
+ {
+ std::atomic<T> a(3.1);
+ std::same_as<T> decltype(auto) r = a.fetch_add(1.2);
+ assert(r == T(3.1));
+ assert(a.load() == T(3.1) + T(1.2));
+ }
+
+ // fetch_sub
+ {
+ std::atomic<T> a(3.1);
+ std::same_as<T> decltype(auto) r = a.fetch_sub(1.2);
+ assert(r == T(3.1));
+ assert(a.load() == T(3.1) - T(1.2));
+ }
+}
+
+int main(int, char**) {
+ test<float>();
+ test<double>();
+ test<long double>();
+
+ return 0;
+}
diff --git a/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/lockfree.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/lockfree.pass.cpp
new file mode 100644
index 000000000000000..b632df79c330486
--- /dev/null
+++ b/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/lockfree.pass.cpp
@@ -0,0 +1,52 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+
+// static constexpr bool is_always_lock_free = implementation-defined;
+// bool is_lock_free() const volatile noexcept;
+// bool is_lock_free() const noexcept;
+
+#include <atomic>
+#include <cassert>
+#include <concepts>
+
+#include "test_macros.h"
+
+template <class T>
+concept isLockFreeNoexcept = requires(T t) {
+ { t.is_lock_free() } noexcept;
+};
+
+template <class T>
+void test() {
+ static_assert(isLockFreeNoexcept<const std::atomic<T>>);
+ static_assert(isLockFreeNoexcept<const volatile std::atomic<T>>);
+
+ // static constexpr bool is_always_lock_free = implementation-defined;
+ { [[maybe_unused]] constexpr std::same_as<const bool> decltype(auto) r = std::atomic<T>::is_always_lock_free; }
+
+ // bool is_lock_free() const volatile noexcept;
+ {
+ const volatile std::atomic<T> a;
+ [[maybe_unused]] std::same_as<bool> decltype(auto) r = a.is_lock_free();
+ }
+
+ // bool is_lock_free() const noexcept;
+ {
+ const std::atomic<T> a;
+ [[maybe_unused]] std::same_as<bool> decltype(auto) r = a.is_lock_free();
+ }
+}
+
+int main(int, char**) {
+ test<float>();
+ test<double>();
+ test<long double>();
+
+ return 0;
+}
diff --git a/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/typedef.compile.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/typedef.compile.pass.cpp
new file mode 100644
index 000000000000000..1046665f1c8fa26
--- /dev/null
+++ b/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/typedef.compile.pass.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
+//
+//===----------------------------------------------------------------------===//
+//
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+
+// using value_type = floating-point-type;
+// using difference_type = value_type;
+
+#include <atomic>
+#include <type_traits>
+
+template <class T>
+void test() {
+ static_assert(std::is_same_v<typename std::atomic<T>::value_type, T>);
+ static_assert(std::is_same_v<typename std::atomic<T>::difference_type, T>);
+}
+
+template void test<float>();
+template void test<double>();
+template void test<long double>();
>From 565478b39936c0a1a7818da572db0cab2eeac509 Mon Sep 17 00:00:00 2001
From: Hui <hui.xie0621 at gmail.com>
Date: Mon, 2 Oct 2023 16:16:02 +0100
Subject: [PATCH 2/8] more tests
---
libcxx/include/__atomic/atomic.h | 56 ++++++------
.../atomics.types.float/fetch_add.pass.cpp | 22 +++--
.../atomics.types.float/fetch_sub.pass.cpp | 59 ++++++++++---
.../operator.minus_equals.pass.cpp | 85 +++++++++++++++++++
.../operator.plus_equals.pass.cpp | 85 +++++++++++++++++++
...ompile.pass.cpp => types.compile.pass.cpp} | 3 +
6 files changed, 260 insertions(+), 50 deletions(-)
create mode 100644 libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/operator.minus_equals.pass.cpp
create mode 100644 libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/operator.plus_equals.pass.cpp
rename libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/{typedef.compile.pass.cpp => types.compile.pass.cpp} (77%)
diff --git a/libcxx/include/__atomic/atomic.h b/libcxx/include/__atomic/atomic.h
index 7b17c1cbbd98640..36cfc1c9bbd0a39 100644
--- a/libcxx/include/__atomic/atomic.h
+++ b/libcxx/include/__atomic/atomic.h
@@ -146,49 +146,43 @@ struct atomic<_Tp>
: public __atomic_base<_Tp>
{
private:
- // see lib/Sema/SemaChecking.cpp approx. line 6748, function IsAllowedValueType
- // // LLVM Parser does not allow atomicrmw with x86_fp80 type.
- //if (ValType->isSpecificBuiltinType(BuiltinType::LongDouble) &&
+ // The builtin __cxx_atomic_fetch_add does not work for
+ // long double on some platforms with fp80 type
+ // There is no way on the libc++ side to test whether it is
+ // ok to use the builtin for a certain type.
+ // Therefore, we do not use the builtin here
+ // For more details, see
+ // lib/Sema/SemaChecking.cpp function IsAllowedValueType
+ // LLVM Parser does not allow atomicrmw with x86_fp80 type.
+ // if (ValType->isSpecificBuiltinType(BuiltinType::LongDouble) &&
// &Context.getTargetInfo().getLongDoubleFormat() ==
// &llvm::APFloat::x87DoubleExtended())
-
- // this is platform dependent. How can we get the correct answer?
- _LIBCPP_HIDE_FROM_ABI static constexpr bool __has_rmw_builtin = !std::is_same_v<_Tp, long double>;
-
- template <class _This, class _BuiltinOp, class _Operation>
- _LIBCPP_HIDE_FROM_ABI static _Tp __rmw_op(_This&& __self, _Tp __operand, memory_order __m, _BuiltinOp __builtin_op, _Operation __operation) {
- if constexpr (__has_rmw_builtin) {
- return __builtin_op(std::addressof(std::forward<_This>(__self).__a_), __operand, __m);
- } else {
- _Tp __old = __self.load(memory_order_relaxed);
- _Tp __new = __operation(__old, __operand);
- while(!__self.compare_exchange_weak(__old, __new, __m, memory_order_relaxed)) {
- if constexpr (std::is_same_v<_Tp, long double>){
- // https://reviews.llvm.org/D53965
- // https://bugs.llvm.org/show_bug.cgi?id=48634
- // clang bug: __old is not updated on failure for atomic<long double>
- __old = __self.load(memory_order_relaxed);
- }
- __new = __operation(__old, __operand);
+ // For more info
+ // https://reviews.llvm.org/D53965
+
+ template <class _This, class _Operation>
+ _LIBCPP_HIDE_FROM_ABI static _Tp __rmw_op(_This&& __self, _Tp __operand, memory_order __m, _Operation __operation) {
+ _Tp __old = __self.load(memory_order_relaxed);
+ _Tp __new = __operation(__old, __operand);
+ while(!__self.compare_exchange_weak(__old, __new, __m, memory_order_relaxed)) {
+ if constexpr (std::is_same_v<_Tp, long double>){
+ // https://bugs.llvm.org/show_bug.cgi?id=48634
+ // clang bug: __old is not updated on failure for atomic<long double>
+ __old = __self.load(memory_order_relaxed);
}
- return __old;
+ __new = __operation(__old, __operand);
}
+ return __old;
}
template <class _This>
_LIBCPP_HIDE_FROM_ABI static _Tp __fetch_add(_This&& __self, _Tp __operand, memory_order __m) {
- auto __builtin_op = [](auto __a, auto __operand, auto __order){
- return std::__cxx_atomic_fetch_add(__a, __operand, __order);
- };
- return __rmw_op(std::forward<_This>(__self), __operand, __m, __builtin_op, std::plus<>{});
+ return __rmw_op(std::forward<_This>(__self), __operand, __m, std::plus<>{});
}
template <class _This>
_LIBCPP_HIDE_FROM_ABI static _Tp __fetch_sub(_This&& __self, _Tp __operand, memory_order __m) {
- auto __builtin_op = [](auto __a, auto __operand, auto __order){
- return std::__cxx_atomic_fetch_sub(__a, __operand, __order);
- };
- return __rmw_op(std::forward<_This>(__self), __operand, __m, __builtin_op, std::minus<>{});
+ return __rmw_op(std::forward<_This>(__self), __operand, __m, std::minus<>{});
}
public:
diff --git a/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/fetch_add.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/fetch_add.pass.cpp
index 32f5f4097165ae4..a156364af93e373 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/fetch_add.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/fetch_add.pass.cpp
@@ -21,34 +21,40 @@
#include "test_macros.h"
#include "make_test_thread.h"
+template <class T>
+concept HasVolatileFetchAdd = requires(volatile std::atomic<T> a, T t) { a.fetch_add(t); };
+
template <class T>
void test() {
+ static_assert(noexcept(std::declval<std::atomic<T>&>().fetch_add(T(0))));
+ static_assert(HasVolatileFetchAdd<T> == std::atomic<T>::is_always_lock_free);
+
// fetch_add
{
std::atomic<T> a(3.1);
- std::same_as<T> decltype(auto) r = a.fetch_add(1.2);
+ std::same_as<T> decltype(auto) r = a.fetch_add(T(1.2));
assert(r == T(3.1));
assert(a.load() == T(3.1) + T(1.2));
}
// fetch_add volatile
- {
+ if constexpr (std::atomic<T>::is_always_lock_free) {
volatile std::atomic<T> a(3.1);
- std::same_as<T> decltype(auto) r = a.fetch_add(1.2);
+ std::same_as<T> decltype(auto) r = a.fetch_add(T(1.2));
assert(r == T(3.1));
assert(a.load() == T(3.1) + T(1.2));
}
// fetch_add concurrent
{
- constexpr auto numberOfThreads = 4;
- constexpr auto loop = 1000;
+ constexpr auto number_of_threads = 4;
+ constexpr auto loop = 1000;
std::atomic<T> at;
std::vector<std::thread> threads;
- threads.reserve(numberOfThreads);
- for (auto i = 0; i < numberOfThreads; ++i) {
+ threads.reserve(number_of_threads);
+ for (auto i = 0; i < number_of_threads; ++i) {
threads.emplace_back([&at]() {
for (auto j = 0; j < loop; ++j) {
at.fetch_add(T(1.234));
@@ -68,7 +74,7 @@ void test() {
return res;
};
- assert(at.load() == times(1.234, numberOfThreads * loop));
+ assert(at.load() == times(1.234, number_of_threads * loop));
}
}
diff --git a/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/fetch_sub.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/fetch_sub.pass.cpp
index ce3aa57e506acfd..b37383d0dec5294 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/fetch_sub.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/fetch_sub.pass.cpp
@@ -7,10 +7,6 @@
//===----------------------------------------------------------------------===//
// UNSUPPORTED: c++03, c++11, c++14, c++17
-// floating-point-type fetch_add(floating-point-type,
-// memory_order = memory_order::seq_cst) volatile noexcept;
-// floating-point-type fetch_add(floating-point-type,
-// memory_order = memory_order::seq_cst) noexcept;
// floating-point-type fetch_sub(floating-point-type,
// memory_order = memory_order::seq_cst) volatile noexcept;
// floating-point-type fetch_sub(floating-point-type,
@@ -19,26 +15,67 @@
#include <atomic>
#include <cassert>
#include <concepts>
+#include <thread>
+#include <vector>
#include "test_macros.h"
+#include "make_test_thread.h"
+
+template <class T>
+concept HasVolatileFetchSub = requires(volatile std::atomic<T> a, T t) { a.fetch_sub(t); };
template <class T>
void test() {
- // fetch_add
+ static_assert(noexcept(std::declval<std::atomic<T>&>().fetch_sub(T(0))));
+ static_assert(HasVolatileFetchSub<T> == std::atomic<T>::is_always_lock_free);
+
+ // fetch_sub
{
std::atomic<T> a(3.1);
- std::same_as<T> decltype(auto) r = a.fetch_add(1.2);
+ std::same_as<T> decltype(auto) r = a.fetch_sub(T(1.2));
assert(r == T(3.1));
- assert(a.load() == T(3.1) + T(1.2));
+ assert(a.load() == T(3.1) - T(1.2));
}
- // fetch_sub
- {
- std::atomic<T> a(3.1);
- std::same_as<T> decltype(auto) r = a.fetch_sub(1.2);
+ // fetch_sub volatile
+ if constexpr (std::atomic<T>::is_always_lock_free) {
+ volatile std::atomic<T> a(3.1);
+ std::same_as<T> decltype(auto) r = a.fetch_sub(T(1.2));
assert(r == T(3.1));
assert(a.load() == T(3.1) - T(1.2));
}
+
+ // fetch_sub concurrent
+ {
+ constexpr auto number_of_threads = 4;
+ constexpr auto loop = 1000;
+
+ std::atomic<T> at;
+
+ std::vector<std::thread> threads;
+ threads.reserve(number_of_threads);
+ for (auto i = 0; i < number_of_threads; ++i) {
+ threads.emplace_back([&at]() {
+ for (auto j = 0; j < loop; ++j) {
+ at.fetch_sub(T(1.234));
+ }
+ });
+ }
+
+ for (auto& thread : threads) {
+ thread.join();
+ }
+
+ const auto accu_neg = [](T t, int n) {
+ T res(0);
+ for (auto i = 0; i < n; ++i) {
+ res -= t;
+ }
+ return res;
+ };
+
+ assert(at.load() == accu_neg(1.234, number_of_threads * loop));
+ }
}
int main(int, char**) {
diff --git a/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/operator.minus_equals.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/operator.minus_equals.pass.cpp
new file mode 100644
index 000000000000000..5a04e9e94027d4a
--- /dev/null
+++ b/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/operator.minus_equals.pass.cpp
@@ -0,0 +1,85 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+
+// floating-point-type operator-=(floating-point-type) volatile noexcept;
+// floating-point-type operator-=(floating-point-type) noexcept;
+
+#include <atomic>
+#include <cassert>
+#include <concepts>
+#include <thread>
+#include <vector>
+
+#include "test_macros.h"
+#include "make_test_thread.h"
+
+template <class T>
+concept HasVolatileMinusEquals = requires(volatile std::atomic<T> a, T t) { a -= t; };
+
+template <class T>
+void test() {
+ static_assert(noexcept(std::declval<std::atomic<T>&>() -= T(0)));
+ static_assert(HasVolatileMinusEquals<T> == std::atomic<T>::is_always_lock_free);
+
+ // -=
+ {
+ std::atomic<T> a(3.1);
+ std::same_as<T> decltype(auto) r = a -= T(1.2);
+ assert(r == T(3.1) - T(1.2));
+ assert(a.load() == T(3.1) - T(1.2));
+ }
+
+ // -= volatile
+ if constexpr (std::atomic<T>::is_always_lock_free) {
+ volatile std::atomic<T> a(3.1);
+ std::same_as<T> decltype(auto) r = a -= T(1.2);
+ assert(r == T(3.1) - T(1.2));
+ assert(a.load() == T(3.1) - T(1.2));
+ }
+
+ // -= concurrent
+ {
+ constexpr auto number_of_threads = 4;
+ constexpr auto loop = 1000;
+
+ std::atomic<T> at;
+
+ std::vector<std::thread> threads;
+ threads.reserve(number_of_threads);
+ for (auto i = 0; i < number_of_threads; ++i) {
+ threads.emplace_back([&at]() {
+ for (auto j = 0; j < loop; ++j) {
+ at -= T(1.234);
+ }
+ });
+ }
+
+ for (auto& thread : threads) {
+ thread.join();
+ }
+
+ const auto accu_neg = [](T t, int n) {
+ T res(0);
+ for (auto i = 0; i < n; ++i) {
+ res -= t;
+ }
+ return res;
+ };
+
+ assert(at.load() == accu_neg(1.234, number_of_threads * loop));
+ }
+}
+
+int main(int, char**) {
+ test<float>();
+ test<double>();
+ test<long double>();
+
+ return 0;
+}
diff --git a/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/operator.plus_equals.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/operator.plus_equals.pass.cpp
new file mode 100644
index 000000000000000..9cace80331736b8
--- /dev/null
+++ b/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/operator.plus_equals.pass.cpp
@@ -0,0 +1,85 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+
+// floating-point-type operator+=(floating-point-type) volatile noexcept;
+// floating-point-type operator+=(floating-point-type) noexcept;
+
+#include <atomic>
+#include <cassert>
+#include <concepts>
+#include <thread>
+#include <vector>
+
+#include "test_macros.h"
+#include "make_test_thread.h"
+
+template <class T>
+concept HasVolatilePlusEquals = requires(volatile std::atomic<T> a, T t) { a += t; };
+
+template <class T>
+void test() {
+ static_assert(noexcept(std::declval<std::atomic<T>&>() += T(0)));
+ static_assert(HasVolatilePlusEquals<T> == std::atomic<T>::is_always_lock_free);
+
+ // +=
+ {
+ std::atomic<T> a(3.1);
+ std::same_as<T> decltype(auto) r = a += T(1.2);
+ assert(r == T(3.1) + T(1.2));
+ assert(a.load() == T(3.1) + T(1.2));
+ }
+
+ // += volatile
+ if constexpr (std::atomic<T>::is_always_lock_free) {
+ volatile std::atomic<T> a(3.1);
+ std::same_as<T> decltype(auto) r = a += T(1.2);
+ assert(r == T(3.1) + T(1.2));
+ assert(a.load() == T(3.1) + T(1.2));
+ }
+
+ // += concurrent
+ {
+ constexpr auto number_of_threads = 4;
+ constexpr auto loop = 1000;
+
+ std::atomic<T> at;
+
+ std::vector<std::thread> threads;
+ threads.reserve(number_of_threads);
+ for (auto i = 0; i < number_of_threads; ++i) {
+ threads.emplace_back([&at]() {
+ for (auto j = 0; j < loop; ++j) {
+ at += T(1.234);
+ }
+ });
+ }
+
+ for (auto& thread : threads) {
+ thread.join();
+ }
+
+ const auto times = [](T t, int n) {
+ T res(0);
+ for (auto i = 0; i < n; ++i) {
+ res += t;
+ }
+ return res;
+ };
+
+ assert(at.load() == times(1.234, number_of_threads * loop));
+ }
+}
+
+int main(int, char**) {
+ test<float>();
+ test<double>();
+ test<long double>();
+
+ return 0;
+}
diff --git a/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/typedef.compile.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/types.compile.pass.cpp
similarity index 77%
rename from libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/typedef.compile.pass.cpp
rename to libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/types.compile.pass.cpp
index 1046665f1c8fa26..1a4e6dfe0b31553 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/typedef.compile.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/types.compile.pass.cpp
@@ -10,6 +10,7 @@
// using value_type = floating-point-type;
// using difference_type = value_type;
+// The atomic floating-point specializations are standard-layout structs. They each have a trivial destructor.
#include <atomic>
#include <type_traits>
@@ -18,6 +19,8 @@ template <class T>
void test() {
static_assert(std::is_same_v<typename std::atomic<T>::value_type, T>);
static_assert(std::is_same_v<typename std::atomic<T>::difference_type, T>);
+ static_assert(std::is_standard_layout_v<std::atomic<T>>);
+ static_assert(std::is_trivially_destructible_v<std::atomic<T>>);
}
template void test<float>();
>From f4669e718596a19cc7828bcd30d837b8169a0bb7 Mon Sep 17 00:00:00 2001
From: Hui <hui.xie0621 at gmail.com>
Date: Mon, 9 Oct 2023 17:48:21 +0100
Subject: [PATCH 3/8] address feedback and rebase
---
libcxx/docs/ReleaseNotes/18.rst | 1 +
libcxx/docs/Status/Cxx20Papers.csv | 2 +-
libcxx/include/__atomic/atomic.h | 75 +++++++++----------
libcxx/include/atomic | 66 ++++++++++++++++
.../atomics.types.float/locakfree.pass.cpp | 49 ++++++++++++
.../atomics.types.float/ctor.pass.cpp | 42 +++++++++--
.../atomics.types.float/fetch_add.pass.cpp | 1 +
.../atomics.types.float/fetch_sub.pass.cpp | 1 +
8 files changed, 189 insertions(+), 48 deletions(-)
create mode 100644 libcxx/test/libcxx/atomics/atomics.types.generic/atomics.types.float/locakfree.pass.cpp
diff --git a/libcxx/docs/ReleaseNotes/18.rst b/libcxx/docs/ReleaseNotes/18.rst
index b8d21334a77a5e2..eefd440abc10756 100644
--- a/libcxx/docs/ReleaseNotes/18.rst
+++ b/libcxx/docs/ReleaseNotes/18.rst
@@ -53,6 +53,7 @@ Implemented Papers
- P2443R1 - ``views::chunk_by``
- P2538R1 - ADL-proof ``std::projected``
- P2614R2 - Deprecate ``numeric_limits::has_denorm``
+- P0020R6 - Floating Point Atomic
Improvements and New Features
diff --git a/libcxx/docs/Status/Cxx20Papers.csv b/libcxx/docs/Status/Cxx20Papers.csv
index dd6fcf9a7583ce3..3b28cfa49e18a28 100644
--- a/libcxx/docs/Status/Cxx20Papers.csv
+++ b/libcxx/docs/Status/Cxx20Papers.csv
@@ -2,7 +2,7 @@
"`P0463R1 <https://wg21.link/P0463R1>`__","LWG","Endian just Endian","Toronto","|Complete|","7.0"
"`P0674R1 <https://wg21.link/P0674R1>`__","LWG","Extending make_shared to Support Arrays","Toronto","|Complete|","15.0"
"","","","","","",""
-"`P0020R6 <https://wg21.link/P0020R6>`__","LWG","Floating Point Atomic","Albuquerque","",""
+"`P0020R6 <https://wg21.link/P0020R6>`__","LWG","Floating Point Atomic","Albuquerque","|Complete|","18.0"
"`P0053R7 <https://wg21.link/P0053R7>`__","LWG","C++ Synchronized Buffered Ostream","Albuquerque","",""
"`P0202R3 <https://wg21.link/P0202R3>`__","LWG","Add constexpr modifiers to functions in <algorithm> and <utility> Headers","Albuquerque","|Complete|","12.0"
"`P0415R1 <https://wg21.link/P0415R1>`__","LWG","Constexpr for ``std::complex``\ ","Albuquerque","|Complete|","16.0"
diff --git a/libcxx/include/__atomic/atomic.h b/libcxx/include/__atomic/atomic.h
index 36cfc1c9bbd0a39..c4159dcfb6ffdb2 100644
--- a/libcxx/include/__atomic/atomic.h
+++ b/libcxx/include/__atomic/atomic.h
@@ -141,36 +141,36 @@ struct atomic<_Tp*>
};
#if _LIBCPP_STD_VER >= 20
-template <class _Tp> requires is_floating_point_v<_Tp>
-struct atomic<_Tp>
- : public __atomic_base<_Tp>
-{
+template <class _Tp>
+ requires is_floating_point_v<_Tp>
+struct atomic<_Tp> : public __atomic_base<_Tp> {
private:
// The builtin __cxx_atomic_fetch_add does not work for
// long double on some platforms with fp80 type
// There is no way on the libc++ side to test whether it is
// ok to use the builtin for a certain type.
// Therefore, we do not use the builtin here
- // For more details, see
+ // For more details, see
// lib/Sema/SemaChecking.cpp function IsAllowedValueType
// LLVM Parser does not allow atomicrmw with x86_fp80 type.
// if (ValType->isSpecificBuiltinType(BuiltinType::LongDouble) &&
// &Context.getTargetInfo().getLongDoubleFormat() ==
// &llvm::APFloat::x87DoubleExtended())
// For more info
+ // https://github.com/llvm/llvm-project/issues/68602
// https://reviews.llvm.org/D53965
template <class _This, class _Operation>
_LIBCPP_HIDE_FROM_ABI static _Tp __rmw_op(_This&& __self, _Tp __operand, memory_order __m, _Operation __operation) {
_Tp __old = __self.load(memory_order_relaxed);
_Tp __new = __operation(__old, __operand);
- while(!__self.compare_exchange_weak(__old, __new, __m, memory_order_relaxed)) {
- if constexpr (std::is_same_v<_Tp, long double>){
- // https://bugs.llvm.org/show_bug.cgi?id=48634
- // clang bug: __old is not updated on failure for atomic<long double>
- __old = __self.load(memory_order_relaxed);
- }
- __new = __operation(__old, __operand);
+ while (!__self.compare_exchange_weak(__old, __new, __m, memory_order_relaxed)) {
+ if constexpr (std::is_same_v<_Tp, long double>) {
+ // https://github.com/llvm/llvm-project/issues/47978
+ // clang bug: __old is not updated on failure for atomic<long double>
+ __old = __self.load(memory_order_relaxed);
+ }
+ __new = __operation(__old, __operand);
}
return __old;
}
@@ -193,57 +193,54 @@ struct atomic<_Tp>
_LIBCPP_HIDE_FROM_ABI constexpr atomic() noexcept = default;
_LIBCPP_HIDE_FROM_ABI constexpr atomic(_Tp __d) noexcept : __base(__d) {}
- atomic(const atomic&) = delete;
- atomic& operator=(const atomic&) = delete;
+ atomic(const atomic&) = delete;
+ atomic& operator=(const atomic&) = delete;
atomic& operator=(const atomic&) volatile = delete;
- _LIBCPP_HIDE_FROM_ABI
- _Tp operator=(_Tp __d) volatile noexcept
- {__base::store(__d); return __d;}
- _LIBCPP_HIDE_FROM_ABI
- _Tp operator=(_Tp __d) noexcept
- {__base::store(__d); return __d;}
+ _LIBCPP_HIDE_FROM_ABI _Tp operator=(_Tp __d) volatile noexcept {
+ __base::store(__d);
+ return __d;
+ }
+ _LIBCPP_HIDE_FROM_ABI _Tp operator=(_Tp __d) noexcept {
+ __base::store(__d);
+ return __d;
+ }
- _LIBCPP_HIDE_FROM_ABI
- _Tp fetch_add(_Tp __op, memory_order __m = memory_order_seq_cst) volatile noexcept
- requires __base::is_always_lock_free {
+ _LIBCPP_HIDE_FROM_ABI _Tp fetch_add(_Tp __op, memory_order __m = memory_order_seq_cst) volatile noexcept
+ requires __base::is_always_lock_free
+ {
return __fetch_add(*this, __op, __m);
}
- _LIBCPP_HIDE_FROM_ABI
- _Tp fetch_add(_Tp __op, memory_order __m = memory_order_seq_cst) noexcept {
+ _LIBCPP_HIDE_FROM_ABI _Tp fetch_add(_Tp __op, memory_order __m = memory_order_seq_cst) noexcept {
return __fetch_add(*this, __op, __m);
}
- _LIBCPP_HIDE_FROM_ABI
- _Tp fetch_sub(_Tp __op, memory_order __m = memory_order_seq_cst) volatile noexcept
- requires __base::is_always_lock_free {
+ _LIBCPP_HIDE_FROM_ABI _Tp fetch_sub(_Tp __op, memory_order __m = memory_order_seq_cst) volatile noexcept
+ requires __base::is_always_lock_free
+ {
return __fetch_sub(*this, __op, __m);
}
- _LIBCPP_HIDE_FROM_ABI
- _Tp fetch_sub(_Tp __op, memory_order __m = memory_order_seq_cst) noexcept {
+ _LIBCPP_HIDE_FROM_ABI _Tp fetch_sub(_Tp __op, memory_order __m = memory_order_seq_cst) noexcept {
return __fetch_sub(*this, __op, __m);
}
_LIBCPP_HIDE_FROM_ABI _Tp operator+=(_Tp __op) volatile noexcept
- requires __base::is_always_lock_free {
+ requires __base::is_always_lock_free
+ {
return fetch_add(__op) + __op;
}
- _LIBCPP_HIDE_FROM_ABI _Tp operator+=(_Tp __op) noexcept {
- return fetch_add(__op) + __op;
- }
+ _LIBCPP_HIDE_FROM_ABI _Tp operator+=(_Tp __op) noexcept { return fetch_add(__op) + __op; }
_LIBCPP_HIDE_FROM_ABI _Tp operator-=(_Tp __op) volatile noexcept
- requires __base::is_always_lock_free {
- return fetch_sub(__op) - __op;
- }
-
- _LIBCPP_HIDE_FROM_ABI _Tp operator-=(_Tp __op) noexcept {
+ requires __base::is_always_lock_free
+ {
return fetch_sub(__op) - __op;
}
+ _LIBCPP_HIDE_FROM_ABI _Tp operator-=(_Tp __op) noexcept { return fetch_sub(__op) - __op; }
};
#endif // _LIBCPP_STD_VER >= 20
diff --git a/libcxx/include/atomic b/libcxx/include/atomic
index 2f122a707bdc33a..c52bf7521635f02 100644
--- a/libcxx/include/atomic
+++ b/libcxx/include/atomic
@@ -262,6 +262,72 @@ struct atomic<T*>
void notify_all() noexcept;
};
+template<>
+struct atomic<floating-point-type> {
+ using value_type = floating-point-type;
+ using difference_type = value_type;
+
+ static constexpr bool is_always_lock_free = implementation-defined;
+ bool is_lock_free() const volatile noexcept;
+ bool is_lock_free() const noexcept;
+
+ constexpr atomic() noexcept;
+ constexpr atomic(floating-point-type) noexcept;
+ atomic(const atomic&) = delete;
+ atomic& operator=(const atomic&) = delete;
+ atomic& operator=(const atomic&) volatile = delete;
+
+ void store(floating-point-type, memory_order = memory_order::seq_cst) volatile noexcept;
+ void store(floating-point-type, memory_order = memory_order::seq_cst) noexcept;
+ floating-point-type operator=(floating-point-type) volatile noexcept;
+ floating-point-type operator=(floating-point-type) noexcept;
+ floating-point-type load(memory_order = memory_order::seq_cst) volatile noexcept;
+ floating-point-type load(memory_order = memory_order::seq_cst) noexcept;
+ operator floating-point-type() volatile noexcept;
+ operator floating-point-type() noexcept;
+
+ floating-point-type exchange(floating-point-type,
+ memory_order = memory_order::seq_cst) volatile noexcept;
+ floating-point-type exchange(floating-point-type,
+ memory_order = memory_order::seq_cst) noexcept;
+ bool compare_exchange_weak(floating-point-type&, floating-point-type,
+ memory_order, memory_order) volatile noexcept;
+ bool compare_exchange_weak(floating-point-type&, floating-point-type,
+ memory_order, memory_order) noexcept;
+ bool compare_exchange_strong(floating-point-type&, floating-point-type,
+ memory_order, memory_order) volatile noexcept;
+ bool compare_exchange_strong(floating-point-type&, floating-point-type,
+ memory_order, memory_order) noexcept;
+ bool compare_exchange_weak(floating-point-type&, floating-point-type,
+ memory_order = memory_order::seq_cst) volatile noexcept;
+ bool compare_exchange_weak(floating-point-type&, floating-point-type,
+ memory_order = memory_order::seq_cst) noexcept;
+ bool compare_exchange_strong(floating-point-type&, floating-point-type,
+ memory_order = memory_order::seq_cst) volatile noexcept;
+ bool compare_exchange_strong(floating-point-type&, floating-point-type,
+ memory_order = memory_order::seq_cst) noexcept;
+
+ floating-point-type fetch_add(floating-point-type,
+ memory_order = memory_order::seq_cst) volatile noexcept;
+ floating-point-type fetch_add(floating-point-type,
+ memory_order = memory_order::seq_cst) noexcept;
+ floating-point-type fetch_sub(floating-point-type,
+ memory_order = memory_order::seq_cst) volatile noexcept;
+ floating-point-type fetch_sub(floating-point-type,
+ memory_order = memory_order::seq_cst) noexcept;
+
+ floating-point-type operator+=(floating-point-type) volatile noexcept;
+ floating-point-type operator+=(floating-point-type) noexcept;
+ floating-point-type operator-=(floating-point-type) volatile noexcept;
+ floating-point-type operator-=(floating-point-type) noexcept;
+
+ void wait(floating-point-type, memory_order = memory_order::seq_cst) const volatile noexcept;
+ void wait(floating-point-type, memory_order = memory_order::seq_cst) const noexcept;
+ void notify_one() volatile noexcept;
+ void notify_one() noexcept;
+ void notify_all() volatile noexcept;
+ void notify_all() noexcept;
+};
// [atomics.nonmembers], non-member functions
template<class T>
diff --git a/libcxx/test/libcxx/atomics/atomics.types.generic/atomics.types.float/locakfree.pass.cpp b/libcxx/test/libcxx/atomics/atomics.types.generic/atomics.types.float/locakfree.pass.cpp
new file mode 100644
index 000000000000000..5388ae1c6710fb9
--- /dev/null
+++ b/libcxx/test/libcxx/atomics/atomics.types.generic/atomics.types.float/locakfree.pass.cpp
@@ -0,0 +1,49 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+
+// static constexpr bool is_always_lock_free = implementation-defined;
+// bool is_lock_free() const volatile noexcept;
+// bool is_lock_free() const noexcept;
+
+#include <atomic>
+#include <cassert>
+#include <concepts>
+
+#include "test_macros.h"
+
+template <class T>
+void test() {
+ // static constexpr bool is_always_lock_free = implementation-defined;
+ {
+ bool r = std::atomic<T>::is_always_lock_free;
+ assert(r == __atomic_always_lock_free(sizeof(T), 0));
+ }
+
+ // bool is_lock_free() const volatile noexcept;
+ {
+ const volatile std::atomic<T> a;
+ bool r = a.is_lock_free();
+ assert(r == __cxx_atomic_is_lock_free(sizeof(T)));
+ }
+
+ // bool is_lock_free() const noexcept;
+ {
+ const std::atomic<T> a;
+ bool r = a.is_lock_free();
+ assert(r == __cxx_atomic_is_lock_free(sizeof(T)));
+ }
+}
+
+int main(int, char**) {
+ test<float>();
+ test<double>();
+ test<long double>();
+
+ return 0;
+}
diff --git a/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/ctor.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/ctor.pass.cpp
index 8fc6ac0f34cf1ed..16df7ff23488759 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/ctor.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/ctor.pass.cpp
@@ -17,23 +17,49 @@
#include "test_macros.h"
template <class T>
-void test() {
+constinit std::atomic<T> a1 = T();
+
+template <class T>
+constinit std::atomic<T> a2 = T(5.2);
+
+template <class T>
+constexpr void testOne() {
+ static_assert(std::is_nothrow_constructible_v<std::atomic<T>>);
+ static_assert(std::is_nothrow_constructible_v<std::atomic<T>, T>);
+
// constexpr atomic() noexcept;
{
- constexpr std::atomic<T> a = {};
- assert(a.load() == T(0));
+ std::atomic<T> a = {};
+ if (!TEST_IS_CONSTANT_EVALUATED) {
+ assert(a.load() == T(0));
+ }
}
// constexpr atomic(floating-point-type) noexcept;
{
- constexpr std::atomic<T> a = T(5.2);
- assert(a.load() == T(5.2));
+ std::atomic<T> a = T(5.2);
+ if (!TEST_IS_CONSTANT_EVALUATED) {
+ assert(a.load() == T(5.2));
+ }
+ }
+
+ // test constinit
+ if (!TEST_IS_CONSTANT_EVALUATED) {
+ assert(a1<T> == T(0.0));
+ assert(a2<T> == T(5.2));
}
}
+constexpr bool test() {
+ testOne<float>();
+ testOne<double>();
+ testOne<long double>();
+ return true;
+}
+
int main(int, char**) {
- test<float>();
- test<double>();
- test<long double>();
+ test();
+ static_assert(test());
+
return 0;
}
diff --git a/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/fetch_add.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/fetch_add.pass.cpp
index a156364af93e373..54780e18e9bec4a 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/fetch_add.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/fetch_add.pass.cpp
@@ -5,6 +5,7 @@
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
+// UNSUPPORTED: no-threads
// UNSUPPORTED: c++03, c++11, c++14, c++17
// floating-point-type fetch_add(floating-point-type,
diff --git a/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/fetch_sub.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/fetch_sub.pass.cpp
index b37383d0dec5294..02a42144a4692e1 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/fetch_sub.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/fetch_sub.pass.cpp
@@ -5,6 +5,7 @@
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
+// UNSUPPORTED: no-threads
// UNSUPPORTED: c++03, c++11, c++14, c++17
// floating-point-type fetch_sub(floating-point-type,
>From 87f2cc351da3447e90b7261cf41370c58047983f Mon Sep 17 00:00:00 2001
From: Hui <hui.xie0621 at gmail.com>
Date: Wed, 11 Oct 2023 20:02:59 +0100
Subject: [PATCH 4/8] more tests
---
libcxx/include/atomic | 2 +-
.../atomics.types.float/assign.pass.cpp | 60 ++++
.../compare_exchange_strong.pass.cpp | 224 +++++++++++++++
.../compare_exchange_weak.pass.cpp | 258 ++++++++++++++++++
.../atomics.types.float/exchange.pass.cpp | 72 +++++
.../atomics.types.float/fetch_add.pass.cpp | 52 ++--
.../atomics.types.float/fetch_sub.pass.cpp | 52 ++--
.../atomics.types.float/load.pass.cpp | 129 +++++++++
.../operator.float.pass.cpp | 55 ++++
.../operator.minus_equals.pass.cpp | 35 ++-
.../operator.plus_equals.pass.cpp | 35 ++-
.../atomics.types.float/store.pass.cpp | 104 +++++++
.../atomics.types.float/test_helper.h | 96 +++++++
.../atomics.types.float/wait.pass.cpp | 80 ++++++
14 files changed, 1193 insertions(+), 61 deletions(-)
create mode 100644 libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/assign.pass.cpp
create mode 100644 libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/compare_exchange_strong.pass.cpp
create mode 100644 libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/compare_exchange_weak.pass.cpp
create mode 100644 libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/exchange.pass.cpp
create mode 100644 libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/load.pass.cpp
create mode 100644 libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/operator.float.pass.cpp
create mode 100644 libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/store.pass.cpp
create mode 100644 libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/test_helper.h
create mode 100644 libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/wait.pass.cpp
diff --git a/libcxx/include/atomic b/libcxx/include/atomic
index c52bf7521635f02..cafc1585dd453ce 100644
--- a/libcxx/include/atomic
+++ b/libcxx/include/atomic
@@ -262,7 +262,7 @@ struct atomic<T*>
void notify_all() noexcept;
};
-template<>
+template<>
struct atomic<floating-point-type> {
using value_type = floating-point-type;
using difference_type = value_type;
diff --git a/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/assign.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/assign.pass.cpp
new file mode 100644
index 000000000000000..4f9383c0b934c37
--- /dev/null
+++ b/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/assign.pass.cpp
@@ -0,0 +1,60 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+// UNSUPPORTED: no-threads
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+
+// floating-point-type operator=(floating-point-type) volatile noexcept;
+// floating-point-type operator=(floating-point-type) noexcept;
+
+#include <atomic>
+#include <cassert>
+#include <concepts>
+
+#include "test_helper.h"
+#include "test_macros.h"
+
+template <class T>
+concept HasVolatileAssign = requires(volatile std::atomic<T> a, T t) { a = t; };
+
+template <class T, template <class> class MaybeVolatile = std::type_identity_t>
+void testImpl() {
+ static_assert(HasVolatileAssign<T> == std::atomic<T>::is_always_lock_free);
+
+ static_assert(noexcept(std::declval<MaybeVolatile<std::atomic<T>>&>() = (T(0))));
+
+ // assignment
+ {
+ MaybeVolatile<std::atomic<T>> a(3.1);
+ std::same_as<T> decltype(auto) r = (a = T(1.2));
+ assert(a.load() == T(1.2));
+ assert(r == T(1.2));
+ }
+
+ // memory_order::seq_cst
+ {
+ auto assign = [](MaybeVolatile<std::atomic<T>>& x, T, T new_val) { x = new_val; };
+ auto load = [](MaybeVolatile<std::atomic<T>>& x) { return x.load(); };
+ test_seq_cst<T, MaybeVolatile>(assign, load);
+ }
+}
+
+template <class T>
+void test() {
+ testImpl<T>();
+ if constexpr (std::atomic<T>::is_always_lock_free) {
+ testImpl<T, std::add_volatile_t>();
+ }
+}
+
+int main(int, char**) {
+ test<float>();
+ test<double>();
+ test<long double>();
+
+ return 0;
+}
diff --git a/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/compare_exchange_strong.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/compare_exchange_strong.pass.cpp
new file mode 100644
index 000000000000000..b1f219865d8f95c
--- /dev/null
+++ b/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/compare_exchange_strong.pass.cpp
@@ -0,0 +1,224 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+// UNSUPPORTED: no-threads
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+
+// bool compare_exchange_strong(T& expected, T desired,
+// memory_order success, memory_order failure) volatile noexcept;
+// bool compare_exchange_strong(T& expected, T desired,
+// memory_order success, memory_order failure) noexcept;
+// bool compare_exchange_strong(T& expected, T desired,
+// memory_order order = memory_order::seq_cst) volatile noexcept;
+// bool compare_exchange_strong(T& expected, T desired,
+// memory_order order = memory_order::seq_cst) noexcept;
+
+#include <atomic>
+#include <cassert>
+#include <concepts>
+
+#include "test_helper.h"
+#include "test_macros.h"
+
+template <class T, class... Args>
+concept HasVolatileCompareExchangeStrong =
+ requires(volatile std::atomic<T> a, T t, Args... args) { a.compare_exchange_strong(t, t, args...); };
+
+template <class T, template <class> class MaybeVolatile, class... Args>
+concept HasNoexceptCompareExchangeStrong = requires(MaybeVolatile<std::atomic<T>> a, T t, Args... args) {
+ { a.compare_exchange_strong(t, t, args...) } noexcept;
+};
+
+template <class T, template <class> class MaybeVolatile = std::type_identity_t, class... MemoryOrder>
+void testBasic(MemoryOrder... memory_order) {
+ static_assert(HasVolatileCompareExchangeStrong<T, MemoryOrder...> == std::atomic<T>::is_always_lock_free);
+ static_assert(HasNoexceptCompareExchangeStrong<T, MaybeVolatile, MemoryOrder...>);
+
+ // compare pass
+ {
+ MaybeVolatile<std::atomic<T>> a(T(1.2));
+ T expected(1.2);
+ const T desired(2.3);
+ std::same_as<bool> decltype(auto) r = a.compare_exchange_strong(expected, desired, memory_order...);
+
+ assert(r);
+ assert(a.load() == desired);
+ assert(expected == T(1.2));
+ }
+
+ // compare fail
+ {
+ MaybeVolatile<std::atomic<T>> a(T(1.2));
+ T expected(1.5);
+ const T desired(2.3);
+ std::same_as<bool> decltype(auto) r = a.compare_exchange_strong(expected, desired, memory_order...);
+
+ assert(!r);
+ assert(a.load() == T(1.2));
+ assert(expected == T(1.2));
+ }
+}
+
+template <class T, template <class> class MaybeVolatile = std::type_identity_t>
+void testImpl() {
+ testBasic<T, MaybeVolatile>();
+ testBasic<T, MaybeVolatile>(std::memory_order::relaxed);
+ testBasic<T, MaybeVolatile>(std::memory_order::relaxed, std::memory_order_relaxed);
+
+ // test success memory order release
+ {
+ auto store = [](MaybeVolatile<std::atomic<T>>& x, T old_val, T new_val) {
+ auto r = x.compare_exchange_strong(old_val, new_val, std::memory_order::release, std::memory_order_relaxed);
+ assert(r);
+ };
+
+ auto load = [](MaybeVolatile<std::atomic<T>>& x) { return x.load(std::memory_order::acquire); };
+ test_acquire_release<T, MaybeVolatile>(store, load);
+
+ auto store_one_arg = [](MaybeVolatile<std::atomic<T>>& x, T old_val, T new_val) {
+ auto r = x.compare_exchange_strong(old_val, new_val, std::memory_order::release);
+ assert(r);
+ };
+ test_acquire_release<T, MaybeVolatile>(store_one_arg, load);
+ }
+
+ // test success memory order acquire
+ {
+ auto store = [](MaybeVolatile<std::atomic<T>>& x, T, T new_val) { x.store(new_val, std::memory_order::release); };
+ auto load = [](MaybeVolatile<std::atomic<T>>& x) {
+ auto val = x.load(std::memory_order::relaxed);
+ while (!x.compare_exchange_strong(val, val, std::memory_order::acquire, std::memory_order_relaxed)) {
+ }
+ return val;
+ };
+ test_acquire_release<T, MaybeVolatile>(store, load);
+
+ auto load_one_arg = [](MaybeVolatile<std::atomic<T>>& x) {
+ auto val = x.load(std::memory_order::relaxed);
+ while (!x.compare_exchange_strong(val, val, std::memory_order::acquire)) {
+ }
+ return val;
+ };
+ test_acquire_release<T, MaybeVolatile>(store, load_one_arg);
+ }
+
+ // test success memory order acq_rel
+ {
+ auto store = [](MaybeVolatile<std::atomic<T>>& x, T old_val, T new_val) {
+ auto r = x.compare_exchange_strong(old_val, new_val, std::memory_order::acq_rel, std::memory_order_relaxed);
+ assert(r);
+ };
+ auto load = [](MaybeVolatile<std::atomic<T>>& x) {
+ auto val = x.load(std::memory_order::relaxed);
+ while (!x.compare_exchange_strong(val, val, std::memory_order::acq_rel, std::memory_order_relaxed)) {
+ }
+ return val;
+ };
+ test_acquire_release<T, MaybeVolatile>(store, load);
+
+ auto store_one_arg = [](MaybeVolatile<std::atomic<T>>& x, T old_val, T new_val) {
+ auto r = x.compare_exchange_strong(old_val, new_val, std::memory_order::acq_rel);
+ assert(r);
+ };
+ auto load_one_arg = [](MaybeVolatile<std::atomic<T>>& x) {
+ auto val = x.load(std::memory_order::relaxed);
+ while (!x.compare_exchange_strong(val, val, std::memory_order::acq_rel)) {
+ }
+ return val;
+ };
+ test_acquire_release<T, MaybeVolatile>(store_one_arg, load_one_arg);
+ }
+
+ // test success memory seq_cst
+ {
+ auto store = [](MaybeVolatile<std::atomic<T>>& x, T old_val, T new_val) {
+ auto r = x.compare_exchange_strong(old_val, new_val, std::memory_order::seq_cst, std::memory_order_relaxed);
+ assert(r);
+ };
+ auto load = [](MaybeVolatile<std::atomic<T>>& x) {
+ auto val = x.load(std::memory_order::relaxed);
+ while (!x.compare_exchange_strong(val, val, std::memory_order::seq_cst, std::memory_order_relaxed)) {
+ }
+ return val;
+ };
+ test_seq_cst<T, MaybeVolatile>(store, load);
+
+ auto store_one_arg = [](MaybeVolatile<std::atomic<T>>& x, T old_val, T new_val) {
+ auto r = x.compare_exchange_strong(old_val, new_val, std::memory_order::seq_cst, std::memory_order_relaxed);
+ assert(r);
+ };
+ auto load_one_arg = [](MaybeVolatile<std::atomic<T>>& x) {
+ auto val = x.load(std::memory_order::relaxed);
+ while (!x.compare_exchange_strong(val, val, std::memory_order::seq_cst, std::memory_order_relaxed)) {
+ }
+ return val;
+ };
+ test_seq_cst<T, MaybeVolatile>(store_one_arg, load_one_arg);
+ }
+
+ // test fail memory order acquire
+ {
+ auto store = [](MaybeVolatile<std::atomic<T>>& x, T, T new_val) { x.store(new_val, std::memory_order::release); };
+ auto load = [](MaybeVolatile<std::atomic<T>>& x) {
+ auto result = x.load(std::memory_order::relaxed);
+ T unexpected(-9999.99);
+ bool r = x.compare_exchange_strong(unexpected, unexpected, std::memory_order_relaxed, std::memory_order_acquire);
+ assert(!r);
+ return result;
+ };
+ test_acquire_release<T, MaybeVolatile>(store, load);
+
+ auto load_one_arg = [](MaybeVolatile<std::atomic<T>>& x) {
+ auto result = x.load(std::memory_order::relaxed);
+ T unexpected(-9999.99);
+ bool r = x.compare_exchange_strong(unexpected, unexpected, std::memory_order_acquire);
+ assert(!r);
+ return result;
+ };
+ test_acquire_release<T, MaybeVolatile>(store, load_one_arg);
+
+ // acq_rel replaced by acquire
+ auto load_one_arg_acq_rel = [](MaybeVolatile<std::atomic<T>>& x) {
+ auto result = x.load(std::memory_order::relaxed);
+ T unexpected(-9999.99);
+ bool r = x.compare_exchange_strong(unexpected, unexpected, std::memory_order_acq_rel);
+ assert(!r);
+ return result;
+ };
+ test_acquire_release<T, MaybeVolatile>(store, load_one_arg_acq_rel);
+ }
+
+ // test fail memory order seq_cst
+ {
+ auto store = [](MaybeVolatile<std::atomic<T>>& x, T, T new_val) { x.store(new_val, std::memory_order::seq_cst); };
+ auto load = [](MaybeVolatile<std::atomic<T>>& x) {
+ auto result = x.load(std::memory_order::relaxed);
+ T unexpected(-9999.99);
+ bool r = x.compare_exchange_strong(unexpected, unexpected, std::memory_order_relaxed, std::memory_order::seq_cst);
+ assert(!r);
+ return result;
+ };
+ test_seq_cst<T, MaybeVolatile>(store, load);
+ }
+}
+
+template <class T>
+void test() {
+ testImpl<T>();
+ if constexpr (std::atomic<T>::is_always_lock_free) {
+ testImpl<T, std::add_volatile_t>();
+ }
+}
+
+int main(int, char**) {
+ test<float>();
+ test<double>();
+ // https://github.com/llvm/llvm-project/issues/47978
+ //test<long double>();
+
+ return 0;
+}
diff --git a/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/compare_exchange_weak.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/compare_exchange_weak.pass.cpp
new file mode 100644
index 000000000000000..d320fdefc081289
--- /dev/null
+++ b/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/compare_exchange_weak.pass.cpp
@@ -0,0 +1,258 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+// UNSUPPORTED: no-threads
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+
+// bool compare_exchange_weak(T& expected, T desired,
+// memory_order success, memory_order failure) volatile noexcept;
+// bool compare_exchange_weak(T& expected, T desired,
+// memory_order success, memory_order failure) noexcept;
+// bool compare_exchange_weak(T& expected, T desired,
+// memory_order order = memory_order::seq_cst) volatile noexcept;
+// bool compare_exchange_weak(T& expected, T desired,
+// memory_order order = memory_order::seq_cst) noexcept;
+
+#include <atomic>
+#include <cassert>
+#include <concepts>
+
+#include "test_helper.h"
+#include "test_macros.h"
+
+template <class T, class... Args>
+concept HasVolatileCompareExchangeWeak =
+ requires(volatile std::atomic<T> a, T t, Args... args) { a.compare_exchange_weak(t, t, args...); };
+
+template <class T, template <class> class MaybeVolatile, class... Args>
+concept HasNoexceptCompareExchangeWeak = requires(MaybeVolatile<std::atomic<T>> a, T t, Args... args) {
+ { a.compare_exchange_weak(t, t, args...) } noexcept;
+};
+
+template <class T, template <class> class MaybeVolatile = std::type_identity_t, class... MemoryOrder>
+void testBasic(MemoryOrder... memory_order) {
+ static_assert(HasVolatileCompareExchangeWeak<T, MemoryOrder...> == std::atomic<T>::is_always_lock_free);
+ static_assert(HasNoexceptCompareExchangeWeak<T, MaybeVolatile, MemoryOrder...>);
+
+ // compare pass
+ {
+ MaybeVolatile<std::atomic<T>> a(T(1.2));
+ T expected(1.2);
+ const T desired(2.3);
+ std::same_as<bool> decltype(auto) r = a.compare_exchange_weak(expected, desired, memory_order...);
+
+ // could be false spuriously
+ if (r) {
+ assert(a.load() == desired);
+ }
+ // if r is true, expected should be unmodified (1.2)
+ // if r is false, the original value of a (1.2) is written to expected
+ assert(expected == T(1.2));
+ }
+
+ // compare fail
+ {
+ MaybeVolatile<std::atomic<T>> a(T(1.2));
+ T expected(1.5);
+ const T desired(2.3);
+ std::same_as<bool> decltype(auto) r = a.compare_exchange_weak(expected, desired, memory_order...);
+
+ assert(!r);
+ assert(a.load() == T(1.2));
+
+ // bug
+ // https://github.com/llvm/llvm-project/issues/47978
+ if constexpr (!std::same_as<T, long double>) {
+ assert(expected == T(1.2));
+ }
+ }
+}
+
+// https://github.com/llvm/llvm-project/issues/47978
+template <class A, class T>
+void workaroundClangBug(A& atomic, T& expected) {
+ if constexpr (std::same_as<T, long double>) {
+ expected = atomic.load(std::memory_order::relaxed);
+ }
+}
+
+template <class T, template <class> class MaybeVolatile = std::type_identity_t>
+void testImpl() {
+ testBasic<T, MaybeVolatile>();
+ testBasic<T, MaybeVolatile>(std::memory_order::relaxed);
+ testBasic<T, MaybeVolatile>(std::memory_order::relaxed, std::memory_order_relaxed);
+
+ // test success memory order release
+ {
+ auto store = [](MaybeVolatile<std::atomic<T>>& x, T old_val, T new_val) {
+ // could fail spuriously, so put it in a loop
+ while (!x.compare_exchange_weak(old_val, new_val, std::memory_order::release, std::memory_order_relaxed)) {
+ workaroundClangBug(x, old_val);
+ }
+ };
+
+ auto load = [](MaybeVolatile<std::atomic<T>>& x) { return x.load(std::memory_order::acquire); };
+ test_acquire_release<T, MaybeVolatile>(store, load);
+
+ auto store_one_arg = [](MaybeVolatile<std::atomic<T>>& x, T old_val, T new_val) {
+ // could fail spuriously, so put it in a loop
+ while (!x.compare_exchange_weak(old_val, new_val, std::memory_order::release)) {
+ workaroundClangBug(x, old_val);
+ }
+ };
+ test_acquire_release<T, MaybeVolatile>(store_one_arg, load);
+ }
+
+ // test success memory order acquire
+ {
+ auto store = [](MaybeVolatile<std::atomic<T>>& x, T, T new_val) { x.store(new_val, std::memory_order::release); };
+ auto load = [](MaybeVolatile<std::atomic<T>>& x) {
+ auto val = x.load(std::memory_order::relaxed);
+ while (!x.compare_exchange_weak(val, val, std::memory_order::acquire, std::memory_order_relaxed)) {
+ workaroundClangBug(x, val);
+ }
+ return val;
+ };
+ test_acquire_release<T, MaybeVolatile>(store, load);
+
+ auto load_one_arg = [](MaybeVolatile<std::atomic<T>>& x) {
+ auto val = x.load(std::memory_order::relaxed);
+ while (!x.compare_exchange_weak(val, val, std::memory_order::acquire)) {
+ workaroundClangBug(x, val);
+ }
+ return val;
+ };
+ test_acquire_release<T, MaybeVolatile>(store, load_one_arg);
+ }
+
+ // test success memory order acq_rel
+ {
+ auto store = [](MaybeVolatile<std::atomic<T>>& x, T old_val, T new_val) {
+ // could fail spuriously, so put it in a loop
+ while (!x.compare_exchange_weak(old_val, new_val, std::memory_order::acq_rel, std::memory_order_relaxed)) {
+ workaroundClangBug(x, old_val);
+ }
+ };
+ auto load = [](MaybeVolatile<std::atomic<T>>& x) {
+ auto val = x.load(std::memory_order::relaxed);
+ while (!x.compare_exchange_weak(val, val, std::memory_order::acq_rel, std::memory_order_relaxed)) {
+ workaroundClangBug(x, val);
+ }
+ return val;
+ };
+ test_acquire_release<T, MaybeVolatile>(store, load);
+
+ auto store_one_arg = [](MaybeVolatile<std::atomic<T>>& x, T old_val, T new_val) {
+ // could fail spuriously, so put it in a loop
+ while (!x.compare_exchange_weak(old_val, new_val, std::memory_order::acq_rel)) {
+ workaroundClangBug(x, old_val);
+ }
+ };
+ auto load_one_arg = [](MaybeVolatile<std::atomic<T>>& x) {
+ auto val = x.load(std::memory_order::relaxed);
+ while (!x.compare_exchange_weak(val, val, std::memory_order::acq_rel)) {
+ workaroundClangBug(x, val);
+ }
+ return val;
+ };
+ test_acquire_release<T, MaybeVolatile>(store_one_arg, load_one_arg);
+ }
+
+ // test success memory seq_cst
+ {
+ auto store = [](MaybeVolatile<std::atomic<T>>& x, T old_val, T new_val) {
+ // could fail spuriously, so put it in a loop
+ while (!x.compare_exchange_weak(old_val, new_val, std::memory_order::seq_cst, std::memory_order_relaxed)) {
+ workaroundClangBug(x, old_val);
+ }
+ };
+ auto load = [](MaybeVolatile<std::atomic<T>>& x) {
+ auto val = x.load(std::memory_order::relaxed);
+ while (!x.compare_exchange_weak(val, val, std::memory_order::seq_cst, std::memory_order_relaxed)) {
+ workaroundClangBug(x, val);
+ }
+ return val;
+ };
+ test_seq_cst<T, MaybeVolatile>(store, load);
+
+ auto store_one_arg = [](MaybeVolatile<std::atomic<T>>& x, T old_val, T new_val) {
+ // could fail spuriously, so put it in a loop
+ while (!x.compare_exchange_weak(old_val, new_val, std::memory_order::seq_cst, std::memory_order_relaxed)) {
+ workaroundClangBug(x, old_val);
+ }
+ };
+ auto load_one_arg = [](MaybeVolatile<std::atomic<T>>& x) {
+ auto val = x.load(std::memory_order::relaxed);
+ while (!x.compare_exchange_weak(val, val, std::memory_order::seq_cst, std::memory_order_relaxed)) {
+ workaroundClangBug(x, val);
+ }
+ return val;
+ };
+ test_seq_cst<T, MaybeVolatile>(store_one_arg, load_one_arg);
+ }
+
+ // test fail memory order acquire
+ {
+ auto store = [](MaybeVolatile<std::atomic<T>>& x, T, T new_val) { x.store(new_val, std::memory_order::release); };
+ auto load = [](MaybeVolatile<std::atomic<T>>& x) {
+ auto result = x.load(std::memory_order::relaxed);
+ T unexpected(-9999.99);
+ bool r = x.compare_exchange_weak(unexpected, unexpected, std::memory_order_relaxed, std::memory_order_acquire);
+ assert(!r);
+ return result;
+ };
+ test_acquire_release<T, MaybeVolatile>(store, load);
+
+ auto load_one_arg = [](MaybeVolatile<std::atomic<T>>& x) {
+ auto result = x.load(std::memory_order::relaxed);
+ T unexpected(-9999.99);
+ bool r = x.compare_exchange_weak(unexpected, unexpected, std::memory_order_acquire);
+ assert(!r);
+ return result;
+ };
+ test_acquire_release<T, MaybeVolatile>(store, load_one_arg);
+
+ // acq_rel replaced by acquire
+ auto load_one_arg_acq_rel = [](MaybeVolatile<std::atomic<T>>& x) {
+ auto result = x.load(std::memory_order::relaxed);
+ T unexpected(-9999.99);
+ bool r = x.compare_exchange_weak(unexpected, unexpected, std::memory_order_acq_rel);
+ assert(!r);
+ return result;
+ };
+ test_acquire_release<T, MaybeVolatile>(store, load_one_arg_acq_rel);
+ }
+
+ // test fail memory order seq_cst
+ {
+ auto store = [](MaybeVolatile<std::atomic<T>>& x, T, T new_val) { x.store(new_val, std::memory_order::seq_cst); };
+ auto load = [](MaybeVolatile<std::atomic<T>>& x) {
+ auto result = x.load(std::memory_order::relaxed);
+ T unexpected(-9999.99);
+ bool r = x.compare_exchange_weak(unexpected, unexpected, std::memory_order_relaxed, std::memory_order::seq_cst);
+ assert(!r);
+ return result;
+ };
+ test_seq_cst<T, MaybeVolatile>(store, load);
+ }
+}
+
+template <class T>
+void test() {
+ testImpl<T>();
+ if constexpr (std::atomic<T>::is_always_lock_free) {
+ testImpl<T, std::add_volatile_t>();
+ }
+}
+
+int main(int, char**) {
+ test<float>();
+ test<double>();
+ test<long double>();
+
+ return 0;
+}
diff --git a/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/exchange.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/exchange.pass.cpp
new file mode 100644
index 000000000000000..63162f256cec93b
--- /dev/null
+++ b/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/exchange.pass.cpp
@@ -0,0 +1,72 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+// UNSUPPORTED: no-threads
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+
+// T exchange(T, memory_order = memory_order::seq_cst) volatile noexcept;
+// T exchange(T, memory_order = memory_order::seq_cst) noexcept;
+
+#include <atomic>
+#include <cassert>
+#include <concepts>
+
+#include "test_helper.h"
+#include "test_macros.h"
+
+template <class T>
+concept HasVolatileExchange = requires(volatile std::atomic<T> a, T t) { a.exchange(t); };
+
+template <class T, template <class> class MaybeVolatile = std::type_identity_t>
+void testImpl() {
+ static_assert(HasVolatileExchange<T> == std::atomic<T>::is_always_lock_free);
+ static_assert(noexcept(std::declval<MaybeVolatile<std::atomic<T>>&>() = (T(0))));
+
+ // exchange
+ {
+ MaybeVolatile<std::atomic<T>> a(3.1);
+ std::same_as<T> decltype(auto) r = a.exchange(T(1.2), std::memory_order::relaxed);
+ assert(a.load() == T(1.2));
+ assert(r == T(3.1));
+ }
+
+ // memory_order::release
+ {
+ auto exchange = [](MaybeVolatile<std::atomic<T>>& x, T, T new_val) {
+ x.exchange(new_val, std::memory_order::release);
+ };
+ auto load = [](MaybeVolatile<std::atomic<T>>& x) { return x.load(std::memory_order::acquire); };
+ test_acquire_release<T, MaybeVolatile>(exchange, load);
+ }
+
+ // memory_order::seq_cst
+ {
+ auto exchange_no_arg = [](MaybeVolatile<std::atomic<T>>& x, T, T new_val) { x.exchange(new_val); };
+ auto exchange_with_order = [](MaybeVolatile<std::atomic<T>>& x, T, T new_val) {
+ x.exchange(new_val, std::memory_order::seq_cst);
+ };
+ auto load = [](MaybeVolatile<std::atomic<T>>& x) { return x.load(); };
+ test_seq_cst<T, MaybeVolatile>(exchange_no_arg, load);
+ test_seq_cst<T, MaybeVolatile>(exchange_with_order, load);
+ }
+}
+
+template <class T>
+void test() {
+ testImpl<T>();
+ if constexpr (std::atomic<T>::is_always_lock_free) {
+ testImpl<T, std::add_volatile_t>();
+ }
+}
+
+int main(int, char**) {
+ test<float>();
+ test<double>();
+ test<long double>();
+
+ return 0;
+}
diff --git a/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/fetch_add.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/fetch_add.pass.cpp
index 54780e18e9bec4a..2a30ea8e2488654 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/fetch_add.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/fetch_add.pass.cpp
@@ -19,29 +19,21 @@
#include <thread>
#include <vector>
+#include "test_helper.h"
#include "test_macros.h"
-#include "make_test_thread.h"
template <class T>
concept HasVolatileFetchAdd = requires(volatile std::atomic<T> a, T t) { a.fetch_add(t); };
-template <class T>
-void test() {
- static_assert(noexcept(std::declval<std::atomic<T>&>().fetch_add(T(0))));
+template <class T, template <class> class MaybeVolatile = std::type_identity_t>
+void testImpl() {
static_assert(HasVolatileFetchAdd<T> == std::atomic<T>::is_always_lock_free);
+ static_assert(noexcept(std::declval<MaybeVolatile<std::atomic<T>>&>().fetch_add(T(0))));
// fetch_add
{
- std::atomic<T> a(3.1);
- std::same_as<T> decltype(auto) r = a.fetch_add(T(1.2));
- assert(r == T(3.1));
- assert(a.load() == T(3.1) + T(1.2));
- }
-
- // fetch_add volatile
- if constexpr (std::atomic<T>::is_always_lock_free) {
- volatile std::atomic<T> a(3.1);
- std::same_as<T> decltype(auto) r = a.fetch_add(T(1.2));
+ MaybeVolatile<std::atomic<T>> a(3.1);
+ std::same_as<T> decltype(auto) r = a.fetch_add(T(1.2), std::memory_order::relaxed);
assert(r == T(3.1));
assert(a.load() == T(3.1) + T(1.2));
}
@@ -51,14 +43,14 @@ void test() {
constexpr auto number_of_threads = 4;
constexpr auto loop = 1000;
- std::atomic<T> at;
+ MaybeVolatile<std::atomic<T>> at;
std::vector<std::thread> threads;
threads.reserve(number_of_threads);
for (auto i = 0; i < number_of_threads; ++i) {
threads.emplace_back([&at]() {
for (auto j = 0; j < loop; ++j) {
- at.fetch_add(T(1.234));
+ at.fetch_add(T(1.234), std::memory_order::relaxed);
}
});
}
@@ -77,6 +69,34 @@ void test() {
assert(at.load() == times(1.234, number_of_threads * loop));
}
+
+ // memory_order::release
+ {
+ auto store = [](MaybeVolatile<std::atomic<T>>& x, T old_val, T new_val) {
+ x.fetch_add(new_val - old_val, std::memory_order::release);
+ };
+ auto load = [](MaybeVolatile<std::atomic<T>>& x) { return x.load(std::memory_order::acquire); };
+ test_acquire_release<T, MaybeVolatile>(store, load);
+ }
+
+ // memory_order::seq_cst
+ {
+ auto fetch_add = [](MaybeVolatile<std::atomic<T>>& x, T old_value, T new_val) { x.fetch_add(new_val - old_value); };
+ auto fetch_add_with_order = [](MaybeVolatile<std::atomic<T>>& x, T old_value, T new_val) {
+ x.fetch_add(new_val - old_value, std::memory_order::seq_cst);
+ };
+ auto load = [](MaybeVolatile<std::atomic<T>>& x) { return x.load(); };
+ test_seq_cst<T, MaybeVolatile>(fetch_add, load);
+ test_seq_cst<T, MaybeVolatile>(fetch_add_with_order, load);
+ }
+}
+
+template <class T>
+void test() {
+ testImpl<T>();
+ if constexpr (std::atomic<T>::is_always_lock_free) {
+ testImpl<T, std::add_volatile_t>();
+ }
}
int main(int, char**) {
diff --git a/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/fetch_sub.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/fetch_sub.pass.cpp
index 02a42144a4692e1..5f38be7cf2a75d3 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/fetch_sub.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/fetch_sub.pass.cpp
@@ -19,29 +19,21 @@
#include <thread>
#include <vector>
+#include "test_helper.h"
#include "test_macros.h"
-#include "make_test_thread.h"
template <class T>
concept HasVolatileFetchSub = requires(volatile std::atomic<T> a, T t) { a.fetch_sub(t); };
-template <class T>
-void test() {
- static_assert(noexcept(std::declval<std::atomic<T>&>().fetch_sub(T(0))));
+template <class T, template <class> class MaybeVolatile = std::type_identity_t>
+void testImpl() {
static_assert(HasVolatileFetchSub<T> == std::atomic<T>::is_always_lock_free);
+ static_assert(noexcept(std::declval<MaybeVolatile<std::atomic<T>>&>().fetch_sub(T(0))));
// fetch_sub
{
- std::atomic<T> a(3.1);
- std::same_as<T> decltype(auto) r = a.fetch_sub(T(1.2));
- assert(r == T(3.1));
- assert(a.load() == T(3.1) - T(1.2));
- }
-
- // fetch_sub volatile
- if constexpr (std::atomic<T>::is_always_lock_free) {
- volatile std::atomic<T> a(3.1);
- std::same_as<T> decltype(auto) r = a.fetch_sub(T(1.2));
+ MaybeVolatile<std::atomic<T>> a(3.1);
+ std::same_as<T> decltype(auto) r = a.fetch_sub(T(1.2), std::memory_order::relaxed);
assert(r == T(3.1));
assert(a.load() == T(3.1) - T(1.2));
}
@@ -51,14 +43,14 @@ void test() {
constexpr auto number_of_threads = 4;
constexpr auto loop = 1000;
- std::atomic<T> at;
+ MaybeVolatile<std::atomic<T>> at;
std::vector<std::thread> threads;
threads.reserve(number_of_threads);
for (auto i = 0; i < number_of_threads; ++i) {
threads.emplace_back([&at]() {
for (auto j = 0; j < loop; ++j) {
- at.fetch_sub(T(1.234));
+ at.fetch_sub(T(1.234), std::memory_order::relaxed);
}
});
}
@@ -77,6 +69,34 @@ void test() {
assert(at.load() == accu_neg(1.234, number_of_threads * loop));
}
+
+ // memory_order::release
+ {
+ auto store = [](MaybeVolatile<std::atomic<T>>& x, T old_val, T new_val) {
+ x.fetch_sub(old_val - new_val, std::memory_order::release);
+ };
+ auto load = [](MaybeVolatile<std::atomic<T>>& x) { return x.load(std::memory_order::acquire); };
+ test_acquire_release<T, MaybeVolatile>(store, load);
+ }
+
+ // memory_order::seq_cst
+ {
+ auto fetch_sub = [](MaybeVolatile<std::atomic<T>>& x, T old_value, T new_val) { x.fetch_sub(old_value - new_val); };
+ auto fetch_sub_with_order = [](MaybeVolatile<std::atomic<T>>& x, T old_value, T new_val) {
+ x.fetch_sub(old_value - new_val, std::memory_order::seq_cst);
+ };
+ auto load = [](auto& x) { return x.load(); };
+ test_seq_cst<T, MaybeVolatile>(fetch_sub, load);
+ test_seq_cst<T, MaybeVolatile>(fetch_sub_with_order, load);
+ }
+}
+
+template <class T>
+void test() {
+ testImpl<T>();
+ if constexpr (std::atomic<T>::is_always_lock_free) {
+ testImpl<T, std::add_volatile_t>();
+ }
}
int main(int, char**) {
diff --git a/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/load.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/load.pass.cpp
new file mode 100644
index 000000000000000..b4e08154892230e
--- /dev/null
+++ b/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/load.pass.cpp
@@ -0,0 +1,129 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+// UNSUPPORTED: no-threads
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+
+// floating-point-type load(memory_order = memory_order::seq_cst) volatile noexcept;
+// floating-point-type load(memory_order = memory_order::seq_cst) noexcept;
+
+#include <algorithm>
+#include <atomic>
+#include <cassert>
+#include <concepts>
+#include <ranges>
+#include <thread>
+#include <vector>
+
+#include "test_helper.h"
+#include "test_macros.h"
+
+template <class T>
+concept HasVolatileLoad = requires(volatile std::atomic<T> a, T t) { a.load(); };
+
+template <class T, template <class> class MaybeVolatile = std::type_identity_t>
+void testImpl() {
+ static_assert(HasVolatileLoad<T> == std::atomic<T>::is_always_lock_free);
+ static_assert(noexcept(std::declval<MaybeVolatile<std::atomic<T>>&>().load()));
+
+ // load
+ {
+ MaybeVolatile<std::atomic<T>> a(3.1);
+ a.store(T(1.2));
+ std::same_as<T> decltype(auto) r = a.load(std::memory_order::relaxed);
+ assert(r == T(1.2));
+ }
+
+ // memory_order::relaxed
+ {
+ constexpr auto number_of_threads = 4;
+ constexpr auto loop = 1000;
+
+ MaybeVolatile<std::atomic<T>> at(T(-1.0));
+
+ std::vector<std::thread> threads;
+ threads.reserve(number_of_threads);
+ for (auto i = 0; i < number_of_threads; ++i) {
+ threads.emplace_back([&at, i]() {
+ for (auto j = 0; j < loop; ++j) {
+ at.store(T(i));
+ }
+ });
+ }
+
+ while (at.load(std::memory_order::relaxed) == T(-1.0)) {
+ std::this_thread::yield();
+ }
+
+ for (auto i = 0; i < loop; ++i) {
+ auto r = at.load(std::memory_order_relaxed);
+ assert(std::ranges::any_of(std::views::iota(0, number_of_threads), [r](auto i) { return r == T(i); }));
+ }
+
+ for (auto& thread : threads) {
+ thread.join();
+ }
+ }
+
+ // memory_order::comsume
+ {
+ std::unique_ptr<T> p = std::make_unique<T>(T(0.0));
+ MaybeVolatile<std::atomic<T>> at(T(0.0));
+
+ constexpr auto number_of_threads = 8;
+ std::vector<std::thread> threads;
+ threads.reserve(number_of_threads);
+
+ for (auto i = 0; i < number_of_threads; ++i) {
+ threads.emplace_back([&at, &p] {
+ while (at.load(std::memory_order::consume) == T(0.0)) {
+ std::this_thread::yield();
+ }
+ assert(*p == T(1.0)); // the write from other thread should be visible
+ });
+ }
+
+ *p = T(1.0);
+ at.store(*p, std::memory_order_release);
+
+ for (auto& thread : threads) {
+ thread.join();
+ }
+ }
+
+ // memory_order::acquire
+ {
+ auto store = [](MaybeVolatile<std::atomic<T>>& x, T, T new_val) { x.store(new_val, std::memory_order::release); };
+ auto load = [](MaybeVolatile<std::atomic<T>>& x) { return x.load(std::memory_order::acquire); };
+ test_acquire_release<T, MaybeVolatile>(store, load);
+ }
+
+ // memory_order::seq_cst
+ {
+ auto store = [](MaybeVolatile<std::atomic<T>>& x, T, T new_val) { x.store(new_val); };
+ auto load_no_arg = [](MaybeVolatile<std::atomic<T>>& x) { return x.load(); };
+ auto load_with_order = [](MaybeVolatile<std::atomic<T>>& x) { return x.load(std::memory_order::seq_cst); };
+ test_seq_cst<T, MaybeVolatile>(store, load_no_arg);
+ test_seq_cst<T, MaybeVolatile>(store, load_with_order);
+ }
+}
+
+template <class T>
+void test() {
+ testImpl<T>();
+ if constexpr (std::atomic<T>::is_always_lock_free) {
+ testImpl<T, std::add_volatile_t>();
+ }
+}
+
+int main(int, char**) {
+ test<float>();
+ test<double>();
+ test<long double>();
+
+ return 0;
+}
diff --git a/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/operator.float.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/operator.float.pass.cpp
new file mode 100644
index 000000000000000..88c7f1c6e3bb1be
--- /dev/null
+++ b/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/operator.float.pass.cpp
@@ -0,0 +1,55 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+// UNSUPPORTED: no-threads
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+
+// operator floating-point-type() volatile noexcept;
+// operator floating-point-type() noexcept;
+
+#include <atomic>
+#include <cassert>
+#include <concepts>
+
+#include "test_helper.h"
+#include "test_macros.h"
+
+template <class T, template <class> class MaybeVolatile = std::type_identity_t>
+void testImpl() {
+ static_assert(std::is_convertible_v<volatile std::atomic<T>&, T> == std::atomic<T>::is_always_lock_free);
+ static_assert(noexcept(T(std::declval<MaybeVolatile<std::atomic<T>>&>())));
+
+ // operator float
+ {
+ MaybeVolatile<std::atomic<T>> a(3.1);
+ T r = a;
+ assert(r == T(3.1));
+ }
+
+ // memory_order::seq_cst
+ {
+ auto store = [](MaybeVolatile<std::atomic<T>>& x, T, T new_val) { x.store(new_val); };
+ auto op_float = [](MaybeVolatile<std::atomic<T>>& x) -> T { return x; };
+ test_seq_cst<T, MaybeVolatile>(store, op_float);
+ }
+}
+
+template <class T>
+void test() {
+ testImpl<T>();
+ if constexpr (std::atomic<T>::is_always_lock_free) {
+ testImpl<T, std::add_volatile_t>();
+ }
+}
+
+int main(int, char**) {
+ test<float>();
+ test<double>();
+ test<long double>();
+
+ return 0;
+}
diff --git a/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/operator.minus_equals.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/operator.minus_equals.pass.cpp
index 5a04e9e94027d4a..f48441641012dff 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/operator.minus_equals.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/operator.minus_equals.pass.cpp
@@ -16,28 +16,20 @@
#include <thread>
#include <vector>
+#include "test_helper.h"
#include "test_macros.h"
-#include "make_test_thread.h"
template <class T>
concept HasVolatileMinusEquals = requires(volatile std::atomic<T> a, T t) { a -= t; };
-template <class T>
-void test() {
- static_assert(noexcept(std::declval<std::atomic<T>&>() -= T(0)));
+template <class T, template <class> class MaybeVolatile = std::type_identity_t>
+void testImpl() {
static_assert(HasVolatileMinusEquals<T> == std::atomic<T>::is_always_lock_free);
+ static_assert(noexcept(std::declval<MaybeVolatile<std::atomic<T>>&>() -= T(0)));
// -=
{
- std::atomic<T> a(3.1);
- std::same_as<T> decltype(auto) r = a -= T(1.2);
- assert(r == T(3.1) - T(1.2));
- assert(a.load() == T(3.1) - T(1.2));
- }
-
- // -= volatile
- if constexpr (std::atomic<T>::is_always_lock_free) {
- volatile std::atomic<T> a(3.1);
+ MaybeVolatile<std::atomic<T>> a(3.1);
std::same_as<T> decltype(auto) r = a -= T(1.2);
assert(r == T(3.1) - T(1.2));
assert(a.load() == T(3.1) - T(1.2));
@@ -48,7 +40,7 @@ void test() {
constexpr auto number_of_threads = 4;
constexpr auto loop = 1000;
- std::atomic<T> at;
+ MaybeVolatile<std::atomic<T>> at;
std::vector<std::thread> threads;
threads.reserve(number_of_threads);
@@ -74,6 +66,21 @@ void test() {
assert(at.load() == accu_neg(1.234, number_of_threads * loop));
}
+
+ // memory_order::seq_cst
+ {
+ auto minus_equals = [](MaybeVolatile<std::atomic<T>>& x, T old_value, T new_val) { x -= (old_value - new_val); };
+ auto load = [](MaybeVolatile<std::atomic<T>>& x) { return x.load(); };
+ test_seq_cst<T, MaybeVolatile>(minus_equals, load);
+ }
+}
+
+template <class T>
+void test() {
+ testImpl<T>();
+ if constexpr (std::atomic<T>::is_always_lock_free) {
+ testImpl<T, std::add_volatile_t>();
+ }
}
int main(int, char**) {
diff --git a/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/operator.plus_equals.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/operator.plus_equals.pass.cpp
index 9cace80331736b8..f617ded35138ec6 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/operator.plus_equals.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/operator.plus_equals.pass.cpp
@@ -16,28 +16,20 @@
#include <thread>
#include <vector>
+#include "test_helper.h"
#include "test_macros.h"
-#include "make_test_thread.h"
template <class T>
concept HasVolatilePlusEquals = requires(volatile std::atomic<T> a, T t) { a += t; };
-template <class T>
-void test() {
- static_assert(noexcept(std::declval<std::atomic<T>&>() += T(0)));
+template <class T, template <class> class MaybeVolatile = std::type_identity_t>
+void testImpl() {
static_assert(HasVolatilePlusEquals<T> == std::atomic<T>::is_always_lock_free);
+ static_assert(noexcept(std::declval<MaybeVolatile<std::atomic<T>>&>() += T(0)));
// +=
{
- std::atomic<T> a(3.1);
- std::same_as<T> decltype(auto) r = a += T(1.2);
- assert(r == T(3.1) + T(1.2));
- assert(a.load() == T(3.1) + T(1.2));
- }
-
- // += volatile
- if constexpr (std::atomic<T>::is_always_lock_free) {
- volatile std::atomic<T> a(3.1);
+ MaybeVolatile<std::atomic<T>> a(3.1);
std::same_as<T> decltype(auto) r = a += T(1.2);
assert(r == T(3.1) + T(1.2));
assert(a.load() == T(3.1) + T(1.2));
@@ -48,7 +40,7 @@ void test() {
constexpr auto number_of_threads = 4;
constexpr auto loop = 1000;
- std::atomic<T> at;
+ MaybeVolatile<std::atomic<T>> at;
std::vector<std::thread> threads;
threads.reserve(number_of_threads);
@@ -74,6 +66,21 @@ void test() {
assert(at.load() == times(1.234, number_of_threads * loop));
}
+
+ // memory_order::seq_cst
+ {
+ auto plus_equals = [](MaybeVolatile<std::atomic<T>>& x, T old_value, T new_val) { x += (new_val - old_value); };
+ auto load = [](MaybeVolatile<std::atomic<T>>& x) { return x.load(); };
+ test_seq_cst<T, MaybeVolatile>(plus_equals, load);
+ }
+}
+
+template <class T>
+void test() {
+ testImpl<T>();
+ if constexpr (std::atomic<T>::is_always_lock_free) {
+ testImpl<T, std::add_volatile_t>();
+ }
}
int main(int, char**) {
diff --git a/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/store.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/store.pass.cpp
new file mode 100644
index 000000000000000..cbfbbd3bc091266
--- /dev/null
+++ b/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/store.pass.cpp
@@ -0,0 +1,104 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+// UNSUPPORTED: no-threads
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+
+// void store(floating-point-type, memory_order = memory_order::seq_cst) volatile noexcept;
+// void store(floating-point-type, memory_order = memory_order::seq_cst) noexcept;
+
+#include <algorithm>
+#include <atomic>
+#include <cassert>
+#include <concepts>
+#include <ranges>
+#include <thread>
+#include <vector>
+
+#include "test_helper.h"
+#include "test_macros.h"
+
+template <class T>
+concept HasVolatileStore = requires(volatile std::atomic<T> a, T t) { a.store(t); };
+
+template <class T, template <class> class MaybeVolatile = std::type_identity_t>
+void testImpl() {
+ static_assert(HasVolatileStore<T> == std::atomic<T>::is_always_lock_free);
+ static_assert(noexcept(std::declval<MaybeVolatile<std::atomic<T>>&>().store(T(0))));
+
+ // store
+ {
+ MaybeVolatile<std::atomic<T>> a(3.1);
+ a.store(T(1.2), std::memory_order::relaxed);
+ assert(a.load() == T(1.2));
+ }
+
+ // memory_order::relaxed
+ {
+ constexpr auto number_of_threads = 4;
+ constexpr auto loop = 1000;
+
+ MaybeVolatile<std::atomic<T>> at(T(-1.0));
+
+ std::vector<std::thread> threads;
+ threads.reserve(number_of_threads);
+ for (auto i = 0; i < number_of_threads; ++i) {
+ threads.emplace_back([&at, i]() {
+ for (auto j = 0; j < loop; ++j) {
+ at.store(T(i), std::memory_order_relaxed);
+ }
+ });
+ }
+
+ while (at.load() == T(-1.0)) {
+ std::this_thread::yield();
+ }
+
+ for (auto i = 0; i < loop; ++i) {
+ auto r = at.load();
+ assert(std::ranges::any_of(std::views::iota(0, number_of_threads), [r](auto i) { return r == T(i); }));
+ }
+
+ for (auto& thread : threads) {
+ thread.join();
+ }
+ }
+
+ // memory_order::release
+ {
+ auto store = [](MaybeVolatile<std::atomic<T>>& x, T, T new_val) { x.store(new_val, std::memory_order::release); };
+ auto load = [](MaybeVolatile<std::atomic<T>>& x) { return x.load(std::memory_order::acquire); };
+ test_acquire_release<T, MaybeVolatile>(store, load);
+ }
+
+ // memory_order::seq_cst
+ {
+ auto store_no_arg = [](MaybeVolatile<std::atomic<T>>& x, T, T new_val) { x.store(new_val); };
+ auto store_with_order = [](MaybeVolatile<std::atomic<T>>& x, T, T new_val) {
+ x.store(new_val, std::memory_order::seq_cst);
+ };
+ auto load = [](MaybeVolatile<std::atomic<T>>& x) { return x.load(); };
+ test_seq_cst<T, MaybeVolatile>(store_no_arg, load);
+ test_seq_cst<T, MaybeVolatile>(store_with_order, load);
+ }
+}
+
+template <class T>
+void test() {
+ testImpl<T>();
+ if constexpr (std::atomic<T>::is_always_lock_free) {
+ testImpl<T, std::add_volatile_t>();
+ }
+}
+
+int main(int, char**) {
+ test<float>();
+ test<double>();
+ test<long double>();
+
+ return 0;
+}
diff --git a/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/test_helper.h b/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/test_helper.h
new file mode 100644
index 000000000000000..fac869b1d32ab55
--- /dev/null
+++ b/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/test_helper.h
@@ -0,0 +1,96 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef TEST_STD_ATOMICS_ATOMICS_TYPES_FLOAT_TEST_HELPER_H
+#define TEST_STD_ATOMICS_ATOMICS_TYPES_FLOAT_TEST_HELPER_H
+
+#include <atomic>
+#include <cassert>
+#include <thread>
+#include <vector>
+
+// Test that all threads see the exact same sequence of events
+// Test will pass 100% if store_op and load_op are correctly
+// affecting the memory with seq_cst order
+template <class T, template <class> class MaybeVolatile, class StoreOp, class LoadOp>
+void test_seq_cst(StoreOp store_op, LoadOp load_op) {
+ for (int i = 0; i < 100; ++i) {
+ T old_value = 0.0;
+ T new_value = 1.0;
+
+ MaybeVolatile<std::atomic<T>> x(old_value);
+ MaybeVolatile<std::atomic<T>> y(old_value);
+
+ std::atomic_bool x_update_first(false);
+ std::atomic_bool y_update_first(false);
+
+ std::thread t1([&] { store_op(x, old_value, new_value); });
+
+ std::thread t2([&] { store_op(y, old_value, new_value); });
+
+ std::thread t3([&] {
+ while (load_op(x) != new_value) {
+ std::this_thread::yield();
+ }
+ if (load_op(y) != new_value) {
+ x_update_first.store(true, std::memory_order_relaxed);
+ }
+ });
+
+ std::thread t4([&] {
+ while (load_op(y) != new_value) {
+ std::this_thread::yield();
+ }
+ if (load_op(x) != new_value) {
+ y_update_first.store(true, std::memory_order_relaxed);
+ }
+ });
+
+ t1.join();
+ t2.join();
+ t3.join();
+ t4.join();
+ assert(!(x_update_first && y_update_first));
+ }
+}
+
+// Test that all writes before the store are seen by other threads after the load
+// Test will pass 100% if store_op and load_op are correctly
+// affecting the memory with acquire-release order
+template <class T, template <class> class MaybeVolatile, class StoreOp, class LoadOp>
+void test_acquire_release(StoreOp store_op, LoadOp load_op) {
+ for (auto i = 0; i < 100; ++i) {
+ T old_value = 0.0;
+ T new_value = 1.0;
+
+ MaybeVolatile<std::atomic<T>> at(old_value);
+ int non_atomic = 5;
+
+ constexpr auto number_of_threads = 4;
+ std::vector<std::thread> threads;
+ threads.reserve(number_of_threads);
+
+ for (auto j = 0; j < number_of_threads; ++j) {
+ threads.emplace_back([&at, &non_atomic, load_op, new_value] {
+ while (load_op(at) != new_value) {
+ std::this_thread::yield();
+ }
+ assert(non_atomic == 6);
+ });
+ }
+
+ non_atomic = 6;
+ store_op(at, old_value, new_value);
+
+ for (auto& thread : threads) {
+ thread.join();
+ }
+ }
+}
+
+#endif // TEST_STD_ATOMICS_ATOMICS_TYPES_FLOAT_TEST_HELPER_H
diff --git a/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/wait.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/wait.pass.cpp
new file mode 100644
index 000000000000000..a697a6706de503c
--- /dev/null
+++ b/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/wait.pass.cpp
@@ -0,0 +1,80 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+// UNSUPPORTED: no-threads
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+
+// void wait(T old, memory_order order = memory_order::seq_cst) const volatile noexcept;
+// void wait(T old, memory_order order = memory_order::seq_cst) const noexcept;
+
+#include <atomic>
+#include <cassert>
+#include <concepts>
+#include <thread>
+#include <vector>
+
+#include "test_helper.h"
+#include "test_macros.h"
+
+template <class T>
+concept HasVolatileWait = requires(volatile std::atomic<T> a, T t) { a.wait(T()); };
+
+template <class T, template <class> class MaybeVolatile = std::type_identity_t>
+void testImpl() {
+ static_assert(HasVolatileWait<T> == std::atomic<T>::is_always_lock_free);
+ static_assert(noexcept(std::declval<MaybeVolatile<std::atomic<T>>&>().wait(T())));
+
+ // wait different value
+ {
+ MaybeVolatile<std::atomic<T>> a(3.1);
+ a.wait(T(1.1), std::memory_order::relaxed);
+ }
+
+ // memory_order::acquire
+ {
+ auto store = [](MaybeVolatile<std::atomic<T>>& x, T, T new_val) { x.store(new_val, std::memory_order::release); };
+ auto load = [](MaybeVolatile<std::atomic<T>>& x) {
+ auto result = x.load(std::memory_order::relaxed);
+ x.wait(T(9999.999), std::memory_order::acquire);
+ return result;
+ };
+ test_acquire_release<T, MaybeVolatile>(store, load);
+ }
+
+ // memory_order::seq_cst
+ {
+ auto store = [](MaybeVolatile<std::atomic<T>>& x, T, T new_val) { x.store(new_val); };
+ auto load_no_arg = [](MaybeVolatile<std::atomic<T>>& x) {
+ auto result = x.load(std::memory_order::relaxed);
+ x.wait(T(9999.999));
+ return result;
+ };
+ auto load_with_order = [](MaybeVolatile<std::atomic<T>>& x) {
+ auto result = x.load(std::memory_order::relaxed);
+ x.wait(T(9999.999), std::memory_order::seq_cst);
+ return result;
+ };
+ test_seq_cst<T, MaybeVolatile>(store, load_no_arg);
+ test_seq_cst<T, MaybeVolatile>(store, load_with_order);
+ }
+}
+
+template <class T>
+void test() {
+ testImpl<T>();
+ if constexpr (std::atomic<T>::is_always_lock_free) {
+ testImpl<T, std::add_volatile_t>();
+ }
+}
+
+int main(int, char**) {
+ test<float>();
+ test<double>();
+ test<long double>();
+
+ return 0;
+}
>From 71664eff8225525252d4b5c7dc7af22f7cb76611 Mon Sep 17 00:00:00 2001
From: Hui <hui.xie0621 at gmail.com>
Date: Wed, 11 Oct 2023 23:47:09 +0100
Subject: [PATCH 5/8] more tests
---
.../atomics.types.float/notify_all.pass.cpp | 94 +++++++++++++++++++
.../atomics.types.float/notify_one.pass.cpp | 80 ++++++++++++++++
.../atomics.types.float/test_helper.h | 2 +-
.../atomics.types.float/wait.pass.cpp | 37 +++++++-
4 files changed, 210 insertions(+), 3 deletions(-)
create mode 100644 libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/notify_all.pass.cpp
create mode 100644 libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/notify_one.pass.cpp
diff --git a/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/notify_all.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/notify_all.pass.cpp
new file mode 100644
index 000000000000000..0c4898e0b121c26
--- /dev/null
+++ b/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/notify_all.pass.cpp
@@ -0,0 +1,94 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+// UNSUPPORTED: no-threads
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+
+// void notify_all() volatile noexcept;
+// void notify_all() noexcept;
+
+#include <atomic>
+#include <cassert>
+#include <concepts>
+#include <thread>
+#include <vector>
+
+#include "test_helper.h"
+#include "test_macros.h"
+
+template <class T>
+concept HasVolatileNotifyAll = requires(volatile std::atomic<T> a, T t) { a.notify_all(); };
+
+template <class T, template <class> class MaybeVolatile = std::type_identity_t>
+void testImpl() {
+ static_assert(HasVolatileNotifyAll<T> == std::atomic<T>::is_always_lock_free);
+ static_assert(noexcept(std::declval<MaybeVolatile<std::atomic<T>>&>().notify_all()));
+
+ // bug?? wait can also fail for long double ??
+ // should x87 80bit long double work at all?
+ if constexpr (!std::same_as<T, long double>) {
+ for (auto i = 0; i < 100; ++i) {
+ const T old = 3.1;
+ MaybeVolatile<std::atomic<T>> a(old);
+
+ bool done = false;
+ std::atomic<int> started_num = 0;
+ std::atomic<int> wait_done_num = 0;
+
+ constexpr auto number_of_threads = 8;
+ std::vector<std::thread> threads;
+ threads.reserve(number_of_threads);
+
+ for (auto j = 0; j < number_of_threads; ++j) {
+ threads.emplace_back([&a, &started_num, old, &done, &wait_done_num] {
+ started_num.fetch_add(1, std::memory_order::relaxed);
+
+ a.wait(old);
+ wait_done_num.fetch_add(1, std::memory_order::relaxed);
+
+ // likely to fail if wait did not block
+ assert(done);
+ });
+ }
+
+ while (started_num.load(std::memory_order::relaxed) != number_of_threads) {
+ std::this_thread::yield();
+ }
+
+ std::this_thread::sleep_for(std::chrono::milliseconds(1));
+
+ done = true;
+ a.store(T(9.9));
+ a.notify_all();
+
+ // notify_all should unblock all the threads so that the loop below won't stuck
+ while (wait_done_num.load(std::memory_order::relaxed) != number_of_threads) {
+ std::this_thread::yield();
+ }
+
+ for (auto& thread : threads) {
+ thread.join();
+ }
+ }
+ }
+}
+
+template <class T>
+void test() {
+ testImpl<T>();
+ if constexpr (std::atomic<T>::is_always_lock_free) {
+ testImpl<T, std::add_volatile_t>();
+ }
+}
+
+int main(int, char**) {
+ test<float>();
+ test<double>();
+ test<long double>();
+
+ return 0;
+}
diff --git a/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/notify_one.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/notify_one.pass.cpp
new file mode 100644
index 000000000000000..a6265f78cd11572
--- /dev/null
+++ b/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/notify_one.pass.cpp
@@ -0,0 +1,80 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+// UNSUPPORTED: no-threads
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+
+// void notify_one() volatile noexcept;
+// void notify_one() noexcept;
+
+#include <atomic>
+#include <cassert>
+#include <concepts>
+#include <thread>
+#include <vector>
+
+#include "test_helper.h"
+#include "test_macros.h"
+
+template <class T>
+concept HasVolatileNotifyOne = requires(volatile std::atomic<T> a, T t) { a.notify_one(); };
+
+template <class T, template <class> class MaybeVolatile = std::type_identity_t>
+void testImpl() {
+ static_assert(HasVolatileNotifyOne<T> == std::atomic<T>::is_always_lock_free);
+ static_assert(noexcept(std::declval<MaybeVolatile<std::atomic<T>>&>().notify_one()));
+
+
+ // bug?? wait can also fail for long double ??
+ // should x87 80bit long double work at all?
+ if constexpr (!std::same_as<T, long double>) {
+ for (auto i = 0; i < 100; ++i) {
+ const T old = 3.1;
+ MaybeVolatile<std::atomic<T>> a(old);
+
+ std::atomic_bool started = false;
+ bool done = false;
+
+ std::thread t([&a, &started, old, &done] {
+ started.store(true, std::memory_order::relaxed);
+
+ a.wait(old);
+
+ // likely to fail if wait did not block
+ assert(done);
+ });
+
+ while (!started.load(std::memory_order::relaxed)) {
+ std::this_thread::yield();
+ }
+
+ std::this_thread::sleep_for(std::chrono::milliseconds(1));
+
+ done = true;
+ a.store(T(9.9));
+ a.notify_one();
+ t.join();
+ }
+ }
+
+}
+
+template <class T>
+void test() {
+ testImpl<T>();
+ if constexpr (std::atomic<T>::is_always_lock_free) {
+ testImpl<T, std::add_volatile_t>();
+ }
+}
+
+int main(int, char**) {
+ test<float>();
+ test<double>();
+ test<long double>();
+
+ return 0;
+}
diff --git a/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/test_helper.h b/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/test_helper.h
index fac869b1d32ab55..5ccb9cb86a1803e 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/test_helper.h
+++ b/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/test_helper.h
@@ -71,7 +71,7 @@ void test_acquire_release(StoreOp store_op, LoadOp load_op) {
MaybeVolatile<std::atomic<T>> at(old_value);
int non_atomic = 5;
- constexpr auto number_of_threads = 4;
+ constexpr auto number_of_threads = 8;
std::vector<std::thread> threads;
threads.reserve(number_of_threads);
diff --git a/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/wait.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/wait.pass.cpp
index a697a6706de503c..94b915dd236fd66 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/wait.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/wait.pass.cpp
@@ -28,12 +28,45 @@ void testImpl() {
static_assert(HasVolatileWait<T> == std::atomic<T>::is_always_lock_free);
static_assert(noexcept(std::declval<MaybeVolatile<std::atomic<T>>&>().wait(T())));
- // wait different value
+ // wait with different value
{
- MaybeVolatile<std::atomic<T>> a(3.1);
+ MaybeVolatile<std::atomic<T>> a(T(3.1));
a.wait(T(1.1), std::memory_order::relaxed);
}
+ // equal at the beginning and changed later
+ // bug?? wait can also fail for long double ??
+ // should x87 80bit long double work at all?
+ if constexpr (!std::same_as<T, long double>) {
+ for (auto i = 0; i < 100; ++i) {
+ const T old = 3.1;
+ MaybeVolatile<std::atomic<T>> a(old);
+
+ std::atomic_bool started = false;
+ bool done = false;
+
+ std::thread t([&a, &started, old, &done] {
+ started.store(true, std::memory_order::relaxed);
+
+ a.wait(old);
+
+ // likely to fail if wait did not block
+ assert(done);
+ });
+
+ while (!started.load(std::memory_order::relaxed)) {
+ std::this_thread::yield();
+ }
+
+ std::this_thread::sleep_for(std::chrono::milliseconds(1));
+
+ done = true;
+ a.store(T(9.9));
+ a.notify_all();
+ t.join();
+ }
+ }
+
// memory_order::acquire
{
auto store = [](MaybeVolatile<std::atomic<T>>& x, T, T new_val) { x.store(new_val, std::memory_order::release); };
>From 161ea3fc774c4582eddb15e8bf083112fef8d753 Mon Sep 17 00:00:00 2001
From: Hui <hui.xie0621 at gmail.com>
Date: Thu, 12 Oct 2023 00:05:21 +0100
Subject: [PATCH 6/8] shadow
---
.../atomics.types.generic/atomics.types.float/load.pass.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/load.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/load.pass.cpp
index b4e08154892230e..b80e84a2c75fe3f 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/load.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/load.pass.cpp
@@ -61,7 +61,7 @@ void testImpl() {
for (auto i = 0; i < loop; ++i) {
auto r = at.load(std::memory_order_relaxed);
- assert(std::ranges::any_of(std::views::iota(0, number_of_threads), [r](auto i) { return r == T(i); }));
+ assert(std::ranges::any_of(std::views::iota(0, number_of_threads), [r](auto j) { return r == T(j); }));
}
for (auto& thread : threads) {
>From 788bb07e20dee36000ac195c06cde5f68dee98e6 Mon Sep 17 00:00:00 2001
From: Hui <hui.xie0621 at gmail.com>
Date: Fri, 13 Oct 2023 13:06:17 +0100
Subject: [PATCH 7/8] CI
---
libcxx/test/libcxx/transitive_includes/cxx03.csv | 2 ++
libcxx/test/libcxx/transitive_includes/cxx11.csv | 2 ++
libcxx/test/libcxx/transitive_includes/cxx14.csv | 2 ++
libcxx/test/libcxx/transitive_includes/cxx17.csv | 2 ++
libcxx/test/libcxx/transitive_includes/cxx20.csv | 2 ++
libcxx/test/libcxx/transitive_includes/cxx23.csv | 6 ++++++
libcxx/test/libcxx/transitive_includes/cxx26.csv | 6 ++++++
.../atomics.types.float/notify_one.pass.cpp | 2 --
8 files changed, 22 insertions(+), 2 deletions(-)
diff --git a/libcxx/test/libcxx/transitive_includes/cxx03.csv b/libcxx/test/libcxx/transitive_includes/cxx03.csv
index f6aeb837a7292f6..b662f79d6e0a9b7 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx03.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx03.csv
@@ -54,6 +54,7 @@ atomic cmath
atomic compare
atomic cstddef
atomic cstdint
+atomic cstdio
atomic cstdlib
atomic cstring
atomic ctime
@@ -800,6 +801,7 @@ stdexcept iosfwd
stop_token atomic
stop_token cstddef
stop_token cstdint
+stop_token cstdio
stop_token cstring
stop_token ctime
stop_token iosfwd
diff --git a/libcxx/test/libcxx/transitive_includes/cxx11.csv b/libcxx/test/libcxx/transitive_includes/cxx11.csv
index 08fd94393f47320..016b2d23c34f6b1 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx11.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx11.csv
@@ -54,6 +54,7 @@ atomic cmath
atomic compare
atomic cstddef
atomic cstdint
+atomic cstdio
atomic cstdlib
atomic cstring
atomic ctime
@@ -806,6 +807,7 @@ stdexcept iosfwd
stop_token atomic
stop_token cstddef
stop_token cstdint
+stop_token cstdio
stop_token cstring
stop_token ctime
stop_token iosfwd
diff --git a/libcxx/test/libcxx/transitive_includes/cxx14.csv b/libcxx/test/libcxx/transitive_includes/cxx14.csv
index 33384a628e65c01..78342c38ca50dfb 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx14.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx14.csv
@@ -54,6 +54,7 @@ atomic cmath
atomic compare
atomic cstddef
atomic cstdint
+atomic cstdio
atomic cstdlib
atomic cstring
atomic ctime
@@ -808,6 +809,7 @@ stdexcept iosfwd
stop_token atomic
stop_token cstddef
stop_token cstdint
+stop_token cstdio
stop_token cstring
stop_token ctime
stop_token iosfwd
diff --git a/libcxx/test/libcxx/transitive_includes/cxx17.csv b/libcxx/test/libcxx/transitive_includes/cxx17.csv
index 33384a628e65c01..78342c38ca50dfb 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx17.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx17.csv
@@ -54,6 +54,7 @@ atomic cmath
atomic compare
atomic cstddef
atomic cstdint
+atomic cstdio
atomic cstdlib
atomic cstring
atomic ctime
@@ -808,6 +809,7 @@ stdexcept iosfwd
stop_token atomic
stop_token cstddef
stop_token cstdint
+stop_token cstdio
stop_token cstring
stop_token ctime
stop_token iosfwd
diff --git a/libcxx/test/libcxx/transitive_includes/cxx20.csv b/libcxx/test/libcxx/transitive_includes/cxx20.csv
index 8f6e8dd646df583..d662a62d208450d 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx20.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx20.csv
@@ -53,6 +53,7 @@ atomic cmath
atomic compare
atomic cstddef
atomic cstdint
+atomic cstdio
atomic cstdlib
atomic cstring
atomic ctime
@@ -813,6 +814,7 @@ stdexcept iosfwd
stop_token atomic
stop_token cstddef
stop_token cstdint
+stop_token cstdio
stop_token cstring
stop_token ctime
stop_token iosfwd
diff --git a/libcxx/test/libcxx/transitive_includes/cxx23.csv b/libcxx/test/libcxx/transitive_includes/cxx23.csv
index d0d858056b1b00d..0c8a99a5f8786f2 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx23.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx23.csv
@@ -1,6 +1,7 @@
algorithm climits
algorithm cstddef
algorithm cstdint
+algorithm cstdio
algorithm cstring
algorithm ctime
algorithm cwchar
@@ -30,6 +31,7 @@ array stdexcept
array version
atomic cstddef
atomic cstdint
+atomic cstdio
atomic cstdlib
atomic cstring
atomic ctime
@@ -123,6 +125,7 @@ condition_variable atomic
condition_variable cerrno
condition_variable cstddef
condition_variable cstdint
+condition_variable cstdio
condition_variable cstring
condition_variable ctime
condition_variable iosfwd
@@ -299,6 +302,7 @@ ios cerrno
ios clocale
ios cstddef
ios cstdint
+ios cstdio
ios cstdlib
ios cstring
ios ctime
@@ -427,6 +431,7 @@ numeric climits
numeric cmath
numeric cstddef
numeric cstdint
+numeric cstdio
numeric cstring
numeric ctime
numeric execution
@@ -585,6 +590,7 @@ stdexcept iosfwd
stop_token atomic
stop_token cstddef
stop_token cstdint
+stop_token cstdio
stop_token cstring
stop_token ctime
stop_token iosfwd
diff --git a/libcxx/test/libcxx/transitive_includes/cxx26.csv b/libcxx/test/libcxx/transitive_includes/cxx26.csv
index d0d858056b1b00d..0c8a99a5f8786f2 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx26.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx26.csv
@@ -1,6 +1,7 @@
algorithm climits
algorithm cstddef
algorithm cstdint
+algorithm cstdio
algorithm cstring
algorithm ctime
algorithm cwchar
@@ -30,6 +31,7 @@ array stdexcept
array version
atomic cstddef
atomic cstdint
+atomic cstdio
atomic cstdlib
atomic cstring
atomic ctime
@@ -123,6 +125,7 @@ condition_variable atomic
condition_variable cerrno
condition_variable cstddef
condition_variable cstdint
+condition_variable cstdio
condition_variable cstring
condition_variable ctime
condition_variable iosfwd
@@ -299,6 +302,7 @@ ios cerrno
ios clocale
ios cstddef
ios cstdint
+ios cstdio
ios cstdlib
ios cstring
ios ctime
@@ -427,6 +431,7 @@ numeric climits
numeric cmath
numeric cstddef
numeric cstdint
+numeric cstdio
numeric cstring
numeric ctime
numeric execution
@@ -585,6 +590,7 @@ stdexcept iosfwd
stop_token atomic
stop_token cstddef
stop_token cstdint
+stop_token cstdio
stop_token cstring
stop_token ctime
stop_token iosfwd
diff --git a/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/notify_one.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/notify_one.pass.cpp
index a6265f78cd11572..b7827143d701679 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/notify_one.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/notify_one.pass.cpp
@@ -28,7 +28,6 @@ void testImpl() {
static_assert(HasVolatileNotifyOne<T> == std::atomic<T>::is_always_lock_free);
static_assert(noexcept(std::declval<MaybeVolatile<std::atomic<T>>&>().notify_one()));
-
// bug?? wait can also fail for long double ??
// should x87 80bit long double work at all?
if constexpr (!std::same_as<T, long double>) {
@@ -60,7 +59,6 @@ void testImpl() {
t.join();
}
}
-
}
template <class T>
>From 253bf486fa43d6fad4440331b3b9bed6e611b1de Mon Sep 17 00:00:00 2001
From: Hui <hui.xie0621 at gmail.com>
Date: Fri, 13 Oct 2023 20:08:36 +0100
Subject: [PATCH 8/8] add linker flag
---
.../{locakfree.pass.cpp => lockfree.pass.cpp} | 0
.../atomics.types.generic/atomics.types.float/assign.pass.cpp | 1 +
.../atomics.types.float/compare_exchange_strong.pass.cpp | 1 +
.../atomics.types.float/compare_exchange_weak.pass.cpp | 1 +
.../atomics.types.generic/atomics.types.float/ctor.pass.cpp | 1 +
.../atomics.types.float/exchange.pass.cpp | 1 +
.../atomics.types.float/fetch_add.pass.cpp | 1 +
.../atomics.types.float/fetch_sub.pass.cpp | 1 +
.../atomics.types.generic/atomics.types.float/load.pass.cpp | 1 +
.../atomics.types.float/lockfree.pass.cpp | 1 +
.../atomics.types.float/notify_all.pass.cpp | 1 +
.../atomics.types.float/notify_one.pass.cpp | 1 +
.../atomics.types.float/operator.float.pass.cpp | 1 +
.../atomics.types.float/operator.minus_equals.pass.cpp | 1 +
.../atomics.types.float/operator.plus_equals.pass.cpp | 1 +
.../atomics.types.generic/atomics.types.float/store.pass.cpp | 1 +
libcxx/utils/libcxx/test/features.py | 4 ++++
17 files changed, 19 insertions(+)
rename libcxx/test/libcxx/atomics/atomics.types.generic/atomics.types.float/{locakfree.pass.cpp => lockfree.pass.cpp} (100%)
diff --git a/libcxx/test/libcxx/atomics/atomics.types.generic/atomics.types.float/locakfree.pass.cpp b/libcxx/test/libcxx/atomics/atomics.types.generic/atomics.types.float/lockfree.pass.cpp
similarity index 100%
rename from libcxx/test/libcxx/atomics/atomics.types.generic/atomics.types.float/locakfree.pass.cpp
rename to libcxx/test/libcxx/atomics/atomics.types.generic/atomics.types.float/lockfree.pass.cpp
diff --git a/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/assign.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/assign.pass.cpp
index 4f9383c0b934c37..6f1bc734d5dabd0 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/assign.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/assign.pass.cpp
@@ -7,6 +7,7 @@
//===----------------------------------------------------------------------===//
// UNSUPPORTED: no-threads
// UNSUPPORTED: c++03, c++11, c++14, c++17
+// ADDITIONAL_COMPILE_FLAGS(has-latomic): -latomic
// floating-point-type operator=(floating-point-type) volatile noexcept;
// floating-point-type operator=(floating-point-type) noexcept;
diff --git a/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/compare_exchange_strong.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/compare_exchange_strong.pass.cpp
index b1f219865d8f95c..a7064eef01bf3ff 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/compare_exchange_strong.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/compare_exchange_strong.pass.cpp
@@ -7,6 +7,7 @@
//===----------------------------------------------------------------------===//
// UNSUPPORTED: no-threads
// UNSUPPORTED: c++03, c++11, c++14, c++17
+// ADDITIONAL_COMPILE_FLAGS(has-latomic): -latomic
// bool compare_exchange_strong(T& expected, T desired,
// memory_order success, memory_order failure) volatile noexcept;
diff --git a/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/compare_exchange_weak.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/compare_exchange_weak.pass.cpp
index d320fdefc081289..1e2affe696952a7 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/compare_exchange_weak.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/compare_exchange_weak.pass.cpp
@@ -7,6 +7,7 @@
//===----------------------------------------------------------------------===//
// UNSUPPORTED: no-threads
// UNSUPPORTED: c++03, c++11, c++14, c++17
+// ADDITIONAL_COMPILE_FLAGS(has-latomic): -latomic
// bool compare_exchange_weak(T& expected, T desired,
// memory_order success, memory_order failure) volatile noexcept;
diff --git a/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/ctor.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/ctor.pass.cpp
index 16df7ff23488759..7a3b08c7934c114 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/ctor.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/ctor.pass.cpp
@@ -6,6 +6,7 @@
//
//===----------------------------------------------------------------------===//
// UNSUPPORTED: c++03, c++11, c++14, c++17
+// ADDITIONAL_COMPILE_FLAGS(has-latomic): -latomic
// constexpr atomic() noexcept;
// constexpr atomic(floating-point-type) noexcept;
diff --git a/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/exchange.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/exchange.pass.cpp
index 63162f256cec93b..bbaef97d4075e12 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/exchange.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/exchange.pass.cpp
@@ -7,6 +7,7 @@
//===----------------------------------------------------------------------===//
// UNSUPPORTED: no-threads
// UNSUPPORTED: c++03, c++11, c++14, c++17
+// ADDITIONAL_COMPILE_FLAGS(has-latomic): -latomic
// T exchange(T, memory_order = memory_order::seq_cst) volatile noexcept;
// T exchange(T, memory_order = memory_order::seq_cst) noexcept;
diff --git a/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/fetch_add.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/fetch_add.pass.cpp
index 2a30ea8e2488654..9bca92ef5de2868 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/fetch_add.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/fetch_add.pass.cpp
@@ -7,6 +7,7 @@
//===----------------------------------------------------------------------===//
// UNSUPPORTED: no-threads
// UNSUPPORTED: c++03, c++11, c++14, c++17
+// ADDITIONAL_COMPILE_FLAGS(has-latomic): -latomic
// floating-point-type fetch_add(floating-point-type,
// memory_order = memory_order::seq_cst) volatile noexcept;
diff --git a/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/fetch_sub.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/fetch_sub.pass.cpp
index 5f38be7cf2a75d3..46e113a7b7b4aff 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/fetch_sub.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/fetch_sub.pass.cpp
@@ -7,6 +7,7 @@
//===----------------------------------------------------------------------===//
// UNSUPPORTED: no-threads
// UNSUPPORTED: c++03, c++11, c++14, c++17
+// ADDITIONAL_COMPILE_FLAGS(has-latomic): -latomic
// floating-point-type fetch_sub(floating-point-type,
// memory_order = memory_order::seq_cst) volatile noexcept;
diff --git a/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/load.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/load.pass.cpp
index b80e84a2c75fe3f..78dcf5881328adc 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/load.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/load.pass.cpp
@@ -7,6 +7,7 @@
//===----------------------------------------------------------------------===//
// UNSUPPORTED: no-threads
// UNSUPPORTED: c++03, c++11, c++14, c++17
+// ADDITIONAL_COMPILE_FLAGS(has-latomic): -latomic
// floating-point-type load(memory_order = memory_order::seq_cst) volatile noexcept;
// floating-point-type load(memory_order = memory_order::seq_cst) noexcept;
diff --git a/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/lockfree.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/lockfree.pass.cpp
index b632df79c330486..62c5eb17e81538e 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/lockfree.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/lockfree.pass.cpp
@@ -6,6 +6,7 @@
//
//===----------------------------------------------------------------------===//
// UNSUPPORTED: c++03, c++11, c++14, c++17
+// ADDITIONAL_COMPILE_FLAGS(has-latomic): -latomic
// static constexpr bool is_always_lock_free = implementation-defined;
// bool is_lock_free() const volatile noexcept;
diff --git a/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/notify_all.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/notify_all.pass.cpp
index 0c4898e0b121c26..9c9bc2085324bcd 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/notify_all.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/notify_all.pass.cpp
@@ -7,6 +7,7 @@
//===----------------------------------------------------------------------===//
// UNSUPPORTED: no-threads
// UNSUPPORTED: c++03, c++11, c++14, c++17
+// ADDITIONAL_COMPILE_FLAGS(has-latomic): -latomic
// void notify_all() volatile noexcept;
// void notify_all() noexcept;
diff --git a/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/notify_one.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/notify_one.pass.cpp
index b7827143d701679..97d7c1a41c3b4c9 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/notify_one.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/notify_one.pass.cpp
@@ -7,6 +7,7 @@
//===----------------------------------------------------------------------===//
// UNSUPPORTED: no-threads
// UNSUPPORTED: c++03, c++11, c++14, c++17
+// ADDITIONAL_COMPILE_FLAGS(has-latomic): -latomic
// void notify_one() volatile noexcept;
// void notify_one() noexcept;
diff --git a/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/operator.float.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/operator.float.pass.cpp
index 88c7f1c6e3bb1be..f6d6d8626049654 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/operator.float.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/operator.float.pass.cpp
@@ -7,6 +7,7 @@
//===----------------------------------------------------------------------===//
// UNSUPPORTED: no-threads
// UNSUPPORTED: c++03, c++11, c++14, c++17
+// ADDITIONAL_COMPILE_FLAGS(has-latomic): -latomic
// operator floating-point-type() volatile noexcept;
// operator floating-point-type() noexcept;
diff --git a/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/operator.minus_equals.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/operator.minus_equals.pass.cpp
index f48441641012dff..7be7570092622b9 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/operator.minus_equals.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/operator.minus_equals.pass.cpp
@@ -6,6 +6,7 @@
//
//===----------------------------------------------------------------------===//
// UNSUPPORTED: c++03, c++11, c++14, c++17
+// ADDITIONAL_COMPILE_FLAGS(has-latomic): -latomic
// floating-point-type operator-=(floating-point-type) volatile noexcept;
// floating-point-type operator-=(floating-point-type) noexcept;
diff --git a/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/operator.plus_equals.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/operator.plus_equals.pass.cpp
index f617ded35138ec6..acce07c748dc8aa 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/operator.plus_equals.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/operator.plus_equals.pass.cpp
@@ -6,6 +6,7 @@
//
//===----------------------------------------------------------------------===//
// UNSUPPORTED: c++03, c++11, c++14, c++17
+// ADDITIONAL_COMPILE_FLAGS(has-latomic): -latomic
// floating-point-type operator+=(floating-point-type) volatile noexcept;
// floating-point-type operator+=(floating-point-type) noexcept;
diff --git a/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/store.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/store.pass.cpp
index cbfbbd3bc091266..276086deefa8464 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/store.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/store.pass.cpp
@@ -7,6 +7,7 @@
//===----------------------------------------------------------------------===//
// UNSUPPORTED: no-threads
// UNSUPPORTED: c++03, c++11, c++14, c++17
+// ADDITIONAL_COMPILE_FLAGS(has-latomic): -latomic
// void store(floating-point-type, memory_order = memory_order::seq_cst) volatile noexcept;
// void store(floating-point-type, memory_order = memory_order::seq_cst) noexcept;
diff --git a/libcxx/utils/libcxx/test/features.py b/libcxx/utils/libcxx/test/features.py
index 3779af1094d5d0f..b72ca6fa6bedc8c 100644
--- a/libcxx/utils/libcxx/test/features.py
+++ b/libcxx/utils/libcxx/test/features.py
@@ -94,6 +94,10 @@ def _getSuitableClangTidy(cfg):
name="verify-support",
when=lambda cfg: hasCompileFlag(cfg, "-Xclang -verify-ignore-unexpected"),
),
+ Feature(
+ name="has-latomic",
+ when=lambda cfg: hasCompileFlag(cfg, "-latomic"),
+ ),
Feature(
name="non-lockfree-atomics",
when=lambda cfg: sourceBuilds(
More information about the libcxx-commits
mailing list