summaryrefslogtreecommitdiff
path: root/lib/tsan/rtl/tsan_libdispatch_mac.cc
diff options
context:
space:
mode:
authorKuba Brecka <kuba.brecka@gmail.com>2016-06-27 16:49:23 +0000
committerKuba Brecka <kuba.brecka@gmail.com>2016-06-27 16:49:23 +0000
commitd648ecb29ccd3ec70c14a8a812ea04626835f3e3 (patch)
tree5b7c2042950b6a7ee2b333d2aa741213ce4358d5 /lib/tsan/rtl/tsan_libdispatch_mac.cc
parentcb4b1c102c265675a2074354d7634da5e8dd6ad4 (diff)
[tsan] Add HB edges for GCD barrier blocks
Adding support for GCD barrier blocks in concurrent queues. This uses two sync object in the same way as read-write locks do. This also simplifies the use of dispatch groups (the notifications act as barrier blocks). Differential Revision: http://reviews.llvm.org/D21604 git-svn-id: https://llvm.org/svn/llvm-project/compiler-rt/trunk@273893 91177308-0d34-0410-b5e6-96231b3b80d8
Diffstat (limited to 'lib/tsan/rtl/tsan_libdispatch_mac.cc')
-rw-r--r--lib/tsan/rtl/tsan_libdispatch_mac.cc83
1 files changed, 35 insertions, 48 deletions
diff --git a/lib/tsan/rtl/tsan_libdispatch_mac.cc b/lib/tsan/rtl/tsan_libdispatch_mac.cc
index 7f1b8809b..e015a2af4 100644
--- a/lib/tsan/rtl/tsan_libdispatch_mac.cc
+++ b/lib/tsan/rtl/tsan_libdispatch_mac.cc
@@ -33,10 +33,9 @@ typedef struct {
dispatch_queue_t queue;
void *orig_context;
dispatch_function_t orig_work;
- uptr sync_object;
- dispatch_object_t object_to_release;
bool free_context_in_callback;
- bool release_sync_object_in_callback;
+ bool submitted_synchronously;
+ bool is_barrier_block;
} tsan_block_context_t;
// The offsets of different fields of the dispatch_queue_t structure, exported
@@ -77,35 +76,32 @@ static tsan_block_context_t *AllocContext(ThreadState *thr, uptr pc,
new_context->queue = queue;
new_context->orig_context = orig_context;
new_context->orig_work = orig_work;
- new_context->sync_object = (uptr)new_context;
- new_context->object_to_release = nullptr;
new_context->free_context_in_callback = true;
- new_context->release_sync_object_in_callback = false;
+ new_context->submitted_synchronously = false;
+ new_context->is_barrier_block = false;
return new_context;
}
static void dispatch_callback_wrap(void *param) {
SCOPED_INTERCEPTOR_RAW(dispatch_callback_wrap);
tsan_block_context_t *context = (tsan_block_context_t *)param;
+ dispatch_queue_t q = context->queue;
- Acquire(thr, pc, context->sync_object);
+ uptr serial_sync = (uptr)q;
+ uptr concurrent_sync = ((uptr)q) + sizeof(uptr);
+ uptr submit_sync = (uptr)context;
+ bool serial_task = IsQueueSerial(q) || context->is_barrier_block;
- // Extra retain/release is required for dispatch groups. We use the group
- // itself to synchronize, but in a notification (dispatch_group_notify
- // callback), it may be disposed already. To solve this, we retain the group
- // and release it here.
- if (context->object_to_release) dispatch_release(context->object_to_release);
+ Acquire(thr, pc, submit_sync);
+ Acquire(thr, pc, serial_sync);
+ if (serial_task) Acquire(thr, pc, concurrent_sync);
- // In serial queues, work items can be executed on different threads, we need
- // to explicitly synchronize on the queue itself.
- if (IsQueueSerial(context->queue)) Acquire(thr, pc, (uptr)context->queue);
SCOPED_TSAN_INTERCEPTOR_USER_CALLBACK_START();
context->orig_work(context->orig_context);
SCOPED_TSAN_INTERCEPTOR_USER_CALLBACK_END();
- if (IsQueueSerial(context->queue)) Release(thr, pc, (uptr)context->queue);
- if (context->release_sync_object_in_callback)
- Release(thr, pc, context->sync_object);
+ Release(thr, pc, serial_task ? serial_sync : concurrent_sync);
+ if (context->submitted_synchronously) Release(thr, pc, submit_sync);
if (context->free_context_in_callback) user_free(thr, pc, context);
}
@@ -116,7 +112,7 @@ static void invoke_and_release_block(void *param) {
Block_release(block);
}
-#define DISPATCH_INTERCEPT_B(name) \
+#define DISPATCH_INTERCEPT_B(name, barrier) \
TSAN_INTERCEPTOR(void, name, dispatch_queue_t q, dispatch_block_t block) { \
SCOPED_TSAN_INTERCEPTOR(name, q, block); \
SCOPED_TSAN_INTERCEPTOR_USER_CALLBACK_START(); \
@@ -124,21 +120,21 @@ static void invoke_and_release_block(void *param) {
SCOPED_TSAN_INTERCEPTOR_USER_CALLBACK_END(); \
tsan_block_context_t *new_context = \
AllocContext(thr, pc, q, heap_block, &invoke_and_release_block); \
+ new_context->is_barrier_block = barrier; \
Release(thr, pc, (uptr)new_context); \
SCOPED_TSAN_INTERCEPTOR_USER_CALLBACK_START(); \
REAL(name##_f)(q, new_context, dispatch_callback_wrap); \
SCOPED_TSAN_INTERCEPTOR_USER_CALLBACK_END(); \
}
-#define DISPATCH_INTERCEPT_SYNC_B(name) \
+#define DISPATCH_INTERCEPT_SYNC_B(name, barrier) \
TSAN_INTERCEPTOR(void, name, dispatch_queue_t q, dispatch_block_t block) { \
SCOPED_TSAN_INTERCEPTOR(name, q, block); \
SCOPED_TSAN_INTERCEPTOR_USER_CALLBACK_START(); \
dispatch_block_t heap_block = Block_copy(block); \
SCOPED_TSAN_INTERCEPTOR_USER_CALLBACK_END(); \
tsan_block_context_t new_context = { \
- q, heap_block, &invoke_and_release_block, 0, 0, false, true}; \
- new_context.sync_object = (uptr)&new_context; \
+ q, heap_block, &invoke_and_release_block, false, true, barrier}; \
Release(thr, pc, (uptr)&new_context); \
SCOPED_TSAN_INTERCEPTOR_USER_CALLBACK_START(); \
REAL(name##_f)(q, &new_context, dispatch_callback_wrap); \
@@ -146,24 +142,25 @@ static void invoke_and_release_block(void *param) {
Acquire(thr, pc, (uptr)&new_context); \
}
-#define DISPATCH_INTERCEPT_F(name) \
+#define DISPATCH_INTERCEPT_F(name, barrier) \
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); \
+ new_context->is_barrier_block = barrier; \
Release(thr, pc, (uptr)new_context); \
SCOPED_TSAN_INTERCEPTOR_USER_CALLBACK_START(); \
REAL(name)(q, new_context, dispatch_callback_wrap); \
SCOPED_TSAN_INTERCEPTOR_USER_CALLBACK_END(); \
}
-#define DISPATCH_INTERCEPT_SYNC_F(name) \
+#define DISPATCH_INTERCEPT_SYNC_F(name, barrier) \
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 = {q, context, work, 0, 0, false, true}; \
- new_context.sync_object = (uptr)&new_context; \
+ tsan_block_context_t new_context = { \
+ q, context, work, false, true, barrier}; \
Release(thr, pc, (uptr)&new_context); \
SCOPED_TSAN_INTERCEPTOR_USER_CALLBACK_START(); \
REAL(name)(q, &new_context, dispatch_callback_wrap); \
@@ -175,14 +172,14 @@ static void invoke_and_release_block(void *param) {
// 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_SYNC_B(dispatch_sync)
-DISPATCH_INTERCEPT_SYNC_B(dispatch_barrier_sync)
-DISPATCH_INTERCEPT_SYNC_F(dispatch_sync_f)
-DISPATCH_INTERCEPT_SYNC_F(dispatch_barrier_sync_f)
+DISPATCH_INTERCEPT_B(dispatch_async, false)
+DISPATCH_INTERCEPT_B(dispatch_barrier_async, true)
+DISPATCH_INTERCEPT_F(dispatch_async_f, false)
+DISPATCH_INTERCEPT_F(dispatch_barrier_async_f, true)
+DISPATCH_INTERCEPT_SYNC_B(dispatch_sync, false)
+DISPATCH_INTERCEPT_SYNC_B(dispatch_barrier_sync, true)
+DISPATCH_INTERCEPT_SYNC_F(dispatch_sync_f, false)
+DISPATCH_INTERCEPT_SYNC_F(dispatch_barrier_sync_f, true)
TSAN_INTERCEPTOR(void, dispatch_after, dispatch_time_t when,
dispatch_queue_t queue, dispatch_block_t block) {
@@ -314,13 +311,8 @@ TSAN_INTERCEPTOR(void, dispatch_group_notify, dispatch_group_t group,
SCOPED_TSAN_INTERCEPTOR_USER_CALLBACK_END();
tsan_block_context_t *new_context =
AllocContext(thr, pc, q, heap_block, &invoke_and_release_block);
- new_context->sync_object = (uptr)group;
-
- // Will be released in dispatch_callback_wrap.
- new_context->object_to_release = group;
- dispatch_retain(group);
-
- Release(thr, pc, (uptr)group);
+ new_context->is_barrier_block = true;
+ Release(thr, pc, (uptr)new_context);
REAL(dispatch_group_notify_f)(group, q, new_context,
dispatch_callback_wrap);
}
@@ -329,13 +321,8 @@ TSAN_INTERCEPTOR(void, dispatch_group_notify_f, dispatch_group_t group,
dispatch_queue_t q, void *context, dispatch_function_t work) {
SCOPED_TSAN_INTERCEPTOR(dispatch_group_notify_f, group, q, context, work);
tsan_block_context_t *new_context = AllocContext(thr, pc, q, context, work);
- new_context->sync_object = (uptr)group;
-
- // Will be released in dispatch_callback_wrap.
- new_context->object_to_release = group;
- dispatch_retain(group);
-
- Release(thr, pc, (uptr)group);
+ new_context->is_barrier_block = true;
+ Release(thr, pc, (uptr)new_context);
REAL(dispatch_group_notify_f)(group, q, new_context,
dispatch_callback_wrap);
}