xref: /llvm-project/libcxx/test/std/atomics/atomics.ref/fetch_sub.pass.cpp (revision 09e3a360581dc36d0820d3fb6da9bd7cfed87b5d)
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