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 // 7.25.4.2 173 static inline int 174 mtx_init(mtx_t *mtx, int type) 175 { 176 pthread_mutexattr_t attr; 177 assert(mtx != NULL); 178 if (type != mtx_plain && type != mtx_timed && type != mtx_try 179 && type != (mtx_plain|mtx_recursive) 180 && type != (mtx_timed|mtx_recursive) 181 && type != (mtx_try|mtx_recursive)) 182 return thrd_error; 183 pthread_mutexattr_init(&attr); 184 if ((type & mtx_recursive) != 0) 185 pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); 186 pthread_mutex_init(mtx, &attr); 187 pthread_mutexattr_destroy(&attr); 188 return thrd_success; 189 } 190 191 // 7.25.4.3 192 static inline int 193 mtx_lock(mtx_t *mtx) 194 { 195 assert(mtx != NULL); 196 return (pthread_mutex_lock(mtx) == 0) ? thrd_success : thrd_error; 197 } 198 199 static inline int 200 mtx_trylock(mtx_t *mtx); 201 202 static inline void 203 thrd_yield(void); 204 205 // 7.25.4.4 206 static inline int 207 mtx_timedlock(mtx_t *mtx, const xtime *xt) 208 { 209 assert(mtx != NULL); 210 assert(xt != NULL); 211 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 238 static inline int 239 mtx_trylock(mtx_t *mtx) 240 { 241 assert(mtx != NULL); 242 return (pthread_mutex_trylock(mtx) == 0) ? thrd_success : thrd_busy; 243 } 244 245 // 7.25.4.6 246 static inline int 247 mtx_unlock(mtx_t *mtx) 248 { 249 assert(mtx != NULL); 250 return (pthread_mutex_unlock(mtx) == 0) ? thrd_success : thrd_error; 251 } 252 253 254 /*------------------- 7.25.5 Thread functions -------------------*/ 255 // 7.25.5.1 256 static inline int 257 thrd_create(thrd_t *thr, thrd_start_t func, void *arg) 258 { 259 struct impl_thrd_param *pack; 260 assert(thr != NULL); 261 pack = (struct impl_thrd_param *)malloc(sizeof(struct impl_thrd_param)); 262 if (!pack) return thrd_nomem; 263 pack->func = func; 264 pack->arg = arg; 265 if (pthread_create(thr, NULL, impl_thrd_routine, pack) != 0) { 266 free(pack); 267 return thrd_error; 268 } 269 return thrd_success; 270 } 271 272 // 7.25.5.2 273 static inline thrd_t 274 thrd_current(void) 275 { 276 return pthread_self(); 277 } 278 279 // 7.25.5.3 280 static inline int 281 thrd_detach(thrd_t thr) 282 { 283 return (pthread_detach(thr) == 0) ? thrd_success : thrd_error; 284 } 285 286 // 7.25.5.4 287 static inline int 288 thrd_equal(thrd_t thr0, thrd_t thr1) 289 { 290 return pthread_equal(thr0, thr1); 291 } 292 293 // 7.25.5.5 294 static inline void 295 thrd_exit(int res) 296 { 297 pthread_exit((void*)(intptr_t)res); 298 } 299 300 // 7.25.5.6 301 static inline int 302 thrd_join(thrd_t thr, int *res) 303 { 304 void *code; 305 if (pthread_join(thr, &code) != 0) 306 return thrd_error; 307 if (res) 308 *res = (int)(intptr_t)code; 309 return thrd_success; 310 } 311 312 // 7.25.5.7 313 static inline void 314 thrd_sleep(const xtime *xt) 315 { 316 struct timespec req; 317 assert(xt); 318 req.tv_sec = xt->sec; 319 req.tv_nsec = xt->nsec; 320 nanosleep(&req, NULL); 321 } 322 323 // 7.25.5.8 324 static inline void 325 thrd_yield(void) 326 { 327 sched_yield(); 328 } 329 330 331 /*----------- 7.25.6 Thread-specific storage functions -----------*/ 332 // 7.25.6.1 333 static inline int 334 tss_create(tss_t *key, tss_dtor_t dtor) 335 { 336 assert(key != NULL); 337 return (pthread_key_create(key, dtor) == 0) ? thrd_success : thrd_error; 338 } 339 340 // 7.25.6.2 341 static inline void 342 tss_delete(tss_t key) 343 { 344 pthread_key_delete(key); 345 } 346 347 // 7.25.6.3 348 static inline void * 349 tss_get(tss_t key) 350 { 351 return pthread_getspecific(key); 352 } 353 354 // 7.25.6.4 355 static inline int 356 tss_set(tss_t key, void *val) 357 { 358 return (pthread_setspecific(key, val) == 0) ? thrd_success : thrd_error; 359 } 360 361 362 /*-------------------- 7.25.7 Time functions --------------------*/ 363 // 7.25.6.1 364 static inline int 365 xtime_get(xtime *xt, int base) 366 { 367 if (!xt) return 0; 368 if (base == TIME_UTC) { 369 xt->sec = time(NULL); 370 xt->nsec = 0; 371 return base; 372 } 373 return 0; 374 } 375