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