From 419ebb289104f75207dc5a5c56e75d497534eb8a Mon Sep 17 00:00:00 2001 From: Kuba Brecka Date: Thu, 8 Sep 2016 10:15:20 +0000 Subject: [PATCH] [tsan] Support C++11 call_once in TSan on Darwin This patch adds a wrapper for call_once, which uses an already-compiled helper __call_once with an atomic release which is invisible to TSan. To avoid false positives, the interceptor performs an explicit atomic release in the callback wrapper. Differential Revision: https://reviews.llvm.org/D24188 llvm-svn: 280920 --- compiler-rt/lib/tsan/rtl/tsan_interceptors_mac.cc | 27 ++++++++++++++++++ compiler-rt/test/tsan/Darwin/libcxx-call-once.mm | 34 +++++++++++++++++++++++ 2 files changed, 61 insertions(+) create mode 100644 compiler-rt/test/tsan/Darwin/libcxx-call-once.mm diff --git a/compiler-rt/lib/tsan/rtl/tsan_interceptors_mac.cc b/compiler-rt/lib/tsan/rtl/tsan_interceptors_mac.cc index 03cf54f..015e48e 100644 --- a/compiler-rt/lib/tsan/rtl/tsan_interceptors_mac.cc +++ b/compiler-rt/lib/tsan/rtl/tsan_interceptors_mac.cc @@ -327,6 +327,33 @@ STDCXX_INTERCEPTOR(void, _ZNSt3__119__shared_weak_count16__release_sharedEv, } } +namespace { +struct call_once_callback_args { + void (*orig_func)(void *arg); + void *orig_arg; + void *flag; +}; + +void call_once_callback_wrapper(void *arg) { + call_once_callback_args *new_args = (call_once_callback_args *)arg; + new_args->orig_func(new_args->orig_arg); + __tsan_release(new_args->flag); +} +} // namespace + +// This adds a libc++ interceptor for: +// void __call_once(volatile unsigned long&, void*, void(*)(void*)); +// C++11 call_once is implemented via an internal function __call_once which is +// inside libc++.dylib, and the atomic release store inside it is thus +// TSan-invisible. To avoid false positives, this interceptor wraps the callback +// function and performs an explicit Release after the user code has run. +STDCXX_INTERCEPTOR(void, _ZNSt3__111__call_onceERVmPvPFvS2_E, void *flag, + void *arg, void (*func)(void *arg)) { + call_once_callback_args new_args = {func, arg, flag}; + REAL(_ZNSt3__111__call_onceERVmPvPFvS2_E)(flag, &new_args, + call_once_callback_wrapper); +} + } // namespace __tsan #endif // SANITIZER_MAC diff --git a/compiler-rt/test/tsan/Darwin/libcxx-call-once.mm b/compiler-rt/test/tsan/Darwin/libcxx-call-once.mm new file mode 100644 index 0000000..5388e49 --- /dev/null +++ b/compiler-rt/test/tsan/Darwin/libcxx-call-once.mm @@ -0,0 +1,34 @@ +// RUN: %clangxx_tsan %s -o %t -framework Foundation -std=c++11 +// RUN: %env_tsan_opts=ignore_interceptors_accesses=1 %run %t 2>&1 | FileCheck %s + +#import + +#import +#import + +long my_global; +std::once_flag once_token; + +void thread_func() { + std::call_once(once_token, [] { + my_global = 17; + }); + + long val = my_global; + fprintf(stderr, "my_global = %ld\n", val); +} + +int main(int argc, const char *argv[]) { + fprintf(stderr, "Hello world.\n"); + + std::thread t1(thread_func); + std::thread t2(thread_func); + t1.join(); + t2.join(); + + fprintf(stderr, "Done.\n"); +} + +// CHECK: Hello world. +// CHECK-NOT: WARNING: ThreadSanitizer +// CHECK: Done. -- 2.7.4