Subversion Repositories Kolibri OS

Rev

Blame | Last modification | View Log | RSS feed

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