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 |
|
|
|