]> git.saurik.com Git - apple/objc4.git/blob - test/initializeVersusWeak.m
objc4-779.1.tar.gz
[apple/objc4.git] / test / initializeVersusWeak.m
1 // TEST_CONFIG MEM=arc
2 // TEST_CFLAGS -framework Foundation
3
4 // Problem: If weak reference operations provoke +initialize, the runtime
5 // can deadlock (recursive weak lock, or lock inversion between weak lock
6 // and +initialize lock).
7 // Solution: object_setClass() and objc_storeWeak() perform +initialize
8 // if needed so that no weakly-referenced object can ever have an
9 // un-+initialized isa.
10
11 #include <Foundation/Foundation.h>
12 #include <objc/objc-internal.h>
13 #include "test.h"
14
15 #pragma clang diagnostic ignored "-Warc-unsafe-retained-assign"
16
17 // This is StripedMap's pointer hash
18 #if TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR
19 enum { StripeCount = 8 };
20 #else
21 enum { StripeCount = 64 };
22 #endif
23 uintptr_t stripehash(id obj) {
24 uintptr_t addr = (uintptr_t)obj;
25 return ((addr >> 4) ^ (addr >> 9)) % StripeCount;
26 }
27
28 bool sameAlignment(id o1, id o2)
29 {
30 return stripehash(o1) == stripehash(o2);
31 }
32
33 // Return a new non-tagged object that uses the same striped weak locks as `obj`
34 NSObject *newAlignedObject(id obj)
35 {
36 // Use immutable arrays because their contents are stored inline,
37 // which prevents Guard Malloc from using the same alignment for all of them
38 NSArray *result = [NSArray new];
39 while (!sameAlignment(obj, result)) {
40 result = [result arrayByAddingObject:result];
41 }
42 return result;
43 }
44
45
46 __weak NSObject *weak1;
47 __weak NSObject *weak2;
48 NSObject *strong2;
49
50 @interface A : NSObject @end
51 @implementation A
52 +(void)initialize {
53 weak2 = strong2; // weak store #2
54 strong2 = nil;
55 }
56 @end
57
58 void testA()
59 {
60 // Weak store #1 provokes +initialize which performs weak store #2.
61 // Solution: weak store #1 runs +initialize if needed
62 // without holding locks.
63 @autoreleasepool {
64 A *obj = [A new];
65 strong2 = newAlignedObject(obj);
66 [obj addObserver:obj forKeyPath:@"foo" options:0 context:0];
67 weak1 = obj; // weak store #1
68 [obj removeObserver:obj forKeyPath:@"foo"];
69 obj = nil;
70 }
71 }
72
73
74 __weak NSObject *weak3;
75 __weak NSObject *weak4;
76 NSObject *strong4;
77
78 @interface B : NSObject @end
79 @implementation B
80 +(void)initialize {
81 weak4 = strong4; // weak store #4
82 strong4 = nil;
83 }
84 @end
85
86
87 void testB()
88 {
89 // Weak load #3 provokes +initialize which performs weak store #4.
90 // Solution: object_setClass() runs +initialize if needed
91 // without holding locks.
92 @autoreleasepool {
93 B *obj = [B new];
94 strong4 = newAlignedObject(obj);
95 weak3 = obj;
96 [obj addObserver:obj forKeyPath:@"foo" options:0 context:0];
97 [weak3 self]; // weak load #3
98 [obj removeObserver:obj forKeyPath:@"foo"];
99 obj = nil;
100 }
101 }
102
103
104 __weak id weak5;
105
106 @interface C : NSObject @end
107 @implementation C
108 +(void)initialize {
109 weak5 = [self new];
110 }
111 @end
112
113 void testC()
114 {
115 // +initialize performs a weak store of itself.
116 // Make sure the retry in objc_storeWeak() doesn't spin.
117 @autoreleasepool {
118 [C self];
119 }
120 }
121
122
123 __weak id weak6;
124 NSObject *strong6;
125 semaphore_t Dgo;
126 semaphore_t Ddone;
127
128 void *Dthread(void *arg __unused)
129 {
130 @autoreleasepool {
131 semaphore_wait(Dgo);
132 for (int i = 0; i < 1000; i++) {
133 id x = weak6;
134 testassert(x == strong6);
135 }
136 return nil;
137 }
138 }
139
140 @interface D : NSObject @end
141 @implementation D
142 +(void)initialize {
143 strong6 = [self new];
144 weak6 = strong6;
145 semaphore_signal(Dgo);
146 for (int i = 0; i < 1000; i++) {
147 id x = weak6;
148 testassert(x == strong6);
149 }
150 }
151 @end
152
153 void testD()
154 {
155 // +initialize performs a weak store of itself, then another thread
156 // tries to load that weak variable before +initialize completes.
157 // Deadlock occurs if the +initialize thread tries to acquire the
158 // sidetable lock for another operation and the second thread holds
159 // the sidetable lock while waiting for +initialize.
160
161 @autoreleasepool {
162 semaphore_create(mach_task_self(), &Dgo, 0, 0);
163 semaphore_create(mach_task_self(), &Ddone, 0, 0);
164 pthread_t th;
165 pthread_create(&th, nil, Dthread, nil);
166 [D self];
167 pthread_join(th, nil);
168 }
169 }
170
171 int main()
172 {
173 if (is_guardmalloc() && getenv("MALLOC_PROTECT_BEFORE")) {
174 testwarn("fixme malloc guard before breaks this with debug libobjc");
175 }
176 else {
177 alarm(10); // replace hangs with crashes
178
179 testA();
180 testB();
181 testC();
182 testD();
183 }
184
185 succeed(__FILE__);
186 }
187