116 lines · cpp
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-exception5//6//===----------------------------------------------------------------------===//7 8// UNSUPPORTED: c++03, c++11, c++14, c++179// XFAIL: !has-64-bit-atomics10// XFAIL: target={{x86_64-.*}} && tsan11 12// integral-type fetch_sub(integral-type, memory_order = memory_order::seq_cst) const noexcept;13// floating-point-type fetch_sub(floating-point-type, memory_order = memory_order::seq_cst) const noexcept;14// T* fetch_sub(difference_type, memory_order = memory_order::seq_cst) const noexcept;15 16#include <atomic>17#include <cassert>18#include <concepts>19#include <type_traits>20#include <utility>21 22#include "atomic_helpers.h"23#include "test_helper.h"24#include "test_macros.h"25 26template <typename T>27concept has_fetch_sub = requires {28 std::declval<T const>().fetch_sub(std::declval<T>());29 std::declval<T const>().fetch_sub(std::declval<T>(), std::declval<std::memory_order>());30};31 32template <typename T>33struct TestDoesNotHaveFetchSub {34 void operator()() const { static_assert(!has_fetch_sub<std::atomic_ref<T>>); }35};36 37template <typename T>38struct TestFetchSub {39 void operator()() const {40 if constexpr (std::is_arithmetic_v<T>) {41 T x(T(7));42 std::atomic_ref<T> const a(x);43 44 {45 std::same_as<T> decltype(auto) y = a.fetch_sub(T(4));46 assert(y == T(7));47 assert(x == T(3));48 ASSERT_NOEXCEPT(a.fetch_sub(T(0)));49 }50 51 {52 std::same_as<T> decltype(auto) y = a.fetch_sub(T(2), std::memory_order_relaxed);53 assert(y == T(3));54 assert(x == T(1));55 ASSERT_NOEXCEPT(a.fetch_sub(T(0), std::memory_order_relaxed));56 }57 } else if constexpr (std::is_pointer_v<T>) {58 using U = std::remove_pointer_t<T>;59 U t[9] = {};60 T p{&t[7]};61 std::atomic_ref<T> const a(p);62 63 {64 std::same_as<T> decltype(auto) y = a.fetch_sub(4);65 assert(y == &t[7]);66 assert(a == &t[3]);67 ASSERT_NOEXCEPT(a.fetch_sub(0));68 }69 70 {71 std::same_as<T> decltype(auto) y = a.fetch_sub(2, std::memory_order_relaxed);72 assert(y == &t[3]);73 assert(a == &t[1]);74 ASSERT_NOEXCEPT(a.fetch_sub(0, std::memory_order_relaxed));75 }76 } else {77 static_assert(std::is_void_v<T>);78 }79 80 // memory_order::release81 {82 auto fetch_sub = [](std::atomic_ref<T> const& x, T old_val, T new_val) {83 x.fetch_sub(old_val - new_val, std::memory_order::release);84 };85 auto load = [](std::atomic_ref<T> const& x) { return x.load(std::memory_order::acquire); };86 test_acquire_release<T>(fetch_sub, load);87 }88 89 // memory_order::seq_cst90 {91 auto fetch_sub_no_arg = [](std::atomic_ref<T> const& x, T old_val, T new_val) { x.fetch_sub(old_val - new_val); };92 auto fetch_sub_with_order = [](std::atomic_ref<T> const& x, T old_val, T new_val) {93 x.fetch_sub(old_val - new_val, std::memory_order::seq_cst);94 };95 auto load = [](std::atomic_ref<T> const& x) { return x.load(); };96 test_seq_cst<T>(fetch_sub_no_arg, load);97 test_seq_cst<T>(fetch_sub_with_order, load);98 }99 }100};101 102int main(int, char**) {103 TestEachIntegralType<TestFetchSub>()();104 105 TestFetchSub<float>()();106 TestFetchSub<double>()();107 108 TestEachPointerType<TestFetchSub>()();109 110 TestDoesNotHaveFetchSub<bool>()();111 TestDoesNotHaveFetchSub<UserAtomicType>()();112 TestDoesNotHaveFetchSub<LargeUserAtomicType>()();113 114 return 0;115}116