1 /*
2 * C11 <threads.h> emulation library
3 *
4 * (C) Copyright yohhoy 2012.
5 * Distributed under the Boost Software License, Version 1.0.
6 *
7 * Permission is hereby granted, free of charge, to any person or organization
8 * obtaining a copy of the software and accompanying documentation covered by
9 * this license (the "Software") to use, reproduce, display, distribute,
10 * execute, and transmit the Software, and to prepare [[derivative work]]s of the
11 * Software, and to permit third-parties to whom the Software is furnished to
12 * do so, all subject to the following:
13 *
14 * The copyright notices in the Software and this entire statement, including
15 * the above license grant, this restriction and the following disclaimer,
16 * must be included in all copies of the Software, in whole or in part, and
17 * all derivative works of the Software, unless such copies or derivative
18 * works are solely in the form of machine-executable object code generated by
19 * a source language processor.
20 *
21 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23 * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
24 * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
25 * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
26 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
27 * DEALINGS IN THE SOFTWARE.
28 */
29 #include <stdlib.h>
30 #include <assert.h>
31 #include <limits.h>
32 #include <errno.h>
33 #include <unistd.h>
34 #include <sched.h>
35 #include <stdint.h> /* for intptr_t */
36
37 #include "c11/threads.h"
38
39 /*
40 Configuration macro:
41
42 EMULATED_THREADS_USE_NATIVE_TIMEDLOCK
43 Use pthread_mutex_timedlock() for `mtx_timedlock()'
44 Otherwise use mtx_trylock() + *busy loop* emulation.
45 */
46 #if !defined(__CYGWIN__) && !defined(__APPLE__) && !defined(__NetBSD__)
47 #define EMULATED_THREADS_USE_NATIVE_TIMEDLOCK
48 #endif
49
50 /*---------------------------- types ----------------------------*/
51
52 /*
53 Implementation limits:
54 - Conditionally emulation for "mutex with timeout"
55 (see EMULATED_THREADS_USE_NATIVE_TIMEDLOCK macro)
56 */
57 struct impl_thrd_param {
58 thrd_start_t func;
59 void *arg;
60 };
61
62 static void *
impl_thrd_routine(void * p)63 impl_thrd_routine(void *p)
64 {
65 struct impl_thrd_param pack = *((struct impl_thrd_param *)p);
66 free(p);
67 return (void*)(intptr_t)pack.func(pack.arg);
68 }
69
70
71 /*--------------- 7.25.2 Initialization functions ---------------*/
72 // 7.25.2.1
73 void
call_once(once_flag * flag,void (* func)(void))74 call_once(once_flag *flag, void (*func)(void))
75 {
76 pthread_once(flag, func);
77 }
78
79
80 /*------------- 7.25.3 Condition variable functions -------------*/
81 // 7.25.3.1
82 int
cnd_broadcast(cnd_t * cond)83 cnd_broadcast(cnd_t *cond)
84 {
85 assert(cond != NULL);
86 return (pthread_cond_broadcast(cond) == 0) ? thrd_success : thrd_error;
87 }
88
89 // 7.25.3.2
90 void
cnd_destroy(cnd_t * cond)91 cnd_destroy(cnd_t *cond)
92 {
93 assert(cond);
94 pthread_cond_destroy(cond);
95 }
96
97 // 7.25.3.3
98 int
cnd_init(cnd_t * cond)99 cnd_init(cnd_t *cond)
100 {
101 assert(cond != NULL);
102 return (pthread_cond_init(cond, NULL) == 0) ? thrd_success : thrd_error;
103 }
104
105 // 7.25.3.4
106 int
cnd_signal(cnd_t * cond)107 cnd_signal(cnd_t *cond)
108 {
109 assert(cond != NULL);
110 return (pthread_cond_signal(cond) == 0) ? thrd_success : thrd_error;
111 }
112
113 // 7.25.3.5
114 int
cnd_timedwait(cnd_t * cond,mtx_t * mtx,const struct timespec * abs_time)115 cnd_timedwait(cnd_t *cond, mtx_t *mtx, const struct timespec *abs_time)
116 {
117 int rt;
118
119 assert(mtx != NULL);
120 assert(cond != NULL);
121 assert(abs_time != NULL);
122
123 rt = pthread_cond_timedwait(cond, mtx, abs_time);
124 if (rt == ETIMEDOUT)
125 return thrd_timedout;
126 return (rt == 0) ? thrd_success : thrd_error;
127 }
128
129 // 7.25.3.6
130 int
cnd_wait(cnd_t * cond,mtx_t * mtx)131 cnd_wait(cnd_t *cond, mtx_t *mtx)
132 {
133 assert(mtx != NULL);
134 assert(cond != NULL);
135 return (pthread_cond_wait(cond, mtx) == 0) ? thrd_success : thrd_error;
136 }
137
138
139 /*-------------------- 7.25.4 Mutex functions --------------------*/
140 // 7.25.4.1
141 void
mtx_destroy(mtx_t * mtx)142 mtx_destroy(mtx_t *mtx)
143 {
144 assert(mtx != NULL);
145 pthread_mutex_destroy(mtx);
146 }
147
148 /*
149 * XXX: Workaround when building with -O0 and without pthreads link.
150 *
151 * In such cases constant folding and dead code elimination won't be
152 * available, thus the compiler will always add the pthread_mutexattr*
153 * functions into the binary. As we try to link, we'll fail as the
154 * symbols are unresolved.
155 *
156 * Ideally we'll enable the optimisations locally, yet that does not
157 * seem to work.
158 *
159 * So the alternative workaround is to annotate the symbols as weak.
160 * Thus the linker will be happy and things don't clash when building
161 * with -O1 or greater.
162 */
163 #if defined(HAVE_FUNC_ATTRIBUTE_WEAK) && !defined(__CYGWIN__)
164 __attribute__((weak))
165 int pthread_mutexattr_init(pthread_mutexattr_t *attr);
166
167 __attribute__((weak))
168 int pthread_mutexattr_settype(pthread_mutexattr_t *attr, int type);
169
170 __attribute__((weak))
171 int pthread_mutexattr_destroy(pthread_mutexattr_t *attr);
172 #endif
173
174 // 7.25.4.2
175 int
mtx_init(mtx_t * mtx,int type)176 mtx_init(mtx_t *mtx, int type)
177 {
178 pthread_mutexattr_t attr;
179 assert(mtx != NULL);
180 if (type != mtx_plain && type != mtx_timed && type != mtx_try
181 && type != (mtx_plain|mtx_recursive)
182 && type != (mtx_timed|mtx_recursive)
183 && type != (mtx_try|mtx_recursive))
184 return thrd_error;
185
186 if ((type & mtx_recursive) == 0) {
187 pthread_mutex_init(mtx, NULL);
188 return thrd_success;
189 }
190
191 pthread_mutexattr_init(&attr);
192 pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
193 pthread_mutex_init(mtx, &attr);
194 pthread_mutexattr_destroy(&attr);
195 return thrd_success;
196 }
197
198 // 7.25.4.3
199 int
mtx_lock(mtx_t * mtx)200 mtx_lock(mtx_t *mtx)
201 {
202 assert(mtx != NULL);
203 return (pthread_mutex_lock(mtx) == 0) ? thrd_success : thrd_error;
204 }
205
206 // 7.25.4.4
207 int
mtx_timedlock(mtx_t * mtx,const struct timespec * ts)208 mtx_timedlock(mtx_t *mtx, const struct timespec *ts)
209 {
210 assert(mtx != NULL);
211 assert(ts != NULL);
212
213 {
214 #ifdef EMULATED_THREADS_USE_NATIVE_TIMEDLOCK
215 int rt;
216 rt = pthread_mutex_timedlock(mtx, ts);
217 if (rt == 0)
218 return thrd_success;
219 return (rt == ETIMEDOUT) ? thrd_timedout : thrd_error;
220 #else
221 time_t expire = time(NULL);
222 expire += ts->tv_sec;
223 while (mtx_trylock(mtx) != thrd_success) {
224 time_t now = time(NULL);
225 if (expire < now)
226 return thrd_timedout;
227 // busy loop!
228 thrd_yield();
229 }
230 return thrd_success;
231 #endif
232 }
233 }
234
235 // 7.25.4.5
236 int
mtx_trylock(mtx_t * mtx)237 mtx_trylock(mtx_t *mtx)
238 {
239 assert(mtx != NULL);
240 return (pthread_mutex_trylock(mtx) == 0) ? thrd_success : thrd_busy;
241 }
242
243 // 7.25.4.6
244 int
mtx_unlock(mtx_t * mtx)245 mtx_unlock(mtx_t *mtx)
246 {
247 assert(mtx != NULL);
248 return (pthread_mutex_unlock(mtx) == 0) ? thrd_success : thrd_error;
249 }
250
251
252 /*------------------- 7.25.5 Thread functions -------------------*/
253 // 7.25.5.1
254 int
thrd_create(thrd_t * thr,thrd_start_t func,void * arg)255 thrd_create(thrd_t *thr, thrd_start_t func, void *arg)
256 {
257 struct impl_thrd_param *pack;
258 assert(thr != NULL);
259 pack = (struct impl_thrd_param *)malloc(sizeof(struct impl_thrd_param));
260 if (!pack) return thrd_nomem;
261 pack->func = func;
262 pack->arg = arg;
263 if (pthread_create(thr, NULL, impl_thrd_routine, pack) != 0) {
264 free(pack);
265 return thrd_error;
266 }
267 return thrd_success;
268 }
269
270 // 7.25.5.2
271 thrd_t
thrd_current(void)272 thrd_current(void)
273 {
274 return pthread_self();
275 }
276
277 // 7.25.5.3
278 int
thrd_detach(thrd_t thr)279 thrd_detach(thrd_t thr)
280 {
281 return (pthread_detach(thr) == 0) ? thrd_success : thrd_error;
282 }
283
284 // 7.25.5.4
285 int
thrd_equal(thrd_t thr0,thrd_t thr1)286 thrd_equal(thrd_t thr0, thrd_t thr1)
287 {
288 return pthread_equal(thr0, thr1);
289 }
290
291 // 7.25.5.5
292 _Noreturn
293 void
thrd_exit(int res)294 thrd_exit(int res)
295 {
296 pthread_exit((void*)(intptr_t)res);
297 }
298
299 // 7.25.5.6
300 int
thrd_join(thrd_t thr,int * res)301 thrd_join(thrd_t thr, int *res)
302 {
303 void *code;
304 if (pthread_join(thr, &code) != 0)
305 return thrd_error;
306 if (res)
307 *res = (int)(intptr_t)code;
308 return thrd_success;
309 }
310
311 // 7.25.5.7
312 int
thrd_sleep(const struct timespec * time_point,struct timespec * remaining)313 thrd_sleep(const struct timespec *time_point, struct timespec *remaining)
314 {
315 assert(time_point != NULL);
316 return nanosleep(time_point, remaining);
317 }
318
319 // 7.25.5.8
320 void
thrd_yield(void)321 thrd_yield(void)
322 {
323 sched_yield();
324 }
325
326
327 /*----------- 7.25.6 Thread-specific storage functions -----------*/
328 // 7.25.6.1
329 int
tss_create(tss_t * key,tss_dtor_t dtor)330 tss_create(tss_t *key, tss_dtor_t dtor)
331 {
332 assert(key != NULL);
333 return (pthread_key_create(key, dtor) == 0) ? thrd_success : thrd_error;
334 }
335
336 // 7.25.6.2
337 void
tss_delete(tss_t key)338 tss_delete(tss_t key)
339 {
340 pthread_key_delete(key);
341 }
342
343 // 7.25.6.3
344 void *
tss_get(tss_t key)345 tss_get(tss_t key)
346 {
347 return pthread_getspecific(key);
348 }
349
350 // 7.25.6.4
351 int
tss_set(tss_t key,void * val)352 tss_set(tss_t key, void *val)
353 {
354 return (pthread_setspecific(key, val) == 0) ? thrd_success : thrd_error;
355 }
356