ObjFW  Check-in [f5927f8a84]

Overview
Comment:New autorelease pools.

This uses the runtime's autorelease pools and implements autorelease
pools in the ObjFW runtime. It therefore uses ObjFW's autorelease pools
when using the ObjFW runtime and Apple's autorelease pools when using
the Apple runtime.

These new pools should be ARC-compatible now and finally, it should be
possible to mix OFAutoreleasePools and NSAutoreleasePools again, even
@autoreleasepool is allowed in the mix now. This also means the old
bridge to NSAutoreleasePool should not be required anymore, as both use
the runtime's autorelease pools now.

As a bonus, it's significantly faster to use the ObjFW runtime with
ObjFW's autorelease pools than to use Apple's runtime with Apple's
autorelease pools, as a quick benchmark using OFXMLParser on large files
showed. (Note: This is not only due to the different autorelease pools,
but also due to the fact that even with the same autorelease pools it is
faster using the ObjFW runtime, as can be seen in versions prior to this
commit.)

Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: f5927f8a8401bbcbf704764a1e150745b9d97527415cea088fa4d3e74b4acd07
User & Date: js on 2012-07-14 20:00:11
Other Links: manifest | tags
Context
2012-07-14
20:38
Let of_tlskey_* use void* instead of id. check-in: e379516a39 user: js tags: trunk
20:00
New autorelease pools. check-in: f5927f8a84 user: js tags: trunk
09:59
objfw-compile: Always add -Wall. check-in: 7e95e4a343 user: js tags: trunk
Changes

Modified src/OFAutoreleasePool.h from [ed23b75b52] to [d6d789a7b7].

22
23
24
25
26
27
28
29
30
31

32
33
34
35
36
37
38

39
40
41
42
43
44
45
46
47
48
49
50
 * The OFAutoreleasePool class is 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
{
	OFAutoreleasePool *nextPool, *previousPool;
	id *objects;
	size_t count, size;

}

/**
 * \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

 */
+ (void)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
 * the pool, so reusing the pool does not allocate any memory until the previous
 * number of objects is exceeded. It behaves this way to optimize loops that







<
|
<
>







>

|


<







22
23
24
25
26
27
28

29

30
31
32
33
34
35
36
37
38
39
40
41
42

