xref: /openbsd/lib/libc/thread/rthread_libc.c (revision a7244e9f)
1 /* $OpenBSD: rthread_libc.c,v 1.4 2021/01/06 19:54:17 otto Exp $ */
2 
3 /* PUBLIC DOMAIN: No Rights Reserved. Marco S Hyman <marc@snafu.org> */
4 
5 #include <pthread.h>
6 #include <stdlib.h>
7 #include <string.h>
8 
9 #include "rthread.h"
10 #include "rthread_cb.h"
11 
12 /*
13  * A thread tag is a pointer to a structure of this type.  An opaque
14  * tag is used to decouple libc from the thread library.
15  */
16 struct _thread_tag {
17 	pthread_mutex_t	m;	/* the tag's mutex */
18 	pthread_key_t	k;	/* a key for private data */
19 };
20 
21 /*
22  * local mutex to protect against tag creation races.
23  */
24 static pthread_mutex_t	_thread_tag_mutex = PTHREAD_MUTEX_INITIALIZER;
25 
26 /*
27  * Initialize a thread tag structure once.   This function is called
28  * if the tag is null.  Allocation and initialization are controlled
29  * by a mutex.   If the tag is not null when the mutex is obtained
30  * the caller lost a race -- some other thread initialized the tag.
31  * This function will never return NULL.
32  */
33 static void
_thread_tag_init(void ** tag,void (* dt)(void *))34 _thread_tag_init(void **tag, void (*dt)(void *))
35 {
36 	struct _thread_tag *tt;
37 	int result;
38 
39 	result = pthread_mutex_lock(&_thread_tag_mutex);
40 	if (result == 0) {
41 		if (*tag == NULL) {
42 			tt = malloc(sizeof *tt);
43 			if (tt != NULL) {
44 				result = pthread_mutex_init(&tt->m, NULL);
45 				result |= pthread_key_create(&tt->k, dt ? dt :
46 				    free);
47 				*tag = tt;
48 			}
49 		}
50 		result |= pthread_mutex_unlock(&_thread_tag_mutex);
51 	}
52 	if (result != 0)
53 		_rthread_debug(1, "tag init failure");
54 }
55 
56 /*
57  * lock the mutex associated with the given tag
58  */
59 void
_thread_tag_lock(void ** tag)60 _thread_tag_lock(void **tag)
61 {
62 	struct _thread_tag *tt;
63 
64 	if (__isthreaded) {
65 		if (*tag == NULL)
66 			_thread_tag_init(tag, NULL);
67 		tt = *tag;
68 		if (pthread_mutex_lock(&tt->m) != 0)
69 			_rthread_debug(1, "tag mutex lock failure");
70 	}
71 }
72 
73 /*
74  * unlock the mutex associated with the given tag
75  */
76 void
_thread_tag_unlock(void ** tag)77 _thread_tag_unlock(void **tag)
78 {
79 	struct _thread_tag *tt;
80 
81 	if (__isthreaded) {
82 		if (*tag == NULL)
83 			_thread_tag_init(tag, NULL);
84 		tt = *tag;
85 		if (pthread_mutex_unlock(&tt->m) != 0)
86 			_rthread_debug(1, "tag mutex unlock failure");
87 	}
88 }
89 
90 /*
91  * return the thread specific data for the given tag.   If there
92  * is no data for this thread allocate and initialize it from 'storage'
93  * or clear it for non-main threads.
94  * On any error return 'err'.
95  */
96 void *
_thread_tag_storage(void ** tag,void * storage,size_t sz,void (* dt)(void *),void * err)97 _thread_tag_storage(void **tag, void *storage, size_t sz, void (*dt)(void *),
98     void *err)
99 {
100 	struct _thread_tag *tt;
101 	void *ret;
102 
103 	if (*tag == NULL)
104 		_thread_tag_init(tag, dt);
105 	tt = *tag;
106 
107 	ret = pthread_getspecific(tt->k);
108 	if (ret == NULL) {
109 		ret = calloc(1, sz);
110 		if (ret == NULL)
111 			ret = err;
112 		else {
113 			if (pthread_setspecific(tt->k, ret) == 0) {
114 				if (pthread_self() == &_initial_thread)
115 					memcpy(ret, storage, sz);
116 			} else {
117 				free(ret);
118 				ret = err;
119 			}
120 		}
121 	}
122 	return ret;
123 }
124 
125 void
_thread_mutex_lock(void ** mutex)126 _thread_mutex_lock(void **mutex)
127 {
128 	pthread_mutex_t	*pmutex = (pthread_mutex_t *)mutex;
129 
130 	if (pthread_mutex_lock(pmutex) != 0)
131 		_rthread_debug(1, "mutex lock failure");
132 }
133 
134 void
_thread_mutex_unlock(void ** mutex)135 _thread_mutex_unlock(void **mutex)
136 {
137 	pthread_mutex_t	*pmutex = (pthread_mutex_t *)mutex;
138 
139 	if (pthread_mutex_unlock(pmutex) != 0)
140 		_rthread_debug(1, "mutex unlock failure");
141 }
142 
143 void
_thread_mutex_destroy(void ** mutex)144 _thread_mutex_destroy(void **mutex)
145 {
146 	pthread_mutex_t	*pmutex = (pthread_mutex_t *)mutex;
147 
148 	if (pthread_mutex_destroy(pmutex) != 0)
149 		_rthread_debug(1, "mutex destroy failure");
150 }
151 
152 /*
153  * the malloc lock
154  */
155 #ifndef FUTEX
156 #define MALLOC_LOCK_INITIALIZER(n) { \
157 	_SPINLOCK_UNLOCKED,	\
158 	TAILQ_HEAD_INITIALIZER(malloc_lock[n].lockers), \
159 	PTHREAD_MUTEX_DEFAULT,	\
160 	NULL,			\
161 	0,			\
162 	-1 }
163 #else
164 #define MALLOC_LOCK_INITIALIZER(n) { \
165 	_SPINLOCK_UNLOCKED,	\
166 	PTHREAD_MUTEX_DEFAULT,	\
167 	NULL,			\
168 	0,			\
169 	-1 }
170 #endif
171 
172 static struct pthread_mutex malloc_lock[_MALLOC_MUTEXES] = {
173 	MALLOC_LOCK_INITIALIZER(0),
174 	MALLOC_LOCK_INITIALIZER(1),
175 	MALLOC_LOCK_INITIALIZER(2),
176 	MALLOC_LOCK_INITIALIZER(3),
177 	MALLOC_LOCK_INITIALIZER(4),
178 	MALLOC_LOCK_INITIALIZER(5),
179 	MALLOC_LOCK_INITIALIZER(6),
180 	MALLOC_LOCK_INITIALIZER(7),
181 	MALLOC_LOCK_INITIALIZER(8),
182 	MALLOC_LOCK_INITIALIZER(9),
183 	MALLOC_LOCK_INITIALIZER(10),
184 	MALLOC_LOCK_INITIALIZER(11),
185 	MALLOC_LOCK_INITIALIZER(12),
186 	MALLOC_LOCK_INITIALIZER(13),
187 	MALLOC_LOCK_INITIALIZER(14),
188 	MALLOC_LOCK_INITIALIZER(15),
189 	MALLOC_LOCK_INITIALIZER(16),
190 	MALLOC_LOCK_INITIALIZER(17),
191 	MALLOC_LOCK_INITIALIZER(18),
192 	MALLOC_LOCK_INITIALIZER(19),
193 	MALLOC_LOCK_INITIALIZER(20),
194 	MALLOC_LOCK_INITIALIZER(21),
195 	MALLOC_LOCK_INITIALIZER(22),
196 	MALLOC_LOCK_INITIALIZER(23),
197 	MALLOC_LOCK_INITIALIZER(24),
198 	MALLOC_LOCK_INITIALIZER(25),
199 	MALLOC_LOCK_INITIALIZER(26),
200 	MALLOC_LOCK_INITIALIZER(27),
201 	MALLOC_LOCK_INITIALIZER(28),
202 	MALLOC_LOCK_INITIALIZER(29),
203 	MALLOC_LOCK_INITIALIZER(30),
204 	MALLOC_LOCK_INITIALIZER(31)
205 };
206 
207 static pthread_mutex_t malloc_mutex[_MALLOC_MUTEXES] = {
208 	&malloc_lock[0],
209 	&malloc_lock[1],
210 	&malloc_lock[2],
211 	&malloc_lock[3],
212 	&malloc_lock[4],
213 	&malloc_lock[5],
214 	&malloc_lock[6],
215 	&malloc_lock[7],
216 	&malloc_lock[8],
217 	&malloc_lock[9],
218 	&malloc_lock[10],
219 	&malloc_lock[11],
220 	&malloc_lock[12],
221 	&malloc_lock[13],
222 	&malloc_lock[14],
223 	&malloc_lock[15],
224 	&malloc_lock[16],
225 	&malloc_lock[17],
226 	&malloc_lock[18],
227 	&malloc_lock[19],
228 	&malloc_lock[20],
229 	&malloc_lock[21],
230 	&malloc_lock[22],
231 	&malloc_lock[23],
232 	&malloc_lock[24],
233 	&malloc_lock[25],
234 	&malloc_lock[26],
235 	&malloc_lock[27],
236 	&malloc_lock[28],
237 	&malloc_lock[29],
238 	&malloc_lock[30],
239 	&malloc_lock[31]
240 };
241 
242 void
_thread_malloc_lock(int i)243 _thread_malloc_lock(int i)
244 {
245 	pthread_mutex_lock(&malloc_mutex[i]);
246 }
247 
248 void
_thread_malloc_unlock(int i)249 _thread_malloc_unlock(int i)
250 {
251 	pthread_mutex_unlock(&malloc_mutex[i]);
252 }
253 
254 static void
_thread_malloc_reinit(void)255 _thread_malloc_reinit(void)
256 {
257 	int i;
258 
259 	for (i = 0; i < _MALLOC_MUTEXES; i++) {
260 		malloc_lock[i].lock = _SPINLOCK_UNLOCKED;
261 #ifndef FUTEX
262 		TAILQ_INIT(&malloc_lock[i].lockers);
263 #endif
264 		malloc_lock[i].owner = NULL;
265 		malloc_lock[i].count = 0;
266 	}
267 }
268 
269 /*
270  * atexit lock
271  */
272 static _atomic_lock_t atexit_lock = _SPINLOCK_UNLOCKED;
273 
274 void
_thread_atexit_lock(void)275 _thread_atexit_lock(void)
276 {
277 	_spinlock(&atexit_lock);
278 }
279 
280 void
_thread_atexit_unlock(void)281 _thread_atexit_unlock(void)
282 {
283 	_spinunlock(&atexit_lock);
284 }
285 
286 /*
287  * atfork lock
288  */
289 static _atomic_lock_t atfork_lock = _SPINLOCK_UNLOCKED;
290 
291 void
_thread_atfork_lock(void)292 _thread_atfork_lock(void)
293 {
294 	_spinlock(&atfork_lock);
295 }
296 
297 void
_thread_atfork_unlock(void)298 _thread_atfork_unlock(void)
299 {
300 	_spinunlock(&atfork_lock);
301 }
302 
303 /*
304  * arc4random lock
305  */
306 static _atomic_lock_t arc4_lock = _SPINLOCK_UNLOCKED;
307 
308 void
_thread_arc4_lock(void)309 _thread_arc4_lock(void)
310 {
311 	_spinlock(&arc4_lock);
312 }
313 
314 void
_thread_arc4_unlock(void)315 _thread_arc4_unlock(void)
316 {
317 	_spinunlock(&arc4_lock);
318 }
319 
320 pid_t
_thread_dofork(pid_t (* sys_fork)(void))321 _thread_dofork(pid_t (*sys_fork)(void))
322 {
323 	int i;
324 	pid_t newid;
325 
326 	_thread_atexit_lock();
327 	for (i = 0; i < _MALLOC_MUTEXES; i++)
328 		_thread_malloc_lock(i);
329 	_thread_arc4_lock();
330 
331 	newid = sys_fork();
332 
333 	_thread_arc4_unlock();
334 	if (newid == 0)
335 		_thread_malloc_reinit();
336 	else
337 		for (i = 0; i < _MALLOC_MUTEXES; i++)
338 			_thread_malloc_unlock(i);
339 	_thread_atexit_unlock();
340 
341 	return newid;
342 }
343 
344