summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--compiler-rt/lib/tsan/CMakeLists.txt1
-rw-r--r--compiler-rt/lib/tsan/rtl/tsan_interceptors.cc11
-rw-r--r--compiler-rt/lib/tsan/rtl/tsan_interceptors.h12
-rw-r--r--compiler-rt/lib/tsan/rtl/tsan_libdispatch_mac.cc72
-rw-r--r--compiler-rt/test/tsan/Darwin/gcd-once.mm55
5 files changed, 140 insertions, 11 deletions
diff --git a/compiler-rt/lib/tsan/CMakeLists.txt b/compiler-rt/lib/tsan/CMakeLists.txt
index de0fb439eaf..66ba30c031c 100644
--- a/compiler-rt/lib/tsan/CMakeLists.txt
+++ b/compiler-rt/lib/tsan/CMakeLists.txt
@@ -47,6 +47,7 @@ set(TSAN_CXX_SOURCES
if(APPLE)
list(APPEND TSAN_SOURCES
+ rtl/tsan_libdispatch_mac.cc
rtl/tsan_platform_mac.cc
rtl/tsan_platform_posix.cc)
elseif(UNIX)
diff --git a/compiler-rt/lib/tsan/rtl/tsan_interceptors.cc b/compiler-rt/lib/tsan/rtl/tsan_interceptors.cc
index 7aaa4c304bc..f389d6596e1 100644
--- a/compiler-rt/lib/tsan/rtl/tsan_interceptors.cc
+++ b/compiler-rt/lib/tsan/rtl/tsan_interceptors.cc
@@ -264,17 +264,6 @@ ScopedInterceptor::~ScopedInterceptor() {
}
}
-#define SCOPED_TSAN_INTERCEPTOR(func, ...) \
- SCOPED_INTERCEPTOR_RAW(func, __VA_ARGS__); \
- if (REAL(func) == 0) { \
- Report("FATAL: ThreadSanitizer: failed to intercept %s\n", #func); \
- Die(); \
- } \
- if (thr->ignore_interceptors || thr->in_ignored_lib) \
- return REAL(func)(__VA_ARGS__); \
-/**/
-
-#define TSAN_INTERCEPTOR(ret, func, ...) INTERCEPTOR(ret, func, __VA_ARGS__)
#define TSAN_INTERCEPT(func) INTERCEPT_FUNCTION(func)
#if SANITIZER_FREEBSD
# define TSAN_INTERCEPT_VER(func, ver) INTERCEPT_FUNCTION(func)
diff --git a/compiler-rt/lib/tsan/rtl/tsan_interceptors.h b/compiler-rt/lib/tsan/rtl/tsan_interceptors.h
index 49b79a7c5f9..451e8645fc8 100644
--- a/compiler-rt/lib/tsan/rtl/tsan_interceptors.h
+++ b/compiler-rt/lib/tsan/rtl/tsan_interceptors.h
@@ -26,6 +26,18 @@ class ScopedInterceptor {
(void)pc; \
/**/
+#define SCOPED_TSAN_INTERCEPTOR(func, ...) \
+ SCOPED_INTERCEPTOR_RAW(func, __VA_ARGS__); \
+ if (REAL(func) == 0) { \
+ Report("FATAL: ThreadSanitizer: failed to intercept %s\n", #func); \
+ Die(); \
+ } \
+ if (thr->ignore_interceptors || thr->in_ignored_lib) \
+ return REAL(func)(__VA_ARGS__); \
+/**/
+
+#define TSAN_INTERCEPTOR(ret, func, ...) INTERCEPTOR(ret, func, __VA_ARGS__)
+
#if SANITIZER_FREEBSD
#define __libc_free __free
#define __libc_malloc __malloc
diff --git a/compiler-rt/lib/tsan/rtl/tsan_libdispatch_mac.cc b/compiler-rt/lib/tsan/rtl/tsan_libdispatch_mac.cc
new file mode 100644
index 00000000000..7f80a470288
--- /dev/null
+++ b/compiler-rt/lib/tsan/rtl/tsan_libdispatch_mac.cc
@@ -0,0 +1,72 @@
+//===-- tsan_libdispatch_mac.cc -------------------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of ThreadSanitizer (TSan), a race detector.
+//
+// Mac-specific libdispatch (GCD) support.
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_common/sanitizer_platform.h"
+#if SANITIZER_MAC
+
+#include "sanitizer_common/sanitizer_common.h"
+#include "interception/interception.h"
+#include "tsan_interceptors.h"
+#include "tsan_platform.h"
+#include "tsan_rtl.h"
+
+#include <dispatch/dispatch.h>
+#include <pthread.h>
+
+namespace __tsan {
+
+// GCD's dispatch_once implementation has a fast path that contains a racy read
+// and it's inlined into user's code. Furthermore, this fast path doesn't
+// establish a proper happens-before relations between the initialization and
+// code following the call to dispatch_once. We could deal with this in
+// instrumented code, but there's not much we can do about it in system
+// libraries. Let's disable the fast path (by never storing the value ~0 to
+// predicate), so the interceptor is always called, and let's add proper release
+// and acquire semantics. Since TSan does not see its own atomic stores, the
+// race on predicate won't be reported - the only accesses to it that TSan sees
+// are the loads on the fast path. Loads don't race. Secondly, dispatch_once is
+// both a macro and a real function, we want to intercept the function, so we
+// need to undefine the macro.
+#undef dispatch_once
+TSAN_INTERCEPTOR(void, dispatch_once, dispatch_once_t *predicate,
+ dispatch_block_t block) {
+ SCOPED_TSAN_INTERCEPTOR(dispatch_once, predicate, block);
+ atomic_uint32_t *a = reinterpret_cast<atomic_uint32_t *>(predicate);
+ u32 v = atomic_load(a, memory_order_acquire);
+ if (v == 0 &&
+ atomic_compare_exchange_strong(a, &v, 1, memory_order_relaxed)) {
+ block();
+ Release(thr, pc, (uptr)a);
+ atomic_store(a, 2, memory_order_release);
+ } else {
+ while (v != 2) {
+ internal_sched_yield();
+ v = atomic_load(a, memory_order_acquire);
+ }
+ Acquire(thr, pc, (uptr)a);
+ }
+}
+
+#undef dispatch_once_f
+TSAN_INTERCEPTOR(void, dispatch_once_f, dispatch_once_t *predicate,
+ void *context, dispatch_function_t function) {
+ SCOPED_TSAN_INTERCEPTOR(dispatch_once_f, predicate, context, function);
+ WRAP(dispatch_once)(predicate, ^(void) {
+ function(context);
+ });
+}
+
+} // namespace __tsan
+
+#endif // SANITIZER_MAC
diff --git a/compiler-rt/test/tsan/Darwin/gcd-once.mm b/compiler-rt/test/tsan/Darwin/gcd-once.mm
new file mode 100644
index 00000000000..17757d20375
--- /dev/null
+++ b/compiler-rt/test/tsan/Darwin/gcd-once.mm
@@ -0,0 +1,55 @@
+// RUN: %clang_tsan %s -o %t -framework Foundation
+// RUN: %run %t 2>&1
+
+#import <Foundation/Foundation.h>
+
+#import "../test.h"
+
+static const long kNumThreads = 4;
+
+long global;
+long global2;
+
+static dispatch_once_t once_token;
+static dispatch_once_t once_token2;
+
+void f(void *) {
+ global2 = 42;
+ usleep(100000);
+}
+
+void *Thread(void *a) {
+ barrier_wait(&barrier);
+
+ dispatch_once(&once_token, ^{
+ global = 42;
+ usleep(100000);
+ });
+ long x = global;
+
+ dispatch_once_f(&once_token2, NULL, f);
+ long x2 = global2;
+
+ fprintf(stderr, "global = %ld\n", x);
+ fprintf(stderr, "global2 = %ld\n", x2);
+ return 0;
+}
+
+int main() {
+ fprintf(stderr, "Hello world.\n");
+ barrier_init(&barrier, kNumThreads);
+
+ pthread_t t[kNumThreads];
+ for (int i = 0; i < kNumThreads; i++) {
+ pthread_create(&t[i], 0, Thread, 0);
+ }
+ for (int i = 0; i < kNumThreads; i++) {
+ pthread_join(t[i], 0);
+ }
+
+ fprintf(stderr, "Done.\n");
+}
+
+// CHECK: Hello world.
+// CHECK: Done.
+// CHECK-NOT: WARNING: ThreadSanitizer
OpenPOWER on IntegriCloud