Index: compiler-rt/lib/tsan/rtl/tsan_interface_atomic.cpp =================================================================== --- compiler-rt/lib/tsan/rtl/tsan_interface_atomic.cpp +++ compiler-rt/lib/tsan/rtl/tsan_interface_atomic.cpp @@ -402,7 +402,6 @@ template static bool AtomicCAS(ThreadState *thr, uptr pc, volatile T *a, T *c, T v, morder mo, morder fmo) { - (void)fmo; // Unused because llvm does not pass it yet. MemoryWriteAtomic(thr, pc, (uptr)a, SizeLog()); SyncVar *s = 0; bool write_lock = mo != mo_acquire && mo != mo_consume; @@ -429,6 +428,20 @@ if (pr == cc) return true; *c = pr; + + // Honor failure memory order. + CHECK(IsLoadOrder(fmo)); + if (fmo != mo_acquire) + return false; + SyncVar *sf = ctx->metamap.GetIfExistsAndLock((uptr)a, false); + if (sf) { + AcquireImpl(thr, pc, &sf->clock); + // Re-read under sync mutex because we need a consistent snapshot + // of the value and the clock we acquire. + *c = NoTsanAtomicLoad(a, fmo); + sf->mtx.ReadUnlock(); + MemoryReadAtomic(thr, pc, (uptr)a, SizeLog()); + } return false; } Index: compiler-rt/test/tsan/compare_exchange_release_acquire.cpp =================================================================== --- /dev/null +++ compiler-rt/test/tsan/compare_exchange_release_acquire.cpp @@ -0,0 +1,39 @@ +// RUN: %clangxx_tsan -O1 %s %link_libcxx_tsan -o %t && %run %t 2>&1 | FileCheck %s +#include +#include +#include +#include + +struct node { int val; }; +std::atomic _node{nullptr}; + +void f1() { + auto n = new node(); + n->val = 42; + _node.store(n, std::memory_order_release); +} + +void f2() { + node* expected = nullptr; + while (expected == nullptr) { + _node.compare_exchange_weak(expected, nullptr, std::memory_order_release, + std::memory_order_acquire); + }; + + ++expected->val; + assert(expected->val == 43); +} + +int main() { + std::thread t1(f1); + std::thread t2(f2); + + t1.join(); + t2.join(); + + fprintf(stderr, "DONE\n"); + return 0; +} + +// CHECK-NOT: WARNING: ThreadSanitizer: data race +// CHECK: DONE Index: compiler-rt/test/tsan/compare_exchange_release_relaxed.cpp =================================================================== --- /dev/null +++ compiler-rt/test/tsan/compare_exchange_release_relaxed.cpp @@ -0,0 +1,36 @@ +// RUN: %clangxx_tsan -O1 %s %link_libcxx_tsan -o %t && %deflake %run %t 2>&1 | FileCheck %s +#include +#include +#include + +struct node { int val; }; +std::atomic _node{nullptr}; + +void f1() { + auto n = new node(); + n->val = 42; + _node.store(n, std::memory_order_release); +} + +void f2() { + node* expected = nullptr; + while (expected == nullptr) { + _node.compare_exchange_weak(expected, nullptr, std::memory_order_release, + std::memory_order_relaxed); + }; + + ++expected->val; + assert(expected->val == 43); +} + +int main() { + std::thread t1(f1); + std::thread t2(f2); + + t1.join(); + t2.join(); + + return 0; +} + +// CHECK: WARNING: ThreadSanitizer: data race Index: llvm/test/Instrumentation/ThreadSanitizer/atomic.ll =================================================================== --- llvm/test/Instrumentation/ThreadSanitizer/atomic.ll +++ llvm/test/Instrumentation/ThreadSanitizer/atomic.ll @@ -370,6 +370,14 @@ ; CHECK-LABEL: atomic8_cas_release ; CHECK: call i8 @__tsan_atomic8_compare_exchange_val(i8* %a, i8 0, i8 1, i32 3, i32 0), !dbg +define void @atomic8_cas_rel_acq(i8* %a) nounwind uwtable { +entry: + cmpxchg i8* %a, i8 0, i8 1 release acquire, !dbg !7 + ret void, !dbg !7 +} +; CHECK-LABEL: atomic8_cas_rel_acq +; CHECK: call i8 @__tsan_atomic8_compare_exchange_val(i8* %a, i8 0, i8 1, i32 3, i32 2), !dbg + define void @atomic8_cas_acq_rel(i8* %a) nounwind uwtable { entry: cmpxchg i8* %a, i8 0, i8 1 acq_rel acquire, !dbg !7