Index: compiler-rt/trunk/lib/tsan/rtl/tsan_libdispatch_mac.cc =================================================================== --- compiler-rt/trunk/lib/tsan/rtl/tsan_libdispatch_mac.cc +++ compiler-rt/trunk/lib/tsan/rtl/tsan_libdispatch_mac.cc @@ -21,11 +21,77 @@ #include "tsan_platform.h" #include "tsan_rtl.h" +#include #include #include 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 Index: compiler-rt/trunk/test/tsan/Darwin/gcd-async-norace.mm =================================================================== --- compiler-rt/trunk/test/tsan/Darwin/gcd-async-norace.mm +++ compiler-rt/trunk/test/tsan/Darwin/gcd-async-norace.mm @@ -0,0 +1,26 @@ +// RUN: %clang_tsan %s -o %t -framework Foundation +// RUN: %run %t 2>&1 + +#import + +long global; + +int main() { + NSLog(@"Hello world."); + + global = 42; + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + global = 43; + + dispatch_sync(dispatch_get_main_queue(), ^{ + CFRunLoopStop(CFRunLoopGetCurrent()); + }); + }); + + CFRunLoopRun(); + NSLog(@"Done."); +} + +// CHECK: Hello world. +// CHECK: Done. +// CHECK-NOT: WARNING: ThreadSanitizer Index: compiler-rt/trunk/test/tsan/Darwin/gcd-async-race.mm =================================================================== --- compiler-rt/trunk/test/tsan/Darwin/gcd-async-race.mm +++ compiler-rt/trunk/test/tsan/Darwin/gcd-async-race.mm @@ -0,0 +1,38 @@ +// RUN: %clang_tsan %s -o %t -framework Foundation +// RUN: %deflake %run %t 2>&1 + +#import + +#import "../test.h" + +long global; + +int main() { + NSLog(@"Hello world."); + NSLog(@"addr=%p\n", &global); + barrier_init(&barrier, 2); + + global = 42; + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + global = 43; + barrier_wait(&barrier); + }); + + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + barrier_wait(&barrier); + global = 44; + + dispatch_sync(dispatch_get_main_queue(), ^{ + CFRunLoopStop(CFRunLoopGetCurrent()); + }); + }); + + CFRunLoopRun(); + NSLog(@"Done."); +} + +// CHECK: Hello world. +// CHECK: addr=[[ADDR:0x[0-9,a-f]+]] +// CHECK: WARNING: ThreadSanitizer: data race +// CHECK: Location is global 'global' at [[ADDR]] (global_race.cc.exe+0x{{[0-9,a-f]+}}) +// CHECK: Done. Index: compiler-rt/trunk/test/tsan/Darwin/gcd-sync-norace.mm =================================================================== --- compiler-rt/trunk/test/tsan/Darwin/gcd-sync-norace.mm +++ compiler-rt/trunk/test/tsan/Darwin/gcd-sync-norace.mm @@ -0,0 +1,32 @@ +// RUN: %clang_tsan %s -o %t -framework Foundation +// RUN: %run %t 2>&1 + +#import + +long global; + +static const long nIter = 1000; + +int main() { + NSLog(@"Hello world."); + + global = 42; + for (int i = 0; i < nIter; i++) { + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + dispatch_sync(dispatch_get_main_queue(), ^{ + global = i; + + if (i == nIter - 1) { + CFRunLoopStop(CFRunLoopGetCurrent()); + } + }); + }); + } + + CFRunLoopRun(); + NSLog(@"Done."); +} + +// CHECK: Hello world. +// CHECK: Done. +// CHECK-NOT: WARNING: ThreadSanitizer Index: compiler-rt/trunk/test/tsan/Darwin/gcd-sync-race.mm =================================================================== --- compiler-rt/trunk/test/tsan/Darwin/gcd-sync-race.mm +++ compiler-rt/trunk/test/tsan/Darwin/gcd-sync-race.mm @@ -0,0 +1,44 @@ +// RUN: %clang_tsan %s -o %t -framework Foundation +// RUN: %deflake %run %t 2>&1 + +#import + +#import "../test.h" + +long global; + +int main() { + NSLog(@"Hello world."); + NSLog(@"addr=%p\n", &global); + barrier_init(&barrier, 2); + + dispatch_queue_t q1 = dispatch_queue_create("my.queue1", DISPATCH_QUEUE_CONCURRENT); + dispatch_queue_t q2 = dispatch_queue_create("my.queue2", DISPATCH_QUEUE_CONCURRENT); + + global = 42; + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + dispatch_sync(q1, ^{ + global = 43; + barrier_wait(&barrier); + }); + }); + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + dispatch_sync(q2, ^{ + barrier_wait(&barrier); + global = 44; + + dispatch_sync(dispatch_get_main_queue(), ^{ + CFRunLoopStop(CFRunLoopGetCurrent()); + }); + }); + }); + + CFRunLoopRun(); + NSLog(@"Done."); +} + +// CHECK: Hello world. +// CHECK: addr=[[ADDR:0x[0-9,a-f]+]] +// CHECK: WARNING: ThreadSanitizer: data race +// CHECK: Location is global 'global' at [[ADDR]] (global_race.cc.exe+0x{{[0-9,a-f]+}}) +// CHECK: Done.