P99
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Friends Defines
p99_threads.h
Go to the documentation of this file.
00001 /* This may look like nonsense, but it really is -*- mode: C -*-             */
00002 /*                                                                           */
00003 /* Except of parts copied from previous work and as explicitly stated below, */
00004 /* the author and copyright holder for this work is                          */
00005 /* (C) copyright  2011-2012 Jens Gustedt, INRIA, France                      */
00006 /*                                                                           */
00007 /* This file is free software; it is part of the P99 project.                */
00008 /* You can redistribute it and/or modify it under the terms of the QPL as    */
00009 /* given in the file LICENSE. It is distributed without any warranty;        */
00010 /* without even the implied warranty of merchantability or fitness for a     */
00011 /* particular purpose.                                                       */
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   /* special care for bogus warning given by icc */
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 // 7.26.2 Initialization functions
00255 
00259 p99_inline
00260 void thrd_yield(void) {
00261   if (P99_UNLIKELY(sched_yield())) errno = 0;
00262 }
00263 
00264 /* Tentative definitions for global variables. This has the advantage
00265    that this defines weak symbols and we avoid to have to create a
00266    specific library. */
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         /* we are doing the initialization */
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           /* we are called recursively, abandon and return */
00323           atomic_flag_unlock(&flag->p00_flg);
00324           return;
00325         }
00326         /* otherwise fall through */
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         /* we are doing the initialization */
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           /* we are called recursively, abandon and return */
00356           atomic_flag_unlock(&flag->p00_flg);
00357           return;
00358         }
00359         /* otherwise fall through */
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   /* fprintf(stderr, "Initializing " #T "\n");*/                         \
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 // 7.26.3 Condition variable functions
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 // 7.26.4 Mutex functions
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   /* The thread is not yet detached so its pthread id is still
00739      valid. If it already has finished, this will just free the
00740      resources that pthread holds for it. */
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     /* The thread has already finished. Free the state, since nobody
00744        will join it, anyhow. */
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   /* should only be reached by threads that where created directly
00772      with pthreads, e.g main */
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   This is static inline because of the following message from gcc:
00812 
00813    sorry, unimplemented: function ‘p00_thrd_create’ can never be inlined because it uses setjmp
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
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Friends Defines