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 @@ -389,6 +389,40 @@ WRAP(dispatch_source_set_registration_handler)(source, block); } +TSAN_INTERCEPTOR(void, dispatch_apply, size_t iterations, + dispatch_queue_t queue, void (^block)(size_t)) { + SCOPED_TSAN_INTERCEPTOR(dispatch_apply, iterations, queue, block); + + void *parent_to_child_sync = nullptr; + uptr parent_to_child_sync_uptr = (uptr)&parent_to_child_sync; + void *child_to_parent_sync = nullptr; + uptr child_to_parent_sync_uptr = (uptr)&child_to_parent_sync; + + Release(thr, pc, parent_to_child_sync_uptr); + void (^new_block)(size_t) = ^(size_t iteration) { + SCOPED_INTERCEPTOR_RAW(dispatch_apply); + Acquire(thr, pc, parent_to_child_sync_uptr); + SCOPED_TSAN_INTERCEPTOR_USER_CALLBACK_START(); + block(iteration); + SCOPED_TSAN_INTERCEPTOR_USER_CALLBACK_END(); + Release(thr, pc, child_to_parent_sync_uptr); + }; + SCOPED_TSAN_INTERCEPTOR_USER_CALLBACK_START(); + REAL(dispatch_apply)(iterations, queue, new_block); + SCOPED_TSAN_INTERCEPTOR_USER_CALLBACK_END(); + Acquire(thr, pc, child_to_parent_sync_uptr); +} + +TSAN_INTERCEPTOR(void, dispatch_apply_f, size_t iterations, + dispatch_queue_t queue, void *context, + void (*work)(void *, size_t)) { + SCOPED_TSAN_INTERCEPTOR(dispatch_apply_f, iterations, queue, context, work); + void (^new_block)(size_t) = ^(size_t iteration) { + work(context, iteration); + }; + WRAP(dispatch_apply)(iterations, queue, new_block); +} + } // namespace __tsan #endif // SANITIZER_MAC Index: compiler-rt/trunk/test/tsan/Darwin/gcd-apply-race.mm =================================================================== --- compiler-rt/trunk/test/tsan/Darwin/gcd-apply-race.mm +++ compiler-rt/trunk/test/tsan/Darwin/gcd-apply-race.mm @@ -0,0 +1,26 @@ +// RUN: %clang_tsan %s -o %t -framework Foundation +// RUN: %env_tsan_opts=ignore_interceptors_accesses=1 not %run %t 2>&1 | FileCheck %s + +#import + +#import "../test.h" + +long global; + +int main(int argc, const char *argv[]) { + barrier_init(&barrier, 2); + fprintf(stderr, "start\n"); + dispatch_queue_t q = dispatch_queue_create("my.queue", DISPATCH_QUEUE_CONCURRENT); + dispatch_apply(2, q, ^(size_t i) { + global = i; + barrier_wait(&barrier); + }); + + fprintf(stderr, "done\n"); + return 0; +} + +// CHECK: start +// CHECK: WARNING: ThreadSanitizer: data race +// CHECK: Location is global 'global' +// CHECK: done Index: compiler-rt/trunk/test/tsan/Darwin/gcd-apply.mm =================================================================== --- compiler-rt/trunk/test/tsan/Darwin/gcd-apply.mm +++ compiler-rt/trunk/test/tsan/Darwin/gcd-apply.mm @@ -0,0 +1,44 @@ +// RUN: %clang_tsan %s -o %t -framework Foundation +// RUN: %env_tsan_opts=ignore_interceptors_accesses=1 %run %t 2>&1 | FileCheck %s + +#import + +#import "../test.h" + +long global; +long array[2]; + +void callback(void *context, size_t i) { + long n = global; + array[i] = n + i; + barrier_wait(&barrier); +} + +int main(int argc, const char *argv[]) { + barrier_init(&barrier, 2); + fprintf(stderr, "start\n"); + dispatch_queue_t q = dispatch_queue_create("my.queue", DISPATCH_QUEUE_CONCURRENT); + + global = 42; + + dispatch_apply(100, q, ^(size_t i) { + long n = global; + array[i] = n + i; + barrier_wait(&barrier); + }); + + for (int i = 0; i < 100; i++) { + fprintf(stderr, "array[%d] = %ld\n", i, array[i]); + } + + global = 43; + + dispatch_apply_f(100, q, NULL, &callback); + + fprintf(stderr, "done\n"); + return 0; +} + +// CHECK: start +// CHECK: done +// CHECK-NOT: WARNING: ThreadSanitizer