ObjFW  Check-in [e7f4f80e23]

Overview
Comment:runtime: Correctly handle AR pool push during pop

Getting a pointer and increasing it until we reach the top pointer does
not work: Releasing an object can temporarily create new autorelease
pools, which can trigger resizing of "objects" using realloc, which can
move it to a different address, which will then lead to continuing to
iterate on a now invalid pointer.

This is now solved by using an index into "objects" instead. Since we're
now indexing for the pop, let's use indexes everywhere, as they're more
readable anyway.

While debugging this, I noticed that the last pool is popped quite
frequently, only for a new pool to be pushed immediately again. This
resulted in a free followed by a malloc every time. Instead, keep the
pool, but let OFThread explicitly say when to free everything.

Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: e7f4f80e2313fb6294ed2e0a210e4fefc5dab9f4c93fa71b04313e52451f1e82
User & Date: js on 2020-01-24 03:03:32
Other Links: manifest | tags
Context
2020-01-25
20:04
tlskey.m: Use hashtable from runtime on AmigaOS check-in: 49aee5736e user: js tags: trunk
2020-01-24
03:03
runtime: Correctly handle AR pool push during pop check-in: e7f4f80e23 user: js tags: trunk
2020-01-19
15:37
OFHTTPClient: Fix type mismatch on Windows check-in: 5256e9acaf user: js tags: trunk
Changes

Modified src/OFThread.h from [ccaa08b8db] to [4c0a8350b3].

69
70
71
72
73
74
75

76

77
78
79
80
81
82
83
	of_thread_t _thread;
	of_thread_attr_t _attr;
	enum of_thread_running {
		OF_THREAD_NOT_RUNNING,
		OF_THREAD_RUNNING,
		OF_THREAD_WAITING_FOR_JOIN
	} _running;

	void *_pool;

# ifdef OF_HAVE_BLOCKS
	of_thread_block_t _Nullable _threadBlock;
# endif
	jmp_buf _exitEnv;
	id _returnValue;
	bool _supportsSockets;
	OFRunLoop *_Nullable _runLoop;







>

>







69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
	of_thread_t _thread;
	of_thread_attr_t _attr;
	enum of_thread_running {
		OF_THREAD_NOT_RUNNING,
		OF_THREAD_RUNNING,
		OF_THREAD_WAITING_FOR_JOIN
	} _running;
# ifndef OF_OBJFW_RUNTIME
	void *_pool;
# endif
# ifdef OF_HAVE_BLOCKS
	of_thread_block_t _Nullable _threadBlock;
# endif
	jmp_buf _exitEnv;
	id _returnValue;
	bool _supportsSockets;
	OFRunLoop *_Nullable _runLoop;

Modified src/OFThread.m from [82882ba976] to [5a3f2982c0].

103
104
105
106
107
108
109

110

111
112
113
114
115
116
117
	OFThread *thread = (OFThread *)object;
	OFString *name;

	if (!of_tlskey_set(threadSelfKey, thread))
		@throw [OFInitializationFailedException
		    exceptionWithClass: thread.class];


	thread->_pool = objc_autoreleasePoolPush();


	name = thread.name;
	if (name != nil)
		of_thread_set_name(
		    [name cStringWithEncoding: [OFLocale encoding]]);
	else
		of_thread_set_name(object_getClassName(thread));







>

>







103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
	OFThread *thread = (OFThread *)object;
	OFString *name;

	if (!of_tlskey_set(threadSelfKey, thread))
		@throw [OFInitializationFailedException
		    exceptionWithClass: thread.class];

#ifndef OF_OBJFW_RUNTIME
	thread->_pool = objc_autoreleasePoolPush();
#endif

	name = thread.name;
	if (name != nil)
		of_thread_set_name(
		    [name cStringWithEncoding: [OFLocale encoding]]);
	else
		of_thread_set_name(object_getClassName(thread));
134
135
136
137
138
139
140



141

142
143
144
145
146
147
148
		else
# endif
			thread->_returnValue = [[thread main] retain];
	}

	[thread handleTermination];




	objc_autoreleasePoolPop(thread->_pool);


#if defined(OF_AMIGAOS) && defined(OF_HAVE_SOCKETS)
	if (thread.supportsSockets)
		of_socket_deinit();
