1// RUN: %clang_tsan %s -lc++ -fobjc-arc -lobjc -o %t -framework Foundation
2// RUN: %env_tsan_opts=ignore_interceptors_accesses=1 %run %t 2>&1 | FileCheck %s
3
4// Check that we do not report races between:
5// - Object retain and initialize
6// - Object release and dealloc
7// - Object release and .cxx_destruct
8
9#import <Foundation/Foundation.h>
10#include "../test.h"
11invisible_barrier_t barrier2;
12
13class NeedCleanup {
14  public:
15    int x;
16    NeedCleanup() {
17      x = 1;
18    }
19    ~NeedCleanup() {
20      x = 0;
21    }
22};
23
24@interface TestDeallocObject : NSObject {
25  @public
26    int v;
27  }
28  - (id)init;
29  - (void)accessMember;
30  - (void)dealloc;
31@end
32
33@implementation TestDeallocObject
34  - (id)init {
35    if ([super self]) {
36      v = 1;
37      return self;
38    }
39    return nil;
40  }
41  - (void)accessMember {
42    int local = v;
43    local++;
44  }
45  - (void)dealloc {
46    v = 0;
47  }
48@end
49
50@interface TestCXXDestructObject : NSObject {
51  @public
52    NeedCleanup cxxMemberWithCleanup;
53  }
54  - (void)accessMember;
55@end
56
57@implementation TestCXXDestructObject
58  - (void)accessMember {
59    int local = cxxMemberWithCleanup.x;
60    local++;
61  }
62@end
63
64@interface TestInitializeObject : NSObject
65@end
66
67@implementation TestInitializeObject
68  static long InitializerAccessedGlobal = 0;
69  + (void)initialize {
70      InitializerAccessedGlobal = 42;
71  }
72@end
73
74int main(int argc, const char *argv[]) {
75  // Ensure that there is no race when calling initialize on TestInitializeObject;
76  // otherwise, the locking from ObjC runtime becomes observable. Also ensures that
77  // blocks are dispatched to 2 different threads.
78  barrier_init(&barrier, 2);
79  // Ensure that objects are destructed during block object release.
80  barrier_init(&barrier2, 3);
81
82  TestDeallocObject *tdo = [[TestDeallocObject alloc] init];
83  TestCXXDestructObject *tcxxdo = [[TestCXXDestructObject alloc] init];
84  [tdo accessMember];
85  [tcxxdo accessMember];
86  {
87    dispatch_queue_t q = dispatch_queue_create(NULL, DISPATCH_QUEUE_CONCURRENT);
88    dispatch_async(q, ^{
89        [TestInitializeObject new];
90        barrier_wait(&barrier);
91        long local = InitializerAccessedGlobal;
92        local++;
93        [tdo accessMember];
94        [tcxxdo accessMember];
95        barrier_wait(&barrier2);
96    });
97    dispatch_async(q, ^{
98        barrier_wait(&barrier);
99        [TestInitializeObject new];
100        long local = InitializerAccessedGlobal;
101        local++;
102        [tdo accessMember];
103        [tcxxdo accessMember];
104        barrier_wait(&barrier2);
105    });
106  }
107  barrier_wait(&barrier2);
108  NSLog(@"Done.");
109  return 0;
110}
111
112// CHECK: Done.
113// CHECK-NOT: ThreadSanitizer: data race
114