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_weak(T&, T, memory_order, memory_order) const noexcept; |
16 | // bool compare_exchange_weak(T&, T, memory_order = memory_order::seq_cst) const noexcept; |
17 | |
18 | #include <atomic> |
19 | #include <concepts> |
20 | #include <cassert> |
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 TestCompareExchangeWeak { |
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_weak(t, T(2)); |
36 | assert(y == true); |
37 | assert(a == T(2)); |
38 | assert(t == T(1)); |
39 | y = a.compare_exchange_weak(t, T(3)); |
40 | assert(y == false); |
41 | assert(a == T(2)); |
42 | assert(t == T(2)); |
43 | |
44 | ASSERT_NOEXCEPT(a.compare_exchange_weak(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_weak(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_weak(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_weak(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_weak(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_weak(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_weak(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 | // could fail spuriously, so put it in a loop |
84 | while (!x.compare_exchange_weak(old_val, new_val, std::memory_order::release, std::memory_order::relaxed)) { |
85 | } |
86 | }; |
87 | |
88 | auto load = [](std::atomic_ref<T> const& x) { return x.load(std::memory_order::acquire); }; |
89 | test_acquire_release<T>(store, load); |
90 | |
91 | auto store_one_arg = [](std::atomic_ref<T> const& x, T old_val, T new_val) { |
92 | // could fail spuriously, so put it in a loop |
93 | while (!x.compare_exchange_weak(old_val, new_val, std::memory_order::release)) { |
94 | } |
95 | }; |
96 | test_acquire_release<T>(store_one_arg, load); |
97 | } |
98 | |
99 | // success memory_order::acquire |
100 | { |
101 | auto store = [](std::atomic_ref<T> const& x, T, T new_val) { x.store(new_val, std::memory_order::release); }; |
102 | auto load = [](std::atomic_ref<T> const& x) { |
103 | auto val = x.load(std::memory_order::relaxed); |
104 | while (!x.compare_exchange_weak(val, val, std::memory_order::acquire, std::memory_order::relaxed)) { |
105 | } |
106 | return val; |
107 | }; |
108 | test_acquire_release<T>(store, load); |
109 | |
110 | auto load_one_arg = [](std::atomic_ref<T> const& x) { |
111 | auto val = x.load(std::memory_order::relaxed); |
112 | while (!x.compare_exchange_weak(val, val, std::memory_order::acquire)) { |
113 | } |
114 | return val; |
115 | }; |
116 | test_acquire_release<T>(store, load_one_arg); |
117 | } |
118 | |
119 | // success memory_order::acq_rel |
120 | { |
121 | auto store = [](std::atomic_ref<T> const& x, T old_val, T new_val) { |
122 | // could fail spuriously, so put it in a loop |
123 | while (!x.compare_exchange_weak(old_val, new_val, std::memory_order::acq_rel, std::memory_order::relaxed)) { |
124 | } |
125 | }; |
126 | auto load = [](std::atomic_ref<T> const& x) { |
127 | auto val = x.load(std::memory_order::relaxed); |
128 | while (!x.compare_exchange_weak(val, val, std::memory_order::acq_rel, std::memory_order::relaxed)) { |
129 | } |
130 | return val; |
131 | }; |
132 | test_acquire_release<T>(store, load); |
133 | |
134 | auto store_one_arg = [](std::atomic_ref<T> const& x, T old_val, T new_val) { |
135 | // could fail spuriously, so put it in a loop |
136 | while (!x.compare_exchange_weak(old_val, new_val, std::memory_order::acq_rel)) { |
137 | } |
138 | }; |
139 | auto load_one_arg = [](std::atomic_ref<T> const& x) { |
140 | auto val = x.load(std::memory_order::relaxed); |
141 | while (!x.compare_exchange_weak(val, val, std::memory_order::acq_rel)) { |
142 | } |
143 | return val; |
144 | }; |
145 | test_acquire_release<T>(store_one_arg, load_one_arg); |
146 | } |
147 | |
148 | // success memory_order::seq_cst |
149 | { |
150 | auto store = [](std::atomic_ref<T> const& x, T old_val, T new_val) { |
151 | // could fail spuriously, so put it in a loop |
152 | while (!x.compare_exchange_weak(old_val, new_val, std::memory_order::seq_cst, std::memory_order::relaxed)) { |
153 | } |
154 | }; |
155 | auto load = [](std::atomic_ref<T> const& x) { |
156 | auto val = x.load(std::memory_order::relaxed); |
157 | while (!x.compare_exchange_weak(val, val, std::memory_order::seq_cst, std::memory_order::relaxed)) { |
158 | } |
159 | return val; |
160 | }; |
161 | test_seq_cst<T>(store, load); |
162 | |
163 | auto store_one_arg = [](std::atomic_ref<T> const& x, T old_val, T new_val) { |
164 | // could fail spuriously, so put it in a loop |
165 | while (!x.compare_exchange_weak(old_val, new_val, std::memory_order::seq_cst)) { |
166 | } |
167 | }; |
168 | auto load_one_arg = [](std::atomic_ref<T> const& x) { |
169 | auto val = x.load(std::memory_order::relaxed); |
170 | while (!x.compare_exchange_weak(val, val, std::memory_order::seq_cst)) { |
171 | } |
172 | return val; |
173 | }; |
174 | test_seq_cst<T>(store_one_arg, load_one_arg); |
175 | } |
176 | |
177 | // failure memory_order::acquire |
178 | { |
179 | auto store = [](std::atomic_ref<T> const& x, T, T new_val) { x.store(new_val, std::memory_order::release); }; |
180 | auto load = [](std::atomic_ref<T> const& x) { |
181 | auto result = x.load(std::memory_order::relaxed); |
182 | T unexpected(T(255)); |
183 | bool r = |
184 | x.compare_exchange_weak(unexpected, unexpected, std::memory_order::relaxed, std::memory_order::acquire); |
185 | assert(!r); |
186 | return result; |
187 | }; |
188 | test_acquire_release<T>(store, load); |
189 | |
190 | auto load_one_arg = [](std::atomic_ref<T> const& x) { |
191 | auto result = x.load(std::memory_order::relaxed); |
192 | T unexpected(T(255)); |
193 | bool r = x.compare_exchange_weak(unexpected, unexpected, std::memory_order::acquire); |
194 | assert(!r); |
195 | return result; |
196 | }; |
197 | test_acquire_release<T>(store, load_one_arg); |
198 | |
199 | // acq_rel replaced by acquire |
200 | auto load_one_arg_acq_rel = [](std::atomic_ref<T> const& x) { |
201 | auto result = x.load(std::memory_order::relaxed); |
202 | T unexpected(T(255)); |
203 | bool r = x.compare_exchange_weak(unexpected, unexpected, std::memory_order::acq_rel); |
204 | assert(!r); |
205 | return result; |
206 | }; |
207 | test_acquire_release<T>(store, load_one_arg_acq_rel); |
208 | } |
209 | |
210 | // failure memory_order::seq_cst |
211 | { |
212 | auto store = [](std::atomic_ref<T> const& x, T, T new_val) { x.store(new_val, std::memory_order::seq_cst); }; |
213 | auto load = [](std::atomic_ref<T> const& x) { |
214 | auto result = x.load(std::memory_order::relaxed); |
215 | T unexpected(T(255)); |
216 | bool r = |
217 | x.compare_exchange_weak(unexpected, unexpected, std::memory_order::relaxed, std::memory_order::seq_cst); |
218 | assert(!r); |
219 | return result; |
220 | }; |
221 | test_seq_cst<T>(store, load); |
222 | } |
223 | } |
224 | }; |
225 | |
226 | int main(int, char**) { |
227 | TestEachAtomicType<TestCompareExchangeWeak>()(); |
228 | return 0; |
229 | } |
230 | |