#endif

	thread->_running = OF_THREAD_WAITING_FOR_JOIN;







>
>
>

>







136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
		else
# endif
			thread->_returnValue = [[thread main] retain];
	}

	[thread handleTermination];

#ifdef OF_OBJFW_RUNTIME
	objc_autoreleasePoolPop((void *)(uintptr_t)-1);
#else
	objc_autoreleasePoolPop(thread->_pool);
#endif

#if defined(OF_AMIGAOS) && defined(OF_HAVE_SOCKETS)
	if (thread.supportsSockets)
		of_socket_deinit();
#endif

	thread->_running = OF_THREAD_WAITING_FOR_JOIN;

Modified src/runtime/autorelease.m from [6a15e61b81] to [99568ccb12].

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

#if !defined(OF_HAVE_COMPILER_TLS) && defined(OF_HAVE_THREADS)
# import "tlskey.h"
#endif

#if defined(OF_HAVE_COMPILER_TLS)
static thread_local id *objects = NULL;
static thread_local id *top = NULL;
static thread_local size_t size = 0;
#elif defined(OF_HAVE_THREADS)
static of_tlskey_t objectsKey, topKey, sizeKey;
#else
static id *objects = NULL;
static id *top = NULL;
static size_t size = 0;
#endif

#if !defined(OF_HAVE_COMPILER_TLS) && defined(OF_HAVE_THREADS)
OF_CONSTRUCTOR()
{
	OF_ENSURE(of_tlskey_new(&objectsKey));
	OF_ENSURE(of_tlskey_new(&topKey));
	OF_ENSURE(of_tlskey_new(&sizeKey));
}
#endif

void *
objc_autoreleasePoolPush()
{
#if !defined(OF_HAVE_COMPILER_TLS) && defined(OF_HAVE_THREADS)
	id *top = of_tlskey_get(topKey);
	id *objects = of_tlskey_get(objectsKey);
#endif
	ptrdiff_t offset = top - objects;

	return (void *)offset;
}

void
objc_autoreleasePoolPop(void *offset)
{
#if !defined(OF_HAVE_COMPILER_TLS) && defined(OF_HAVE_THREADS)
	id *top = of_tlskey_get(topKey);
	id *objects = of_tlskey_get(objectsKey);

#endif


	id *pool = objects + (ptrdiff_t)offset;

	id *iter;


	for (iter = pool; iter < top; iter++)

		[*iter release];






	top = pool;


	if (top == objects) {
		free(objects);

		objects = NULL;

		top = NULL;




	}

#if !defined(OF_HAVE_COMPILER_TLS) && defined(OF_HAVE_THREADS)
	OF_ENSURE(of_tlskey_set(topKey, top));
	OF_ENSURE(of_tlskey_set(objectsKey, objects));
#endif
}

id
_objc_rootAutorelease(id object)
{
#if !defined(OF_HAVE_COMPILER_TLS) && defined(OF_HAVE_THREADS)
	id *top = of_tlskey_get(topKey);
	id *objects = of_tlskey_get(objectsKey);

	size_t size = (size_t)(uintptr_t)of_tlskey_get(sizeKey);
#endif


	if (objects == NULL) {
		size = 16 * sizeof(id);

		OF_ENSURE((objects = malloc(size)) != NULL);

		top = objects;

#if !defined(OF_HAVE_COMPILER_TLS) && defined(OF_HAVE_THREADS)
		OF_ENSURE(of_tlskey_set(objectsKey, objects));
		OF_ENSURE(of_tlskey_set(sizeKey, (void *)(uintptr_t)size));
#endif
	}

	if ((uintptr_t)top >= (uintptr_t)objects + size) {
		ptrdiff_t diff = top - objects;

		size *= 2;
		OF_ENSURE((objects = realloc(objects, size)) != NULL);


#if !defined(OF_HAVE_COMPILER_TLS) && defined(OF_HAVE_THREADS)
		OF_ENSURE(of_tlskey_set(objectsKey, objects));
		OF_ENSURE(of_tlskey_set(sizeKey, (void *)(uintptr_t)size));
#endif

		top = objects + diff;
	}

	*top = object;
	top++;

#if !defined(OF_HAVE_COMPILER_TLS) && defined(OF_HAVE_THREADS)
	OF_ENSURE(of_tlskey_set(topKey, top));
#endif

	return object;
}







