|
2 | 2 | // https://en.cppreference.com/w/cpp/atomic/atomic#Example |
3 | 3 | // |
4 | 4 | #include <atomic> |
| 5 | +#include <chrono> |
| 6 | +#include <cassert> |
5 | 7 | #include <functional> |
6 | 8 | #include <iostream> |
7 | 9 | #include <mutex> |
8 | 10 | #include <numeric> |
9 | 11 | #include <stdexcept> |
| 12 | +#include <string> |
10 | 13 | #include <thread> |
11 | 14 | #include <vector> |
12 | 15 |
|
@@ -137,3 +140,181 @@ void run() { |
137 | 140 | } |
138 | 141 | } |
139 | 142 | } // namespace atomic_flag_test |
| 143 | + |
| 144 | +// |
| 145 | +// https://en.cppreference.com/w/cpp/atomic/atomic_thread_fence#Example |
| 146 | +// |
| 147 | +namespace atomic_thread_fence_test { |
| 148 | +constexpr int num_mailboxes = 32; |
| 149 | +std::atomic<int> mailbox_receiver[num_mailboxes]; |
| 150 | +std::string mailbox_data[num_mailboxes]; |
| 151 | + |
| 152 | +void run() { |
| 153 | + constexpr int my_id = 7; |
| 154 | + for (int i = 0; i < num_mailboxes; ++i) { |
| 155 | + mailbox_receiver[i].store(-1, std::memory_order_relaxed); |
| 156 | + mailbox_data[i].clear(); |
| 157 | + } |
| 158 | + |
| 159 | + // cppreference presents this as a snippet with "..." placeholders. The |
| 160 | + // driver test supplies concrete mailbox data so the fence path is executable. |
| 161 | + mailbox_data[11] = "fence-protected message"; |
| 162 | + std::atomic_store_explicit(&mailbox_receiver[11], my_id, |
| 163 | + std::memory_order_release); |
| 164 | + |
| 165 | + bool observed = false; |
| 166 | + for (int i = 0; i < num_mailboxes; ++i) { |
| 167 | + if (std::atomic_load_explicit(&mailbox_receiver[i], |
| 168 | + std::memory_order_relaxed) == my_id) { |
| 169 | + // synchronize with just one writer |
| 170 | + std::atomic_thread_fence(std::memory_order_acquire); |
| 171 | + // guaranteed to observe everything done in the writer thread |
| 172 | + // before the atomic_store_explicit() |
| 173 | + std::cout << mailbox_data[i] << '\n'; |
| 174 | + observed = mailbox_data[i] == "fence-protected message"; |
| 175 | + } |
| 176 | + } |
| 177 | + assert(observed); |
| 178 | +} |
| 179 | +} // namespace atomic_thread_fence_test |
| 180 | + |
| 181 | +// |
| 182 | +// https://en.cppreference.com/w/cpp/atomic/atomic_signal_fence |
| 183 | +// |
| 184 | +namespace atomic_signal_fence_test { |
| 185 | +void run() { |
| 186 | + // cppreference documents atomic_signal_fence as an API page without a |
| 187 | + // standalone runnable example; keep direct API coverage here. |
| 188 | + std::atomic_signal_fence(std::memory_order_seq_cst); |
| 189 | + std::atomic_signal_fence(std::memory_order_acquire); |
| 190 | + std::cout << "atomic_signal_fence calls completed\n"; |
| 191 | +} |
| 192 | +} // namespace atomic_signal_fence_test |
| 193 | + |
| 194 | +// |
| 195 | +// https://en.cppreference.com/w/cpp/atomic/atomic_fetch_add#Example |
| 196 | +// |
| 197 | +namespace atomic_fetch_add_test { |
| 198 | +using namespace std::chrono_literals; |
| 199 | +// meaning of cnt: |
| 200 | +// 5: readers and writer are in race. There are no active readers or writers. |
| 201 | +// 4...0: there are 1...5 active readers, The writer is blocked. |
| 202 | +// -1: writer won the race and readers are blocked. |
| 203 | + |
| 204 | +const int N = 5; // four concurrent readers are allowed |
| 205 | +std::atomic<int> cnt(N); |
| 206 | + |
| 207 | +std::vector<int> data; |
| 208 | +void reader(int id) { |
| 209 | + for (;;) { |
| 210 | + // lock |
| 211 | + while (std::atomic_fetch_sub(&cnt, 1) <= 0) { |
| 212 | + std::atomic_fetch_add(&cnt, 1); |
| 213 | + } |
| 214 | + |
| 215 | + // read |
| 216 | + if (!data.empty()) { |
| 217 | + std::cout << ("reader " + std::to_string(id) + " sees " + |
| 218 | + std::to_string(*data.rbegin()) + '\n'); |
| 219 | + } |
| 220 | + if (data.size() == 25) { |
| 221 | + break; |
| 222 | + } |
| 223 | + |
| 224 | + // unlock |
| 225 | + std::atomic_fetch_add(&cnt, 1); |
| 226 | + // pause |
| 227 | + std::this_thread::sleep_for(1ms); |
| 228 | + } |
| 229 | +} |
| 230 | + |
| 231 | +void writer() { |
| 232 | + for (int n = 0; n < 25; ++n) { |
| 233 | + // lock |
| 234 | + while (std::atomic_fetch_sub(&cnt, N + 1) != N) { |
| 235 | + std::atomic_fetch_add(&cnt, N + 1); |
| 236 | + } |
| 237 | + |
| 238 | + // write |
| 239 | + data.push_back(n); |
| 240 | + std::cout << "writer pushed back " << n << '\n'; |
| 241 | + |
| 242 | + // unlock |
| 243 | + std::atomic_fetch_add(&cnt, N + 1); |
| 244 | + // pause |
| 245 | + std::this_thread::sleep_for(1ms); |
| 246 | + } |
| 247 | +} |
| 248 | + |
| 249 | +void run() { |
| 250 | + cnt = N; |
| 251 | + data.clear(); |
| 252 | + |
| 253 | + std::vector<std::thread> v; |
| 254 | + for (int n = 0; n < N; ++n) { |
| 255 | + v.emplace_back(reader, n); |
| 256 | + } |
| 257 | + v.emplace_back(writer); |
| 258 | + |
| 259 | + for (auto &t : v) { |
| 260 | + t.join(); |
| 261 | + } |
| 262 | + |
| 263 | + if (data.size() != 25) { |
| 264 | + throw std::runtime_error("unexpected atomic_fetch_add data size"); |
| 265 | + } |
| 266 | +} |
| 267 | +} // namespace atomic_fetch_add_test |
| 268 | + |
| 269 | +// |
| 270 | +// https://en.cppreference.com/w/cpp/atomic/atomic_compare_exchange#Example |
| 271 | +// |
| 272 | +namespace atomic_compare_exchange_test { |
| 273 | +template <class T> struct node { |
| 274 | + T data; |
| 275 | + node *next; |
| 276 | + node(const T &data) : data(data), next(nullptr) {} |
| 277 | +}; |
| 278 | + |
| 279 | +template <class T> class stack { |
| 280 | + std::atomic<node<T> *> head{nullptr}; |
| 281 | + |
| 282 | +public: |
| 283 | + ~stack() { |
| 284 | + node<T> *current = head.load(std::memory_order_relaxed); |
| 285 | + while (current != nullptr) { |
| 286 | + node<T> *next = current->next; |
| 287 | + delete current; |
| 288 | + current = next; |
| 289 | + } |
| 290 | + } |
| 291 | + |
| 292 | + void push(const T &data) { |
| 293 | + node<T> *new_node = new node<T>(data); |
| 294 | + // put the current value of head into new_node->next |
| 295 | + new_node->next = head.load(std::memory_order_relaxed); |
| 296 | + // now make new_node the new head, but if the head |
| 297 | + // is no longer what's stored in new_node->next |
| 298 | + // (some other thread must have inserted a node just now) |
| 299 | + // then put that new head into new_node->next and try again |
| 300 | + while (!std::atomic_compare_exchange_weak_explicit( |
| 301 | + &head, &new_node->next, new_node, std::memory_order_release, |
| 302 | + std::memory_order_relaxed)) { |
| 303 | + ; // the body of the loop is empty |
| 304 | + } |
| 305 | + // note: the above loop is not thread-safe in at least |
| 306 | + // GCC prior to 4.8.3 (bug 60272), clang prior to 2014-05-05 (bug 18899) |
| 307 | + // MSVC prior to 2014-03-17 (bug 819819). See member function version for |
| 308 | + // workaround |
| 309 | + } |
| 310 | +}; |
| 311 | + |
| 312 | +void run() { |
| 313 | + // cppreference's minimal program exits immediately after main(); the driver |
| 314 | + // harness initializes head explicitly and frees the nodes before unload. |
| 315 | + stack<int> s; |
| 316 | + s.push(1); |
| 317 | + s.push(2); |
| 318 | + s.push(3); |
| 319 | +} |
| 320 | +} // namespace atomic_compare_exchange_test |
0 commit comments