1 | //===----------------------------------------------------------------------===// |
2 | // |
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | // See https://llvm.org/LICENSE.txt for license information. |
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | // UNSUPPORTED: c++03, c++11, c++14, c++17 |
9 | // XFAIL: availability-synchronization_library-missing |
10 | // XFAIL: !has-64-bit-atomics |
11 | |
12 | // void wait(T old, memory_order order = memory_order::seq_cst) const volatile noexcept; |
13 | // void wait(T old, memory_order order = memory_order::seq_cst) const noexcept; |
14 | |
15 | #include <atomic> |
16 | #include <cassert> |
17 | #include <concepts> |
18 | #include <type_traits> |
19 | #include <vector> |
20 | |
21 | #include "test_helper.h" |
22 | #include "test_macros.h" |
23 | |
24 | #ifndef TEST_HAS_NO_THREADS |
25 | # include "make_test_thread.h" |
26 | # include <thread> |
27 | #endif |
28 | |
29 | template <class T> |
30 | concept HasVolatileWait = requires(volatile std::atomic<T>& a, T t) { a.wait(T()); }; |
31 | |
32 | template <class T, template <class> class MaybeVolatile = std::type_identity_t> |
33 | void test_impl() { |
34 | // Uncomment the test after P1831R1 is implemented |
35 | // static_assert(HasVolatileWait<T> == std::atomic<T>::is_always_lock_free); |
36 | static_assert(noexcept(std::declval<MaybeVolatile<std::atomic<T>>&>().wait(T()))); |
37 | |
38 | // wait with different value |
39 | { |
40 | MaybeVolatile<std::atomic<T>> a(T(3.1)); |
41 | a.wait(T(1.1), std::memory_order::relaxed); |
42 | } |
43 | |
44 | #ifndef TEST_HAS_NO_THREADS |
45 | // equal at the beginning and changed later |
46 | // bug?? wait can also fail for long double ?? |
47 | // should x87 80bit long double work at all? |
48 | if constexpr (!std::same_as<T, long double>) { |
49 | for (auto i = 0; i < 100; ++i) { |
50 | const T old = T(3.1); |
51 | MaybeVolatile<std::atomic<T>> a(old); |
52 | |
53 | std::atomic_bool started = false; |
54 | bool done = false; |
55 | |
56 | auto t = support::make_test_thread([&a, &started, old, &done] { |
57 | started.store(true, std::memory_order::relaxed); |
58 | |
59 | a.wait(old); |
60 | |
61 | // likely to fail if wait did not block |
62 | assert(done); |
63 | }); |
64 | |
65 | while (!started.load(std::memory_order::relaxed)) { |
66 | std::this_thread::yield(); |
67 | } |
68 | |
69 | std::this_thread::sleep_for(rtime: std::chrono::milliseconds(1)); |
70 | |
71 | done = true; |
72 | a.store(T(9.9)); |
73 | a.notify_all(); |
74 | t.join(); |
75 | } |
76 | } |
77 | #endif |
78 | |
79 | // memory_order::acquire |
80 | { |
81 | auto store = [](MaybeVolatile<std::atomic<T>>& x, T, T new_val) { x.store(new_val, std::memory_order::release); }; |
82 | auto load = [](MaybeVolatile<std::atomic<T>>& x) { |
83 | auto result = x.load(std::memory_order::relaxed); |
84 | x.wait(T(9999.999), std::memory_order::acquire); |
85 | return result; |
86 | }; |
87 | test_acquire_release<T, MaybeVolatile>(store, load); |
88 | } |
89 | |
90 | // memory_order::seq_cst |
91 | { |
92 | auto store = [](MaybeVolatile<std::atomic<T>>& x, T, T new_val) { x.store(new_val); }; |
93 | auto load_no_arg = [](MaybeVolatile<std::atomic<T>>& x) { |
94 | auto result = x.load(std::memory_order::relaxed); |
95 | x.wait(T(9999.999)); |
96 | return result; |
97 | }; |
98 | auto load_with_order = [](MaybeVolatile<std::atomic<T>>& x) { |
99 | auto result = x.load(std::memory_order::relaxed); |
100 | x.wait(T(9999.999), std::memory_order::seq_cst); |
101 | return result; |
102 | }; |
103 | test_seq_cst<T, MaybeVolatile>(store, load_no_arg); |
104 | test_seq_cst<T, MaybeVolatile>(store, load_with_order); |
105 | } |
106 | } |
107 | |
108 | template <class T> |
109 | void test() { |
110 | test_impl<T>(); |
111 | if constexpr (std::atomic<T>::is_always_lock_free) { |
112 | test_impl<T, std::add_volatile_t>(); |
113 | } |
114 | } |
115 | |
116 | int main(int, char**) { |
117 | test<float>(); |
118 | test<double>(); |
119 | // TODO https://github.com/llvm/llvm-project/issues/47978 |
120 | // test<long double>(); |
121 | |
122 | return 0; |
123 | } |
124 | |