142ba740aSDamien L-G // 242ba740aSDamien L-G // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 342ba740aSDamien L-G // See https://llvm.org/LICENSE.txt for license information. 442ba740aSDamien L-G // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 542ba740aSDamien L-G // 642ba740aSDamien L-G //===----------------------------------------------------------------------===// 742ba740aSDamien L-G 842ba740aSDamien L-G // UNSUPPORTED: c++03, c++11, c++14, c++17 942ba740aSDamien L-G // XFAIL: !has-64-bit-atomics 1042ba740aSDamien L-G 1142ba740aSDamien L-G // integral-type fetch_sub(integral-type, memory_order = memory_order::seq_cst) const noexcept; 1242ba740aSDamien L-G // floating-point-type fetch_sub(floating-point-type, memory_order = memory_order::seq_cst) const noexcept; 1342ba740aSDamien L-G // T* fetch_sub(difference_type, memory_order = memory_order::seq_cst) const noexcept; 1442ba740aSDamien L-G 1542ba740aSDamien L-G #include <atomic> 1642ba740aSDamien L-G #include <cassert> 1742ba740aSDamien L-G #include <concepts> 1842ba740aSDamien L-G #include <type_traits> 19*09e3a360SLouis Dionne #include <utility> 2042ba740aSDamien L-G 2142ba740aSDamien L-G #include "atomic_helpers.h" 2242ba740aSDamien L-G #include "test_helper.h" 2342ba740aSDamien L-G #include "test_macros.h" 2442ba740aSDamien L-G 2542ba740aSDamien L-G template <typename T> 2642ba740aSDamien L-G concept has_fetch_sub = requires { 2742ba740aSDamien L-G std::declval<T const>().fetch_sub(std::declval<T>()); 2842ba740aSDamien L-G std::declval<T const>().fetch_sub(std::declval<T>(), std::declval<std::memory_order>()); 2942ba740aSDamien L-G }; 3042ba740aSDamien L-G 3142ba740aSDamien L-G template <typename T> 3242ba740aSDamien L-G struct TestDoesNotHaveFetchSub { 3342ba740aSDamien L-G void operator()() const { static_assert(!has_fetch_sub<std::atomic_ref<T>>); } 3442ba740aSDamien L-G }; 3542ba740aSDamien L-G 3642ba740aSDamien L-G template <typename T> 3742ba740aSDamien L-G struct TestFetchSub { 3842ba740aSDamien L-G void operator()() const { 3942ba740aSDamien L-G if constexpr (std::is_arithmetic_v<T>) { 4042ba740aSDamien L-G T x(T(7)); 4142ba740aSDamien L-G std::atomic_ref<T> const a(x); 4242ba740aSDamien L-G 4342ba740aSDamien L-G { 4442ba740aSDamien L-G std::same_as<T> decltype(auto) y = a.fetch_sub(T(4)); 4542ba740aSDamien L-G assert(y == T(7)); 4642ba740aSDamien L-G assert(x == T(3)); 4742ba740aSDamien L-G ASSERT_NOEXCEPT(a.fetch_sub(T(0))); 4842ba740aSDamien L-G } 4942ba740aSDamien L-G 5042ba740aSDamien L-G { 5142ba740aSDamien L-G std::same_as<T> decltype(auto) y = a.fetch_sub(T(2), std::memory_order_relaxed); 5242ba740aSDamien L-G assert(y == T(3)); 5342ba740aSDamien L-G assert(x == T(1)); 5442ba740aSDamien L-G ASSERT_NOEXCEPT(a.fetch_sub(T(0), std::memory_order_relaxed)); 5542ba740aSDamien L-G } 5642ba740aSDamien L-G } else if constexpr (std::is_pointer_v<T>) { 5742ba740aSDamien L-G using U = std::remove_pointer_t<T>; 5842ba740aSDamien L-G U t[9] = {}; 5942ba740aSDamien L-G T p{&t[7]}; 6042ba740aSDamien L-G std::atomic_ref<T> const a(p); 6142ba740aSDamien L-G 6242ba740aSDamien L-G { 6342ba740aSDamien L-G std::same_as<T> decltype(auto) y = a.fetch_sub(4); 6442ba740aSDamien L-G assert(y == &t[7]); 6542ba740aSDamien L-G assert(a == &t[3]); 6642ba740aSDamien L-G ASSERT_NOEXCEPT(a.fetch_sub(0)); 6742ba740aSDamien L-G } 6842ba740aSDamien L-G 6942ba740aSDamien L-G { 7042ba740aSDamien L-G std::same_as<T> decltype(auto) y = a.fetch_sub(2, std::memory_order_relaxed); 7142ba740aSDamien L-G assert(y == &t[3]); 7242ba740aSDamien L-G assert(a == &t[1]); 7342ba740aSDamien L-G ASSERT_NOEXCEPT(a.fetch_sub(0, std::memory_order_relaxed)); 7442ba740aSDamien L-G } 7542ba740aSDamien L-G } else { 7642ba740aSDamien L-G static_assert(std::is_void_v<T>); 7742ba740aSDamien L-G } 7842ba740aSDamien L-G 7942ba740aSDamien L-G // memory_order::release 8042ba740aSDamien L-G { 8142ba740aSDamien L-G auto fetch_sub = [](std::atomic_ref<T> const& x, T old_val, T new_val) { 8242ba740aSDamien L-G x.fetch_sub(old_val - new_val, std::memory_order::release); 8342ba740aSDamien L-G }; 8442ba740aSDamien L-G auto load = [](std::atomic_ref<T> const& x) { return x.load(std::memory_order::acquire); }; 8542ba740aSDamien L-G test_acquire_release<T>(fetch_sub, load); 8642ba740aSDamien L-G } 8742ba740aSDamien L-G 8842ba740aSDamien L-G // memory_order::seq_cst 8942ba740aSDamien L-G { 9042ba740aSDamien L-G auto fetch_sub_no_arg = [](std::atomic_ref<T> const& x, T old_val, T new_val) { x.fetch_sub(old_val - new_val); }; 9142ba740aSDamien L-G auto fetch_sub_with_order = [](std::atomic_ref<T> const& x, T old_val, T new_val) { 9242ba740aSDamien L-G x.fetch_sub(old_val - new_val, std::memory_order::seq_cst); 9342ba740aSDamien L-G }; 9442ba740aSDamien L-G auto load = [](std::atomic_ref<T> const& x) { return x.load(); }; 9542ba740aSDamien L-G test_seq_cst<T>(fetch_sub_no_arg, load); 9642ba740aSDamien L-G test_seq_cst<T>(fetch_sub_with_order, load); 9742ba740aSDamien L-G } 9842ba740aSDamien L-G } 9942ba740aSDamien L-G }; 10042ba740aSDamien L-G 10142ba740aSDamien L-G int main(int, char**) { 10242ba740aSDamien L-G TestEachIntegralType<TestFetchSub>()(); 10342ba740aSDamien L-G 10442ba740aSDamien L-G TestFetchSub<float>()(); 10542ba740aSDamien L-G TestFetchSub<double>()(); 10642ba740aSDamien L-G 10742ba740aSDamien L-G TestEachPointerType<TestFetchSub>()(); 10842ba740aSDamien L-G 10942ba740aSDamien L-G TestDoesNotHaveFetchSub<bool>()(); 11042ba740aSDamien L-G TestDoesNotHaveFetchSub<UserAtomicType>()(); 11142ba740aSDamien L-G TestDoesNotHaveFetchSub<LargeUserAtomicType>()(); 11242ba740aSDamien L-G 11342ba740aSDamien L-G return 0; 11442ba740aSDamien L-G } 115