xref: /openbsd-src/lib/libc/thread/rthread_libc.c (revision ae3cb403620ab940fbaabb3055fac045a63d56b7)
1 /* $OpenBSD: rthread_libc.c,v 1.2 2017/09/05 02:40:54 guenther 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
34 _thread_tag_init(void **tag)
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, free);
46 				*tag = tt;
47 			}
48 		}
49 		result |= pthread_mutex_unlock(&_thread_tag_mutex);
50 	}
51 	if (result != 0)
52 		_rthread_debug(1, "tag init failure");
53 }
54 
55 /*
56  * lock the mutex associated with the given tag
57  */
58 void
59 _thread_tag_lock(void **tag)
60 {
61 	struct _thread_tag *tt;
62 
63 	if (__isthreaded) {
64 		if (*tag == NULL)
65 			_thread_tag_init(tag);
66 		tt = *tag;
67 		if (pthread_mutex_lock(&tt->m) != 0)
68 			_rthread_debug(1, "tag mutex lock failure");
69 	}
70 }
71 
72 /*
73  * unlock the mutex associated with the given tag
74  */
75 void
76 _thread_tag_unlock(void **tag)
77 {
78 	struct _thread_tag *tt;
79 
80 	if (__isthreaded) {
81 		if (*tag == NULL)
82 			_thread_tag_init(tag);
83 		tt = *tag;
84 		if (pthread_mutex_unlock(&tt->m) != 0)
85 			_rthread_debug(1, "tag mutex unlock failure");
86 	}
87 }
88 
89 /*
90  * return the thread specific data for the given tag.   If there
91  * is no data for this thread initialize it from 'storage'.
92  * On any error return 'err'.
93  */
94 void *
95 _thread_tag_storage(void **tag, void *storage, size_t sz, void *err)
96 {
97 	struct _thread_tag *tt;
98 	void *ret;
99 
100 	if (*tag == NULL)
101 		_thread_tag_init(tag);
102 	tt = *tag;
103 
104 	ret = pthread_getspecific(tt->k);
105 	if (ret == NULL) {
106 		ret = malloc(sz);
107 		if (ret == NULL)
108 			ret = err;
109 		else {
110 			if (pthread_setspecific(tt->k, ret) == 0)
111 				memcpy(ret, storage, sz);
112 			else {
113 				free(ret);
114 				ret = err;
115 			}
116 		}
117 	}
118 	return ret;
119 }
120 
121 void
122 _thread_mutex_lock(void **mutex)
123 {
124 	pthread_mutex_t	*pmutex = (pthread_mutex_t *)mutex;
125 
126 	if (pthread_mutex_lock(pmutex) != 0)
127 		_rthread_debug(1, "mutex lock failure");
128 }
129 
130 void
131 _thread_mutex_unlock(void **mutex)
132 {
133 	pthread_mutex_t	*pmutex = (pthread_mutex_t *)mutex;
134 
135 	if (pthread_mutex_unlock(pmutex) != 0)
136 		_rthread_debug(1, "mutex unlock failure");
137 }
138 
139 void
140 _thread_mutex_destroy(void **mutex)
141 {
142 	pthread_mutex_t	*pmutex = (pthread_mutex_t *)mutex;
143 
144 	if (pthread_mutex_destroy(pmutex) != 0)
145 		_rthread_debug(1, "mutex destroy failure");
146 }
147 
148 /*
149  * the malloc lock
150  */
151 #ifndef FUTEX
152 #define MALLOC_LOCK_INITIALIZER(n) { \
153 	_SPINLOCK_UNLOCKED,	\
154 	TAILQ_HEAD_INITIALIZER(malloc_lock[n].lockers), \
155 	PTHREAD_MUTEX_DEFAULT,	\
156 	NULL,			\
157 	0,			\
158 	-1 }
159 #else
160 #define MALLOC_LOCK_INITIALIZER(n) { \
161 	_SPINLOCK_UNLOCKED,	\
162 	PTHREAD_MUTEX_DEFAULT,	\
163 	NULL,			\
164 	0,			\
165 	-1 }
166 #endif
167 
168 static struct pthread_mutex malloc_lock[_MALLOC_MUTEXES] = {
169 	MALLOC_LOCK_INITIALIZER(0),
170 	MALLOC_LOCK_INITIALIZER(1),
171 	MALLOC_LOCK_INITIALIZER(2),
172 	MALLOC_LOCK_INITIALIZER(3)
173 };
174 
175 static pthread_mutex_t malloc_mutex[_MALLOC_MUTEXES] = {
176 	&malloc_lock[0],
177 	&malloc_lock[1],
178 	&malloc_lock[2],
179 	&malloc_lock[3]
180 };
181 
182 void
183 _thread_malloc_lock(int i)
184 {
185 	pthread_mutex_lock(&malloc_mutex[i]);
186 }
187 
188 void
189 _thread_malloc_unlock(int i)
190 {
191 	pthread_mutex_unlock(&malloc_mutex[i]);
192 }
193 
194 static void
195 _thread_malloc_reinit(void)
196 {
197 	int i;
198 
199 	for (i = 0; i < _MALLOC_MUTEXES; i++) {
200 		malloc_lock[i].lock = _SPINLOCK_UNLOCKED;
201 #ifndef FUTEX
202 		TAILQ_INIT(&malloc_lock[i].lockers);
203 #endif
204 		malloc_lock[i].owner = NULL;
205 		malloc_lock[i].count = 0;
206 	}
207 }
208 
209 /*
210  * atexit lock
211  */
212 static _atomic_lock_t atexit_lock = _SPINLOCK_UNLOCKED;
213 
214 void
215 _thread_atexit_lock(void)
216 {
217 	_spinlock(&atexit_lock);
218 }
219 
220 void
221 _thread_atexit_unlock(void)
222 {
223 	_spinunlock(&atexit_lock);
224 }
225 
226 /*
227  * atfork lock
228  */
229 static _atomic_lock_t atfork_lock = _SPINLOCK_UNLOCKED;
230 
231 void
232 _thread_atfork_lock(void)
233 {
234 	_spinlock(&atfork_lock);
235 }
236 
237 void
238 _thread_atfork_unlock(void)
239 {
240 	_spinunlock(&atfork_lock);
241 }
242 
243 /*
244  * arc4random lock
245  */
246 static _atomic_lock_t arc4_lock = _SPINLOCK_UNLOCKED;
247 
248 void
249 _thread_arc4_lock(void)
250 {
251 	_spinlock(&arc4_lock);
252 }
253 
254 void
255 _thread_arc4_unlock(void)
256 {
257 	_spinunlock(&arc4_lock);
258 }
259 
260 pid_t
261 _thread_dofork(pid_t (*sys_fork)(void))
262 {
263 	int i;
264 	pid_t newid;
265 
266 	_thread_atexit_lock();
267 	for (i = 0; i < _MALLOC_MUTEXES; i++)
268 		_thread_malloc_lock(i);
269 	_thread_arc4_lock();
270 
271 	newid = sys_fork();
272 
273 	_thread_arc4_unlock();
274 	if (newid == 0)
275 		_thread_malloc_reinit();
276 	else
277 		for (i = 0; i < _MALLOC_MUTEXES; i++)
278 			_thread_malloc_unlock(i);
279 	_thread_atexit_unlock();
280 
281 	return newid;
282 }
283 
284