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" |
11 | invisible_barrier_t barrier2; |
12 | |
13 | class 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 | |
74 | int 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: &barrier, count: 2); |
79 | // Ensure that objects are destructed during block object release. |
80 | barrier_init(barrier: &barrier2, count: 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: &barrier); |
91 | long local = InitializerAccessedGlobal; |
92 | local++; |
93 | [tdo accessMember]; |
94 | [tcxxdo accessMember]; |
95 | barrier_wait(barrier: &barrier2); |
96 | }); |
97 | dispatch_async(q, ^{ |
98 | barrier_wait(barrier: &barrier); |
99 | [TestInitializeObject new]; |
100 | long local = InitializerAccessedGlobal; |
101 | local++; |
102 | [tdo accessMember]; |
103 | [tcxxdo accessMember]; |
104 | barrier_wait(barrier: &barrier2); |
105 | }); |
106 | } |
107 | barrier_wait(barrier: &barrier2); |
108 | NSLog(@"Done." ); |
109 | return 0; |
110 | } |
111 | |
112 | // CHECK: Done. |
113 | // CHECK-NOT: ThreadSanitizer: data race |
114 | |