threads_posix.h revision 848b8605
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 if (!cond) return thrd_error; 106 pthread_cond_broadcast(cond); 107 return thrd_success; 108} 109 110// 7.25.3.2 111static inline void 112cnd_destroy(cnd_t *cond) 113{ 114 assert(cond); 115 pthread_cond_destroy(cond); 116} 117 118// 7.25.3.3 119static inline int 120cnd_init(cnd_t *cond) 121{ 122 if (!cond) return thrd_error; 123 pthread_cond_init(cond, NULL); 124 return thrd_success; 125} 126 127// 7.25.3.4 128static inline int 129cnd_signal(cnd_t *cond) 130{ 131 if (!cond) return thrd_error; 132 pthread_cond_signal(cond); 133 return thrd_success; 134} 135 136// 7.25.3.5 137static inline int 138cnd_timedwait(cnd_t *cond, mtx_t *mtx, const xtime *xt) 139{ 140 struct timespec abs_time; 141 int rt; 142 if (!cond || !mtx || !xt) return thrd_error; 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 if (!cond || !mtx) return thrd_error; 154 pthread_cond_wait(cond, mtx); 155 return thrd_success; 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); 165 pthread_mutex_destroy(mtx); 166} 167 168// 7.25.4.2 169static inline int 170mtx_init(mtx_t *mtx, int type) 171{ 172 pthread_mutexattr_t attr; 173 if (!mtx) return thrd_error; 174 if (type != mtx_plain && type != mtx_timed && type != mtx_try 175 && type != (mtx_plain|mtx_recursive) 176 && type != (mtx_timed|mtx_recursive) 177 && type != (mtx_try|mtx_recursive)) 178 return thrd_error; 179 pthread_mutexattr_init(&attr); 180 if ((type & mtx_recursive) != 0) { 181#if defined(__linux__) || defined(__linux) 182 pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE_NP); 183#else 184 pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); 185#endif 186 } 187 pthread_mutex_init(mtx, &attr); 188 pthread_mutexattr_destroy(&attr); 189 return thrd_success; 190} 191 192// 7.25.4.3 193static inline int 194mtx_lock(mtx_t *mtx) 195{ 196 if (!mtx) return thrd_error; 197 pthread_mutex_lock(mtx); 198 return thrd_success; 199} 200 201static inline int 202mtx_trylock(mtx_t *mtx); 203 204static inline void 205thrd_yield(void); 206 207// 7.25.4.4 208static inline int 209mtx_timedlock(mtx_t *mtx, const xtime *xt) 210{ 211 if (!mtx || !xt) return thrd_error; 212 { 213#ifdef EMULATED_THREADS_USE_NATIVE_TIMEDLOCK 214 struct timespec ts; 215 int rt; 216 ts.tv_sec = xt->sec; 217 ts.tv_nsec = xt->nsec; 218 rt = pthread_mutex_timedlock(mtx, &ts); 219 if (rt == 0) 220 return thrd_success; 221 return (rt == ETIMEDOUT) ? thrd_busy : thrd_error; 222#else 223 time_t expire = time(NULL); 224 expire += xt->sec; 225 while (mtx_trylock(mtx) != thrd_success) { 226 time_t now = time(NULL); 227 if (expire < now) 228 return thrd_busy; 229 // busy loop! 230 thrd_yield(); 231 } 232 return thrd_success; 233#endif 234 } 235} 236 237// 7.25.4.5 238static inline int 239mtx_trylock(mtx_t *mtx) 240{ 241 if (!mtx) return thrd_error; 242 return (pthread_mutex_trylock(mtx) == 0) ? thrd_success : thrd_busy; 243} 244 245// 7.25.4.6 246static inline int 247mtx_unlock(mtx_t *mtx) 248{ 249 if (!mtx) return thrd_error; 250 pthread_mutex_unlock(mtx); 251 return thrd_success; 252} 253 254 255/*------------------- 7.25.5 Thread functions -------------------*/ 256// 7.25.5.1 257static inline int 258thrd_create(thrd_t *thr, thrd_start_t func, void *arg) 259{ 260 struct impl_thrd_param *pack; 261 if (!thr) return thrd_error; 262 pack = (struct impl_thrd_param *)malloc(sizeof(struct impl_thrd_param)); 263 if (!pack) return thrd_nomem; 264 pack->func = func; 265 pack->arg = arg; 266 if (pthread_create(thr, NULL, impl_thrd_routine, pack) != 0) { 267 free(pack); 268 return thrd_error; 269 } 270 return thrd_success; 271} 272 273// 7.25.5.2 274static inline thrd_t 275thrd_current(void) 276{ 277 return pthread_self(); 278} 279 280// 7.25.5.3 281static inline int 282thrd_detach(thrd_t thr) 283{ 284 return (pthread_detach(thr) == 0) ? thrd_success : thrd_error; 285} 286 287// 7.25.5.4 288static inline int 289thrd_equal(thrd_t thr0, thrd_t thr1) 290{ 291 return pthread_equal(thr0, thr1); 292} 293 294// 7.25.5.5 295static inline void 296thrd_exit(int res) 297{ 298 pthread_exit((void*)(intptr_t)res); 299} 300 301// 7.25.5.6 302static inline int 303thrd_join(thrd_t thr, int *res) 304{ 305 void *code; 306 if (pthread_join(thr, &code) != 0) 307 return thrd_error; 308 if (res) 309 *res = (int)(intptr_t)code; 310 return thrd_success; 311} 312 313// 7.25.5.7 314static inline void 315thrd_sleep(const xtime *xt) 316{ 317 struct timespec req; 318 assert(xt); 319 req.tv_sec = xt->sec; 320 req.tv_nsec = xt->nsec; 321 nanosleep(&req, NULL); 322} 323 324// 7.25.5.8 325static inline void 326thrd_yield(void) 327{ 328 sched_yield(); 329} 330 331 332/*----------- 7.25.6 Thread-specific storage functions -----------*/ 333// 7.25.6.1 334static inline int 335tss_create(tss_t *key, tss_dtor_t dtor) 336{ 337 if (!key) return thrd_error; 338 return (pthread_key_create(key, dtor) == 0) ? thrd_success : thrd_error; 339} 340 341// 7.25.6.2 342static inline void 343tss_delete(tss_t key) 344{ 345 pthread_key_delete(key); 346} 347 348// 7.25.6.3 349static inline void * 350tss_get(tss_t key) 351{ 352 return pthread_getspecific(key); 353} 354 355// 7.25.6.4 356static inline int 357tss_set(tss_t key, void *val) 358{ 359 return (pthread_setspecific(key, val) == 0) ? thrd_success : thrd_error; 360} 361 362 363/*-------------------- 7.25.7 Time functions --------------------*/ 364// 7.25.6.1 365static inline int 366xtime_get(xtime *xt, int base) 367{ 368 if (!xt) return 0; 369 if (base == TIME_UTC) { 370 xt->sec = time(NULL); 371 xt->nsec = 0; 372 return base; 373 } 374 return 0; 375} 376