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 | // XFAIL: !has-1024-bit-atomics |
11 | |
12 | // MSVC warning C4310: cast truncates constant value |
13 | // ADDITIONAL_COMPILE_FLAGS(cl-style-warnings): /wd4310 |
14 | |
15 | // bool compare_exchange_strong(T&, T, memory_order, memory_order) const noexcept; |
16 | // bool compare_exchange_strong(T&, T, memory_order = memory_order::seq_cst) const noexcept; |
17 | |
18 | #include <atomic> |
19 | #include <cassert> |
20 | #include <concepts> |
21 | #include <type_traits> |
22 | |
23 | #include "atomic_helpers.h" |
24 | #include "test_helper.h" |
25 | #include "test_macros.h" |
26 | |
27 | template <typename T> |
28 | struct TestCompareExchangeStrong { |
29 | void operator()() const { |
30 | { |
31 | T x(T(1)); |
32 | std::atomic_ref<T> const a(x); |
33 | |
34 | T t(T(1)); |
35 | std::same_as<bool> decltype(auto) y = a.compare_exchange_strong(t, T(2)); |
36 | assert(y == true); |
37 | assert(a == T(2)); |
38 | assert(t == T(1)); |
39 | y = a.compare_exchange_strong(t, T(3)); |
40 | assert(y == false); |
41 | assert(a == T(2)); |
42 | assert(t == T(2)); |
43 | |
44 | ASSERT_NOEXCEPT(a.compare_exchange_strong(t, T(2))); |
45 | } |
46 | { |
47 | T x(T(1)); |
48 | std::atomic_ref<T> const a(x); |
49 | |
50 | T t(T(1)); |
51 | std::same_as<bool> decltype(auto) y = a.compare_exchange_strong(t, T(2), std::memory_order_seq_cst); |
52 | assert(y == true); |
53 | assert(a == T(2)); |
54 | assert(t == T(1)); |
55 | y = a.compare_exchange_strong(t, T(3), std::memory_order_seq_cst); |
56 | assert(y == false); |
57 | assert(a == T(2)); |
58 | assert(t == T(2)); |
59 | |
60 | ASSERT_NOEXCEPT(a.compare_exchange_strong(t, T(2), std::memory_order_seq_cst)); |
61 | } |
62 | { |
63 | T x(T(1)); |
64 | std::atomic_ref<T> const a(x); |
65 | |
66 | T t(T(1)); |
67 | std::same_as<bool> decltype(auto) y = |
68 | a.compare_exchange_strong(t, T(2), std::memory_order_release, std::memory_order_relaxed); |
69 | assert(y == true); |
70 | assert(a == T(2)); |
71 | assert(t == T(1)); |
72 | y = a.compare_exchange_strong(t, T(3), std::memory_order_release, std::memory_order_relaxed); |
73 | assert(y == false); |
74 | assert(a == T(2)); |
75 | assert(t == T(2)); |
76 | |
77 | ASSERT_NOEXCEPT(a.compare_exchange_strong(t, T(2), std::memory_order_release, std::memory_order_relaxed)); |
78 | } |
79 | |
80 | // success memory_order::release |
81 | { |
82 | auto store = [](std::atomic_ref<T> const& x, T old_val, T new_val) { |
83 | auto r = x.compare_exchange_strong(old_val, new_val, std::memory_order::release, std::memory_order::relaxed); |
84 | assert(r); |
85 | }; |
86 | |
87 | auto load = [](std::atomic_ref<T> const& x) { return x.load(std::memory_order::acquire); }; |
88 | test_acquire_release<T>(store, load); |
89 | |
90 | auto store_one_arg = [](std::atomic_ref<T> const& x, T old_val, T new_val) { |
91 | auto r = x.compare_exchange_strong(old_val, new_val, std::memory_order::release); |
92 | assert(r); |
93 | }; |
94 | test_acquire_release<T>(store_one_arg, load); |
95 | } |
96 | |
97 | // success memory_order::acquire |
98 | { |
99 | auto store = [](std::atomic_ref<T> const& x, T, T new_val) { x.store(new_val, std::memory_order::release); }; |
100 | |
101 | auto load = [](std::atomic_ref<T> const& x) { |
102 | auto val = x.load(std::memory_order::relaxed); |
103 | while (!x.compare_exchange_strong(val, val, std::memory_order::acquire, std::memory_order::relaxed)) { |
104 | } |
105 | return val; |
106 | }; |
107 | test_acquire_release<T>(store, load); |
108 | |
109 | auto load_one_arg = [](std::atomic_ref<T> const& x) { |
110 | auto val = x.load(std::memory_order::relaxed); |
111 | while (!x.compare_exchange_strong(val, val, std::memory_order::acquire)) { |
112 | } |
113 | return val; |
114 | }; |
115 | test_acquire_release<T>(store, load_one_arg); |
116 | } |
117 | |
118 | // success memory_order::acq_rel |
119 | { |
120 | auto store = [](std::atomic_ref<T> const& x, T old_val, T new_val) { |
121 | auto r = x.compare_exchange_strong(old_val, new_val, std::memory_order::acq_rel, std::memory_order::relaxed); |
122 | assert(r); |
123 | }; |
124 | auto load = [](std::atomic_ref<T> const& x) { |
125 | auto val = x.load(std::memory_order::relaxed); |
126 | while (!x.compare_exchange_strong(val, val, std::memory_order::acq_rel, std::memory_order::relaxed)) { |
127 | } |
128 | return val; |
129 | }; |
130 | test_acquire_release<T>(store, load); |
131 | |
132 | auto store_one_arg = [](std::atomic_ref<T> const& x, T old_val, T new_val) { |
133 | auto r = x.compare_exchange_strong(old_val, new_val, std::memory_order::acq_rel); |
134 | assert(r); |
135 | }; |
136 | auto load_one_arg = [](std::atomic_ref<T> const& x) { |
137 | auto val = x.load(std::memory_order::relaxed); |
138 | while (!x.compare_exchange_strong(val, val, std::memory_order::acq_rel)) { |
139 | } |
140 | return val; |
141 | }; |
142 | test_acquire_release<T>(store_one_arg, load_one_arg); |
143 | } |
144 | |
145 | // success memory_order::seq_cst |
146 | { |
147 | auto store = [](std::atomic_ref<T> const& x, T old_val, T new_val) { |
148 | auto r = x.compare_exchange_strong(old_val, new_val, std::memory_order::seq_cst, std::memory_order::relaxed); |
149 | assert(r); |
150 | }; |
151 | auto load = [](std::atomic_ref<T> const& x) { |
152 | auto val = x.load(std::memory_order::relaxed); |
153 | while (!x.compare_exchange_strong(val, val, std::memory_order::seq_cst, std::memory_order::relaxed)) { |
154 | } |
155 | return val; |
156 | }; |
157 | test_seq_cst<T>(store, load); |
158 | |
159 | auto store_one_arg = [](std::atomic_ref<T> const& x, T old_val, T new_val) { |
160 | auto r = x.compare_exchange_strong(old_val, new_val, std::memory_order::seq_cst); |
161 | assert(r); |
162 | }; |
163 | auto load_one_arg = [](std::atomic_ref<T> const& x) { |
164 | auto val = x.load(std::memory_order::relaxed); |
165 | while (!x.compare_exchange_strong(val, val, std::memory_order::seq_cst)) { |
166 | } |
167 | return val; |
168 | }; |
169 | test_seq_cst<T>(store_one_arg, load_one_arg); |
170 | } |
171 | |
172 | // failure memory_order::acquire |
173 | { |
174 | auto store = [](std::atomic_ref<T> const& x, T, T new_val) { x.store(new_val, std::memory_order::release); }; |
175 | auto load = [](std::atomic_ref<T> const& x) { |
176 | auto result = x.load(std::memory_order::relaxed); |
177 | T unexpected(T(255)); |
178 | bool r = |
179 | x.compare_exchange_strong(unexpected, unexpected, std::memory_order::relaxed, std::memory_order::acquire); |
180 | assert(!r); |
181 | return result; |
182 | }; |
183 | test_acquire_release<T>(store, load); |
184 | |
185 | auto load_one_arg = [](std::atomic_ref<T> const& x) { |
186 | auto result = x.load(std::memory_order::relaxed); |
187 | T unexpected(T(255)); |
188 | bool r = x.compare_exchange_strong(unexpected, unexpected, std::memory_order::acquire); |
189 | assert(!r); |
190 | return result; |
191 | }; |
192 | test_acquire_release<T>(store, load_one_arg); |
193 | |
194 | // acq_rel replaced by acquire |
195 | auto load_one_arg_acq_rel = [](std::atomic_ref<T> const& x) { |
196 | auto result = x.load(std::memory_order::relaxed); |
197 | T unexpected(T(255)); |
198 | bool r = x.compare_exchange_strong(unexpected, unexpected, std::memory_order::acq_rel); |
199 | assert(!r); |
200 | return result; |
201 | }; |
202 | test_acquire_release<T>(store, load_one_arg_acq_rel); |
203 | } |
204 | |
205 | // failure memory_order::seq_cst |
206 | { |
207 | auto store = [](std::atomic_ref<T> const& x, T, T new_val) { x.store(new_val, std::memory_order::seq_cst); }; |
208 | auto load = [](std::atomic_ref<T> const& x) { |
209 | auto result = x.load(std::memory_order::relaxed); |
210 | T unexpected(T(255)); |
211 | bool r = |
212 | x.compare_exchange_strong(unexpected, unexpected, std::memory_order::relaxed, std::memory_order::seq_cst); |
213 | assert(!r); |
214 | return result; |
215 | }; |
216 | test_seq_cst<T>(store, load); |
217 | } |
218 | } |
219 | }; |
220 | |
221 | int main(int, char**) { |
222 | TestEachAtomicType<TestCompareExchangeStrong>()(); |
223 | return 0; |
224 | } |
225 | |