Index: lib/tsan/rtl/tsan_libdispatch_mac.cc =================================================================== --- lib/tsan/rtl/tsan_libdispatch_mac.cc +++ lib/tsan/rtl/tsan_libdispatch_mac.cc @@ -25,30 +25,35 @@ #include #include +typedef long long_t; // NOLINT + namespace __tsan { typedef struct { dispatch_queue_t queue; void *orig_context; dispatch_function_t orig_work; + uptr object_to_acquire; } 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) { + dispatch_function_t orig_work, + uptr object_to_acquire) { 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; + new_context->object_to_acquire = object_to_acquire; 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); context->orig_work(context->orig_context); user_free(thr, pc, context); } @@ -59,24 +64,24 @@ 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_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, (uptr)new_context); \ + 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); \ +#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, (uptr)new_context); \ + 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 @@ -133,6 +138,66 @@ }); } +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, (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, (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 Index: test/tsan/Darwin/gcd-groups-norace.mm =================================================================== --- test/tsan/Darwin/gcd-groups-norace.mm +++ test/tsan/Darwin/gcd-groups-norace.mm @@ -0,0 +1,51 @@ +// RUN: %clang_tsan %s -o %t -framework Foundation +// RUN: %run %t 2>&1 + +#import + +#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); + + dispatch_group_enter(g); + dispatch_async(q, ^{ + global = 44; + dispatch_group_leave(g); + }); + dispatch_group_wait(g, DISPATCH_TIME_FOREVER); + + global = 45; + + dispatch_group_enter(g); + dispatch_async(q, ^{ + global = 46; + dispatch_group_leave(g); + }); + dispatch_group_notify(g, q, ^{ + global = 45; + + dispatch_sync(dispatch_get_main_queue(), ^{ + CFRunLoopStop(CFRunLoopGetCurrent()); + }); + }); + + CFRunLoopRun(); + NSLog(@"Done."); +} + +// CHECK: Hello world. +// CHECK: Done. +// CHECK-NOT: WARNING: ThreadSanitizer