Index: src/OFAutoreleasePool.h ================================================================== --- src/OFAutoreleasePool.h +++ src/OFAutoreleasePool.h @@ -24,25 +24,24 @@ * * Every thread has its own stack of autorelease pools. */ @interface OFAutoreleasePool: OFObject { - OFAutoreleasePool *nextPool, *previousPool; - id *objects; - size_t count, size; + void *pool; + BOOL ignoreRelease; } /** * \brief Adds an object to the autorelease pool at the top of the * thread-specific autorelease pool stack. * * \param object The object to add to the autorelease pool + * \return The object */ -+ (void)addObject: (id)object; ++ (id)addObject: (id)object; + (void)_releaseAll; -- (void)_addObject: (id)object; /** * \brief Releases all objects in the autorelease pool. * * This does not free the memory allocated to store pointers to the objects in Index: src/OFAutoreleasePool.m ================================================================== --- src/OFAutoreleasePool.m +++ src/OFAutoreleasePool.m @@ -19,143 +19,61 @@ #include #import "OFAutoreleasePool.h" #import "OFArray.h" -#import "OFInitializationFailedException.h" #import "OFNotImplementedException.h" -#ifdef OF_THREADS -# import "threading.h" -static of_tlskey_t firstKey, lastKey; -#else -static OFAutoreleasePool *firstPool = nil, *lastPool = nil; -#endif - -#define GROW_SIZE 16 +extern id _objc_rootAutorelease(id); +extern void* objc_autoreleasePoolPush(void); +extern void objc_autoreleasePoolPop(void*); + +static __thread void *first = NULL; @implementation OFAutoreleasePool -#ifdef OF_THREADS -+ (void)initialize -{ - if (self != [OFAutoreleasePool class]) - return; - - if (!of_tlskey_new(&firstKey) || !of_tlskey_new(&lastKey)) - @throw [OFInitializationFailedException - exceptionWithClass: self]; -} -#endif - -+ (void)addObject: (id)object -{ -#ifdef OF_THREADS - id lastPool = of_tlskey_get(lastKey); -#endif - - if (lastPool == nil) { - @try { - [[self alloc] init]; - } @catch (id e) { - [object release]; - @throw e; - } - -#ifdef OF_THREADS - lastPool = of_tlskey_get(lastKey); -#endif - } - - if (lastPool == nil) { - [object release]; - @throw [OFInitializationFailedException - exceptionWithClass: self]; - } - - @try { - [lastPool _addObject: object]; - } @catch (id e) { - [object release]; - @throw e; - } ++ (id)addObject: (id)object +{ + if (first == NULL) + [[OFAutoreleasePool alloc] init]; + + return _objc_rootAutorelease(object); } + (void)_releaseAll { -#ifdef OF_THREADS - [of_tlskey_get(firstKey) release]; -#else - [firstPool release]; -#endif + objc_autoreleasePoolPop(first); } - init { self = [super init]; @try { -#ifdef OF_THREADS - id firstPool = of_tlskey_get(firstKey); - previousPool = of_tlskey_get(lastKey); - - if (!of_tlskey_set(lastKey, self)) - @throw [OFInitializationFailedException - exceptionWithClass: [self class]]; -#else - previousPool = lastPool; - lastPool = self; -#endif - - if (firstPool == nil) { -#ifdef OF_THREADS - if (!of_tlskey_set(firstKey, self)) { - of_tlskey_set(lastKey, previousPool); - @throw [OFInitializationFailedException - exceptionWithClass: [self class]]; - } -#else - firstPool = self; -#endif - } - - if (previousPool != nil) - previousPool->nextPool = self; - - size = GROW_SIZE; - objects = [self allocMemoryWithSize: sizeof(id) - count: GROW_SIZE]; + pool = objc_autoreleasePoolPush(); + + if (first == NULL) + first = pool; + + _objc_rootAutorelease(self); } @catch (id e) { [self release]; @throw e; } return self; } -- (void)_addObject: (id)object -{ - if (count + 1 > size) { - objects = [self resizeMemory: objects - size: sizeof(id) - count: size + GROW_SIZE]; - size += GROW_SIZE; - } - - objects[count] = object; - count++; -} - - (void)releaseObjects { - size_t i; - - [nextPool releaseObjects]; - - for (i = 0; i < count; i++) - [objects[i] release]; - - count = 0; + ignoreRelease = YES; + + objc_autoreleasePoolPop(pool); + pool = objc_autoreleasePoolPush(); + + _objc_rootAutorelease(self); + + ignoreRelease = NO; } - (void)release { [self dealloc]; @@ -166,47 +84,19 @@ [self dealloc]; } - (void)dealloc { - size_t i; - - [nextPool dealloc]; - - for (i = 0; i < count; i++) - [objects[i] release]; - - /* - * If of_tlskey_set fails, this is a real problem. The best we can do - * is to not change the pool below the current pool and stop - * deallocation. This way, new objects will be added to the current - * pool, but released when the pool below gets released - and maybe - * the pool itself will be released as well then, because maybe - * of_tlskey_set will work this time. - */ -#ifdef OF_THREADS - if (!of_tlskey_set(lastKey, previousPool)) - return; -#else - lastPool = previousPool; -#endif - - if (previousPool != nil) - previousPool->nextPool = nil; - - /* - * If of_tlskey_set fails here, this is even worse, as this will - * definitely be a memory leak. But this should never happen anyway. - */ -#ifdef OF_THREADS - if (of_tlskey_get(firstKey) == self) - if (!of_tlskey_set(firstKey, nil)) - return; -#else - if (firstPool == self) - firstPool = nil; -#endif + if (ignoreRelease) + return; + + ignoreRelease = YES; + + if (first == pool) + first = NULL; + + objc_autoreleasePoolPop(pool); [super dealloc]; } - retain Index: src/OFObject.m ================================================================== --- src/OFObject.m +++ src/OFObject.m @@ -769,13 +769,11 @@ * GNU ABI. */ if (autoreleasePool == Nil) autoreleasePool = [OFAutoreleasePool class]; - [autoreleasePool addObject: self]; - - return self; + return [autoreleasePool addObject: self]; } - self { return self; Index: src/runtime/Makefile ================================================================== --- src/runtime/Makefile +++ src/runtime/Makefile @@ -1,11 +1,12 @@ include ../../extra.mk STATIC_PIC_LIB_NOINST = ${RUNTIME_LIB_A} STATIC_LIB_NOINST = ${RUNTIME_A} -SRCS = category.m \ +SRCS = autorelease.m \ + category.m \ class.m \ exception.m \ hashtable.m \ init.m \ lookup.m \ ADDED src/runtime/autorelease.m Index: src/runtime/autorelease.m ================================================================== --- src/runtime/autorelease.m +++ src/runtime/autorelease.m @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2008, 2009, 2010, 2011, 2012 + * Jonathan Schleifer + * + * All rights reserved. + * + * This file is part of ObjFW. It may be distributed under the terms of the + * Q Public License 1.0, which can be found in the file LICENSE.QPL included in + * the packaging of this file. + * + * Alternatively, it may be distributed under the terms of the GNU General + * Public License, either version 2 or 3, which can be found in the file + * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this + * file. + */ + +#include "config.h" + +#include +#include + +#import "runtime.h" +#import "runtime-private.h" + +#import "OFObject.h" + +#import "macros.h" + +static __thread id *objects = NULL; +static __thread id *top = NULL; +static size_t size = 0; + +id +objc_autorelease(id object) +{ + return [object autorelease]; +} + +void* +objc_autoreleasePoolPush() +{ + ptrdiff_t offset = top - objects; + + return (void*)offset; +} + +void +objc_autoreleasePoolPop(void *offset) +{ + id *pool = objects + (ptrdiff_t)offset; + id *iter; + + for (iter = pool; iter < top; iter++) + [*iter release]; + + top = pool; +} + +id +_objc_rootAutorelease(id object) +{ + if (objects == NULL) { + if ((objects = malloc(of_pagesize)) == NULL) + ERROR("Out of memory for autorelease pools!") + + top = objects; + } + + if ((uintptr_t)top >= (uintptr_t)objects + size) { + ptrdiff_t diff = top - objects; + + size += of_pagesize; + if ((objects = realloc(objects, size)) == NULL) + ERROR("Out of memory for autorelease pools!") + + top = objects + diff; + } + + *top = object; + top++; + + return object; +} Index: src/runtime/runtime.h ================================================================== --- src/runtime/runtime.h +++ src/runtime/runtime.h @@ -130,10 +130,14 @@ extern void objc_thread_add(void); extern void objc_thread_remove(void); extern void objc_exit(void); extern objc_uncaught_exception_handler objc_setUncaughtExceptionHandler( objc_uncaught_exception_handler); +extern id objc_autorelease(id); +extern void* objc_autoreleasePoolPush(void); +extern void objc_autoreleasePoolPop(void*); +extern id _objc_rootAutorelease(id); static inline Class object_getClass(id obj_) { struct objc_object *obj = (struct objc_object*)obj_;