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#ifndef assert 31#include <assert.h> 32#endif 33#include <limits.h> 34#include <errno.h> 35#include <unistd.h> 36#include <sched.h> 37#include <stdint.h> /* for intptr_t */ 38 39/* 40Configuration 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 51#include <pthread.h> 52 53/*---------------------------- macros ----------------------------*/ 54#define ONCE_FLAG_INIT PTHREAD_ONCE_INIT 55#ifdef INIT_ONCE_STATIC_INIT 56#define TSS_DTOR_ITERATIONS PTHREAD_DESTRUCTOR_ITERATIONS 57#else 58#define TSS_DTOR_ITERATIONS 1 // assume TSS dtor MAY be called at least once. 59#endif 60 61// FIXME: temporary non-standard hack to ease transition 62#define _MTX_INITIALIZER_NP PTHREAD_MUTEX_INITIALIZER 63 64/*---------------------------- types ----------------------------*/ 65typedef pthread_cond_t cnd_t; 66typedef pthread_t thrd_t; 67typedef pthread_key_t tss_t; 68typedef pthread_mutex_t mtx_t; 69typedef pthread_once_t once_flag; 70 71 72/* 73Implementation limits: 74 - Conditionally emulation for "mutex with timeout" 75 (see EMULATED_THREADS_USE_NATIVE_TIMEDLOCK macro) 76*/ 77struct impl_thrd_param { 78 thrd_start_t func; 79 void *arg; 80}; 81 82static inline void * 83impl_thrd_routine(void *p) 84{ 85 struct impl_thrd_param pack = *((struct impl_thrd_param *)p); 86 free(p); 87 return (void*)(intptr_t)pack.func(pack.arg); 88} 89 90 91/*--------------- 7.25.2 Initialization functions ---------------*/ 92// 7.25.2.1 93static inline void 94call_once(once_flag *flag, void (*func)(void)) 95{ 96 pthread_once(flag, func); 97} 98 99 100/*------------- 7.25.3 Condition variable functions -------------*/ 101// 7.25.3.1 102static inline int 103cnd_broadcast(cnd_t *cond) 104{ 105 assert(cond != NULL); 106 return (pthread_cond_broadcast(cond) == 0) ? thrd_success : thrd_error; 107} 108 109// 7.25.3.2 110static inline void 111cnd_destroy(cnd_t *cond) 112{ 113 assert(cond); 114 pthread_cond_destroy(cond); 115} 116 117// 7.25.3.3 118static inline int 119cnd_init(cnd_t *cond) 120{ 121 assert(cond != NULL); 122 return (pthread_cond_init(cond, NULL) == 0) ? thrd_success : thrd_error; 123} 124 125// 7.25.3.4 126static inline int 127cnd_signal(cnd_t *cond) 128{ 129 assert(cond != NULL); 130 return (pthread_cond_signal(cond) == 0) ? thrd_success : thrd_error; 131} 132 133// 7.25.3.5 134static inline int 135cnd_timedwait(cnd_t *cond, mtx_t *mtx, const struct timespec *abs_time) 136{ 137 int rt; 138 139 assert(mtx != NULL); 140 assert(cond != NULL); 141 assert(abs_time != NULL); 142 143 rt = pthread_cond_timedwait(cond, mtx, abs_time); 144 if (rt == ETIMEDOUT) 145 return thrd_busy; 146 return (rt == 0) ? thrd_success : thrd_error; 147} 148 149// 7.25.3.6 150static inline int 151cnd_wait(cnd_t *cond, mtx_t *mtx) 152{ 153 assert(mtx != NULL); 154 assert(cond != NULL); 155 return (pthread_cond_wait(cond, mtx) == 0) ? thrd_success : thrd_error; 156} 157 158 159/*-------------------- 7.25.4 Mutex functions --------------------*/ 160// 7.25.4.1 161static inline void 162mtx_destroy(mtx_t *mtx) 163{ 164 assert(mtx != NULL); 165 pthread_mutex_destroy(mtx); 166} 167 168/* 169 * XXX: Workaround when building with -O0 and without pthreads link. 170 * 171 * In such cases constant folding and dead code elimination won't be 172 * available, thus the compiler will always add the pthread_mutexattr* 173 * functions into the binary. As we try to link, we'll fail as the 174 * symbols are unresolved. 175 * 176 * Ideally we'll enable the optimisations locally, yet that does not 177 * seem to work. 178 * 179 * So the alternative workaround is to annotate the symbols as weak. 180 * Thus the linker will be happy and things don't clash when building 181 * with -O1 or greater. 182 */ 183#if defined(HAVE_FUNC_ATTRIBUTE_WEAK) && !defined(__CYGWIN__) 184__attribute__((weak)) 185int pthread_mutexattr_init(pthread_mutexattr_t *attr); 186 187__attribute__((weak)) 188int pthread_mutexattr_settype(pthread_mutexattr_t *attr, int type); 189 190__attribute__((weak)) 191int pthread_mutexattr_destroy(pthread_mutexattr_t *attr); 192#endif 193 194// 7.25.4.2 195static inline int 196mtx_init(mtx_t *mtx, int type) 197{ 198 pthread_mutexattr_t attr; 199 assert(mtx != NULL); 200 if (type != mtx_plain && type != mtx_timed && type != mtx_try 201 && type != (mtx_plain|mtx_recursive) 202 && type != (mtx_timed|mtx_recursive) 203 && type != (mtx_try|mtx_recursive)) 204 return thrd_error; 205 206 if ((type & mtx_recursive) == 0) { 207 pthread_mutex_init(mtx, NULL); 208 return thrd_success; 209 } 210 211 pthread_mutexattr_init(&attr); 212 pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); 213 pthread_mutex_init(mtx, &attr); 214 pthread_mutexattr_destroy(&attr); 215 return thrd_success; 216} 217 218// 7.25.4.3 219static inline int 220mtx_lock(mtx_t *mtx) 221{ 222 assert(mtx != NULL); 223 return (pthread_mutex_lock(mtx) == 0) ? thrd_success : thrd_error; 224} 225 226static inline int 227mtx_trylock(mtx_t *mtx); 228 229static inline void 230thrd_yield(void); 231 232// 7.25.4.4 233static inline int 234mtx_timedlock(mtx_t *mtx, const struct timespec *ts) 235{ 236 assert(mtx != NULL); 237 assert(ts != NULL); 238 239 { 240#ifdef EMULATED_THREADS_USE_NATIVE_TIMEDLOCK 241 int rt; 242 rt = pthread_mutex_timedlock(mtx, ts); 243 if (rt == 0) 244 return thrd_success; 245 return (rt == ETIMEDOUT) ? thrd_busy : thrd_error; 246#else 247 time_t expire = time(NULL); 248 expire += ts->tv_sec; 249 while (mtx_trylock(mtx) != thrd_success) { 250 time_t now = time(NULL); 251 if (expire < now) 252 return thrd_busy; 253 // busy loop! 254 thrd_yield(); 255 } 256 return thrd_success; 257#endif 258 } 259} 260 261// 7.25.4.5 262static inline int 263mtx_trylock(mtx_t *mtx) 264{ 265 assert(mtx != NULL); 266 return (pthread_mutex_trylock(mtx) == 0) ? thrd_success : thrd_busy; 267} 268 269// 7.25.4.6 270static inline int 271mtx_unlock(mtx_t *mtx) 272{ 273 assert(mtx != NULL); 274 return (pthread_mutex_unlock(mtx) == 0) ? thrd_success : thrd_error; 275} 276 277 278/*------------------- 7.25.5 Thread functions -------------------*/ 279// 7.25.5.1 280static inline int 281thrd_create(thrd_t *thr, thrd_start_t func, void *arg) 282{ 283 struct impl_thrd_param *pack; 284 assert(thr != NULL); 285 pack = (struct impl_thrd_param *)malloc(sizeof(struct impl_thrd_param)); 286 if (!pack) return thrd_nomem; 287 pack->func = func; 288 pack->arg = arg; 289 if (pthread_create(thr, NULL, impl_thrd_routine, pack) != 0) { 290 free(pack); 291 return thrd_error; 292 } 293 return thrd_success; 294} 295 296// 7.25.5.2 297static inline thrd_t 298thrd_current(void) 299{ 300 return pthread_self(); 301} 302 303// 7.25.5.3 304static inline int 305thrd_detach(thrd_t thr) 306{ 307 return (pthread_detach(thr) == 0) ? thrd_success : thrd_error; 308} 309 310// 7.25.5.4 311static inline int 312thrd_equal(thrd_t thr0, thrd_t thr1) 313{ 314 return pthread_equal(thr0, thr1); 315} 316 317// 7.25.5.5 318static inline void 319thrd_exit(int res) 320{ 321 pthread_exit((void*)(intptr_t)res); 322} 323 324// 7.25.5.6 325static inline int 326thrd_join(thrd_t thr, int *res) 327{ 328 void *code; 329 if (pthread_join(thr, &code) != 0) 330 return thrd_error; 331 if (res) 332 *res = (int)(intptr_t)code; 333 return thrd_success; 334} 335 336// 7.25.5.7 337static inline void 338thrd_sleep(const struct timespec *time_point, struct timespec *remaining) 339{ 340 assert(time_point != NULL); 341 nanosleep(time_point, remaining); 342} 343 344// 7.25.5.8 345static inline void 346thrd_yield(void) 347{ 348 sched_yield(); 349} 350 351 352/*----------- 7.25.6 Thread-specific storage functions -----------*/ 353// 7.25.6.1 354static inline int 355tss_create(tss_t *key, tss_dtor_t dtor) 356{ 357 assert(key != NULL); 358 return (pthread_key_create(key, dtor) == 0) ? thrd_success : thrd_error; 359} 360 361// 7.25.6.2 362static inline void 363tss_delete(tss_t key) 364{ 365 pthread_key_delete(key); 366} 367 368// 7.25.6.3 369static inline void * 370tss_get(tss_t key) 371{ 372 return pthread_getspecific(key); 373} 374 375// 7.25.6.4 376static inline int 377tss_set(tss_t key, void *val) 378{ 379 return (pthread_setspecific(key, val) == 0) ? thrd_success : thrd_error; 380} 381 382 383/*-------------------- 7.25.7 Time functions --------------------*/ 384// 7.25.6.1 385#ifndef HAVE_TIMESPEC_GET 386static inline int 387timespec_get(struct timespec *ts, int base) 388{ 389 if (!ts) return 0; 390 if (base == TIME_UTC) { 391 clock_gettime(CLOCK_REALTIME, ts); 392 return base; 393 } 394 return 0; 395} 396#endif 397