123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384 |
- // Define FOUNDATION=1 for NSObject and NSAutoreleasePool
- // Define FOUNDATION=0 for _objc_root* and _objc_autoreleasePool*
- #include "test.h"
- #if FOUNDATION
- # define RR_PUSH() [[NSAutoreleasePool alloc] init]
- # define RR_POP(p) [(id)p release]
- # define RR_RETAIN(o) [o retain]
- # define RR_RELEASE(o) [o release]
- # define RR_AUTORELEASE(o) [o autorelease]
- # define RR_RETAINCOUNT(o) [o retainCount]
- #else
- # define RR_PUSH() _objc_autoreleasePoolPush()
- # define RR_POP(p) _objc_autoreleasePoolPop(p)
- # define RR_RETAIN(o) _objc_rootRetain((id)o)
- # define RR_RELEASE(o) _objc_rootRelease((id)o)
- # define RR_AUTORELEASE(o) _objc_rootAutorelease((id)o)
- # define RR_RETAINCOUNT(o) _objc_rootRetainCount((id)o)
- #endif
- #include <objc/objc-internal.h>
- #include <Foundation/Foundation.h>
- static int state;
- static pthread_attr_t smallstack;
- #define NESTED_COUNT 8
- @interface Deallocator : NSObject @end
- @implementation Deallocator
- -(void) dealloc
- {
- // testprintf("-[Deallocator %p dealloc]\n", self);
- state++;
- [super dealloc];
- }
- @end
- @interface AutoreleaseDuringDealloc : NSObject @end
- @implementation AutoreleaseDuringDealloc
- -(void) dealloc
- {
- state++;
- RR_AUTORELEASE([[Deallocator alloc] init]);
- [super dealloc];
- }
- @end
- @interface AutoreleasePoolDuringDealloc : NSObject @end
- @implementation AutoreleasePoolDuringDealloc
- -(void) dealloc
- {
- // caller's pool
- for (int i = 0; i < NESTED_COUNT; i++) {
- RR_AUTORELEASE([[Deallocator alloc] init]);
- }
- // local pool, popped
- void *pool = RR_PUSH();
- for (int i = 0; i < NESTED_COUNT; i++) {
- RR_AUTORELEASE([[Deallocator alloc] init]);
- }
- RR_POP(pool);
- // caller's pool again
- for (int i = 0; i < NESTED_COUNT; i++) {
- RR_AUTORELEASE([[Deallocator alloc] init]);
- }
- #if FOUNDATION
- {
- static bool warned;
- if (!warned) testwarn("rdar://7138159 NSAutoreleasePool leaks");
- warned = true;
- }
- state += NESTED_COUNT;
- #else
- // local pool, not popped
- RR_PUSH();
- for (int i = 0; i < NESTED_COUNT; i++) {
- RR_AUTORELEASE([[Deallocator alloc] init]);
- }
- #endif
- [super dealloc];
- }
- @end
- void *autorelease_lots_fn(void *singlePool)
- {
- // Enough to blow out the stack if AutoreleasePoolPage is recursive.
- const int COUNT = 1024*1024;
- state = 0;
- int p = 0;
- void **pools = (void**)malloc((COUNT+1) * sizeof(void*));
- pools[p++] = RR_PUSH();
- id obj = RR_AUTORELEASE([[Deallocator alloc] init]);
- // last pool has only 1 autorelease in it
- pools[p++] = RR_PUSH();
- for (int i = 0; i < COUNT; i++) {
- if (rand() % 1000 == 0 && !singlePool) {
- pools[p++] = RR_PUSH();
- } else {
- RR_AUTORELEASE(RR_RETAIN(obj));
- }
- }
- testassert(state == 0);
- while (--p) {
- RR_POP(pools[p]);
- }
- testassert(state == 0);
- testassert(RR_RETAINCOUNT(obj) == 1);
- RR_POP(pools[0]);
- testassert(state == 1);
- free(pools);
- return NULL;
- }
- void *nsthread_fn(void *arg __unused)
- {
- [NSThread currentThread];
- void *pool = RR_PUSH();
- RR_AUTORELEASE([[Deallocator alloc] init]);
- RR_POP(pool);
- return NULL;
- }
- void cycle(void)
- {
- // Normal autorelease.
- testprintf("-- Normal autorelease.\n");
- {
- void *pool = RR_PUSH();
- state = 0;
- RR_AUTORELEASE([[Deallocator alloc] init]);
- testassert(state == 0);
- RR_POP(pool);
- testassert(state == 1);
- }
- // Autorelease during dealloc during autoreleasepool-pop.
- // That autorelease is handled by the popping pool, not the one above it.
- testprintf("-- Autorelease during dealloc during autoreleasepool-pop.\n");
- {
- void *pool = RR_PUSH();
- state = 0;
- RR_AUTORELEASE([[AutoreleaseDuringDealloc alloc] init]);
- testassert(state == 0);
- RR_POP(pool);
- testassert(state == 2);
- }
- // Autorelease pool during dealloc during autoreleasepool-pop.
- testprintf("-- Autorelease pool during dealloc during autoreleasepool-pop.\n");
- {
- void *pool = RR_PUSH();
- state = 0;
- RR_AUTORELEASE([[AutoreleasePoolDuringDealloc alloc] init]);
- testassert(state == 0);
- RR_POP(pool);
- testassert(state == 4 * NESTED_COUNT);
- }
- // Top-level thread pool popped normally.
- // Check twice - once for empty placeholder, once without.
- # if DEBUG_POOL_ALLOCATION || FOUNDATION
- // DebugPoolAllocation disables the empty placeholder pool.
- // Guard Malloc disables the empty placeholder pool (checked at runtime)
- // Foundation makes RR_PUSH return an NSAutoreleasePool not the raw token.
- # define CHECK_PLACEHOLDER 0
- # else
- # define CHECK_PLACEHOLDER 1
- # endif
- testprintf("-- Thread-level pool popped normally.\n");
- {
- state = 0;
- testonthread(^{
- void *pool = RR_PUSH();
- #if CHECK_PLACEHOLDER
- if (!is_guardmalloc()) {
- testassert(pool == (void*)1);
- }
- #endif
- RR_AUTORELEASE([[Deallocator alloc] init]);
- RR_POP(pool);
- pool = RR_PUSH();
- #if CHECK_PLACEHOLDER
- if (!is_guardmalloc()) {
- testassert(pool != (void*)1);
- }
- #endif
- RR_AUTORELEASE([[Deallocator alloc] init]);
- RR_POP(pool);
- });
- testassert(state == 2);
- }
- // Autorelease with no pool.
- testprintf("-- Autorelease with no pool.\n");
- {
- state = 0;
- testonthread(^{
- RR_AUTORELEASE([[Deallocator alloc] init]);
- });
- testassert(state == 1);
- }
- // Autorelease with no pool after popping the top-level pool.
- testprintf("-- Autorelease with no pool after popping the last pool.\n");
- {
- state = 0;
- testonthread(^{
- void *pool = RR_PUSH();
- RR_AUTORELEASE([[Deallocator alloc] init]);
- RR_POP(pool);
- RR_AUTORELEASE([[Deallocator alloc] init]);
- });
- testassert(state == 2);
- }
- // Top-level thread pool not popped.
- // The runtime should clean it up.
- #if FOUNDATION
- {
- static bool warned;
- if (!warned) testwarn("rdar://7138159 NSAutoreleasePool leaks");
- warned = true;
- }
- #else
- testprintf("-- Thread-level pool not popped.\n");
- {
- state = 0;
- testonthread(^{
- RR_PUSH();
- RR_AUTORELEASE([[Deallocator alloc] init]);
- // pool not popped
- });
- testassert(state == 1);
- }
- #endif
- // Intermediate pool not popped.
- // Popping the containing pool should clean up the skipped pool first.
- #if FOUNDATION
- {
- static bool warned;
- if (!warned) testwarn("rdar://7138159 NSAutoreleasePool leaks");
- warned = true;
- }
- #else
- testprintf("-- Intermediate pool not popped.\n");
- {
- void *pool = RR_PUSH();
- void *pool2 = RR_PUSH();
- RR_AUTORELEASE([[Deallocator alloc] init]);
- state = 0;
- (void)pool2; // pool2 not popped
- RR_POP(pool);
- testassert(state == 1);
- }
- #endif
- }
- static void
- slow_cycle(void)
- {
- // Large autorelease stack.
- // Do this only once because it's slow.
- testprintf("-- Large autorelease stack.\n");
- {
- // limit stack size: autorelease pop should not be recursive
- pthread_t th;
- pthread_create(&th, &smallstack, &autorelease_lots_fn, NULL);
- pthread_join(th, NULL);
- }
- // Single large autorelease pool.
- // Do this only once because it's slow.
- testprintf("-- Large autorelease pool.\n");
- {
- // limit stack size: autorelease pop should not be recursive
- pthread_t th;
- pthread_create(&th, &smallstack, &autorelease_lots_fn, (void*)1);
- pthread_join(th, NULL);
- }
- }
- int main()
- {
- pthread_attr_init(&smallstack);
- pthread_attr_setstacksize(&smallstack, 32768);
- // inflate the refcount side table so it doesn't show up in leak checks
- {
- int count = 10000;
- id *objs = (id *)malloc(count*sizeof(id));
- for (int i = 0; i < count; i++) {
- objs[i] = RR_RETAIN([NSObject new]);
- }
- for (int i = 0; i < count; i++) {
- RR_RELEASE(objs[i]);
- RR_RELEASE(objs[i]);
- }
- free(objs);
- }
- #if FOUNDATION
- // inflate NSAutoreleasePool's instance cache
- {
- int count = 32;
- id *objs = (id *)malloc(count * sizeof(id));
- for (int i = 0; i < count; i++) {
- objs[i] = [[NSAutoreleasePool alloc] init];
- }
- for (int i = 0; i < count; i++) {
- [objs[count-i-1] release];
- }
-
- free(objs);
- }
- #endif
- // preheat
- {
- for (int i = 0; i < 100; i++) {
- cycle();
- }
-
- slow_cycle();
- }
-
- // check for leaks using top-level pools
- {
- leak_mark();
-
- for (int i = 0; i < 1000; i++) {
- cycle();
- }
-
- leak_check(0);
-
- slow_cycle();
-
- leak_check(0);
- }
-
- // check for leaks using pools not at top level
- // fixme for FOUNDATION this leak mark/check needs
- // to be outside the autorelease pool for some reason
- leak_mark();
- void *pool = RR_PUSH();
- {
- for (int i = 0; i < 1000; i++) {
- cycle();
- }
-
- slow_cycle();
- }
- RR_POP(pool);
- leak_check(0);
- // NSThread.
- // Can't leak check this because it's too noisy.
- testprintf("-- NSThread.\n");
- {
- pthread_t th;
- pthread_create(&th, &smallstack, &nsthread_fn, 0);
- pthread_join(th, NULL);
- }
-
- // NO LEAK CHECK HERE
- succeed(NAME);
- }
|