43
44
45
46
47
48
49
 * The OFAutoreleasePool class is 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
{

	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
 */
+ (id)addObject: (id)object;

+ (void)_releaseAll;


/**
 * \brief Releases all objects in the autorelease pool.
 *
 * This does not free the memory allocated to store pointers to the objects in
 * the pool, so reusing the pool does not allocate any memory until the previous
 * number of objects is exceeded. It behaves this way to optimize loops that

Modified src/OFAutoreleasePool.m from [6d60b227fa] to [9b9e08d0f0].

17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207

208
209
210
211
212
213
214
#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: [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];
	} @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;
}

- (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: [self class]







<


<
<
<
<
<
<
|
<
|
|
<
<
|
<
<
|
<
<
<
|
<
|
|

<
<
<
|
<
<
|
<
<
<
|
|
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<




<
<
<
|
<







<
<
<
|
<
<
<
<
<
<
<

|
<
<
<
<
<
<
<
|
<
|
|
<
<
<
<
<
<








|

<
<
<
<
<
<
|
<
<
|
|
|
|
<
|
<

<
<
|
<














<
|
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<

<
<
<

<
|

<
<
<
<
<
<
<
<
<
|
|
|
>







17
18
19
20
21
22
23

24
25






26

27
28


29


30



31

32
33
34



35


36



37
38

















39
40
41
42



43

44
45
46
47
48
49
50



51







52
53







54

55
56






57
58
59
60
61
62
63
64
65
66






67


68
69
70
71

72

73


74

75
76
77
78
79
80
81
82
83
84
85
86
87
88

89















90



91

92
93









94
95
96
97
98
99
100
101
102
103
104
#include "config.h"

#include <stdlib.h>

#import "OFAutoreleasePool.h"
#import "OFArray.h"


#import "OFNotImplementedException.h"







extern id _objc_rootAutorelease(id);

extern void* objc_autoreleasePoolPush(void);
extern void objc_autoreleasePoolPop(void*);





static __thread void *first = NULL;





@implementation OFAutoreleasePool
+ (id)addObject: (id)object
{



	if (first == NULL)


		[[OFAutoreleasePool alloc] init];




	return _objc_rootAutorelease(object);

















}

+ (void)_releaseAll
{



	objc_autoreleasePoolPop(first);

}

- init
{
	self = [super init];

	@try {



		pool = objc_autoreleasePoolPush();








		if (first == NULL)







			first = pool;


		_objc_rootAutorelease(self);






	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}

- (void)releaseObjects
{






	ignoreRelease = YES;



	objc_autoreleasePoolPop(pool);
	pool = objc_autoreleasePoolPush();


	_objc_rootAutorelease(self);




	ignoreRelease = NO;

}

- (void)release
{
	[self dealloc];
}

- (void)drain
{
	[self dealloc];
}

- (void)dealloc
{

	if (ignoreRelease)















		return;





	ignoreRelease = YES;










	if (first == pool)
		first = NULL;

	objc_autoreleasePoolPop(pool);

	[super dealloc];
}

- retain
{
	@throw [OFNotImplementedException exceptionWithClass: [self class]

Modified src/OFObject.m from [470fa11181] to [d5e3e3b36f].

767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
	/*
	 * Cache OFAutoreleasePool since class lookups are expensive with the
	 * GNU ABI.
	 */
	if (autoreleasePool == Nil)
		autoreleasePool = [OFAutoreleasePool class];

	[autoreleasePool addObject: self];

	return self;
}

- self
{
	return self;
}








|
<
<







767
768
769
770
771
772
773
774


775
776
777
778
779
780
781
	/*
	 * Cache OFAutoreleasePool since class lookups are expensive with the
	 * GNU ABI.
	 */
	if (autoreleasePool == Nil)
		autoreleasePool = [OFAutoreleasePool class];

	return [autoreleasePool addObject: self];


}

- self
{
	return self;
}

Modified src/runtime/Makefile from [756512ba6a] to [a09545f782].

1
2
3
4
5

6
7
8
9
10
11
12
13
include ../../extra.mk

STATIC_PIC_LIB_NOINST = ${RUNTIME_LIB_A}
STATIC_LIB_NOINST = ${RUNTIME_A}


SRCS = category.m		\
       class.m			\
       exception.m		\
       hashtable.m		\
       init.m			\
       lookup.m			\
       ${LOOKUP_S}		\
       property.m		\





>
|







1
2
3
4
5
6
7
8
9
10
11
12
13
14
include ../../extra.mk

STATIC_PIC_LIB_NOINST = ${RUNTIME_LIB_A}
STATIC_LIB_NOINST = ${RUNTIME_A}

SRCS = autorelease.m		\
       category.m		\
       class.m			\
       exception.m		\
       hashtable.m		\
       init.m			\
       lookup.m			\
       ${LOOKUP_S}		\
       property.m		\

Added src/runtime/autorelease.m version [aef7e3462c].







































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
/*
 * 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 <stdio.h>
#include <stdlib.h>

#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;
}

Modified src/runtime/runtime.h from [dd41eb5b33] to [10b66bd071].

128
129
130
131
132
133
134




135
136
137
138
139
140
141
extern BOOL protocol_isEqual(Protocol*, Protocol*);
extern BOOL protocol_conformsToProtocol(Protocol*, Protocol*);
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);





static inline Class
object_getClass(id obj_)
{
	struct objc_object *obj = (struct objc_object*)obj_;

	return obj->isa;







>
>
>
>







128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
extern BOOL protocol_isEqual(Protocol*, Protocol*);
extern BOOL protocol_conformsToProtocol(Protocol*, Protocol*);
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_;

	return obj->isa;