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_sub(integral-type, memory_order = memory_order::seq_cst) const noexcept; 12 // floating-point-type fetch_sub(floating-point-type, memory_order = memory_order::seq_cst) const noexcept; 13 // T* fetch_sub(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_sub = requires { 27 std::declval<T const>().fetch_sub(std::declval<T>()); 28 std::declval<T const>().fetch_sub(std::declval<T>(), std::declval<std::memory_order>()); 29 }; 30 31 template <typename T> 32 struct TestDoesNotHaveFetchSub { 33 void operator()() const { static_assert(!has_fetch_sub<std::atomic_ref<T>>); } 34 }; 35 36 template <typename T> 37 struct TestFetchSub { 38 void operator()() const { 39 if constexpr (std::is_arithmetic_v<T>) { 40 T x(T(7)); 41 std::atomic_ref<T> const a(x); 42 43 { 44 std::same_as<T> decltype(auto) y = a.fetch_sub(T(4)); 45 assert(y == T(7)); 46 assert(x == T(3)); 47 ASSERT_NOEXCEPT(a.fetch_sub(T(0))); 48 } 49 50 { 51 std::same_as<T> decltype(auto) y = a.fetch_sub(T(2), std::memory_order_relaxed); 52 assert(y == T(3)); 53 assert(x == T(1)); 54 ASSERT_NOEXCEPT(a.fetch_sub(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[7]}; 60 std::atomic_ref<T> const a(p); 61 62 { 63 std::same_as<T> decltype(auto) y = a.fetch_sub(4); 64 assert(y == &t[7]); 65 assert(a == &t[3]); 66 ASSERT_NOEXCEPT(a.fetch_sub(0)); 67 } 68 69 { 70 std::same_as<T> decltype(auto) y = a.fetch_sub(2, std::memory_order_relaxed); 71 assert(y == &t[3]); 72 assert(a == &t[1]); 73 ASSERT_NOEXCEPT(a.fetch_sub(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_sub = [](std::atomic_ref<T> const& x, T old_val, T new_val) { 82 x.fetch_sub(old_val - new_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_sub, load); 86 } 87 88 // memory_order::seq_cst 89 { 90 auto fetch_sub_no_arg = [](std::atomic_ref<T> const& x, T old_val, T new_val) { x.fetch_sub(old_val - new_val); }; 91 auto fetch_sub_with_order = [](std::atomic_ref<T> const& x, T old_val, T new_val) { 92 x.fetch_sub(old_val - new_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_sub_no_arg, load); 96 test_seq_cst<T>(fetch_sub_with_order, load); 97 } 98 } 99 }; 100 101 int main(int, char**) { 102 TestEachIntegralType<TestFetchSub>()(); 103 104 TestFetchSub<float>()(); 105 TestFetchSub<double>()(); 106 107 TestEachPointerType<TestFetchSub>()(); 108 109 TestDoesNotHaveFetchSub<bool>()(); 110 TestDoesNotHaveFetchSub<UserAtomicType>()(); 111 TestDoesNotHaveFetchSub<LargeUserAtomicType>()(); 112 113 return 0; 114 } 115