1 // 2 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 3 // See https://llvm.org/LICENSE.txt for license information. 4 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 5 // 6 //===----------------------------------------------------------------------===// 7 8 // UNSUPPORTED: c++03, c++11, c++14, c++17 9 // XFAIL: !has-64-bit-atomics 10 11 // integral-type fetch_add(integral-type, memory_order = memory_order::seq_cst) const noexcept; 12 // floating-point-type fetch_add(floating-point-type, memory_order = memory_order::seq_cst) const noexcept; 13 // T* fetch_add(difference_type, memory_order = memory_order::seq_cst) const noexcept; 14 15 #include <atomic> 16 #include <cassert> 17 #include <concepts> 18 #include <type_traits> 19 #include <utility> 20 21 #include "atomic_helpers.h" 22 #include "test_helper.h" 23 #include "test_macros.h" 24 25 template <typename T> 26 concept has_fetch_add = requires { 27 std::declval<T const>().fetch_add(std::declval<T>()); 28 std::declval<T const>().fetch_add(std::declval<T>(), std::declval<std::memory_order>()); 29 }; 30 31 template <typename T> 32 struct TestDoesNotHaveFetchAdd { 33 void operator()() const { static_assert(!has_fetch_add<std::atomic_ref<T>>); } 34 }; 35 36 template <typename T> 37 struct TestFetchAdd { 38 void operator()() const { 39 if constexpr (std::is_arithmetic_v<T>) { 40 T x(T(1)); 41 std::atomic_ref<T> const a(x); 42 43 { 44 std::same_as<T> decltype(auto) y = a.fetch_add(T(2)); 45 assert(y == T(1)); 46 assert(x == T(3)); 47 ASSERT_NOEXCEPT(a.fetch_add(T(0))); 48 } 49 50 { 51 std::same_as<T> decltype(auto) y = a.fetch_add(T(4), std::memory_order_relaxed); 52 assert(y == T(3)); 53 assert(x == T(7)); 54 ASSERT_NOEXCEPT(a.fetch_add(T(0), std::memory_order_relaxed)); 55 } 56 } else if constexpr (std::is_pointer_v<T>) { 57 using U = std::remove_pointer_t<T>; 58 U t[9] = {}; 59 T p{&t[1]}; 60 std::atomic_ref<T> const a(p); 61 62 { 63 std::same_as<T> decltype(auto) y = a.fetch_add(2); 64 assert(y == &t[1]); 65 assert(a == &t[3]); 66 ASSERT_NOEXCEPT(a.fetch_add(0)); 67 } 68 69 { 70 std::same_as<T> decltype(auto) y = a.fetch_add(4, std::memory_order_relaxed); 71 assert(y == &t[3]); 72 assert(a == &t[7]); 73 ASSERT_NOEXCEPT(a.fetch_add(0, std::memory_order_relaxed)); 74 } 75 } else { 76 static_assert(std::is_void_v<T>); 77 } 78 79 // memory_order::release 80 { 81 auto fetch_add = [](std::atomic_ref<T> const& x, T old_val, T new_val) { 82 x.fetch_add(new_val - old_val, std::memory_order::release); 83 }; 84 auto load = [](std::atomic_ref<T> const& x) { return x.load(std::memory_order::acquire); }; 85 test_acquire_release<T>(fetch_add, load); 86 } 87 88 // memory_order::seq_cst 89 { 90 auto fetch_add_no_arg = [](std::atomic_ref<T> const& x, T old_val, T new_val) { x.fetch_add(new_val - old_val); }; 91 auto fetch_add_with_order = [](std::atomic_ref<T> const& x, T old_val, T new_val) { 92 x.fetch_add(new_val - old_val, std::memory_order::seq_cst); 93 }; 94 auto load = [](std::atomic_ref<T> const& x) { return x.load(); }; 95 test_seq_cst<T>(fetch_add_no_arg, load); 96 test_seq_cst<T>(fetch_add_with_order, load); 97 } 98 } 99 }; 100 101 int main(int, char**) { 102 TestEachIntegralType<TestFetchAdd>()(); 103 104 TestFetchAdd<float>()(); 105 TestFetchAdd<double>()(); 106 107 TestEachPointerType<TestFetchAdd>()(); 108 109 TestDoesNotHaveFetchAdd<bool>()(); 110 TestDoesNotHaveFetchAdd<UserAtomicType>()(); 111 TestDoesNotHaveFetchAdd<LargeUserAtomicType>()(); 112 113 return 0; 114 } 115