Index: TODO ================================================================== --- TODO +++ TODO @@ -1,5 +1,7 @@ +Test if autorelease pool releases everything correctly when thread is ended + Serialization Tests for OFFile. Tests for OFNumber. OFBase64 @@ -6,11 +8,10 @@ OFDirectory OFDictionary OFSortedArray OFThread -OFAutoreleasePool OFStack OFQueue OFPlugin Index: configure.ac ================================================================== --- configure.ac +++ configure.ac @@ -65,15 +65,23 @@ AC_MSG_RESULT($ac_cv_snprintf_useful_ret) test x"$have_asprintf" != x"yes" -a x"$ac_cv_snprintf_useful_ret" != x"yes" && \ AC_MSG_ERROR(No asprintf and no snprintf returning required space!) -ACX_PTHREAD([ - CPPLAGS="$CPPFLAGS $PTHREAD_CFLAGS" - LIBS="$LIBS $PTHREAD_LIBS" - ], [ - AC_MSG_ERROR(You need pthreads!)]) +case "$target" in + *-*-mingw*) + AC_MSG_CHECKING(for threads) + AC_MSG_RESULT(win32) + ;; + *) + ACX_PTHREAD([ + CPPLAGS="$CPPFLAGS $PTHREAD_CFLAGS" + LIBS="$LIBS $PTHREAD_LIBS" + ], [ + AC_MSG_ERROR(No pthreads or other supported threads!)]) + ;; +esac AC_CHECK_LIB(ws2_32, main, LIBS="$LIBS -lws2_32") AC_MSG_CHECKING(whether we have IPv6 support) AC_CACHE_VAL(ac_cv_have_ipv6, [ Index: src/Makefile ================================================================== --- src/Makefile +++ src/Makefile @@ -3,10 +3,11 @@ LIB = ${LIB_PREFIX}objfw${LIB_SUFFIX} LIB_MAJOR = 1 LIB_MINOR = 0 SRCS = OFArray.m \ + OFAutoreleasePool.m \ OFExceptions.m \ OFHashes.m \ OFFile.m \ OFList.m \ OFListObject.m \ ADDED src/OFAutoreleasePool.h Index: src/OFAutoreleasePool.h ================================================================== --- src/OFAutoreleasePool.h +++ src/OFAutoreleasePool.h @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2008 + * Jonathan Schleifer + * + * All rights reserved. + * + * This file is part of libobjfw. It may be distributed under the terms of the + * Q Public License 1.0, which can be found in the file LICENSE included in + * the packaging of this file. + */ + +#import "OFObject.h" + +/** + * The OFAutoreleasePool class provides a class that keeps track of objects + * that will be released when the autorelease pool is released. + * Every thread has its own stack of autorelease pools. + */ +@interface OFAutoreleasePool: OFObject +{ + OFObject **objects; + size_t size; +} + +/** + * Adds an object to the autorelease pool at the top of the thread-specific + * stack. + * + * \param obj The object to add to the autorelease pool + */ ++ (void)addToPool: (OFObject*)obj; + +/** + * Adds an object to the specific autorelease pool. + * stack. + * + * \param obj The object to add to the autorelease pool + */ +- addToPool: (OFObject*)obj; + +/** + * Releases all objects in the autorelease pool. + */ +- release; + +/** + * \returns All objects in the autorelease pool + */ +- (OFObject**)objects; +@end ADDED src/OFAutoreleasePool.m Index: src/OFAutoreleasePool.m ================================================================== --- src/OFAutoreleasePool.m +++ src/OFAutoreleasePool.m @@ -0,0 +1,204 @@ +/* + * Copyright (c) 2008 + * Jonathan Schleifer + * + * All rights reserved. + * + * This file is part of libobjfw. It may be distributed under the terms of the + * Q Public License 1.0, which can be found in the file LICENSE included in + * the packaging of this file. + */ + +#import + +#ifndef _WIN32 +#import +#endif + +#import "OFAutoreleasePool.h" +#import "OFExceptions.h" + +#ifdef _WIN32 +#import +#endif + +#ifndef _WIN32 +static pthread_key_t pool_stack_key; +static pthread_key_t pool_index_key; +#else +static DWORD pool_stack_key; +static DWORD pool_index_key; +#endif + +#ifndef _WIN32 +static void +free_each(void *ptr) +{ + OFAutoreleasePool **p; + OFObject **o; + + for (p = (OFAutoreleasePool**)ptr; *p != nil; p++) { + for (o = [*p objects]; *o != nil; o++) + [*o release]; + [*p free]; + } +} +#endif + +@implementation OFAutoreleasePool ++ (void)initialize +{ +#ifndef _WIN32 + if (pthread_key_create(&pool_stack_key, free_each)) + @throw [OFInitializationFailedException newWithClass: self]; + if (pthread_key_create(&pool_index_key, free)) { + pthread_key_delete(pool_stack_key); + @throw [OFInitializationFailedException newWithClass: self]; + } +#else + /* FIXME: Free stuff when thread is terminated! */ + if ((pool_stack_key = TlsAlloc()) == TLS_OUT_OF_INDEXES) + @throw [OFInitializationFailedException newWithClass: self]; + if ((pool_index_key = TlsAlloc()) == TLS_OUT_OF_INDEXES) { + TlsFree(pool_stack_key); + @throw [OFInitializationFailedException newWithClass: self]; + } + +#endif +} + ++ (void)addToPool: (OFObject*)obj +{ + OFAutoreleasePool **pool_stack; + int *pool_index; + +#ifndef _WIN32 + pool_stack = pthread_getspecific(pool_stack_key); + pool_index = pthread_getspecific(pool_index_key); +#else + pool_stack = TlsGetValue(pool_stack_key); + pool_index = TlsGetValue(pool_index_key); +#endif + + if (pool_stack == NULL || pool_index == NULL) { + [[self alloc] init]; + +#ifndef _WIN32 + pool_stack = pthread_getspecific(pool_stack_key); + pool_index = pthread_getspecific(pool_index_key); +#else + pool_stack = TlsGetValue(pool_stack_key); + pool_index = TlsGetValue(pool_index_key); +#endif + } + + if (*pool_stack == nil || *pool_index == -1) + @throw [OFInitializationFailedException newWithClass: self]; + + [pool_stack[*pool_index] addToPool: obj]; +} + +- init +{ + OFAutoreleasePool **pool_stack, **pool_stack2; + int *pool_index; + Class c; + + if ((self = [super init])) { + objects = NULL; + size = 0; + +#ifndef _WIN32 + pool_stack = pthread_getspecific(pool_stack_key); + pool_index = pthread_getspecific(pool_index_key); +#else + pool_stack = TlsGetValue(pool_stack_key); + pool_index = TlsGetValue(pool_index_key); +#endif + + if (pool_index == NULL) { + if ((pool_index = malloc(sizeof(int))) == NULL) { + c = [self class]; + [super free]; + @throw [OFNoMemException newWithClass: c]; + } + + *pool_index = -1; +#ifndef _WIN32 + pthread_setspecific(pool_index_key, pool_index); +#else + TlsSetValue(pool_index_key, pool_index); +#endif + } + + if ((pool_stack2 = realloc(pool_stack, + (*pool_index + 3) * sizeof(OFAutoreleasePool*))) == NULL) { + c = [self class]; + [super free]; + @throw [OFNoMemException newWithClass: c]; + } + pool_stack = pool_stack2; +#ifndef _WIN32 + pthread_setspecific(pool_stack_key, pool_stack); +#else + TlsSetValue(pool_stack_key, pool_stack); +#endif + (*pool_index)++; + + pool_stack[*pool_index] = self; + pool_stack[*pool_index + 1] = nil; + } + + return self; +} + +- free +{ + [self release]; + + return [super free]; +} + +- addToPool: (OFObject*)obj +{ + OFObject **objects2; + size_t size2; + + size2 = size + 1; + + if (SIZE_MAX - size < 1 || size2 > SIZE_MAX / sizeof(OFObject*)) + @throw [OFOutOfRangeException newWithClass: [self class]]; + + if ((objects2 = realloc(objects, size2 * sizeof(OFObject*))) == NULL) + @throw [OFNoMemException newWithClass: [self class] + andSize: size2]; + + objects = objects2; + objects[size] = obj; + size = size2; + + return self; +} + +- release +{ + size_t i; + + if (objects != NULL) { + for (i = 0; size < i; i++) + [objects[i] release]; + + free(objects); + } + + objects = NULL; + size = 0; + + return self; +} + +- (OFObject**)objects +{ + return objects; +} +@end Index: src/OFObject.h ================================================================== --- src/OFObject.h +++ src/OFObject.h @@ -18,10 +18,11 @@ */ @interface OFObject: Object { void **__memchunks; size_t __memchunks_size; + size_t __retain_count; } /** * Initialize the already allocated object. * Also sets up the memory pool for the object. @@ -28,10 +29,25 @@ * * \return An initialized object */ - init; +/** + * Increases the retain count. + */ +- retain; + +/** + * Decreases the retain cound and frees the object if it reaches 0. + */ +- release; + +/** + * Adds the object to the autorelease pool that is on top of the thread's stack. + */ +- autorelease; + /** * Adds a pointer to the memory pool. * This is useful to add memory allocated by functions such as asprintf to the * pool so it gets freed automatically when the object is freed. * Index: src/OFObject.m ================================================================== --- src/OFObject.m +++ src/OFObject.m @@ -14,20 +14,23 @@ #import #import #import #import "OFObject.h" +#import "OFAutoreleasePool.h" #import "OFExceptions.h" #import "OFMacros.h" @implementation OFObject - init { if ((self = [super init]) != nil) { __memchunks = NULL; __memchunks_size = 0; + __retain_count = 1; } + return self; } - free { @@ -39,19 +42,41 @@ if (__memchunks != NULL) free(__memchunks); return [super free]; } + +- retain +{ + __retain_count++; + + return self; +} + +- release +{ + if (!--__retain_count) + return [self free]; + + return self; +} + +- autorelease +{ + [OFAutoreleasePool addToPool: self]; + + return self; +} - addToMemoryPool: (void*)ptr { void **memchunks; size_t memchunks_size; memchunks_size = __memchunks_size + 1; - if (SIZE_MAX - __memchunks_size == 0 || + if (SIZE_MAX - __memchunks_size < 1 || memchunks_size > SIZE_MAX / sizeof(void*)) @throw [OFOutOfRangeException newWithClass: [self class]]; if ((memchunks = realloc(__memchunks, memchunks_size * sizeof(void*))) == NULL) @@ -60,11 +85,11 @@ __memchunks = memchunks; __memchunks[__memchunks_size] = ptr; __memchunks_size = memchunks_size; - return ptr; + return self; } - (void*)getMemWithSize: (size_t)size { void *ptr, **memchunks; Index: tests/Makefile ================================================================== --- tests/Makefile +++ tests/Makefile @@ -1,9 +1,10 @@ -SUBDIRS = OFObject \ - OFArray \ - OFHashes \ - OFString \ - OFTCPSocket \ - OFList \ +SUBDIRS = OFObject \ + OFAutoreleasePool \ + OFArray \ + OFHashes \ + OFString \ + OFTCPSocket \ + OFList \ OFXMLFactory include ../buildsys.mk ADDED tests/OFAutoreleasePool/Makefile Index: tests/OFAutoreleasePool/Makefile ================================================================== --- tests/OFAutoreleasePool/Makefile +++ tests/OFAutoreleasePool/Makefile @@ -0,0 +1,23 @@ +PROG_NOINST = ofautoreleasepool${PROG_SUFFIX} +SRCS = OFAutoreleasePool.m + +include ../../buildsys.mk +include ../../extra.mk + +CPPFLAGS += -I../../src -I../.. +LIBS := -L../../src -lobjfw ${LIBS} + +.PHONY: run + +all: run +run: ${PROG_NOINST} + rm -f libobjfw.so.1 libobjfw.so.1.0 libobjfw.dylib + ln -s ../../src/libobjfw.so libobjfw.so.1 + ln -s ../../src/libobjfw.so libobjfw.so.1.0 + ln -s ../../src/libobjfw.dll libobjfw.dll + ln -s ../../src/libobjfw.dylib libobjfw.dylib + LD_LIBRARY_PATH=.$${LD_LIBRARY_PATH+:}$$LD_LIBRARY_PATH \ + DYLD_LIBRARY_PATH=.$${DYLD_LIBRARY_PATH+:}$$DYLD_LIBRARY_PATH \ + ${TEST_LAUNCHER} ./${PROG_NOINST}; EXIT=$$?; \ + rm -f libobjfw.so.1 libobjfw.so.1.0 libobjfw.dll libobjfw.dylib; \ + exit $$EXIT ADDED tests/OFAutoreleasePool/OFAutoreleasePool.m Index: tests/OFAutoreleasePool/OFAutoreleasePool.m ================================================================== --- tests/OFAutoreleasePool/OFAutoreleasePool.m +++ tests/OFAutoreleasePool/OFAutoreleasePool.m @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2008 + * Jonathan Schleifer + * + * All rights reserved. + * + * This file is part of libobjfw. It may be distributed under the terms of the + * Q Public License 1.0, which can be found in the file LICENSE included in + * the packaging of this file. + */ + +#import "config.h" + +#import "OFAutoreleasePool.h" + +/* FIXME: Just crashtests */ + +int +main() +{ + OFObject *o1, *o2, *o3; + OFAutoreleasePool *pool1, *pool2; + + o1 = [[OFObject new] autorelease]; + + pool1 = [OFAutoreleasePool new]; + o2 = [[OFObject new] autorelease]; + [pool1 release]; + + o2 = [[OFObject new] autorelease]; + + pool2 = [OFAutoreleasePool new]; + o3 = [[OFObject new] autorelease]; + + [pool1 release]; + [o3 free]; + + return 0; +}