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