// RUN: %clang_tsan %s -lc++ -fobjc-arc -lobjc -o %t -framework Foundation // RUN: %env_tsan_opts=ignore_interceptors_accesses=1 %run %t 2>&1 | FileCheck %s // Check that we do not report races between: // - Object retain and initialize // - Object release and dealloc // - Object release and .cxx_destruct #import #include "../test.h" invisible_barrier_t barrier2; class NeedCleanup { public: int x; NeedCleanup() { x = 1; } ~NeedCleanup() { x = 0; } }; @interface TestDeallocObject : NSObject { @public int v; } - (id)init; - (void)accessMember; - (void)dealloc; @end @implementation TestDeallocObject - (id)init { if ([super self]) { v = 1; return self; } return nil; } - (void)accessMember { int local = v; local++; } - (void)dealloc { v = 0; } @end @interface TestCXXDestructObject : NSObject { @public NeedCleanup cxxMemberWithCleanup; } - (void)accessMember; @end @implementation TestCXXDestructObject - (void)accessMember { int local = cxxMemberWithCleanup.x; local++; } @end @interface TestInitializeObject : NSObject @end @implementation TestInitializeObject static long InitializerAccessedGlobal = 0; + (void)initialize { InitializerAccessedGlobal = 42; } @end int main(int argc, const char *argv[]) { // Ensure that there is no race when calling initialize on TestInitializeObject; // otherwise, the locking from ObjC runtime becomes observable. Also ensures that // blocks are dispatched to 2 different threads. barrier_init(&barrier, 2); // Ensure that objects are destructed during block object release. barrier_init(&barrier2, 3); TestDeallocObject *tdo = [[TestDeallocObject alloc] init]; TestCXXDestructObject *tcxxdo = [[TestCXXDestructObject alloc] init]; [tdo accessMember]; [tcxxdo accessMember]; { dispatch_queue_t q = dispatch_queue_create(NULL, DISPATCH_QUEUE_CONCURRENT); dispatch_async(q, ^{ [TestInitializeObject new]; barrier_wait(&barrier); long local = InitializerAccessedGlobal; local++; [tdo accessMember]; [tcxxdo accessMember]; barrier_wait(&barrier2); }); dispatch_async(q, ^{ barrier_wait(&barrier); [TestInitializeObject new]; long local = InitializerAccessedGlobal; local++; [tdo accessMember]; [tcxxdo accessMember]; barrier_wait(&barrier2); }); } barrier_wait(&barrier2); NSLog(@"Done."); return 0; } // CHECK: Done. // CHECK-NOT: ThreadSanitizer: data race