// 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 #include #include #include #include #define NUM_ORDS 16 #define NUM_THREADS NUM_ORDS * 2 struct node { int val; }; std::atomic _nodes[NUM_THREADS] = {}; void f1(int i) { auto n = new node(); n->val = 42; _nodes[i].store(n, std::memory_order_release); } template void f2(int i, std::memory_order mo, std::memory_order fmo) { node *expected = nullptr; while (expected == nullptr) { _nodes[i].compare_exchange_weak(expected, nullptr, mo, fmo); }; ++expected->val; assert(expected->val == 43); } struct MemOrdSuccFail { std::memory_order mo; std::memory_order fmo; }; MemOrdSuccFail OrdList[NUM_ORDS] = { {std::memory_order_release, std::memory_order_relaxed}, {std::memory_order_release, std::memory_order_acquire}, {std::memory_order_release, std::memory_order_consume}, {std::memory_order_release, std::memory_order_seq_cst}, {std::memory_order_acq_rel, std::memory_order_relaxed}, {std::memory_order_acq_rel, std::memory_order_acquire}, {std::memory_order_acq_rel, std::memory_order_consume}, {std::memory_order_acq_rel, std::memory_order_seq_cst}, {std::memory_order_seq_cst, std::memory_order_relaxed}, {std::memory_order_seq_cst, std::memory_order_acquire}, {std::memory_order_seq_cst, std::memory_order_consume}, {std::memory_order_seq_cst, std::memory_order_seq_cst}, {std::memory_order_relaxed, std::memory_order_relaxed}, {std::memory_order_relaxed, std::memory_order_acquire}, {std::memory_order_relaxed, std::memory_order_consume}, {std::memory_order_relaxed, std::memory_order_seq_cst}, }; int main() { std::thread threads[NUM_THREADS]; int ords = 0; // Instantiate a new f2 for each MO so we can dedup reports and actually // make sure relaxed FMO triggers a warning for every different MO. for (unsigned t = 0; t < 8; t += 2) { threads[t] = std::thread(f1, t); threads[t + 1] = std::thread(f2<0>, t, OrdList[ords].mo, OrdList[ords].fmo); threads[t].join(); threads[t + 1].join(); ords++; } for (unsigned t = 8; t < 16; t += 2) { threads[t] = std::thread(f1, t); threads[t + 1] = std::thread(f2<1>, t, OrdList[ords].mo, OrdList[ords].fmo); threads[t].join(); threads[t + 1].join(); ords++; } for (unsigned t = 16; t < 24; t += 2) { threads[t] = std::thread(f1, t); threads[t + 1] = std::thread(f2<2>, t, OrdList[ords].mo, OrdList[ords].fmo); threads[t].join(); threads[t + 1].join(); ords++; } for (unsigned t = 24; t < 32; t += 2) { threads[t] = std::thread(f1, t); threads[t + 1] = std::thread(f2<3>, t, OrdList[ords].mo, OrdList[ords].fmo); threads[t].join(); threads[t + 1].join(); ords++; } fprintf(stderr, "DONE\n"); return 0; } // CHECK-REPORT: WARNING: ThreadSanitizer: data race // CHECK-REPORT: WARNING: ThreadSanitizer: data race // CHECK-REPORT: WARNING: ThreadSanitizer: data race // CHECK-REPORT: WARNING: ThreadSanitizer: data race // CHECK-REPORT: DONE // CHECK-REPORT: ThreadSanitizer: reported 4 warnings