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