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