|
|

|


|
|






|








|
<

<
<
|



|


<

>

>
>
|
>
|
>
|
|
>
|

>
>
>
>
>
|
>

|

<

>
|
>
>
>
>



|
<







<

>
|


>
|
|
|
<
|
<

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



|

|
<
|
<
|
<


|




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

#if !defined(OF_HAVE_COMPILER_TLS) && defined(OF_HAVE_THREADS)
# import "tlskey.h"
#endif

#if defined(OF_HAVE_COMPILER_TLS)
static thread_local id *objects = NULL;
static thread_local uintptr_t count = 0;
static thread_local uintptr_t size = 0;
#elif defined(OF_HAVE_THREADS)
static of_tlskey_t objectsKey, countKey, sizeKey;
#else
static id *objects = NULL;
static uintptr_t count = 0;
static uintptr_t size = 0;
#endif

#if !defined(OF_HAVE_COMPILER_TLS) && defined(OF_HAVE_THREADS)
OF_CONSTRUCTOR()
{
	OF_ENSURE(of_tlskey_new(&objectsKey));
	OF_ENSURE(of_tlskey_new(&countKey));
	OF_ENSURE(of_tlskey_new(&sizeKey));
}
#endif

void *
objc_autoreleasePoolPush()
{
#if !defined(OF_HAVE_COMPILER_TLS) && defined(OF_HAVE_THREADS)
	uintptr_t count = (uintptr_t)of_tlskey_get(countKey);

#endif


	return (void *)count;
}

void
objc_autoreleasePoolPop(void *pool)
{
#if !defined(OF_HAVE_COMPILER_TLS) && defined(OF_HAVE_THREADS)

	id *objects = of_tlskey_get(objectsKey);
	uintptr_t count = (uintptr_t)of_tlskey_get(countKey);
#endif
	uintptr_t idx = (uintptr_t)pool;
	bool freeMem = false;

	if (idx == (uintptr_t)-1) {
		idx++;
		freeMem = true;
	}

	for (uintptr_t i = idx; i < count; i++) {
		[objects[i] release];

#if !defined(OF_HAVE_COMPILER_TLS) && defined(OF_HAVE_THREADS)
		objects = of_tlskey_get(objectsKey);
		count = (uintptr_t)of_tlskey_get(countKey);
#endif
	}

	count = idx;

	if (freeMem) {
		free(objects);

		objects = NULL;
#if defined(OF_HAVE_COMPILER_TLS) || !defined(OF_HAVE_THREADS)
		size = 0;
#else
		OF_ENSURE(of_tlskey_set(objectsKey, objects));
		OF_ENSURE(of_tlskey_set(sizeKey, (void *)0));
#endif
	}

#if !defined(OF_HAVE_COMPILER_TLS) && defined(OF_HAVE_THREADS)
	OF_ENSURE(of_tlskey_set(countKey, (void *)count));

#endif
}

id
_objc_rootAutorelease(id object)
{
#if !defined(OF_HAVE_COMPILER_TLS) && defined(OF_HAVE_THREADS)

	id *objects = of_tlskey_get(objectsKey);
	uintptr_t count = (uintptr_t)of_tlskey_get(countKey);
	uintptr_t size = (uintptr_t)of_tlskey_get(sizeKey);
#endif

	if (count >= size) {
		if (size == 0)
			size = 16;
		else

			size *= 2;












		OF_ENSURE((objects =
		    realloc(objects, size * sizeof(id))) != NULL);

#if !defined(OF_HAVE_COMPILER_TLS) && defined(OF_HAVE_THREADS)
		OF_ENSURE(of_tlskey_set(objectsKey, objects));
		OF_ENSURE(of_tlskey_set(sizeKey, (void *)size));
#endif
	}



	objects[count++] = object;


#if !defined(OF_HAVE_COMPILER_TLS) && defined(OF_HAVE_THREADS)
	OF_ENSURE(of_tlskey_set(countKey, (void *)count));
#endif

	return object;
}