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