c11: Import threads.h emulation library.
[mesa.git] / include / c11 / threads_posix.h
1 /*
2 * C11 <threads.h> emulation library
3 *
4 * (C) Copyright yohhoy 2012.
5 * Distributed under the Boost Software License, Version 1.0.
6 * (See copy at http://www.boost.org/LICENSE_1_0.txt)
7 */
8 #include <stdlib.h>
9 #include <assert.h>
10 #include <limits.h>
11 #include <errno.h>
12 #include <unistd.h>
13 #include <sched.h>
14 #include <stdint.h> /* for intptr_t */
15
16 /*
17 Configuration macro:
18
19 EMULATED_THREADS_USE_NATIVE_TIMEDLOCK
20 Use pthread_mutex_timedlock() for `mtx_timedlock()'
21 Otherwise use mtx_trylock() + *busy loop* emulation.
22 */
23 #if !defined(__CYGWIN__)
24 #define EMULATED_THREADS_USE_NATIVE_TIMEDLOCK
25 #endif
26
27
28 #include <pthread.h>
29
30 /*---------------------------- macros ----------------------------*/
31 #define ONCE_FLAG_INIT PTHREAD_ONCE_INIT
32 #ifdef INIT_ONCE_STATIC_INIT
33 #define TSS_DTOR_ITERATIONS PTHREAD_DESTRUCTOR_ITERATIONS
34 #else
35 #define TSS_DTOR_ITERATIONS 1 // assume TSS dtor MAY be called at least once.
36 #endif
37
38 // FIXME: temporary non-standard hack to ease transition
39 #define _MTX_INITIALIZER_NP PTHREAD_MUTEX_INITIALIZER
40
41 /*---------------------------- types ----------------------------*/
42 typedef pthread_cond_t cnd_t;
43 typedef pthread_t thrd_t;
44 typedef pthread_key_t tss_t;
45 typedef pthread_mutex_t mtx_t;
46 typedef pthread_once_t once_flag;
47
48
49 /*
50 Implementation limits:
51 - Conditionally emulation for "mutex with timeout"
52 (see EMULATED_THREADS_USE_NATIVE_TIMEDLOCK macro)
53 */
54 struct impl_thrd_param {
55 thrd_start_t func;
56 void *arg;
57 };
58
59 static inline void *
60 impl_thrd_routine(void *p)
61 {
62 struct impl_thrd_param pack = *((struct impl_thrd_param *)p);
63 free(p);
64 return (void*)(intptr_t)pack.func(pack.arg);
65 }
66
67
68 /*--------------- 7.25.2 Initialization functions ---------------*/
69 // 7.25.2.1
70 static inline void
71 call_once(once_flag *flag, void (*func)(void))
72 {
73 pthread_once(flag, func);
74 }
75
76
77 /*------------- 7.25.3 Condition variable functions -------------*/
78 // 7.25.3.1
79 static inline int
80 cnd_broadcast(cnd_t *cond)
81 {
82 if (!cond) return thrd_error;
83 pthread_cond_broadcast(cond);
84 return thrd_success;
85 }
86
87 // 7.25.3.2
88 static inline void
89 cnd_destroy(cnd_t *cond)
90 {
91 assert(cond);
92 pthread_cond_destroy(cond);
93 }
94
95 // 7.25.3.3
96 static inline int
97 cnd_init(cnd_t *cond)
98 {
99 if (!cond) return thrd_error;
100 pthread_cond_init(cond, NULL);
101 return thrd_success;
102 }
103
104 // 7.25.3.4
105 static inline int
106 cnd_signal(cnd_t *cond)
107 {
108 if (!cond) return thrd_error;
109 pthread_cond_signal(cond);
110 return thrd_success;
111 }
112
113 // 7.25.3.5
114 static inline int
115 cnd_timedwait(cnd_t *cond, mtx_t *mtx, const xtime *xt)
116 {
117 struct timespec abs_time;
118 int rt;
119 if (!cond || !mtx || !xt) return thrd_error;
120 rt = pthread_cond_timedwait(cond, mtx, &abs_time);
121 if (rt == ETIMEDOUT)
122 return thrd_busy;
123 return (rt == 0) ? thrd_success : thrd_error;
124 }
125
126 // 7.25.3.6
127 static inline int
128 cnd_wait(cnd_t *cond, mtx_t *mtx)
129 {
130 if (!cond || !mtx) return thrd_error;
131 pthread_cond_wait(cond, mtx);
132 return thrd_success;
133 }
134
135
136 /*-------------------- 7.25.4 Mutex functions --------------------*/
137 // 7.25.4.1
138 static inline void
139 mtx_destroy(mtx_t *mtx)
140 {
141 assert(mtx);
142 pthread_mutex_destroy(mtx);
143 }
144
145 // 7.25.4.2
146 static inline int
147 mtx_init(mtx_t *mtx, int type)
148 {
149 pthread_mutexattr_t attr;
150 if (!mtx) return thrd_error;
151 if (type != mtx_plain && type != mtx_timed && type != mtx_try
152 && type != (mtx_plain|mtx_recursive)
153 && type != (mtx_timed|mtx_recursive)
154 && type != (mtx_try|mtx_recursive))
155 return thrd_error;
156 pthread_mutexattr_init(&attr);
157 if ((type & mtx_recursive) != 0) {
158 #if defined(__linux__) || defined(__linux)
159 pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE_NP);
160 #else
161 pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
162 #endif
163 }
164 pthread_mutex_init(mtx, &attr);
165 pthread_mutexattr_destroy(&attr);
166 return thrd_success;
167 }
168
169 // 7.25.4.3
170 static inline int
171 mtx_lock(mtx_t *mtx)
172 {
173 if (!mtx) return thrd_error;
174 pthread_mutex_lock(mtx);
175 return thrd_success;
176 }
177
178 // 7.25.4.4
179 static inline int
180 mtx_timedlock(mtx_t *mtx, const xtime *xt)
181 {
182 if (!mtx || !xt) return thrd_error;
183 {
184 #ifdef EMULATED_THREADS_USE_NATIVE_TIMEDLOCK
185 struct timespec ts;
186 int rt;
187 ts.tv_sec = xt->sec;
188 ts.tv_nsec = xt->nsec;
189 rt = pthread_mutex_timedlock(mtx, &ts);
190 if (rt == 0)
191 return thrd_success;
192 return (rt == ETIMEDOUT) ? thrd_busy : thrd_error;
193 #else
194 time_t expire = time(NULL);
195 expire += xt->sec;
196 while (mtx_trylock(mtx) != thrd_success) {
197 time_t now = time(NULL);
198 if (expire < now)
199 return thrd_busy;
200 // busy loop!
201 thrd_yield();
202 }
203 return thrd_success;
204 #endif
205 }
206 }
207
208 // 7.25.4.5
209 static inline int
210 mtx_trylock(mtx_t *mtx)
211 {
212 if (!mtx) return thrd_error;
213 return (pthread_mutex_trylock(mtx) == 0) ? thrd_success : thrd_busy;
214 }
215
216 // 7.25.4.6
217 static inline int
218 mtx_unlock(mtx_t *mtx)
219 {
220 if (!mtx) return thrd_error;
221 pthread_mutex_unlock(mtx);
222 return thrd_success;
223 }
224
225
226 /*------------------- 7.25.5 Thread functions -------------------*/
227 // 7.25.5.1
228 static inline int
229 thrd_create(thrd_t *thr, thrd_start_t func, void *arg)
230 {
231 struct impl_thrd_param *pack;
232 if (!thr) return thrd_error;
233 pack = (struct impl_thrd_param *)malloc(sizeof(struct impl_thrd_param));
234 if (!pack) return thrd_nomem;
235 pack->func = func;
236 pack->arg = arg;
237 if (pthread_create(thr, NULL, impl_thrd_routine, pack) != 0) {
238 free(pack);
239 return thrd_error;
240 }
241 return thrd_success;
242 }
243
244 // 7.25.5.2
245 static inline thrd_t
246 thrd_current(void)
247 {
248 return pthread_self();
249 }
250
251 // 7.25.5.3
252 static inline int
253 thrd_detach(thrd_t thr)
254 {
255 return (pthread_detach(thr) == 0) ? thrd_success : thrd_error;
256 }
257
258 // 7.25.5.4
259 static inline int
260 thrd_equal(thrd_t thr0, thrd_t thr1)
261 {
262 return pthread_equal(thr0, thr1);
263 }
264
265 // 7.25.5.5
266 static inline void
267 thrd_exit(int res)
268 {
269 pthread_exit((void*)(intptr_t)res);
270 }
271
272 // 7.25.5.6
273 static inline int
274 thrd_join(thrd_t thr, int *res)
275 {
276 void *code;
277 if (pthread_join(thr, &code) != 0)
278 return thrd_error;
279 if (res)
280 *res = (int)(intptr_t)code;
281 return thrd_success;
282 }
283
284 // 7.25.5.7
285 static inline void
286 thrd_sleep(const xtime *xt)
287 {
288 struct timespec req;
289 assert(xt);
290 req.tv_sec = xt->sec;
291 req.tv_nsec = xt->nsec;
292 nanosleep(&req, NULL);
293 }
294
295 // 7.25.5.8
296 static inline void
297 thrd_yield(void)
298 {
299 sched_yield();
300 }
301
302
303 /*----------- 7.25.6 Thread-specific storage functions -----------*/
304 // 7.25.6.1
305 static inline int
306 tss_create(tss_t *key, tss_dtor_t dtor)
307 {
308 if (!key) return thrd_error;
309 return (pthread_key_create(key, dtor) == 0) ? thrd_success : thrd_error;
310 }
311
312 // 7.25.6.2
313 static inline void
314 tss_delete(tss_t key)
315 {
316 pthread_key_delete(key);
317 }
318
319 // 7.25.6.3
320 static inline void *
321 tss_get(tss_t key)
322 {
323 return pthread_getspecific(key);
324 }
325
326 // 7.25.6.4
327 static inline int
328 tss_set(tss_t key, void *val)
329 {
330 return (pthread_setspecific(key, val) == 0) ? thrd_success : thrd_error;
331 }
332
333
334 /*-------------------- 7.25.7 Time functions --------------------*/
335 // 7.25.6.1
336 static inline int
337 xtime_get(xtime *xt, int base)
338 {
339 if (!xt) return 0;
340 if (base == TIME_UTC) {
341 xt->sec = time(NULL);
342 xt->nsec = 0;
343 return base;
344 }
345 return 0;
346 }