]> git.saurik.com Git - apple/objc4.git/blame - test/rr-autorelease2.m
objc4-646.tar.gz
[apple/objc4.git] / test / rr-autorelease2.m
CommitLineData
8972963c
A
1// Define FOUNDATION=1 for NSObject and NSAutoreleasePool
2// Define FOUNDATION=0 for _objc_root* and _objc_autoreleasePool*
3
cd5f04f5
A
4#include "test.h"
5
8972963c 6#if FOUNDATION
cd5f04f5
A
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]
8070259c 12# define RR_RETAINCOUNT(o) [o retainCount]
8972963c 13#else
cd5f04f5
A
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)
8070259c 19# define RR_RETAINCOUNT(o) _objc_rootRetainCount((id)o)
8972963c
A
20#endif
21
8972963c
A
22#include <objc/objc-internal.h>
23#include <Foundation/Foundation.h>
24
25static int state;
7257e56c 26static pthread_attr_t smallstack;
8972963c
A
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++;
cd5f04f5 45 RR_AUTORELEASE([[Deallocator alloc] init]);
8972963c
A
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++) {
cd5f04f5 56 RR_AUTORELEASE([[Deallocator alloc] init]);
8972963c
A
57 }
58
59 // local pool, popped
cd5f04f5 60 void *pool = RR_PUSH();
8972963c 61 for (int i = 0; i < NESTED_COUNT; i++) {
cd5f04f5 62 RR_AUTORELEASE([[Deallocator alloc] init]);
8972963c 63 }
cd5f04f5 64 RR_POP(pool);
8972963c
A
65
66 // caller's pool again
67 for (int i = 0; i < NESTED_COUNT; i++) {
cd5f04f5 68 RR_AUTORELEASE([[Deallocator alloc] init]);
8972963c
A
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
cd5f04f5 80 RR_PUSH();
8972963c 81 for (int i = 0; i < NESTED_COUNT; i++) {
cd5f04f5 82 RR_AUTORELEASE([[Deallocator alloc] init]);
8972963c
A
83 }
84#endif
85
86 [super dealloc];
87}
88@end
89
8972963c
A
90void *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*));
cd5f04f5 98 pools[p++] = RR_PUSH();
8972963c 99
cd5f04f5 100 id obj = RR_AUTORELEASE([[Deallocator alloc] init]);
8972963c 101
8070259c
A
102 // last pool has only 1 autorelease in it
103 pools[p++] = RR_PUSH();
104
8972963c
A
105 for (int i = 0; i < COUNT; i++) {
106 if (rand() % 1000 == 0 && !singlePool) {
cd5f04f5 107 pools[p++] = RR_PUSH();
8972963c 108 } else {
cd5f04f5 109 RR_AUTORELEASE(RR_RETAIN(obj));
8972963c
A
110 }
111 }
112
113 testassert(state == 0);
114 while (--p) {
cd5f04f5 115 RR_POP(pools[p]);
8972963c
A
116 }
117 testassert(state == 0);
8070259c 118 testassert(RR_RETAINCOUNT(obj) == 1);
cd5f04f5 119 RR_POP(pools[0]);
8972963c
A
120 testassert(state == 1);
121 free(pools);
122
123 return NULL;
124}
125
cd5f04f5 126void *nsthread_fn(void *arg __unused)
8972963c
A
127{
128 [NSThread currentThread];
cd5f04f5
A
129 void *pool = RR_PUSH();
130 RR_AUTORELEASE([[Deallocator alloc] init]);
131 RR_POP(pool);
132 return NULL;
8972963c
A
133}
134
135void cycle(void)
136{
137 // Normal autorelease.
138 testprintf("-- Normal autorelease.\n");
139 {
cd5f04f5 140 void *pool = RR_PUSH();
8972963c 141 state = 0;
cd5f04f5 142 RR_AUTORELEASE([[Deallocator alloc] init]);
8972963c 143 testassert(state == 0);
cd5f04f5 144 RR_POP(pool);
8972963c
A
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 {
cd5f04f5 152 void *pool = RR_PUSH();
8972963c 153 state = 0;
cd5f04f5 154 RR_AUTORELEASE([[AutoreleaseDuringDealloc alloc] init]);
8972963c 155 testassert(state == 0);
cd5f04f5 156 RR_POP(pool);
8972963c
A
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 {
cd5f04f5 163 void *pool = RR_PUSH();
8972963c 164 state = 0;
cd5f04f5 165 RR_AUTORELEASE([[AutoreleasePoolDuringDealloc alloc] init]);
8972963c 166 testassert(state == 0);
cd5f04f5 167 RR_POP(pool);
8972963c
A
168 testassert(state == 4 * NESTED_COUNT);
169 }
170
171 // Top-level thread pool popped normally.
172 testprintf("-- Thread-level pool popped normally.\n");
173 {
174 state = 0;
cd5f04f5
A
175 testonthread(^{
176 void *pool = RR_PUSH();
177 RR_AUTORELEASE([[Deallocator alloc] init]);
178 RR_POP(pool);
179 });
8972963c
A
180 testassert(state == 1);
181 }
182
183 // Top-level thread pool not popped.
184 // The runtime should clean it up.
185#if FOUNDATION
186 {
187 static bool warned;
188 if (!warned) testwarn("rdar://7138159 NSAutoreleasePool leaks");
189 warned = true;
190 }
191#else
192 testprintf("-- Thread-level pool not popped.\n");
193 {
194 state = 0;
cd5f04f5
A
195 testonthread(^{
196 RR_PUSH();
197 RR_AUTORELEASE([[Deallocator alloc] init]);
198 // pool not popped
199 });
8972963c
A
200 testassert(state == 1);
201 }
202#endif
203
204 // Intermediate pool not popped.
205 // Popping the containing pool should clean up the skipped pool first.
206#if FOUNDATION
207 {
208 static bool warned;
209 if (!warned) testwarn("rdar://7138159 NSAutoreleasePool leaks");
210 warned = true;
211 }
212#else
213 testprintf("-- Intermediate pool not popped.\n");
214 {
cd5f04f5
A
215 void *pool = RR_PUSH();
216 void *pool2 = RR_PUSH();
217 RR_AUTORELEASE([[Deallocator alloc] init]);
8972963c
A
218 state = 0;
219 (void)pool2; // pool2 not popped
cd5f04f5 220 RR_POP(pool);
8972963c
A
221 testassert(state == 1);
222 }
223#endif
224
225
226#if !FOUNDATION
227 // NSThread calls NSPopAutoreleasePool(0)
228 // rdar://9167170 but that currently breaks CF
229 {
230 static bool warned;
231 if (!warned) testwarn("rdar://9167170 ignore NSPopAutoreleasePool(0)");
232 warned = true;
233 }
234 /*
235 testprintf("-- pop(0).\n");
236 {
cd5f04f5 237 RR_PUSH();
8972963c 238 state = 0;
cd5f04f5 239 RR_AUTORELEASE([[AutoreleaseDuringDealloc alloc] init]);
8972963c 240 testassert(state == 0);
cd5f04f5 241 RR_POP(0);
8972963c
A
242 testassert(state == 2);
243 }
244 */
245#endif
246}
247
7257e56c
A
248
249static void
250slow_cycle(void)
251{
252 // Large autorelease stack.
253 // Do this only once because it's slow.
254 testprintf("-- Large autorelease stack.\n");
255 {
256 // limit stack size: autorelease pop should not be recursive
257 pthread_t th;
258 pthread_create(&th, &smallstack, &autorelease_lots_fn, NULL);
259 pthread_join(th, NULL);
260 }
261
262 // Single large autorelease pool.
263 // Do this only once because it's slow.
264 testprintf("-- Large autorelease pool.\n");
265 {
266 // limit stack size: autorelease pop should not be recursive
267 pthread_t th;
268 pthread_create(&th, &smallstack, &autorelease_lots_fn, (void*)1);
269 pthread_join(th, NULL);
270 }
271}
272
273
8972963c
A
274int main()
275{
7257e56c
A
276 pthread_attr_init(&smallstack);
277 pthread_attr_setstacksize(&smallstack, 16384);
278
8972963c
A
279 // inflate the refcount side table so it doesn't show up in leak checks
280 {
281 int count = 10000;
282 id *objs = (id *)malloc(count*sizeof(id));
283 for (int i = 0; i < count; i++) {
cd5f04f5 284 objs[i] = RR_RETAIN([NSObject new]);
8972963c
A
285 }
286 for (int i = 0; i < count; i++) {
cd5f04f5
A
287 RR_RELEASE(objs[i]);
288 RR_RELEASE(objs[i]);
8972963c
A
289 }
290 free(objs);
291 }
292
293#if FOUNDATION
294 // inflate NSAutoreleasePool's instance cache
295 {
296 int count = 32;
297 id *objs = (id *)malloc(count * sizeof(id));
298 for (int i = 0; i < count; i++) {
299 objs[i] = [[NSAutoreleasePool alloc] init];
300 }
301 for (int i = 0; i < count; i++) {
302 [objs[count-i-1] release];
303 }
304
305 free(objs);
306 }
307#endif
308
309
8972963c
A
310 for (int i = 0; i < 100; i++) {
311 cycle();
312 }
313
7257e56c
A
314 slow_cycle();
315
8972963c
A
316 leak_mark();
317
318 for (int i = 0; i < 1000; i++) {
319 cycle();
320 }
321
322 leak_check(0);
323
7257e56c 324 slow_cycle();
8972963c 325
cd5f04f5 326 leak_check(0);
8972963c
A
327
328
329 // NSThread.
330 // Can't leak check this because it's too noisy.
331 testprintf("-- NSThread.\n");
332 {
333 pthread_t th;
334 pthread_create(&th, &smallstack, &nsthread_fn, 0);
335 pthread_join(th, NULL);
336 }
337
338 // NO LEAK CHECK HERE
339
340 succeed(NAME);
341}