1 | // RUN: %clangxx_tsan -O1 %s %link_libcxx_tsan -o %t && %deflake %env_tsan_opts=atexit_sleep_ms=50 %run %t 2>&1 | FileCheck --check-prefix=CHECK-REPORT %s |
2 | |
3 | #include <atomic> |
4 | #include <cassert> |
5 | #include <stdio.h> |
6 | #include <thread> |
7 | |
8 | #define NUM_ORDS 16 |
9 | #define NUM_THREADS NUM_ORDS * 2 |
10 | struct node { |
11 | int val; |
12 | }; |
13 | std::atomic<node *> _nodes[NUM_THREADS] = {}; |
14 | |
15 | void f1(int i) { |
16 | auto n = new node(); |
17 | n->val = 42; |
18 | _nodes[i].store(n, std::memory_order_release); |
19 | } |
20 | |
21 | template <int version> |
22 | void f2(int i, std::memory_order mo, std::memory_order fmo) { |
23 | node *expected = nullptr; |
24 | while (expected == nullptr) { |
25 | _nodes[i].compare_exchange_weak(expected, nullptr, mo, fmo); |
26 | }; |
27 | |
28 | ++expected->val; |
29 | assert(expected->val == 43); |
30 | } |
31 | |
32 | struct MemOrdSuccFail { |
33 | std::memory_order mo; |
34 | std::memory_order fmo; |
35 | }; |
36 | |
37 | MemOrdSuccFail OrdList[NUM_ORDS] = { |
38 | {std::memory_order_release, std::memory_order_relaxed}, |
39 | {std::memory_order_release, std::memory_order_acquire}, |
40 | {std::memory_order_release, std::memory_order_consume}, |
41 | {std::memory_order_release, std::memory_order_seq_cst}, |
42 | |
43 | {std::memory_order_acq_rel, std::memory_order_relaxed}, |
44 | {std::memory_order_acq_rel, std::memory_order_acquire}, |
45 | {std::memory_order_acq_rel, std::memory_order_consume}, |
46 | {std::memory_order_acq_rel, std::memory_order_seq_cst}, |
47 | |
48 | {std::memory_order_seq_cst, std::memory_order_relaxed}, |
49 | {std::memory_order_seq_cst, std::memory_order_acquire}, |
50 | {std::memory_order_seq_cst, std::memory_order_consume}, |
51 | {std::memory_order_seq_cst, std::memory_order_seq_cst}, |
52 | |
53 | {std::memory_order_relaxed, std::memory_order_relaxed}, |
54 | {std::memory_order_relaxed, std::memory_order_acquire}, |
55 | {std::memory_order_relaxed, std::memory_order_consume}, |
56 | {std::memory_order_relaxed, std::memory_order_seq_cst}, |
57 | }; |
58 | |
59 | int main() { |
60 | std::thread threads[NUM_THREADS]; |
61 | int ords = 0; |
62 | |
63 | // Instantiate a new f2 for each MO so we can dedup reports and actually |
64 | // make sure relaxed FMO triggers a warning for every different MO. |
65 | for (unsigned t = 0; t < 8; t += 2) { |
66 | threads[t] = std::thread(f1, t); |
67 | threads[t + 1] = std::thread(f2<0>, t, OrdList[ords].mo, OrdList[ords].fmo); |
68 | threads[t].join(); |
69 | threads[t + 1].join(); |
70 | ords++; |
71 | } |
72 | |
73 | for (unsigned t = 8; t < 16; t += 2) { |
74 | threads[t] = std::thread(f1, t); |
75 | threads[t + 1] = std::thread(f2<1>, t, OrdList[ords].mo, OrdList[ords].fmo); |
76 | threads[t].join(); |
77 | threads[t + 1].join(); |
78 | ords++; |
79 | } |
80 | |
81 | for (unsigned t = 16; t < 24; t += 2) { |
82 | threads[t] = std::thread(f1, t); |
83 | threads[t + 1] = std::thread(f2<2>, t, OrdList[ords].mo, OrdList[ords].fmo); |
84 | threads[t].join(); |
85 | threads[t + 1].join(); |
86 | ords++; |
87 | } |
88 | |
89 | for (unsigned t = 24; t < 32; t += 2) { |
90 | threads[t] = std::thread(f1, t); |
91 | threads[t + 1] = std::thread(f2<3>, t, OrdList[ords].mo, OrdList[ords].fmo); |
92 | threads[t].join(); |
93 | threads[t + 1].join(); |
94 | ords++; |
95 | } |
96 | |
97 | fprintf(stderr, format: "DONE\n" ); |
98 | return 0; |
99 | } |
100 | |
101 | // CHECK-REPORT: WARNING: ThreadSanitizer: data race |
102 | // CHECK-REPORT: WARNING: ThreadSanitizer: data race |
103 | // CHECK-REPORT: WARNING: ThreadSanitizer: data race |
104 | // CHECK-REPORT: WARNING: ThreadSanitizer: data race |
105 | // CHECK-REPORT: DONE |
106 | // CHECK-REPORT: ThreadSanitizer: reported 4 warnings |
107 | |