xref: /openbsd-src/lib/libc/thread/rthread_libc.c (revision c7e8ea31cd41a963f06f0a8ba93948b06aa6b4a4)
1 /* $OpenBSD: rthread_libc.c,v 1.1 2017/08/15 06:13:24 guenther Exp $ */
2 
3 /* PUBLIC DOMAIN: No Rights Reserved. Marco S Hyman <marc@snafu.org> */
4 
5 #include <sys/time.h>
6 
7 #include <pthread.h>
8 #include <stdlib.h>
9 #include <string.h>
10 
11 #include "thread_private.h"	/* in libc/include */
12 
13 #include "rthread.h"
14 #include "rthread_cb.h"
15 
16 /*
17  * A thread tag is a pointer to a structure of this type.  An opaque
18  * tag is used to decouple libc from the thread library.
19  */
20 struct _thread_tag {
21 	pthread_mutex_t	m;	/* the tag's mutex */
22 	pthread_key_t	k;	/* a key for private data */
23 };
24 
25 /*
26  * local mutex to protect against tag creation races.
27  */
28 static pthread_mutex_t	_thread_tag_mutex = PTHREAD_MUTEX_INITIALIZER;
29 
30 /*
31  * Initialize a thread tag structure once.   This function is called
32  * if the tag is null.  Allocation and initialization are controlled
33  * by a mutex.   If the tag is not null when the mutex is obtained
34  * the caller lost a race -- some other thread initialized the tag.
35  * This function will never return NULL.
36  */
37 static void
38 _thread_tag_init(void **tag)
39 {
40 	struct _thread_tag *tt;
41 	int result;
42 
43 	result = pthread_mutex_lock(&_thread_tag_mutex);
44 	if (result == 0) {
45 		if (*tag == NULL) {
46 			tt = malloc(sizeof *tt);
47 			if (tt != NULL) {
48 				result = pthread_mutex_init(&tt->m, NULL);
49 				result |= pthread_key_create(&tt->k, free);
50 				*tag = tt;
51 			}
52 		}
53 		result |= pthread_mutex_unlock(&_thread_tag_mutex);
54 	}
55 	if (result != 0)
56 		_rthread_debug(1, "tag init failure");
57 }
58 
59 /*
60  * lock the mutex associated with the given tag
61  */
62 void
63 _thread_tag_lock(void **tag)
64 {
65 	struct _thread_tag *tt;
66 
67 	if (__isthreaded) {
68 		if (*tag == NULL)
69 			_thread_tag_init(tag);
70 		tt = *tag;
71 		if (pthread_mutex_lock(&tt->m) != 0)
72 			_rthread_debug(1, "tag mutex lock failure");
73 	}
74 }
75 
76 /*
77  * unlock the mutex associated with the given tag
78  */
79 void
80 _thread_tag_unlock(void **tag)
81 {
82 	struct _thread_tag *tt;
83 
84 	if (__isthreaded) {
85 		if (*tag == NULL)
86 			_thread_tag_init(tag);
87 		tt = *tag;
88 		if (pthread_mutex_unlock(&tt->m) != 0)
89 			_rthread_debug(1, "tag mutex unlock failure");
90 	}
91 }
92 
93 /*
94  * return the thread specific data for the given tag.   If there
95  * is no data for this thread initialize it from 'storage'.
96  * On any error return 'err'.
97  */
98 void *
99 _thread_tag_storage(void **tag, void *storage, size_t sz, void *err)
100 {
101 	struct _thread_tag *tt;
102 	void *ret;
103 
104 	if (*tag == NULL)
105 		_thread_tag_init(tag);
106 	tt = *tag;
107 
108 	ret = pthread_getspecific(tt->k);
109 	if (ret == NULL) {
110 		ret = malloc(sz);
111 		if (ret == NULL)
112 			ret = err;
113 		else {
114 			if (pthread_setspecific(tt->k, ret) == 0)
115 				memcpy(ret, storage, sz);
116 			else {
117 				free(ret);
118 				ret = err;
119 			}
120 		}
121 	}
122 	return ret;
123 }
124 
125 void
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
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
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 };
178 
179 static pthread_mutex_t malloc_mutex[_MALLOC_MUTEXES] = {
180 	&malloc_lock[0],
181 	&malloc_lock[1],
182 	&malloc_lock[2],
183 	&malloc_lock[3]
184 };
185 
186 void
187 _thread_malloc_lock(int i)
188 {
189 	pthread_mutex_lock(&malloc_mutex[i]);
190 }
191 
192 void
193 _thread_malloc_unlock(int i)
194 {
195 	pthread_mutex_unlock(&malloc_mutex[i]);
196 }
197 
198 void
199 _thread_malloc_reinit(void)
200 {
201 	int i;
202 
203 	for (i = 0; i < _MALLOC_MUTEXES; i++) {
204 		malloc_lock[i].lock = _SPINLOCK_UNLOCKED;
205 #ifndef FUTEX
206 		TAILQ_INIT(&malloc_lock[i].lockers);
207 #endif
208 		malloc_lock[i].owner = NULL;
209 		malloc_lock[i].count = 0;
210 	}
211 }
212 
213 /*
214  * atexit lock
215  */
216 static _atomic_lock_t atexit_lock = _SPINLOCK_UNLOCKED;
217 
218 void
219 _thread_atexit_lock(void)
220 {
221 	_spinlock(&atexit_lock);
222 }
223 
224 void
225 _thread_atexit_unlock(void)
226 {
227 	_spinunlock(&atexit_lock);
228 }
229 
230 /*
231  * atfork lock
232  */
233 static _atomic_lock_t atfork_lock = _SPINLOCK_UNLOCKED;
234 
235 void
236 _thread_atfork_lock(void)
237 {
238 	_spinlock(&atfork_lock);
239 }
240 
241 void
242 _thread_atfork_unlock(void)
243 {
244 	_spinunlock(&atfork_lock);
245 }
246 
247 /*
248  * arc4random lock
249  */
250 static _atomic_lock_t arc4_lock = _SPINLOCK_UNLOCKED;
251 
252 void
253 _thread_arc4_lock(void)
254 {
255 	_spinlock(&arc4_lock);
256 }
257 
258 void
259 _thread_arc4_unlock(void)
260 {
261 	_spinunlock(&arc4_lock);
262 }
263