diff options
Diffstat (limited to 'source/kit/c11/impl/threads_posix.c')
-rw-r--r-- | source/kit/c11/impl/threads_posix.c | 313 |
1 files changed, 313 insertions, 0 deletions
diff --git a/source/kit/c11/impl/threads_posix.c b/source/kit/c11/impl/threads_posix.c new file mode 100644 index 0000000..ebf411c --- /dev/null +++ b/source/kit/c11/impl/threads_posix.c @@ -0,0 +1,313 @@ +/* + * C11 <threads.h> emulation library + * + * (C) Copyright yohhoy 2012. + * Distributed under the Boost Software License, Version 1.0. + * + * Permission is hereby granted, free of charge, to any person or + * organization obtaining a copy of the software and accompanying + * documentation covered by this license (the "Software") to use, + * reproduce, display, distribute, execute, and transmit the Software, + * and to prepare [[derivative work]]s of the Software, and to permit + * third-parties to whom the Software is furnished to do so, all + * subject to the following: + * + * The copyright notices in the Software and this entire statement, + * including the above license grant, this restriction and the + * following disclaimer, must be included in all copies of the + * Software, in whole or in part, and all derivative works of the + * Software, unless such copies or derivative works are solely in the + * form of machine-executable object code generated by a source + * language processor. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND + * NON-INFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR ANYONE + * DISTRIBUTING THE SOFTWARE BE LIABLE FOR ANY DAMAGES OR OTHER + * LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +#ifndef KIT_HAVE_WINDOWS + +# include <assert.h> +# include <errno.h> +# include <limits.h> +# include <sched.h> +# include <stdint.h> /* for intptr_t */ +# include <stdlib.h> +# include <unistd.h> + +# include "../threads.h" + +/* +Configuration macro: + + EMULATED_THREADS_USE_NATIVE_TIMEDLOCK + Use pthread_mutex_timedlock() for `mtx_timedlock()' + Otherwise use mtx_trylock() + *busy loop* emulation. +*/ +# if !defined(__CYGWIN__) && !defined(__APPLE__) && \ + !defined(__NetBSD__) +# define EMULATED_THREADS_USE_NATIVE_TIMEDLOCK +# endif + +/*---------------------------- types ----------------------------*/ + +/* +Implementation limits: + - Conditionally emulation for "mutex with timeout" + (see EMULATED_THREADS_USE_NATIVE_TIMEDLOCK macro) +*/ +struct impl_thrd_param { + thrd_start_t func; + void *arg; +}; + +static void *impl_thrd_routine(void *p) { + struct impl_thrd_param pack = *((struct impl_thrd_param *) p); + free(p); + return (void *) (intptr_t) pack.func(pack.arg); +} + +/*--------------- 7.25.2 Initialization functions ---------------*/ +// 7.25.2.1 +void call_once(once_flag *flag, void (*func)(void)) { + pthread_once(flag, func); +} + +/*------------- 7.25.3 Condition variable functions -------------*/ +// 7.25.3.1 +int cnd_broadcast(cnd_t *cond) { + assert(cond != NULL); + return (pthread_cond_broadcast(cond) == 0) ? thrd_success + : thrd_error; +} + +// 7.25.3.2 +void cnd_destroy(cnd_t *cond) { + assert(cond); + pthread_cond_destroy(cond); +} + +// 7.25.3.3 +int cnd_init(cnd_t *cond) { + assert(cond != NULL); + return (pthread_cond_init(cond, NULL) == 0) ? thrd_success + : thrd_error; +} + +// 7.25.3.4 +int cnd_signal(cnd_t *cond) { + assert(cond != NULL); + return (pthread_cond_signal(cond) == 0) ? thrd_success : thrd_error; +} + +// 7.25.3.5 +int cnd_timedwait(cnd_t *cond, mtx_t *mtx, + const struct timespec *abs_time) { + int rt; + + assert(mtx != NULL); + assert(cond != NULL); + assert(abs_time != NULL); + + rt = pthread_cond_timedwait(cond, mtx, abs_time); + if (rt == ETIMEDOUT) + return thrd_timedout; + return (rt == 0) ? thrd_success : thrd_error; +} + +// 7.25.3.6 +int cnd_wait(cnd_t *cond, mtx_t *mtx) { + assert(mtx != NULL); + assert(cond != NULL); + return (pthread_cond_wait(cond, mtx) == 0) ? thrd_success + : thrd_error; +} + +/*-------------------- 7.25.4 Mutex functions --------------------*/ +// 7.25.4.1 +void mtx_destroy(mtx_t *mtx) { + assert(mtx != NULL); + pthread_mutex_destroy(mtx); +} + +/* + * XXX: Workaround when building with -O0 and without pthreads link. + * + * In such cases constant folding and dead code elimination won't be + * available, thus the compiler will always add the pthread_mutexattr* + * functions into the binary. As we try to link, we'll fail as the + * symbols are unresolved. + * + * Ideally we'll enable the optimisations locally, yet that does not + * seem to work. + * + * So the alternative workaround is to annotate the symbols as weak. + * Thus the linker will be happy and things don't clash when building + * with -O1 or greater. + */ +# if defined(HAVE_FUNC_ATTRIBUTE_WEAK) && !defined(__CYGWIN__) +__attribute__((weak)) int pthread_mutexattr_init( + pthread_mutexattr_t *attr); + +__attribute__((weak)) int pthread_mutexattr_settype( + pthread_mutexattr_t *attr, int type); + +__attribute__((weak)) int pthread_mutexattr_destroy( + pthread_mutexattr_t *attr); +# endif + +// 7.25.4.2 +int mtx_init(mtx_t *mtx, int type) { + pthread_mutexattr_t attr; + assert(mtx != NULL); + if (type != mtx_plain && type != mtx_timed && + type != (mtx_plain | mtx_recursive) && + type != (mtx_timed | mtx_recursive)) + return thrd_error; + + if ((type & mtx_recursive) == 0) { + pthread_mutex_init(mtx, NULL); + return thrd_success; + } + + pthread_mutexattr_init(&attr); + pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); + pthread_mutex_init(mtx, &attr); + pthread_mutexattr_destroy(&attr); + return thrd_success; +} + +// 7.25.4.3 +int mtx_lock(mtx_t *mtx) { + assert(mtx != NULL); + return (pthread_mutex_lock(mtx) == 0) ? thrd_success : thrd_error; +} + +// 7.25.4.4 +int mtx_timedlock(mtx_t *mtx, const struct timespec *ts) { + assert(mtx != NULL); + assert(ts != NULL); + + { +# ifdef EMULATED_THREADS_USE_NATIVE_TIMEDLOCK + int rt; + rt = pthread_mutex_timedlock(mtx, ts); + if (rt == 0) + return thrd_success; + return (rt == ETIMEDOUT) ? thrd_timedout : thrd_error; +# else + time_t expire = time(NULL); + expire += ts->tv_sec; + while (mtx_trylock(mtx) != thrd_success) { + time_t now = time(NULL); + if (expire < now) + return thrd_timedout; + // busy loop! + thrd_yield(); + } + return thrd_success; +# endif + } +} + +// 7.25.4.5 +int mtx_trylock(mtx_t *mtx) { + assert(mtx != NULL); + return (pthread_mutex_trylock(mtx) == 0) ? thrd_success : thrd_busy; +} + +// 7.25.4.6 +int mtx_unlock(mtx_t *mtx) { + assert(mtx != NULL); + return (pthread_mutex_unlock(mtx) == 0) ? thrd_success : thrd_error; +} + +/*------------------- 7.25.5 Thread functions -------------------*/ +// 7.25.5.1 +int thrd_create(thrd_t *thr, thrd_start_t func, void *arg) { + struct impl_thrd_param *pack; + assert(thr != NULL); + pack = (struct impl_thrd_param *) malloc( + sizeof(struct impl_thrd_param)); + if (!pack) + return thrd_nomem; + pack->func = func; + pack->arg = arg; + if (pthread_create(thr, NULL, impl_thrd_routine, pack) != 0) { + free(pack); + return thrd_error; + } + return thrd_success; +} + +// 7.25.5.2 +thrd_t thrd_current(void) { + return pthread_self(); +} + +// 7.25.5.3 +int thrd_detach(thrd_t thr) { + return (pthread_detach(thr) == 0) ? thrd_success : thrd_error; +} + +// 7.25.5.4 +int thrd_equal(thrd_t thr0, thrd_t thr1) { + return pthread_equal(thr0, thr1); +} + +// 7.25.5.5 +_Noreturn void thrd_exit(int res) { + pthread_exit((void *) (intptr_t) res); +} + +// 7.25.5.6 +int thrd_join(thrd_t thr, int *res) { + void *code; + if (pthread_join(thr, &code) != 0) + return thrd_error; + if (res) + *res = (int) (intptr_t) code; + return thrd_success; +} + +// 7.25.5.7 +int thrd_sleep(const struct timespec *time_point, + struct timespec *remaining) { + assert(time_point != NULL); + return nanosleep(time_point, remaining); +} + +// 7.25.5.8 +void thrd_yield(void) { + sched_yield(); +} + +/*----------- 7.25.6 Thread-specific storage functions -----------*/ +// 7.25.6.1 +int tss_create(tss_t *key, tss_dtor_t dtor) { + assert(key != NULL); + return (pthread_key_create(key, dtor) == 0) ? thrd_success + : thrd_error; +} + +// 7.25.6.2 +void tss_delete(tss_t key) { + pthread_key_delete(key); +} + +// 7.25.6.3 +void *tss_get(tss_t key) { + return pthread_getspecific(key); +} + +// 7.25.6.4 +int tss_set(tss_t key, void *val) { + return (pthread_setspecific(key, val) == 0) ? thrd_success + : thrd_error; +} + +#endif |