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
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
{
	OFAutoreleasePool *nextPool, *previousPool;
	id *objects;
	void *pool;
	size_t count, size;
	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
 * 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
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 "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

extern id _objc_rootAutorelease(id);
#define GROW_SIZE 16

@implementation OFAutoreleasePool
extern void* objc_autoreleasePoolPush(void);
extern void objc_autoreleasePoolPop(void*);
#ifdef OF_THREADS
+ (void)initialize
{

	if (self != [OFAutoreleasePool class])
		return;

static __thread void *first = NULL;
	if (!of_tlskey_new(&firstKey) || !of_tlskey_new(&lastKey))
		@throw [OFInitializationFailedException
		    exceptionWithClass: self];
}

#endif

+ (void)addObject: (id)object
@implementation OFAutoreleasePool
+ (id)addObject: (id)object
{
#ifdef OF_THREADS
	id lastPool = of_tlskey_get(lastKey);
#endif

	if (first == NULL)
	if (lastPool == nil) {
		@try {
			[[self alloc] init];
		[[OFAutoreleasePool alloc] init];
		} @catch (id e) {
			[object release];
			@throw e;
		}


	return _objc_rootAutorelease(object);
#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];
	objc_autoreleasePoolPop(first);
#endif
}

- init
{
	self = [super init];

	@try {
#ifdef OF_THREADS
		id firstPool = of_tlskey_get(firstKey);
		previousPool = of_tlskey_get(lastKey);

		pool = objc_autoreleasePoolPush();
		if (!of_tlskey_set(lastKey, self))
			@throw [OFInitializationFailedException
			    exceptionWithClass: [self class]];
#else
		previousPool = lastPool;
		lastPool = self;
#endif

		if (firstPool == nil) {
		if (first == NULL)
#ifdef OF_THREADS
			if (!of_tlskey_set(firstKey, self)) {
				of_tlskey_set(lastKey, previousPool);
				@throw [OFInitializationFailedException
				    exceptionWithClass: [self class]];
			}
#else
			firstPool = self;
			first = pool;
#endif
		}


		_objc_rootAutorelease(self);
		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
- (void)releaseObjects
{
	if (count + 1 > size) {
		objects = [self resizeMemory: objects
					size: sizeof(id)
				       count: size + GROW_SIZE];
		size += GROW_SIZE;
	}

	ignoreRelease = YES;
	objects[count] = object;
	count++;
}

- (void)releaseObjects
{

	objc_autoreleasePoolPop(pool);
	pool = objc_autoreleasePoolPush();

	size_t i;

	_objc_rootAutorelease(self);
	[nextPool releaseObjects];

	for (i = 0; i < count; i++)
		[objects[i] release];

	ignoreRelease = NO;
	count = 0;
}

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

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

- (void)dealloc
{
	size_t i;

	if (ignoreRelease)
	[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;
	ignoreRelease = YES;

	/*
	 * 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 (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
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];

	[autoreleasePool addObject: self];
	return [autoreleasePool addObject: self];

	return 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
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		\
SRCS = category.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
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;