summaryrefslogtreecommitdiff
path: root/lib/tsan/rtl/tsan_libdispatch_mac.cc
diff options
context:
space:
mode:
authorKuba Brecka <kuba.brecka@gmail.com>2015-11-24 13:36:06 +0000
committerKuba Brecka <kuba.brecka@gmail.com>2015-11-24 13:36:06 +0000
commit980a245889a0de7c8fcec9ba971f2fee30c9ceba (patch)
tree797f99190fb42cdcf4a60ac2b30c6fe7edb2bb71 /lib/tsan/rtl/tsan_libdispatch_mac.cc
parent345db092d694ce1783985959c3da7b3c4c859f7d (diff)
[tsan] Implement basic GCD interceptors for OS X
We need to intercept libdispatch APIs (dispatch_sync, dispatch_async, etc.) to add synchronization between the code that submits the task and the code that gets executed (possibly on a different thread). This patch adds release+acquire semantics for dispatch_sync, and dispatch_async (plus their "_f" and barrier variants). The synchronization is done on malloc'd contexts (separate for each submitted block/callback). Added tests to show usage of dispatch_sync and dispatch_async, for cases where we expect no warnings and for cases where TSan finds races. Differential Revision: http://reviews.llvm.org/D14745 git-svn-id: https://llvm.org/svn/llvm-project/compiler-rt/trunk@253982 91177308-0d34-0410-b5e6-96231b3b80d8
Diffstat (limited to 'lib/tsan/rtl/tsan_libdispatch_mac.cc')
-rw-r--r--lib/tsan/rtl/tsan_libdispatch_mac.cc66
1 files changed, 66 insertions, 0 deletions
diff --git a/lib/tsan/rtl/tsan_libdispatch_mac.cc b/lib/tsan/rtl/tsan_libdispatch_mac.cc
index 7f80a4702..cc624dd93 100644
--- a/lib/tsan/rtl/tsan_libdispatch_mac.cc
+++ b/lib/tsan/rtl/tsan_libdispatch_mac.cc
@@ -21,11 +21,77 @@
#include "tsan_platform.h"
#include "tsan_rtl.h"
+#include <Block.h>
#include <dispatch/dispatch.h>
#include <pthread.h>
namespace __tsan {
+typedef struct {
+ dispatch_queue_t queue;
+ void *orig_context;
+ dispatch_function_t orig_work;
+} tsan_block_context_t;
+
+static tsan_block_context_t *AllocContext(ThreadState *thr, uptr pc,
+ dispatch_queue_t queue,
+ void *orig_context,
+ dispatch_function_t orig_work) {
+ tsan_block_context_t *new_context =
+ (tsan_block_context_t *)user_alloc(thr, pc, sizeof(tsan_block_context_t));
+ new_context->queue = queue;
+ new_context->orig_context = orig_context;
+ new_context->orig_work = orig_work;
+ return new_context;
+}
+
+static void dispatch_callback_wrap_acquire(void *param) {
+ SCOPED_INTERCEPTOR_RAW(dispatch_async_f_callback_wrap);
+ tsan_block_context_t *context = (tsan_block_context_t *)param;
+ Acquire(thr, pc, (uptr)context);
+ context->orig_work(context->orig_context);
+ user_free(thr, pc, context);
+}
+
+static void invoke_and_release_block(void *param) {
+ dispatch_block_t block = (dispatch_block_t)param;
+ block();
+ Block_release(block);
+}
+
+#define DISPATCH_INTERCEPT_B(name) \
+ TSAN_INTERCEPTOR(void, name, dispatch_queue_t q, dispatch_block_t block) { \
+ SCOPED_TSAN_INTERCEPTOR(name, q, block); \
+ dispatch_block_t heap_block = Block_copy(block); \
+ tsan_block_context_t *new_context = \
+ AllocContext(thr, pc, q, heap_block, &invoke_and_release_block); \
+ Release(thr, pc, (uptr)new_context); \
+ REAL(name##_f)(q, new_context, dispatch_callback_wrap_acquire); \
+ }
+
+#define DISPATCH_INTERCEPT_F(name) \
+ TSAN_INTERCEPTOR(void, name, dispatch_queue_t q, void *context, \
+ dispatch_function_t work) { \
+ SCOPED_TSAN_INTERCEPTOR(name, q, context, work); \
+ tsan_block_context_t *new_context = \
+ AllocContext(thr, pc, q, context, work); \
+ Release(thr, pc, (uptr)new_context); \
+ REAL(name)(q, new_context, dispatch_callback_wrap_acquire); \
+ }
+
+// We wrap dispatch_async, dispatch_sync and friends where we allocate a new
+// context, which is used to synchronize (we release the context before
+// submitting, and the callback acquires it before executing the original
+// callback).
+DISPATCH_INTERCEPT_B(dispatch_async)
+DISPATCH_INTERCEPT_B(dispatch_barrier_async)
+DISPATCH_INTERCEPT_F(dispatch_async_f)
+DISPATCH_INTERCEPT_F(dispatch_barrier_async_f)
+DISPATCH_INTERCEPT_B(dispatch_sync)
+DISPATCH_INTERCEPT_B(dispatch_barrier_sync)
+DISPATCH_INTERCEPT_F(dispatch_sync_f)
+DISPATCH_INTERCEPT_F(dispatch_barrier_sync_f)
+
// 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