[tsan] Add dispatch_group API interceptors and synchronization

This patch adds release and acquire semantics for dispatch groups, plus a test case.

Differential Revision: http://reviews.llvm.org/D15048

llvm-svn: 255020
This commit is contained in:
Kuba Brecka 2015-12-08 14:54:43 +00:00
parent 1956244e5a
commit 25dba9b781
2 changed files with 117 additions and 1 deletions

View File

@ -33,6 +33,7 @@ typedef struct {
dispatch_queue_t queue;
void *orig_context;
dispatch_function_t orig_work;
uptr object_to_acquire;
} tsan_block_context_t;
// The offsets of different fields of the dispatch_queue_t structure, exported
@ -73,13 +74,14 @@ 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->object_to_acquire = (uptr)new_context;
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);
Acquire(thr, pc, context->object_to_acquire);
// 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);
@ -183,6 +185,67 @@ TSAN_INTERCEPTOR(long_t, dispatch_semaphore_wait, dispatch_semaphore_t dsema,
return result;
}
TSAN_INTERCEPTOR(long_t, dispatch_group_wait, dispatch_group_t group,
dispatch_time_t timeout) {
SCOPED_TSAN_INTERCEPTOR(dispatch_group_wait, group, timeout);
long_t result = REAL(dispatch_group_wait)(group, timeout);
if (result == 0) Acquire(thr, pc, (uptr)group);
return result;
}
TSAN_INTERCEPTOR(void, dispatch_group_leave, dispatch_group_t group) {
SCOPED_TSAN_INTERCEPTOR(dispatch_group_leave, group);
Release(thr, pc, (uptr)group);
REAL(dispatch_group_leave)(group);
}
TSAN_INTERCEPTOR(void, dispatch_group_async, dispatch_group_t group,
dispatch_queue_t queue, dispatch_block_t block) {
SCOPED_TSAN_INTERCEPTOR(dispatch_group_async, group, queue, block);
dispatch_retain(group);
dispatch_group_enter(group);
WRAP(dispatch_async)(queue, ^(void) {
block();
WRAP(dispatch_group_leave)(group);
dispatch_release(group);
});
}
TSAN_INTERCEPTOR(void, dispatch_group_async_f, dispatch_group_t group,
dispatch_queue_t queue, void *context,
dispatch_function_t work) {
SCOPED_TSAN_INTERCEPTOR(dispatch_group_async_f, group, queue, context, work);
dispatch_retain(group);
dispatch_group_enter(group);
WRAP(dispatch_async)(queue, ^(void) {
work(context);
WRAP(dispatch_group_leave)(group);
dispatch_release(group);
});
}
TSAN_INTERCEPTOR(void, dispatch_group_notify, dispatch_group_t group,
dispatch_queue_t q, dispatch_block_t block) {
SCOPED_TSAN_INTERCEPTOR(dispatch_group_notify, group, 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);
new_context->object_to_acquire = (uptr)group;
Release(thr, pc, (uptr)group);
REAL(dispatch_group_notify_f)(group, q, new_context,
dispatch_callback_wrap_acquire);
}
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->object_to_acquire = (uptr)group;
Release(thr, pc, (uptr)group);
REAL(dispatch_group_notify_f)(group, q, new_context,
dispatch_callback_wrap_acquire);
}
} // namespace __tsan
#endif // SANITIZER_MAC

View File

@ -0,0 +1,53 @@
// RUN: %clang_tsan %s -o %t -framework Foundation
// RUN: %run %t 2>&1
#import <Foundation/Foundation.h>
#import "../test.h"
long global;
int main() {
NSLog(@"Hello world.");
NSLog(@"addr=%p\n", &global);
dispatch_queue_t q = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
global = 42;
dispatch_group_t g = dispatch_group_create();
dispatch_group_async(g, q, ^{
global = 43;
});
dispatch_group_wait(g, DISPATCH_TIME_FOREVER);
global = 44;
dispatch_group_enter(g);
dispatch_async(q, ^{
global = 45;
dispatch_group_leave(g);
});
dispatch_group_wait(g, DISPATCH_TIME_FOREVER);
global = 46;
dispatch_group_enter(g);
dispatch_async(q, ^{
global = 47;
dispatch_group_leave(g);
});
dispatch_group_notify(g, q, ^{
global = 48;
dispatch_sync(dispatch_get_main_queue(), ^{
CFRunLoopStop(CFRunLoopGetCurrent());
});
});
CFRunLoopRun();
NSLog(@"Done.");
}
// CHECK: Hello world.
// CHECK: Done.
// CHECK-NOT: WARNING: ThreadSanitizer