[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
This commit is contained in:
Kuba Brecka 2016-09-08 10:15:20 +00:00
parent 2f1fbaebe2
commit 419ebb2891
2 changed files with 61 additions and 0 deletions

View File

@ -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

View File

@ -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 <Foundation/Foundation.h>
#import <iostream>
#import <thread>
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.