]>
Commit | Line | Data |
---|---|---|
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 | ||
25 | static int state; | |
7257e56c | 26 | static 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 |
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*)); | |
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 | 126 | void *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 | ||
135 | void 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 | |
249 | static void | |
250 | slow_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 |
274 | int 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 | } |