Index: lib/tsan/rtl/tsan_interceptors_mac.cc =================================================================== --- lib/tsan/rtl/tsan_interceptors_mac.cc +++ lib/tsan/rtl/tsan_interceptors_mac.cc @@ -273,6 +273,37 @@ (connection, message, replyq, new_handler); } +// On macOS, libc++ is always linked dynamically, so intercepting works the +// usual way. +#define STDCXX_INTERCEPTOR TSAN_INTERCEPTOR + +namespace { +struct fake_shared_weak_count { + volatile a64 shared_owners; + volatile a64 shared_weak_owners; + virtual void _unused_0x0() = 0; + virtual void _unused_0x8() = 0; + virtual void on_zero_shared() = 0; + virtual void _unused_0x18() = 0; + virtual void on_zero_shared_weak() = 0; +}; +} + +// This adds a libc++ interceptor for: +// void __shared_weak_count::__release_shared() _NOEXCEPT; +// Shared and weak pointers in C++ maintain reference counts via atomics in +// libc++.dylib, which are TSan-invisible, and this leads to false positives in +// destructor code. This interceptor re-implements the whole function so that +// the mo_acq_rel semantics of the atomic decrement are visible. +STDCXX_INTERCEPTOR(void, _ZNSt3__119__shared_weak_count16__release_sharedEv, + fake_shared_weak_count *o) { + if (__tsan_atomic64_fetch_add(&o->shared_owners, -1, mo_acq_rel) == 0) { + o->on_zero_shared(); + if (__tsan_atomic64_fetch_add(&o->shared_weak_owners, -1, mo_acq_rel) == 0) + o->on_zero_shared_weak(); + } +} + } // namespace __tsan #endif // SANITIZER_MAC Index: test/tsan/Darwin/libcxx-shared-ptr.mm =================================================================== --- test/tsan/Darwin/libcxx-shared-ptr.mm +++ test/tsan/Darwin/libcxx-shared-ptr.mm @@ -0,0 +1,50 @@ +// RUN: %clang_tsan %s -o %t -framework Foundation +// RUN: %env_tsan_opts=ignore_interceptors_accesses=1 %run %t 2>&1 | FileCheck %s + +#import + +#import + +#import "../test.h" + +long my_global; + +struct MyStruct { + void setGlobal() { + my_global = 42; + } + ~MyStruct() { + my_global = 43; + } +}; + +int main(int argc, const char *argv[]) { + fprintf(stderr, "Hello world.\n"); + print_address("addr=", 1, &my_global); + barrier_init(&barrier, 2); + + std::shared_ptr shared(new MyStruct()); + + dispatch_queue_t q = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); + + std::weak_ptr weak(shared); + + dispatch_async(q, ^{ + { + std::shared_ptr strong = weak.lock(); + if (!strong) exit(1); + + strong->setGlobal(); + } + barrier_wait(&barrier); + }); + + barrier_wait(&barrier); + shared.reset(); + + fprintf(stderr, "Done.\n"); +} + +// CHECK: Hello world. +// CHECK: Done. +// CHECK-NOT: WARNING: ThreadSanitizer