From b59118f6ec3db2a53b36e5ebc85f77dde2aa6dfb Mon Sep 17 00:00:00 2001 From: Kuba Mracek Date: Thu, 24 Nov 2016 21:24:54 +0000 Subject: [PATCH] [tsan] Add support for GCD dispatch_suspend and dispatch_resume GCD queues can be suspended and resumed with dispatch_suspend and dispatch_resume. We need to add synchronization between the call to dispatch_resume and any subsequent executions of blocks in the queue that was resumed. We already have an Acquire(q) before the block executes, so this patch just adds the Release(q) in an interceptor of dispatch_resume. Differential Revision: https://reviews.llvm.org/D27112 llvm-svn: 287902 --- .../lib/tsan/rtl/tsan_libdispatch_mac.cc | 9 ++++ compiler-rt/test/tsan/Darwin/gcd-suspend.mm | 45 +++++++++++++++++++ 2 files changed, 54 insertions(+) create mode 100644 compiler-rt/test/tsan/Darwin/gcd-suspend.mm diff --git a/compiler-rt/lib/tsan/rtl/tsan_libdispatch_mac.cc b/compiler-rt/lib/tsan/rtl/tsan_libdispatch_mac.cc index 2bd8161006ea..d8c689ebb5fc 100644 --- a/compiler-rt/lib/tsan/rtl/tsan_libdispatch_mac.cc +++ b/compiler-rt/lib/tsan/rtl/tsan_libdispatch_mac.cc @@ -706,6 +706,15 @@ TSAN_INTERCEPTOR(void, dispatch_io_close, dispatch_io_t channel, return REAL(dispatch_io_close)(channel, flags); } +// Resuming a suspended queue needs to synchronize with all subsequent +// executions of blocks in that queue. +TSAN_INTERCEPTOR(void, dispatch_resume, dispatch_object_t o) { + SCOPED_TSAN_INTERCEPTOR(dispatch_resume, o); + Release(thr, pc, (uptr)o); // Synchronizes with the Acquire() on serial_sync + // in dispatch_sync_pre_execute + return REAL(dispatch_resume)(o); +} + } // namespace __tsan #endif // SANITIZER_MAC diff --git a/compiler-rt/test/tsan/Darwin/gcd-suspend.mm b/compiler-rt/test/tsan/Darwin/gcd-suspend.mm new file mode 100644 index 000000000000..3e8818a2d56e --- /dev/null +++ b/compiler-rt/test/tsan/Darwin/gcd-suspend.mm @@ -0,0 +1,45 @@ +// RUN: %clang_tsan %s -o %t -framework Foundation +// RUN: %env_tsan_opts=ignore_interceptors_accesses=1 %run %t 2>&1 | FileCheck %s + +#import + +long my_global = 0; + +int main(int argc, const char *argv[]) { + fprintf(stderr, "Hello world.\n"); + + dispatch_queue_t q1 = dispatch_queue_create("queue1", NULL); + dispatch_queue_t q2 = dispatch_queue_create("queue2", NULL); + dispatch_group_t g = dispatch_group_create(); + + dispatch_sync(q1, ^{ + dispatch_suspend(q1); + dispatch_async(q2, ^{ + my_global++; + dispatch_resume(q1); + }); + }); + + dispatch_sync(q1, ^{ + my_global++; + }); + + dispatch_sync(q1, ^{ + dispatch_suspend(q1); + dispatch_group_enter(g); + dispatch_async(q1,^{ my_global++; }); + dispatch_async(q1,^{ my_global++; }); + dispatch_async(q1,^{ my_global++; dispatch_group_leave(g); }); + my_global++; + dispatch_resume(q1); + }); + + dispatch_group_wait(g, DISPATCH_TIME_FOREVER); + + fprintf(stderr, "Done.\n"); + return 0; +} + +// CHECK: Hello world. +// CHECK-NOT: WARNING: ThreadSanitizer +// CHECK: Done.