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