00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013 #ifndef P99_THREADS_H
00014 #define P99_THREADS_H 1
00015
00016 #include "p99_try.h"
00017
00018
00054 #define P99_ONCE_FLAG_INIT P99_INIT
00055
00056 #ifndef PTHREAD_DESTRUCTOR_ITERATIONS
00057 # warning "definition of PTHREAD_DESTRUCTOR_ITERATIONS is missing"
00058
00064 # define TSS_DTOR_ITERATIONS 1
00065 #else
00066 # define TSS_DTOR_ITERATIONS PTHREAD_DESTRUCTOR_ITERATIONS
00067 #endif
00068
00085 P99_ENC_DECLARE(pthread_cond_t, cnd_t);
00086
00087 typedef struct p00_thrd p00_thrd;
00100 P99_ENC_DECLARE(struct p00_thrd*, thrd_t);
00101
00102 p99_inline thrd_t* thrd_t_init(thrd_t *id) {
00103 if (id) {
00104 *id = P99_LVAL(thrd_t const);
00105 }
00106 return id;
00107 }
00108
00109 p99_inline void thrd_t_destroy(thrd_t *id) {
00110
00111 (void)id;
00112 }
00113
00114 p99_inline
00115 char const* thrd2str(char *p00_buf, thrd_t p00_id) {
00116 unsigned char *p00_p = (unsigned char*)&p00_id;
00117 for (size_t p00_i = 0; p00_i < sizeof(thrd_t); ++p00_i) {
00118 snprintf(p00_buf + 2*p00_i, 3, "%02X", p00_p[p00_i]);
00119 }
00120 return p00_buf;
00121 }
00122
00123 #define THRD2STR(ID) thrd2str((char[1 + sizeof(thrd_t) * 2]){0}, (ID))
00124
00125
00131 P99_ENC_DECLARE(pthread_mutex_t, mtx_t);
00132
00138 typedef int (*thrd_start_t)(void*);
00139
00140 #ifndef ONCE_FLAG_INIT
00141 typedef struct p99_once_flag once_flag;
00142 #define ONCE_FLAG_INIT P99_ONCE_FLAG_INIT
00143 #define call_once p99_call_once
00144 #endif
00145
00146 typedef struct p99_once_flag p99_once_flag;
00147
00148 enum p00_once {
00149 p00_once_uninit = 0,
00150 p00_once_started,
00151 p00_once_finished,
00152 };
00153
00167 struct p99_once_flag {
00168 union {
00169 enum p00_once p00_done;
00170 enum p00_once volatile p00_vdone;
00171 } p00_done;
00172 thrd_t p00_id;
00173 void (*const p00_init)(void);
00174 atomic_flag p00_flg;
00175 };
00176
00177 P00_DOCUMENT_TYPE_ARGUMENT(P99_DECLARE_INIT_ONCE, 0)
00178 P00_DOCUMENT_IDENTIFIER_ARGUMENT(P99_DECLARE_INIT_ONCE, 1)
00179 P00_DOCUMENT_IDENTIFIER_ARGUMENT(P99_DECLARE_INIT_ONCE, 2)
00180 #define P99_DECLARE_INIT_ONCE(T, NAME, ARG) \
00181 \
00182 struct NAME { \
00183 p99_once_flag p00_once; \
00184 T p00_val; \
00185 }; \
00186 P99_DECLARE_STRUCT(NAME); \
00187 p99_inline \
00188 void P99_PASTE3(p00_, NAME, _init_func)(T* ARG); \
00189 p99_inline \
00190 void P99_PASTE3(p00_, NAME, _init_once)(NAME* ARG) { \
00191 if (P99_UNLIKELY(!ARG->p00_once.p00_done.p00_done)) \
00192 do { \
00193 P99_SPIN_EXCLUDE(&ARG->p00_once.p00_flg) { \
00194 if (!ARG->p00_once.p00_done.p00_vdone) { \
00195 P99_PASTE3(p00_, NAME, _init_func)(&ARG->p00_val); \
00196 ARG->p00_once.p00_done.p00_vdone = true; \
00197 } \
00198 } \
00199 } while (!ARG->p00_once.p00_done.p00_vdone); \
00200 } \
00201 p99_inline \
00202 void P99_PASTE3(p00_, NAME, _init_func)(T* ARG)
00203
00204 #define P99_INIT_ONCE(NAME, VARP) P99_PASTE3(p00_, NAME, _init_once)(VARP)
00205
00206
00211 struct p00_thrd {
00212 atomic_flag p00_detached;
00213 unsigned p00_foreign;
00214 int p00_ret;
00215 pthread_t p00_id;
00216 union {
00217 struct {
00218 thrd_start_t p00_func;
00219 void *p00_arg;
00220 } p00_init;
00221 jmp_buf p00_jmp;
00222 } p00_ovrl;
00223 };
00224
00233 enum mtx_type {
00238 mtx_plain = 0,
00242 mtx_recursive = 1,
00246 mtx_timed = 2,
00247 };
00248
00249
00254
00255
00259 p99_inline
00260 void thrd_yield(void) {
00261 if (P99_UNLIKELY(sched_yield())) errno = 0;
00262 }
00263
00264
00265
00266
00267 P99_DECLARE_THREAD_LOCAL(p00_thrd *, p00_thrd_local);
00268
00269 #define P00_THRD_LOCAL P99_THREAD_LOCAL(p00_thrd_local)
00270
00271 P99_WEAK(p00_foreign_threads)
00272 _Atomic(size_t) p00_foreign_threads;
00273
00279 p99_inline
00280 thrd_t thrd_current(void) {
00281 p00_thrd * p00_loc = P00_THRD_LOCAL;
00282 if (P99_UNLIKELY(!p00_loc)) {
00283 size_t p00_foreign = atomic_fetch_add(&p00_foreign_threads, 1);
00284 p00_loc = malloc(sizeof *p00_loc);
00285 *p00_loc = (p00_thrd) {
00286 .p00_id = pthread_self(),
00287 .p00_foreign = p00_foreign + 1,
00288 };
00289 P00_THRD_LOCAL = p00_loc;
00290 if (p00_foreign) fprintf(stderr, "foreign thread %lu is %zu\n", p00_loc->p00_id, p00_foreign + 1);
00291 }
00292 return (thrd_t)P99_ENC_INIT(p00_loc);
00293 }
00294
00301 p99_inline
00302 int thrd_equal(thrd_t p00_thr0, thrd_t p00_thr1) {
00303 return P99_ENC(p00_thr0) == P99_ENC(p00_thr1);
00304 }
00305
00306 p99_inline
00307 void p00_call_once_2(p99_once_flag *flag, void (*p00_func)(void)) {
00308 if (P99_UNLIKELY(flag->p00_done.p00_done != p00_once_finished))
00309 do {
00310 atomic_flag_lock(&flag->p00_flg);
00311 switch (flag->p00_done.p00_vdone) {
00312
00313 case p00_once_uninit:
00314 flag->p00_done.p00_done = 1;
00315 flag->p00_id = thrd_current();
00316 atomic_flag_unlock(&flag->p00_flg);
00317 p00_func();
00318 flag->p00_done.p00_done = 2;
00319 break;
00320 case p00_once_started:
00321 if (thrd_equal(flag->p00_id, thrd_current())) {
00322
00323 atomic_flag_unlock(&flag->p00_flg);
00324 return;
00325 }
00326
00327 case p00_once_finished:
00328 atomic_flag_unlock(&flag->p00_flg);
00329 break;
00330 }
00331 } while (flag->p00_done.p00_vdone != p00_once_finished);
00332 }
00333
00334 p99_inline
00335 void p00_call_once_1(p99_once_flag *flag) {
00336 p00_call_once_2(flag, flag->p00_init);
00337 }
00338
00339 p99_inline
00340 void p00_call_once_3(p99_once_flag *flag, void (*p00_func)(void*), void* p00_arg) {
00341 if (P99_UNLIKELY(flag->p00_done.p00_done != p00_once_finished))
00342 do {
00343 atomic_flag_lock(&flag->p00_flg);
00344 switch (flag->p00_done.p00_vdone) {
00345
00346 case p00_once_uninit:
00347 flag->p00_done.p00_done = 1;
00348 flag->p00_id = thrd_current();
00349 atomic_flag_unlock(&flag->p00_flg);
00350 p00_func(p00_arg);
00351 flag->p00_done.p00_done = 2;
00352 break;
00353 case p00_once_started:
00354 if (thrd_equal(flag->p00_id, thrd_current())) {
00355
00356 atomic_flag_unlock(&flag->p00_flg);
00357 return;
00358 }
00359
00360 case p00_once_finished:
00361 atomic_flag_unlock(&flag->p00_flg);
00362 break;
00363 }
00364 } while (flag->p00_done.p00_vdone != p00_once_finished);
00365 }
00366
00367 #define p00_call_once(N, ...) \
00368 P99_IF_EQ_1(N) \
00369 (p00_call_once_1(__VA_ARGS__)) \
00370 (P99_IF_EQ_2(N) \
00371 (p00_call_once_2(__VA_ARGS__)) \
00372 (p00_call_once_3(__VA_ARGS__)))
00373
00402 #ifdef P00_DOXYGEN
00403 #define p99_call_once(FLAG, FUNC, ARG)
00404 #else
00405 #define p99_call_once(...) p00_call_once(P99_NARG(__VA_ARGS__), __VA_ARGS__)
00406 #endif
00407
00408 #ifdef P00_DOXYGEN
00409
00438 #define P99_DEFINE_ONCE_CHAIN(T, ...) \
00439 p99_once_flag p99_ ## T ## _once; \
00440 void p00_ ## T ## _once_init(void)
00441 #else
00442 #define P99_DEFINE_ONCE_CHAIN(...) \
00443 P99_IF_ELSE(P99_HAS_COMMA(__VA_ARGS__)) \
00444 (P00_P99_DEFINE_ONCE_CHAIN_1(__VA_ARGS__)) \
00445 (P00_P99_DEFINE_ONCE_CHAIN_0(__VA_ARGS__))
00446 #endif
00447
00448 #define P00_P99_DEFINE_ONCE_CHAIN_0(T) \
00449 static void P99_PASTE3(p00_, T, _once_init)(void); \
00450 p99_once_flag P99_PASTE3(p99_, T, _once) = { \
00451 .p00_init = P99_PASTE3(p00_, T, _once_init), \
00452 }; \
00453 static void P99_PASTE3(p00_, T, _once_init)(void)
00454
00455 #define P00_ONCE_INIT(_0, T, _2) P99_INIT_CHAIN(T)
00456
00457 #define P00_P99_DEFINE_ONCE_CHAIN_1(T, ...) \
00458 static void P99_PASTE3(p00_, T, _once_init0)(void); \
00459 static void P99_PASTE3(p00_, T, _once_init)(void) { \
00460 P99_FOR(, P99_NARG(__VA_ARGS__), P00_SEP, P00_ONCE_INIT, __VA_ARGS__); \
00461 \
00462 P99_PASTE3(p00_, T, _once_init0)(); \
00463 } \
00464 struct p99_once_flag P99_PASTE3(p99_, T, _once) = { \
00465 .p00_init = P99_PASTE3(p00_, T, _once_init), \
00466 }; \
00467 static void P99_PASTE3(p00_, T, _once_init0)(void)
00468
00477 #define P99_DECLARE_ONCE_CHAIN(T) \
00478 extern p99_once_flag P99_PASTE3(p99_, T, _once)
00479
00492 #define P99_INIT_CHAIN(T) \
00493 p99_call_once(&P99_PASTE3(p99_, T, _once), P99_PASTE3(p99_, T, _once).p00_init)
00494
00495
00496
00503 p99_inline
00504 int cnd_broadcast(cnd_t *p00_cond) {
00505 return pthread_cond_broadcast(&P99_ENCP(p00_cond)) ? thrd_error : thrd_success;
00506 }
00507
00511 p99_inline
00512 void cnd_destroy(cnd_t *p00_cond) {
00513 (void)pthread_cond_destroy(&P99_ENCP(p00_cond));
00514 }
00515
00524 p99_inline
00525 int cnd_init(cnd_t *p00_cond) {
00526 int p00_ret = pthread_cond_init(&P99_ENCP(p00_cond), 0);
00527 switch (p00_ret) {
00528 case 0: return thrd_success;
00529 case ENOMEM: return thrd_nomem;
00530 default: return thrd_error;
00531 }
00532 }
00533
00540 p99_inline
00541 int cnd_signal(cnd_t *p00_cond) {
00542 return pthread_cond_signal(&P99_ENCP(p00_cond)) ? thrd_error : thrd_success;
00543 }
00544
00553 p99_inline
00554 int cnd_timedwait(cnd_t *restrict p00_cond, mtx_t *restrict p00_mtx, const struct timespec *restrict p00_ts) {
00555 int p00_ret = pthread_cond_timedwait(&P99_ENCP(p00_cond), &P99_ENCP(p00_mtx), p00_ts);
00556 switch (p00_ret) {
00557 case 0: return thrd_success;
00558 case ETIMEDOUT: return thrd_timedout;
00559 default: return thrd_error;
00560 };
00561 }
00562
00569 p99_inline
00570 int cnd_wait(cnd_t *p00_cond, mtx_t *p00_mtx) {
00571 return pthread_cond_wait(&P99_ENCP(p00_cond), &P99_ENCP(p00_mtx)) ? thrd_error : thrd_success;
00572 }
00573
00574
00575
00579 p99_inline
00580 void mtx_destroy(mtx_t *p00_mtx) {
00581 (void)pthread_mutex_destroy(&P99_ENCP(p00_mtx));
00582 }
00583
00593 p99_inline
00594 int mtx_init(mtx_t *p00_mtx, int p00_type) {
00595 pthread_mutexattr_t p00_attr;
00596 int p00_ret = pthread_mutexattr_init(&p00_attr);
00597 if (p00_ret) return thrd_error;
00598 p00_ret = pthread_mutexattr_settype(&p00_attr, (p00_type & mtx_recursive) ? PTHREAD_MUTEX_NORMAL : PTHREAD_MUTEX_RECURSIVE);
00599 if (p00_ret) return thrd_error;
00600 p00_ret = pthread_mutex_init(&P99_ENCP(p00_mtx), &p00_attr);
00601 if (p00_ret) return thrd_error;
00602 else return thrd_success;
00603 }
00604
00610 p99_inline
00611 int mtx_lock(mtx_t *p00_mtx) {
00612 return pthread_mutex_lock(&P99_ENCP(p00_mtx)) ? thrd_error : thrd_success;
00613 }
00614
00623 p99_inline
00624 int mtx_timedlock(mtx_t *restrict p00_mtx, const struct timespec *restrict p00_ts) {
00625 int p00_ret = pthread_mutex_timedlock(&P99_ENCP(p00_mtx), p00_ts);
00626 switch (p00_ret) {
00627 case 0: return thrd_success;
00628 case ETIMEDOUT: return thrd_timedout;
00629 default: return thrd_error;
00630 };
00631 }
00632
00640 p99_inline
00641 int mtx_trylock(mtx_t *p00_mtx) {
00642 int p00_ret = pthread_mutex_trylock(&P99_ENCP(p00_mtx));
00643 switch (p00_ret) {
00644 case 0: return thrd_success;
00645 case EBUSY: return thrd_busy;
00646 default: return thrd_error;
00647 };
00648 }
00649
00655 p99_inline
00656 int mtx_unlock(mtx_t *p00_mtx) {
00657 return pthread_mutex_unlock(&P99_ENCP(p00_mtx)) ? thrd_error : thrd_success;
00658 }
00659
00672 P99_BLOCK_DOCUMENT
00673 P00_DOCUMENT_PERMITTED_ARGUMENT(P99_MUTUAL_EXCLUDE, 0)
00674 #define P99_MUTUAL_EXCLUDE(MUT) \
00675 P00_BLK_START \
00676 P00_BLK_DECL(int, p00_errNo, 0) \
00677 P99_GUARDED_BLOCK(mtx_t*, \
00678 P99_FILEID(mut), \
00679 &(MUT), \
00680 (void)(P99_UNLIKELY(p00_errNo = mtx_lock(P99_FILEID(mut))) \
00681 && (fprintf(stderr, \
00682 __FILE__ ":" \
00683 P99_STRINGIFY(__LINE__) ": lock error for " \
00684 P99_STRINGIFY(MUT) ", %s", \
00685 strerror(p00_errNo)), 1) \
00686 && (P99_FILEID(mut) = 0, 1) \
00687 && (P99_UNWIND(-1), 1) \
00688 ), \
00689 (void)(P99_FILEID(mut) \
00690 && mtx_unlock(P99_FILEID(mut))))
00691
00692
00693
00694 P99_SETJMP_INLINE(p00_thrd_create)
00695 void * p00_thrd_create(void* p00_context);
00696
00704 p99_inline
00705 int thrd_create(thrd_t *p00_thr, thrd_start_t p00_func, void *p00_arg) {
00706 p00_thrd * p00_cntxt = malloc(sizeof *p00_cntxt);
00707 if (!p00_cntxt) return thrd_nomem;
00708 *p00_cntxt = (p00_thrd const) {
00709 .p00_ovrl = {
00710 .p00_init = {
00711 .p00_func = p00_func,
00712 .p00_arg = p00_arg,
00713 },
00714 },
00715 .p00_detached = ATOMIC_FLAG_INIT,
00716 };
00717 int p00_ret = pthread_create(&p00_cntxt->p00_id, 0, p00_thrd_create, p00_cntxt);
00718 if (P99_UNLIKELY(p00_ret)) {
00719 free(p00_cntxt);
00720 switch (p00_ret) {
00721 case ENOMEM: return thrd_nomem;
00722 default: return thrd_error;
00723 };
00724 } else {
00725 P99_ENCP(p00_thr) = p00_cntxt;
00726 return thrd_success;
00727 }
00728 }
00729
00736 p99_inline
00737 int thrd_detach(thrd_t p00_thr) {
00738
00739
00740
00741 int p00_ret = pthread_detach(P99_ENC(p00_thr)->p00_id) ? thrd_error : thrd_success;
00742 if (atomic_flag_test_and_set(&P99_ENC(p00_thr)->p00_detached)) {
00743
00744
00745 free(P99_ENC(p00_thr));
00746 return thrd_success;
00747 } else {
00748 return p00_ret;
00749 }
00750 }
00751
00752 #ifdef P00_DOXYGEN
00753
00756 p99_inline void thrd_exit(int p00_res);
00757 #else
00758 p99_inline
00759 _Noreturn
00760 void thrd_exit(int p00_res) {
00761 p00_thrd * p00_cntxt = P00_THRD_LOCAL;
00762 if (P99_LIKELY(p00_cntxt)) {
00763 if (!p00_cntxt->p00_foreign) {
00764 p00_cntxt->p00_ret = p00_res;
00765 longjmp(p00_cntxt->p00_ovrl.p00_jmp, 1);
00766 } else {
00767 free(p00_cntxt);
00768 P00_THRD_LOCAL = 0;
00769 }
00770 }
00771
00772
00773 pthread_exit(0);
00774 }
00775 #endif
00776
00782 p99_inline
00783 int thrd_join(thrd_t p00_thr, int *p00_res) {
00784 void *p00_res0;
00785 if (P99_UNLIKELY(pthread_join(P99_ENC(p00_thr)->p00_id, &p00_res0))) return thrd_error;
00786 if (p00_res) *p00_res = P99_ENC(p00_thr)->p00_ret;
00787 free(P99_ENC(p00_thr));
00788 return thrd_success;
00789 }
00790
00799 p99_inline
00800 int thrd_sleep(const struct timespec *p00_duration, struct timespec *p00_remaining) {
00801 errno = 0;
00802 int p00_ret = nanosleep(p00_duration, p00_remaining);
00803 if (p00_ret) {
00804 p00_ret = (errno == EINTR) ? thrd_intr : thrd_error;
00805 errno = 0;
00806 return p00_ret;
00807 } else return thrd_success;
00808 }
00809
00810
00811
00812
00813
00814
00815 P99_SETJMP_INLINE(p00_thrd_create)
00816 void * p00_thrd_create(void* p00_context) {
00817 p00_thrd * p00_cntxt = p00_context;
00818 P00_THRD_LOCAL = p00_cntxt;
00819 {
00820 thrd_start_t p00_func = p00_cntxt->p00_ovrl.p00_init.p00_func;
00821 void * p00_arg = p00_cntxt->p00_ovrl.p00_init.p00_arg;
00822 if (!setjmp(p00_cntxt->p00_ovrl.p00_jmp)) {
00823 p00_cntxt->p00_ret = p00_func(p00_arg);
00824 }
00825 if (atomic_flag_test_and_set(&p00_cntxt->p00_detached)) {
00826 free(p00_cntxt);
00827 }
00828 }
00829 P00_THRD_LOCAL = 0;
00830 return 0;
00831 }
00832
00837 #endif