Index: compiler-rt/lib/tsan/rtl/tsan_interceptors_mac.cc =================================================================== --- compiler-rt/lib/tsan/rtl/tsan_interceptors_mac.cc +++ compiler-rt/lib/tsan/rtl/tsan_interceptors_mac.cc @@ -20,7 +20,10 @@ #include "tsan_interface.h" #include "tsan_interface_ann.h" +#include + #include +#include #if defined(__has_include) && __has_include() #include @@ -318,17 +321,25 @@ return (uptr)obj; } -TSAN_INTERCEPTOR(int, objc_sync_enter, void *obj) { +TSAN_INTERCEPTOR(int, objc_sync_enter, id obj) { SCOPED_TSAN_INTERCEPTOR(objc_sync_enter, obj); + if (!obj) return REAL(objc_sync_enter)(obj); + uptr addr = SyncAddressForObjCObject(obj); + MutexPreLock(thr, pc, addr); int result = REAL(objc_sync_enter)(obj); - if (obj) Acquire(thr, pc, SyncAddressForObjCObject(obj)); + assert(result == OBJC_SYNC_SUCCESS); + MutexPostLock(thr, pc, addr); return result; } -TSAN_INTERCEPTOR(int, objc_sync_exit, void *obj) { +TSAN_INTERCEPTOR(int, objc_sync_exit, id obj) { SCOPED_TSAN_INTERCEPTOR(objc_sync_exit, obj); - if (obj) Release(thr, pc, SyncAddressForObjCObject(obj)); - return REAL(objc_sync_exit)(obj); + if (!obj) return REAL(objc_sync_exit)(obj); + uptr addr = SyncAddressForObjCObject(obj); + MutexUnlock(thr, pc, addr); + int result = REAL(objc_sync_exit)(obj); + if (result != OBJC_SYNC_SUCCESS) MutexInvalidAccess(thr, pc, addr); + return result; } // On macOS, libc++ is always linked dynamically, so intercepting works the Index: compiler-rt/test/tsan/Darwin/objc-synchronize-cycle.mm =================================================================== --- /dev/null +++ compiler-rt/test/tsan/Darwin/objc-synchronize-cycle.mm @@ -0,0 +1,31 @@ +// RUN: %clangxx_tsan %s -o %t -framework Foundation -fobjc-arc %darwin_min_target_with_full_runtime_arc_support +// RUN: not %run %t 2>&1 | FileCheck %s +// RUN: %env_tsan_opts=detect_deadlocks=1 not %run %t 2>&1 | FileCheck %s +// RUN: %env_tsan_opts=detect_deadlocks=0 %run %t 2>&1 | FileCheck %s --check-prefix=DISABLED + +#import + +int main() { + @autoreleasepool { + NSObject* obj1 = [NSObject new]; + NSObject* obj2 = [NSObject new]; + + // obj1 -> obj2 + @synchronized(obj1) { + @synchronized(obj2) { + } + } + + // obj1 -> obj1 + @synchronized(obj2) { + @synchronized(obj1) { +// CHECK: ThreadSanitizer: lock-order-inversion (potential deadlock) + } + } + } + + NSLog(@"PASS"); +// DISABLED-NOT: ThreadSanitizer +// DISABLED: PASS + return 0; +} Index: compiler-rt/test/tsan/Darwin/objc-synchronize-nested-recursive.mm =================================================================== --- /dev/null +++ compiler-rt/test/tsan/Darwin/objc-synchronize-nested-recursive.mm @@ -0,0 +1,35 @@ +// RUN: %clangxx_tsan %s -o %t -framework Foundation -fobjc-arc %darwin_min_target_with_full_runtime_arc_support +// RUN: %run %t 2>&1 | FileCheck %s + +#import + +int main() { + @autoreleasepool { + NSObject* obj1 = [NSObject new]; + NSObject* obj2 = [NSObject new]; + + @synchronized(obj1) { + @synchronized(obj1) { + NSLog(@"nested 1-1"); +// CHECK: nested 1-1 + } + } + + @synchronized(obj1) { + @synchronized(obj2) { + @synchronized(obj1) { + @synchronized(obj2) { + NSLog(@"nested 1-2-1-2"); +// CHECK: nested 1-2-1-2 + } + } + } + } + + } + + NSLog(@"PASS"); +// CHECK-NOT: ThreadSanitizer +// CHECK: PASS + return 0; +}