]> git.saurik.com Git - apple/objc4.git/blob - test/rr-autorelease2.m
eb6a37b5c4ec0f6fab4c8e262ecfa745eb9cbed9
[apple/objc4.git] / test / rr-autorelease2.m
1 // Define FOUNDATION=1 for NSObject and NSAutoreleasePool
2 // Define FOUNDATION=0 for _objc_root* and _objc_autoreleasePool*
3
4 #include "test.h"
5
6 #if FOUNDATION
7 # define RR_PUSH() [[NSAutoreleasePool alloc] init]
8 # define RR_POP(p) [(id)p release]
9 # define RR_RETAIN(o) [o retain]
10 # define RR_RELEASE(o) [o release]
11 # define RR_AUTORELEASE(o) [o autorelease]
12 #else
13 # define RR_PUSH() _objc_autoreleasePoolPush()
14 # define RR_POP(p) _objc_autoreleasePoolPop(p)
15 # define RR_RETAIN(o) _objc_rootRetain((id)o)
16 # define RR_RELEASE(o) _objc_rootRelease((id)o)
17 # define RR_AUTORELEASE(o) _objc_rootAutorelease((id)o)
18 #endif
19
20 #include <objc/objc-internal.h>
21 #include <Foundation/Foundation.h>
22
23 static int state;
24
25 #define NESTED_COUNT 8
26
27 @interface Deallocator : NSObject @end
28 @implementation Deallocator
29 -(void) dealloc
30 {
31 // testprintf("-[Deallocator %p dealloc]\n", self);
32 state++;
33 [super dealloc];
34 }
35 @end
36
37 @interface AutoreleaseDuringDealloc : NSObject @end
38 @implementation AutoreleaseDuringDealloc
39 -(void) dealloc
40 {
41 state++;
42 RR_AUTORELEASE([[Deallocator alloc] init]);
43 [super dealloc];
44 }
45 @end
46
47 @interface AutoreleasePoolDuringDealloc : NSObject @end
48 @implementation AutoreleasePoolDuringDealloc
49 -(void) dealloc
50 {
51 // caller's pool
52 for (int i = 0; i < NESTED_COUNT; i++) {
53 RR_AUTORELEASE([[Deallocator alloc] init]);
54 }
55
56 // local pool, popped
57 void *pool = RR_PUSH();
58 for (int i = 0; i < NESTED_COUNT; i++) {
59 RR_AUTORELEASE([[Deallocator alloc] init]);
60 }
61 RR_POP(pool);
62
63 // caller's pool again
64 for (int i = 0; i < NESTED_COUNT; i++) {
65 RR_AUTORELEASE([[Deallocator alloc] init]);
66 }
67
68 #if FOUNDATION
69 {
70 static bool warned;
71 if (!warned) testwarn("rdar://7138159 NSAutoreleasePool leaks");
72 warned = true;
73 }
74 state += NESTED_COUNT;
75 #else
76 // local pool, not popped
77 RR_PUSH();
78 for (int i = 0; i < NESTED_COUNT; i++) {
79 RR_AUTORELEASE([[Deallocator alloc] init]);
80 }
81 #endif
82
83 [super dealloc];
84 }
85 @end
86
87 void *autorelease_lots_fn(void *singlePool)
88 {
89 // Enough to blow out the stack if AutoreleasePoolPage is recursive.
90 const int COUNT = 1024*1024;
91 state = 0;
92
93 int p = 0;
94 void **pools = (void**)malloc((COUNT+1) * sizeof(void*));
95 pools[p++] = RR_PUSH();
96
97 id obj = RR_AUTORELEASE([[Deallocator alloc] init]);
98
99 for (int i = 0; i < COUNT; i++) {
100 if (rand() % 1000 == 0 && !singlePool) {
101 pools[p++] = RR_PUSH();
102 } else {
103 RR_AUTORELEASE(RR_RETAIN(obj));
104 }
105 }
106
107 testassert(state == 0);
108 while (--p) {
109 RR_POP(pools[p]);
110 }
111 testassert(state == 0);
112 RR_POP(pools[0]);
113 testassert(state == 1);
114 free(pools);
115
116 return NULL;
117 }
118
119 void *nsthread_fn(void *arg __unused)
120 {
121 [NSThread currentThread];
122 void *pool = RR_PUSH();
123 RR_AUTORELEASE([[Deallocator alloc] init]);
124 RR_POP(pool);
125 return NULL;
126 }
127
128 void cycle(void)
129 {
130 // Normal autorelease.
131 testprintf("-- Normal autorelease.\n");
132 {
133 void *pool = RR_PUSH();
134 state = 0;
135 RR_AUTORELEASE([[Deallocator alloc] init]);
136 testassert(state == 0);
137 RR_POP(pool);
138 testassert(state == 1);
139 }
140
141 // Autorelease during dealloc during autoreleasepool-pop.
142 // That autorelease is handled by the popping pool, not the one above it.
143 testprintf("-- Autorelease during dealloc during autoreleasepool-pop.\n");
144 {
145 void *pool = RR_PUSH();
146 state = 0;
147 RR_AUTORELEASE([[AutoreleaseDuringDealloc alloc] init]);
148 testassert(state == 0);
149 RR_POP(pool);
150 testassert(state == 2);
151 }
152
153 // Autorelease pool during dealloc during autoreleasepool-pop.
154 testprintf("-- Autorelease pool during dealloc during autoreleasepool-pop.\n");
155 {
156 void *pool = RR_PUSH();
157 state = 0;
158 RR_AUTORELEASE([[AutoreleasePoolDuringDealloc alloc] init]);
159 testassert(state == 0);
160 RR_POP(pool);
161 testassert(state == 4 * NESTED_COUNT);
162 }
163
164 // Top-level thread pool popped normally.
165 testprintf("-- Thread-level pool popped normally.\n");
166 {
167 state = 0;
168 testonthread(^{
169 void *pool = RR_PUSH();
170 RR_AUTORELEASE([[Deallocator alloc] init]);
171 RR_POP(pool);
172 });
173 testassert(state == 1);
174 }
175
176 // Top-level thread pool not popped.
177 // The runtime should clean it up.
178 #if FOUNDATION
179 {
180 static bool warned;
181 if (!warned) testwarn("rdar://7138159 NSAutoreleasePool leaks");
182 warned = true;
183 }
184 #else
185 testprintf("-- Thread-level pool not popped.\n");
186 {
187 state = 0;
188 testonthread(^{
189 RR_PUSH();
190 RR_AUTORELEASE([[Deallocator alloc] init]);
191 // pool not popped
192 });
193 testassert(state == 1);
194 }
195 #endif
196
197 // Intermediate pool not popped.
198 // Popping the containing pool should clean up the skipped pool first.
199 #if FOUNDATION
200 {
201 static bool warned;
202 if (!warned) testwarn("rdar://7138159 NSAutoreleasePool leaks");
203 warned = true;
204 }
205 #else
206 testprintf("-- Intermediate pool not popped.\n");
207 {
208 void *pool = RR_PUSH();
209 void *pool2 = RR_PUSH();
210 RR_AUTORELEASE([[Deallocator alloc] init]);
211 state = 0;
212 (void)pool2; // pool2 not popped
213 RR_POP(pool);
214 testassert(state == 1);
215 }
216 #endif
217
218
219 #if !FOUNDATION
220 // NSThread calls NSPopAutoreleasePool(0)
221 // rdar://9167170 but that currently breaks CF
222 {
223 static bool warned;
224 if (!warned) testwarn("rdar://9167170 ignore NSPopAutoreleasePool(0)");
225 warned = true;
226 }
227 /*
228 testprintf("-- pop(0).\n");
229 {
230 RR_PUSH();
231 state = 0;
232 RR_AUTORELEASE([[AutoreleaseDuringDealloc alloc] init]);
233 testassert(state == 0);
234 RR_POP(0);
235 testassert(state == 2);
236 }
237 */
238 #endif
239 }
240
241 int main()
242 {
243 // inflate the refcount side table so it doesn't show up in leak checks
244 {
245 int count = 10000;
246 id *objs = (id *)malloc(count*sizeof(id));
247 for (int i = 0; i < count; i++) {
248 objs[i] = RR_RETAIN([NSObject new]);
249 }
250 for (int i = 0; i < count; i++) {
251 RR_RELEASE(objs[i]);
252 RR_RELEASE(objs[i]);
253 }
254 free(objs);
255 }
256
257 #if FOUNDATION
258 // inflate NSAutoreleasePool's instance cache
259 {
260 int count = 32;
261 id *objs = (id *)malloc(count * sizeof(id));
262 for (int i = 0; i < count; i++) {
263 objs[i] = [[NSAutoreleasePool alloc] init];
264 }
265 for (int i = 0; i < count; i++) {
266 [objs[count-i-1] release];
267 }
268
269 free(objs);
270 }
271 #endif
272
273
274 pthread_attr_t smallstack;
275 pthread_attr_init(&smallstack);
276 pthread_attr_setstacksize(&smallstack, 4096*4);
277
278 for (int i = 0; i < 100; i++) {
279 cycle();
280 }
281
282 leak_mark();
283
284 for (int i = 0; i < 1000; i++) {
285 cycle();
286 }
287
288 leak_check(0);
289
290 // Large autorelease stack.
291 // Do this only once because it's slow.
292 testprintf("-- Large autorelease stack.\n");
293 {
294 // limit stack size: autorelease pop should not be recursive
295 pthread_t th;
296 pthread_create(&th, &smallstack, &autorelease_lots_fn, NULL);
297 pthread_join(th, NULL);
298 }
299
300 // Single large autorelease pool.
301 // Do this only once because it's slow.
302 testprintf("-- Large autorelease pool.\n");
303 {
304 // limit stack size: autorelease pop should not be recursive
305 pthread_t th;
306 pthread_create(&th, &smallstack, &autorelease_lots_fn, (void*)1);
307 pthread_join(th, NULL);
308 }
309
310 leak_check(0);
311
312
313 // NSThread.
314 // Can't leak check this because it's too noisy.
315 testprintf("-- NSThread.\n");
316 {
317 pthread_t th;
318 pthread_create(&th, &smallstack, &nsthread_fn, 0);
319 pthread_join(th, NULL);
320 }
321
322 // NO LEAK CHECK HERE
323
324 succeed(NAME);
325 }