/*
* Copyright (c) 2008, 2009, 2010, 2011, 2012
* Jonathan Schleifer <js@webkeks.org>
*
* 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 <stdlib.h>
#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
@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;
}
}
+ (void)_releaseAll
{
#ifdef OF_THREADS
[of_tlskey_get(firstKey) release];
#else
[firstPool release];
#endif
}
- 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: isa];
#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: isa];
}
#else
firstPool = self;
#endif
}
if (previousPool != nil)
previousPool->nextPool = self;
size = GROW_SIZE;
objects = [self allocMemoryForNItems: GROW_SIZE
ofSize: sizeof(id)];
} @catch (id e) {
[self release];
@throw e;
}
return self;
}
- (void)_addObject: (id)object
{
if (count + 1 > size) {
objects = [self resizeMemory: objects
toNItems: size + GROW_SIZE
ofSize: sizeof(id)];
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;
}
- (void)release
{
[self dealloc];
}
- (void)drain
{
[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
[super dealloc];
}
- retain
{
@throw [OFNotImplementedException exceptionWithClass: isa
selector: _cmd];
}
- autorelease
{
@throw [OFNotImplementedException exceptionWithClass: isa
selector: _cmd];
}
@end