1848b8605Smrg/*
2848b8605Smrg * C11 <threads.h> emulation library
3848b8605Smrg *
4848b8605Smrg * (C) Copyright yohhoy 2012.
5848b8605Smrg * Distributed under the Boost Software License, Version 1.0.
6848b8605Smrg *
7848b8605Smrg * Permission is hereby granted, free of charge, to any person or organization
8848b8605Smrg * obtaining a copy of the software and accompanying documentation covered by
9848b8605Smrg * this license (the "Software") to use, reproduce, display, distribute,
10848b8605Smrg * execute, and transmit the Software, and to prepare [[derivative work]]s of the
11848b8605Smrg * Software, and to permit third-parties to whom the Software is furnished to
12848b8605Smrg * do so, all subject to the following:
13848b8605Smrg *
14848b8605Smrg * The copyright notices in the Software and this entire statement, including
15848b8605Smrg * the above license grant, this restriction and the following disclaimer,
16848b8605Smrg * must be included in all copies of the Software, in whole or in part, and
17848b8605Smrg * all derivative works of the Software, unless such copies or derivative
18848b8605Smrg * works are solely in the form of machine-executable object code generated by
19848b8605Smrg * a source language processor.
20848b8605Smrg *
21848b8605Smrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22848b8605Smrg * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23848b8605Smrg * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
24848b8605Smrg * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
25848b8605Smrg * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
26848b8605Smrg * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
27848b8605Smrg * DEALINGS IN THE SOFTWARE.
28848b8605Smrg */
29848b8605Smrg#include <stdlib.h>
30848b8605Smrg#ifndef assert
31848b8605Smrg#include <assert.h>
32848b8605Smrg#endif
33848b8605Smrg#include <limits.h>
34848b8605Smrg#include <errno.h>
35848b8605Smrg#include <unistd.h>
36848b8605Smrg#include <sched.h>
37848b8605Smrg#include <stdint.h> /* for intptr_t */
38848b8605Smrg
39848b8605Smrg/*
40848b8605SmrgConfiguration macro:
41848b8605Smrg
42848b8605Smrg  EMULATED_THREADS_USE_NATIVE_TIMEDLOCK
43848b8605Smrg    Use pthread_mutex_timedlock() for `mtx_timedlock()'
44848b8605Smrg    Otherwise use mtx_trylock() + *busy loop* emulation.
45848b8605Smrg*/
46848b8605Smrg#if !defined(__CYGWIN__) && !defined(__APPLE__) && !defined(__NetBSD__)
47848b8605Smrg#define EMULATED_THREADS_USE_NATIVE_TIMEDLOCK
48848b8605Smrg#endif
49848b8605Smrg
50848b8605Smrg
51848b8605Smrg#include <pthread.h>
52848b8605Smrg
53848b8605Smrg/*---------------------------- macros ----------------------------*/
54848b8605Smrg#define ONCE_FLAG_INIT PTHREAD_ONCE_INIT
55848b8605Smrg#ifdef INIT_ONCE_STATIC_INIT
56848b8605Smrg#define TSS_DTOR_ITERATIONS PTHREAD_DESTRUCTOR_ITERATIONS
57848b8605Smrg#else
58848b8605Smrg#define TSS_DTOR_ITERATIONS 1  // assume TSS dtor MAY be called at least once.
59848b8605Smrg#endif
60848b8605Smrg
61848b8605Smrg// FIXME: temporary non-standard hack to ease transition
62848b8605Smrg#define _MTX_INITIALIZER_NP PTHREAD_MUTEX_INITIALIZER
63848b8605Smrg
64848b8605Smrg/*---------------------------- types ----------------------------*/
65848b8605Smrgtypedef pthread_cond_t  cnd_t;
66848b8605Smrgtypedef pthread_t       thrd_t;
67848b8605Smrgtypedef pthread_key_t   tss_t;
68848b8605Smrgtypedef pthread_mutex_t mtx_t;
69848b8605Smrgtypedef pthread_once_t  once_flag;
70848b8605Smrg
71848b8605Smrg
72848b8605Smrg/*
73848b8605SmrgImplementation limits:
74848b8605Smrg  - Conditionally emulation for "mutex with timeout"
75848b8605Smrg    (see EMULATED_THREADS_USE_NATIVE_TIMEDLOCK macro)
76848b8605Smrg*/
77848b8605Smrgstruct impl_thrd_param {
78848b8605Smrg    thrd_start_t func;
79848b8605Smrg    void *arg;
80848b8605Smrg};
81848b8605Smrg
82848b8605Smrgstatic inline void *
83848b8605Smrgimpl_thrd_routine(void *p)
84848b8605Smrg{
85848b8605Smrg    struct impl_thrd_param pack = *((struct impl_thrd_param *)p);
86848b8605Smrg    free(p);
87848b8605Smrg    return (void*)(intptr_t)pack.func(pack.arg);
88848b8605Smrg}
89848b8605Smrg
90848b8605Smrg
91848b8605Smrg/*--------------- 7.25.2 Initialization functions ---------------*/
92848b8605Smrg// 7.25.2.1
93848b8605Smrgstatic inline void
94848b8605Smrgcall_once(once_flag *flag, void (*func)(void))
95848b8605Smrg{
96848b8605Smrg    pthread_once(flag, func);
97848b8605Smrg}
98848b8605Smrg
99848b8605Smrg
100848b8605Smrg/*------------- 7.25.3 Condition variable functions -------------*/
101848b8605Smrg// 7.25.3.1
102848b8605Smrgstatic inline int
103848b8605Smrgcnd_broadcast(cnd_t *cond)
104848b8605Smrg{
105b8e80941Smrg    assert(cond != NULL);
106b8e80941Smrg    return (pthread_cond_broadcast(cond) == 0) ? thrd_success : thrd_error;
107848b8605Smrg}
108848b8605Smrg
109848b8605Smrg// 7.25.3.2
110848b8605Smrgstatic inline void
111848b8605Smrgcnd_destroy(cnd_t *cond)
112848b8605Smrg{
113848b8605Smrg    assert(cond);
114848b8605Smrg    pthread_cond_destroy(cond);
115848b8605Smrg}
116848b8605Smrg
117848b8605Smrg// 7.25.3.3
118848b8605Smrgstatic inline int
119848b8605Smrgcnd_init(cnd_t *cond)
120848b8605Smrg{
121b8e80941Smrg    assert(cond != NULL);
122b8e80941Smrg    return (pthread_cond_init(cond, NULL) == 0) ? thrd_success : thrd_error;
123848b8605Smrg}
124848b8605Smrg
125848b8605Smrg// 7.25.3.4
126848b8605Smrgstatic inline int
127848b8605Smrgcnd_signal(cnd_t *cond)
128848b8605Smrg{
129b8e80941Smrg    assert(cond != NULL);
130b8e80941Smrg    return (pthread_cond_signal(cond) == 0) ? thrd_success : thrd_error;
131848b8605Smrg}
132848b8605Smrg
133848b8605Smrg// 7.25.3.5
134848b8605Smrgstatic inline int
135b8e80941Smrgcnd_timedwait(cnd_t *cond, mtx_t *mtx, const struct timespec *abs_time)
136848b8605Smrg{
137848b8605Smrg    int rt;
138b8e80941Smrg
139b8e80941Smrg    assert(mtx != NULL);
140b8e80941Smrg    assert(cond != NULL);
141b8e80941Smrg    assert(abs_time != NULL);
142b8e80941Smrg
143b8e80941Smrg    rt = pthread_cond_timedwait(cond, mtx, abs_time);
144848b8605Smrg    if (rt == ETIMEDOUT)
145848b8605Smrg        return thrd_busy;
146848b8605Smrg    return (rt == 0) ? thrd_success : thrd_error;
147848b8605Smrg}
148848b8605Smrg
149848b8605Smrg// 7.25.3.6
150848b8605Smrgstatic inline int
151848b8605Smrgcnd_wait(cnd_t *cond, mtx_t *mtx)
152848b8605Smrg{
153b8e80941Smrg    assert(mtx != NULL);
154b8e80941Smrg    assert(cond != NULL);
155b8e80941Smrg    return (pthread_cond_wait(cond, mtx) == 0) ? thrd_success : thrd_error;
156848b8605Smrg}
157848b8605Smrg
158848b8605Smrg
159848b8605Smrg/*-------------------- 7.25.4 Mutex functions --------------------*/
160848b8605Smrg// 7.25.4.1
161848b8605Smrgstatic inline void
162848b8605Smrgmtx_destroy(mtx_t *mtx)
163848b8605Smrg{
164b8e80941Smrg    assert(mtx != NULL);
165848b8605Smrg    pthread_mutex_destroy(mtx);
166848b8605Smrg}
167848b8605Smrg
168b8e80941Smrg/*
169b8e80941Smrg * XXX: Workaround when building with -O0 and without pthreads link.
170b8e80941Smrg *
171b8e80941Smrg * In such cases constant folding and dead code elimination won't be
172b8e80941Smrg * available, thus the compiler will always add the pthread_mutexattr*
173b8e80941Smrg * functions into the binary. As we try to link, we'll fail as the
174b8e80941Smrg * symbols are unresolved.
175b8e80941Smrg *
176b8e80941Smrg * Ideally we'll enable the optimisations locally, yet that does not
177b8e80941Smrg * seem to work.
178b8e80941Smrg *
179b8e80941Smrg * So the alternative workaround is to annotate the symbols as weak.
180b8e80941Smrg * Thus the linker will be happy and things don't clash when building
181b8e80941Smrg * with -O1 or greater.
182b8e80941Smrg */
183b8e80941Smrg#if defined(HAVE_FUNC_ATTRIBUTE_WEAK) && !defined(__CYGWIN__)
184b8e80941Smrg__attribute__((weak))
185b8e80941Smrgint pthread_mutexattr_init(pthread_mutexattr_t *attr);
186b8e80941Smrg
187b8e80941Smrg__attribute__((weak))
188b8e80941Smrgint pthread_mutexattr_settype(pthread_mutexattr_t *attr, int type);
189b8e80941Smrg
190b8e80941Smrg__attribute__((weak))
191b8e80941Smrgint pthread_mutexattr_destroy(pthread_mutexattr_t *attr);
192b8e80941Smrg#endif
193b8e80941Smrg
194848b8605Smrg// 7.25.4.2
195848b8605Smrgstatic inline int
196848b8605Smrgmtx_init(mtx_t *mtx, int type)
197848b8605Smrg{
198848b8605Smrg    pthread_mutexattr_t attr;
199b8e80941Smrg    assert(mtx != NULL);
200848b8605Smrg    if (type != mtx_plain && type != mtx_timed && type != mtx_try
201848b8605Smrg      && type != (mtx_plain|mtx_recursive)
202848b8605Smrg      && type != (mtx_timed|mtx_recursive)
203848b8605Smrg      && type != (mtx_try|mtx_recursive))
204848b8605Smrg        return thrd_error;
205b8e80941Smrg
206b8e80941Smrg    if ((type & mtx_recursive) == 0) {
207b8e80941Smrg        pthread_mutex_init(mtx, NULL);
208b8e80941Smrg        return thrd_success;
209848b8605Smrg    }
210b8e80941Smrg
211b8e80941Smrg    pthread_mutexattr_init(&attr);
212b8e80941Smrg    pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
213848b8605Smrg    pthread_mutex_init(mtx, &attr);
214848b8605Smrg    pthread_mutexattr_destroy(&attr);
215848b8605Smrg    return thrd_success;
216848b8605Smrg}
217848b8605Smrg
218848b8605Smrg// 7.25.4.3
219848b8605Smrgstatic inline int
220848b8605Smrgmtx_lock(mtx_t *mtx)
221848b8605Smrg{
222b8e80941Smrg    assert(mtx != NULL);
223b8e80941Smrg    return (pthread_mutex_lock(mtx) == 0) ? thrd_success : thrd_error;
224848b8605Smrg}
225848b8605Smrg
226848b8605Smrgstatic inline int
227848b8605Smrgmtx_trylock(mtx_t *mtx);
228848b8605Smrg
229848b8605Smrgstatic inline void
230848b8605Smrgthrd_yield(void);
231848b8605Smrg
232848b8605Smrg// 7.25.4.4
233848b8605Smrgstatic inline int
234b8e80941Smrgmtx_timedlock(mtx_t *mtx, const struct timespec *ts)
235848b8605Smrg{
236b8e80941Smrg    assert(mtx != NULL);
237b8e80941Smrg    assert(ts != NULL);
238b8e80941Smrg
239848b8605Smrg    {
240848b8605Smrg#ifdef EMULATED_THREADS_USE_NATIVE_TIMEDLOCK
241848b8605Smrg    int rt;
242b8e80941Smrg    rt = pthread_mutex_timedlock(mtx, ts);
243848b8605Smrg    if (rt == 0)
244848b8605Smrg        return thrd_success;
245848b8605Smrg    return (rt == ETIMEDOUT) ? thrd_busy : thrd_error;
246848b8605Smrg#else
247848b8605Smrg    time_t expire = time(NULL);
248b8e80941Smrg    expire += ts->tv_sec;
249848b8605Smrg    while (mtx_trylock(mtx) != thrd_success) {
250848b8605Smrg        time_t now = time(NULL);
251848b8605Smrg        if (expire < now)
252848b8605Smrg            return thrd_busy;
253848b8605Smrg        // busy loop!
254848b8605Smrg        thrd_yield();
255848b8605Smrg    }
256848b8605Smrg    return thrd_success;
257848b8605Smrg#endif
258848b8605Smrg    }
259848b8605Smrg}
260848b8605Smrg
261848b8605Smrg// 7.25.4.5
262848b8605Smrgstatic inline int
263848b8605Smrgmtx_trylock(mtx_t *mtx)
264848b8605Smrg{
265b8e80941Smrg    assert(mtx != NULL);
266848b8605Smrg    return (pthread_mutex_trylock(mtx) == 0) ? thrd_success : thrd_busy;
267848b8605Smrg}
268848b8605Smrg
269848b8605Smrg// 7.25.4.6
270848b8605Smrgstatic inline int
271848b8605Smrgmtx_unlock(mtx_t *mtx)
272848b8605Smrg{
273b8e80941Smrg    assert(mtx != NULL);
274b8e80941Smrg    return (pthread_mutex_unlock(mtx) == 0) ? thrd_success : thrd_error;
275848b8605Smrg}
276848b8605Smrg
277848b8605Smrg
278848b8605Smrg/*------------------- 7.25.5 Thread functions -------------------*/
279848b8605Smrg// 7.25.5.1
280848b8605Smrgstatic inline int
281848b8605Smrgthrd_create(thrd_t *thr, thrd_start_t func, void *arg)
282848b8605Smrg{
283848b8605Smrg    struct impl_thrd_param *pack;
284b8e80941Smrg    assert(thr != NULL);
285848b8605Smrg    pack = (struct impl_thrd_param *)malloc(sizeof(struct impl_thrd_param));
286848b8605Smrg    if (!pack) return thrd_nomem;
287848b8605Smrg    pack->func = func;
288848b8605Smrg    pack->arg = arg;
289848b8605Smrg    if (pthread_create(thr, NULL, impl_thrd_routine, pack) != 0) {
290848b8605Smrg        free(pack);
291848b8605Smrg        return thrd_error;
292848b8605Smrg    }
293848b8605Smrg    return thrd_success;
294848b8605Smrg}
295848b8605Smrg
296848b8605Smrg// 7.25.5.2
297848b8605Smrgstatic inline thrd_t
298848b8605Smrgthrd_current(void)
299848b8605Smrg{
300848b8605Smrg    return pthread_self();
301848b8605Smrg}
302848b8605Smrg
303848b8605Smrg// 7.25.5.3
304848b8605Smrgstatic inline int
305848b8605Smrgthrd_detach(thrd_t thr)
306848b8605Smrg{
307848b8605Smrg    return (pthread_detach(thr) == 0) ? thrd_success : thrd_error;
308848b8605Smrg}
309848b8605Smrg
310848b8605Smrg// 7.25.5.4
311848b8605Smrgstatic inline int
312848b8605Smrgthrd_equal(thrd_t thr0, thrd_t thr1)
313848b8605Smrg{
314848b8605Smrg    return pthread_equal(thr0, thr1);
315848b8605Smrg}
316848b8605Smrg
317848b8605Smrg// 7.25.5.5
318848b8605Smrgstatic inline void
319848b8605Smrgthrd_exit(int res)
320848b8605Smrg{
321848b8605Smrg    pthread_exit((void*)(intptr_t)res);
322848b8605Smrg}
323848b8605Smrg
324848b8605Smrg// 7.25.5.6
325848b8605Smrgstatic inline int
326848b8605Smrgthrd_join(thrd_t thr, int *res)
327848b8605Smrg{
328848b8605Smrg    void *code;
329848b8605Smrg    if (pthread_join(thr, &code) != 0)
330848b8605Smrg        return thrd_error;
331848b8605Smrg    if (res)
332848b8605Smrg        *res = (int)(intptr_t)code;
333848b8605Smrg    return thrd_success;
334848b8605Smrg}
335848b8605Smrg
336848b8605Smrg// 7.25.5.7
337848b8605Smrgstatic inline void
338b8e80941Smrgthrd_sleep(const struct timespec *time_point, struct timespec *remaining)
339848b8605Smrg{
340b8e80941Smrg    assert(time_point != NULL);
341b8e80941Smrg    nanosleep(time_point, remaining);
342848b8605Smrg}
343848b8605Smrg
344848b8605Smrg// 7.25.5.8
345848b8605Smrgstatic inline void
346848b8605Smrgthrd_yield(void)
347848b8605Smrg{
348848b8605Smrg    sched_yield();
349848b8605Smrg}
350848b8605Smrg
351848b8605Smrg
352848b8605Smrg/*----------- 7.25.6 Thread-specific storage functions -----------*/
353848b8605Smrg// 7.25.6.1
354848b8605Smrgstatic inline int
355848b8605Smrgtss_create(tss_t *key, tss_dtor_t dtor)
356848b8605Smrg{
357b8e80941Smrg    assert(key != NULL);
358848b8605Smrg    return (pthread_key_create(key, dtor) == 0) ? thrd_success : thrd_error;
359848b8605Smrg}
360848b8605Smrg
361848b8605Smrg// 7.25.6.2
362848b8605Smrgstatic inline void
363848b8605Smrgtss_delete(tss_t key)
364848b8605Smrg{
365848b8605Smrg    pthread_key_delete(key);
366848b8605Smrg}
367848b8605Smrg
368848b8605Smrg// 7.25.6.3
369848b8605Smrgstatic inline void *
370848b8605Smrgtss_get(tss_t key)
371848b8605Smrg{
372848b8605Smrg    return pthread_getspecific(key);
373848b8605Smrg}
374848b8605Smrg
375848b8605Smrg// 7.25.6.4
376848b8605Smrgstatic inline int
377848b8605Smrgtss_set(tss_t key, void *val)
378848b8605Smrg{
379848b8605Smrg    return (pthread_setspecific(key, val) == 0) ? thrd_success : thrd_error;
380848b8605Smrg}
381848b8605Smrg
382848b8605Smrg
383848b8605Smrg/*-------------------- 7.25.7 Time functions --------------------*/
384848b8605Smrg// 7.25.6.1
385b8e80941Smrg#ifndef HAVE_TIMESPEC_GET
386848b8605Smrgstatic inline int
387b8e80941Smrgtimespec_get(struct timespec *ts, int base)
388848b8605Smrg{
389b8e80941Smrg    if (!ts) return 0;
390848b8605Smrg    if (base == TIME_UTC) {
391b8e80941Smrg        clock_gettime(CLOCK_REALTIME, ts);
392848b8605Smrg        return base;
393848b8605Smrg    }
394848b8605Smrg    return 0;
395848b8605Smrg}
396b8e80941Smrg#endif
397