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 */
16struct _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 */
24static 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 */
33static 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 */
59void
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 */
76void
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 */
96void *
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
125void
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
134void
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
143void
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
172static 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
207static 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
242void
243_thread_malloc_lock(int i)
244{
245	pthread_mutex_lock(&malloc_mutex[i]);
246}
247
248void
249_thread_malloc_unlock(int i)
250{
251	pthread_mutex_unlock(&malloc_mutex[i]);
252}
253
254static 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 */
272static _atomic_lock_t atexit_lock = _SPINLOCK_UNLOCKED;
273
274void
275_thread_atexit_lock(void)
276{
277	_spinlock(&atexit_lock);
278}
279
280void
281_thread_atexit_unlock(void)
282{
283	_spinunlock(&atexit_lock);
284}
285
286/*
287 * atfork lock
288 */
289static _atomic_lock_t atfork_lock = _SPINLOCK_UNLOCKED;
290
291void
292_thread_atfork_lock(void)
293{
294	_spinlock(&atfork_lock);
295}
296
297void
298_thread_atfork_unlock(void)
299{
300	_spinunlock(&atfork_lock);
301}
302
303/*
304 * arc4random lock
305 */
306static _atomic_lock_t arc4_lock = _SPINLOCK_UNLOCKED;
307
308void
309_thread_arc4_lock(void)
310{
311	_spinlock(&arc4_lock);
312}
313
314void
315_thread_arc4_unlock(void)
316{
317	_spinunlock(&arc4_lock);
318}
319
320pid_t
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