diff options
author | Erlang/OTP <[email protected]> | 2009-11-20 14:54:40 +0000 |
---|---|---|
committer | Erlang/OTP <[email protected]> | 2009-11-20 14:54:40 +0000 |
commit | 84adefa331c4159d432d22840663c38f155cd4c1 (patch) | |
tree | bff9a9c66adda4df2106dfd0e5c053ab182a12bd /erts/lib_src/common/ethread.c | |
download | otp-84adefa331c4159d432d22840663c38f155cd4c1.tar.gz otp-84adefa331c4159d432d22840663c38f155cd4c1.tar.bz2 otp-84adefa331c4159d432d22840663c38f155cd4c1.zip |
The R13B03 release.OTP_R13B03
Diffstat (limited to 'erts/lib_src/common/ethread.c')
-rw-r--r-- | erts/lib_src/common/ethread.c | 3346 |
1 files changed, 3346 insertions, 0 deletions
diff --git a/erts/lib_src/common/ethread.c b/erts/lib_src/common/ethread.c new file mode 100644 index 0000000000..eb4d0cad20 --- /dev/null +++ b/erts/lib_src/common/ethread.c @@ -0,0 +1,3346 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 2004-2009. All Rights Reserved. + * + * The contents of this file are subject to the Erlang Public License, + * Version 1.1, (the "License"); you may not use this file except in + * compliance with the License. You should have received a copy of the + * Erlang Public License along with this software. If not, it can be + * retrieved online at http://www.erlang.org/. + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * %CopyrightEnd% + */ + +/* + * Description: A Thread library for use in the ERTS and other OTP + * applications. + * Author: Rickard Green + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#undef ETHR_STACK_GUARD_SIZE + +#if defined(ETHR_PTHREADS) + +#ifdef ETHR_TIME_WITH_SYS_TIME +# include <time.h> +# include <sys/time.h> +#else +# ifdef ETHR_HAVE_SYS_TIME_H +# include <sys/time.h> +# else +# include <time.h> +# endif +#endif +#include <sys/types.h> +#include <unistd.h> +#include <signal.h> + +#ifdef ETHR_HAVE_PTHREAD_ATTR_SETGUARDSIZE +# define ETHR_STACK_GUARD_SIZE (pagesize) +#endif + +#elif defined(ETHR_WIN32_THREADS) + +#undef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#include <windows.h> +#include <process.h> +#include <winerror.h> + +#else +#error "Missing thread implementation" +#endif + +#include <limits.h> + +#define ETHR_FORCE_INLINE_FUNCS +#define ETHR_INLINE_FUNC_NAME_(X) X ## __ +#include "ethread.h" + +#ifndef ETHR_HAVE_ETHREAD_DEFINES +#error Missing configure defines +#endif + +/* + * ---------------------------------------------------------------------------- + * Common stuff + * ---------------------------------------------------------------------------- + */ + +#define ETHR_MAX_THREADS 2048 /* Has to be an even power of 2 */ + +static int ethr_not_inited = 1; + +#define ASSERT(A) ETHR_ASSERT((A)) + +static void *(*allocp)(size_t) = malloc; +static void *(*reallocp)(void *, size_t) = realloc; +static void (*freep)(void *) = free; + +#ifndef ETHR_HAVE_OPTIMIZED_ATOMIC_OPS +ethr_atomic_protection_t ethr_atomic_protection__[1 << ETHR_ATOMIC_ADDR_BITS]; +#endif + +void *(*thread_create_prepare_func)(void) = NULL; +void (*thread_create_parent_func)(void *) = NULL; +void (*thread_create_child_func)(void *) = NULL; + +typedef struct ethr_xhndl_list_ ethr_xhndl_list; +struct ethr_xhndl_list_ { + ethr_xhndl_list *next; + void (*funcp)(void); +}; + +static size_t pagesize; +#define ETHR_PAGE_ALIGN(SZ) (((((size_t) (SZ)) - 1)/pagesize + 1)*pagesize) +static size_t min_stack_size; /* kilo words */ +static size_t max_stack_size; /* kilo words */ +#define ETHR_B2KW(B) ((((size_t) (B)) - 1)/(sizeof(void *)*1024) + 1) +#define ETHR_KW2B(KW) (((size_t) (KW))*sizeof(void *)*1024) + +ethr_mutex xhndl_mtx; +ethr_xhndl_list *xhndl_list; + +static int +init_common(ethr_init_data *id) +{ + int res; + if (id) { + allocp = id->alloc; + reallocp = id->realloc; + freep = id->free; + thread_create_prepare_func = id->thread_create_prepare_func; + thread_create_parent_func = id->thread_create_parent_func; + thread_create_child_func = id->thread_create_child_func; + } + if (!allocp || !reallocp || !freep) + return EINVAL; + +#ifdef _SC_PAGESIZE + pagesize = (size_t) sysconf(_SC_PAGESIZE); +#elif defined(HAVE_GETPAGESIZE) + pagesize = (size_t) getpagesize(); +#else + pagesize = (size_t) 4*1024; /* Guess 4 KB */ +#endif + + /* User needs at least 4 KB */ + min_stack_size = 4*1024; +#if SIZEOF_VOID_P == 8 + /* Double that on 64-bit archs */ + min_stack_size *= 2; +#endif + /* On some systems as much as about 4 KB is used by the system */ + min_stack_size += 4*1024; + /* There should be room for signal handlers */ +#ifdef SIGSTKSZ + min_stack_size += SIGSTKSZ; +#else + min_stack_size += pagesize; +#endif + /* The system may think that we need more stack */ +#if defined(PTHREAD_STACK_MIN) + if (min_stack_size < PTHREAD_STACK_MIN) + min_stack_size = PTHREAD_STACK_MIN; +#elif defined(_SC_THREAD_STACK_MIN) + { + size_t thr_min_stk_sz = (size_t) sysconf(_SC_THREAD_STACK_MIN); + if (min_stack_size < thr_min_stk_sz) + min_stack_size = thr_min_stk_sz; + } +#endif + /* The guard is at least on some platforms included in the stack size + passed when creating threads */ +#ifdef ETHR_STACK_GUARD_SIZE + min_stack_size += ETHR_STACK_GUARD_SIZE; +#endif + min_stack_size = ETHR_PAGE_ALIGN(min_stack_size); + + min_stack_size = ETHR_B2KW(min_stack_size); + + max_stack_size = 32*1024*1024; +#if SIZEOF_VOID_P == 8 + max_stack_size *= 2; +#endif + max_stack_size = ETHR_B2KW(max_stack_size); + + xhndl_list = NULL; + + res = ethr_mutex_init(&xhndl_mtx); + if (res != 0) + return res; + + res = ethr_mutex_set_forksafe(&xhndl_mtx); + if (res != 0 && res != ENOTSUP) + return res; + + return 0; +} + +int +ethr_install_exit_handler(void (*funcp)(void)) +{ + ethr_xhndl_list *xhp; + int res; + +#if ETHR_XCHK + if (ethr_not_inited) { + ASSERT(0); + return EACCES; + } +#endif + + if (!funcp) + return EINVAL; + + xhp = (ethr_xhndl_list *) (*allocp)(sizeof(ethr_xhndl_list)); + if (!xhp) + return ENOMEM; + + res = ethr_mutex_lock__(&xhndl_mtx); + if (res != 0) { + (*freep)((void *) xhp); + return res; + } + + xhp->funcp = funcp; + xhp->next = xhndl_list; + xhndl_list = xhp; + + res = ethr_mutex_unlock__(&xhndl_mtx); + if (res != 0) + abort(); + + return res; +} + +static void +run_exit_handlers(void) +{ + int res; + ethr_xhndl_list *xhp; + + res = ethr_mutex_lock__(&xhndl_mtx); + if (res != 0) + abort(); + + xhp = xhndl_list; + + res = ethr_mutex_unlock__(&xhndl_mtx); + if (res != 0) + abort(); + + for (; xhp; xhp = xhp->next) + (*xhp->funcp)(); +} + +#if defined(ETHR_PTHREADS) +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\ + * pthread implementation * +\* */ + +typedef struct { + pthread_mutex_t mtx; + pthread_cond_t cnd; + int initialized; + void *(*thr_func)(void *); + void *arg; + void *prep_func_res; +} thr_wrap_data_; + +static int no_ethreads; +static ethr_mutex no_ethrs_mtx; + +#ifndef ETHR_HAVE_PTHREAD_ATFORK +#define ETHR_HAVE_PTHREAD_ATFORK 0 +#endif + +#if !ETHR_HAVE_PTHREAD_ATFORK +#warning "Cannot enforce fork-safety" +#endif + +/* + * ---------------------------------------------------------------------------- + * Static functions + * ---------------------------------------------------------------------------- + */ + +/* + * Functions with safe_ prefix aborts on failure. To be used when + * we cannot recover after failure. + */ + +static ETHR_INLINE void +safe_mutex_lock(pthread_mutex_t *mtxp) +{ + int res = pthread_mutex_lock(mtxp); + if (res != 0) + abort(); +} + +static ETHR_INLINE void +safe_mutex_unlock(pthread_mutex_t *mtxp) +{ + int res = pthread_mutex_unlock(mtxp); + if (res != 0) + abort(); +} + +static ETHR_INLINE void +safe_cond_signal(pthread_cond_t *cndp) +{ + int res = pthread_cond_signal(cndp); + if (res != 0) + abort(); +} + +#ifdef ETHR_HAVE_ETHR_REC_MUTEX_INIT + +static volatile int rec_mtx_attr_need_init = 1; +static pthread_mutexattr_t rec_mtx_attr; + +static int init_rec_mtx_attr(void); + +#endif + +#if ETHR_HAVE_PTHREAD_ATFORK + +static ethr_mutex forksafe_mtx = ETHR_MUTEX_INITER; + +static void lock_mutexes(void) +{ + ethr_mutex *m = &forksafe_mtx; + do { + + safe_mutex_lock(&m->pt_mtx); + + m = m->next; + + } while (m != &forksafe_mtx); +} + +static void unlock_mutexes(void) +{ + ethr_mutex *m = forksafe_mtx.prev; + do { + + safe_mutex_unlock(&m->pt_mtx); + + m = m->prev; + + } while (m->next != &forksafe_mtx); +} + +#if ETHR_INIT_MUTEX_IN_CHILD_AT_FORK + +static void reinit_mutexes(void) +{ + ethr_mutex *m = forksafe_mtx.prev; + do { + pthread_mutexattr_t *attrp = NULL; + +#ifdef ETHR_HAVE_ETHR_REC_MUTEX_INIT + if (m->is_rec_mtx) { + if (rec_mtx_attr_need_init) { + int res = init_rec_mtx_attr(); + if (res != 0) + abort(); + } + attrp = &rec_mtx_attr; + } +#endif + if (pthread_mutex_init(&m->pt_mtx, attrp) != 0) + abort(); + + m = m->prev; + + } while (m->next != &forksafe_mtx); +} + +#endif + +static int +init_forksafe(void) +{ + static int init_done = 0; + int res = 0; + + if (init_done) + return res; + + forksafe_mtx.prev = &forksafe_mtx; + forksafe_mtx.next = &forksafe_mtx; + + res = pthread_atfork(lock_mutexes, + unlock_mutexes, +#if ETHR_INIT_MUTEX_IN_CHILD_AT_FORK + reinit_mutexes +#else + unlock_mutexes +#endif + ); + + init_done = 1; + return res; +} + +#endif + + +#ifdef ETHR_HAVE_ETHR_REC_MUTEX_INIT + +#if defined(ETHR_HAVE_PTHREAD_MUTEXATTR_SETTYPE) + +#define SET_REC_MUTEX_ATTR(AP) \ + pthread_mutexattr_settype((AP), PTHREAD_MUTEX_RECURSIVE); + +#elif defined(ETHR_HAVE_PTHREAD_MUTEXATTR_SETKIND_NP) + +#define SET_REC_MUTEX_ATTR(AP) \ + pthread_mutexattr_setkind_np((AP), PTHREAD_MUTEX_RECURSIVE_NP); + +#else + +#error "Don't know how to set recursive mutex attributes" + +#endif + +static int +init_rec_mtx_attr(void) +{ + int res, mres; + static pthread_mutex_t attrinit_mtx = PTHREAD_MUTEX_INITIALIZER; + + mres = pthread_mutex_lock(&attrinit_mtx); + if (mres != 0) + return mres; + /* Got here under race conditions; check again ... */ + if (!rec_mtx_attr_need_init) + res = 0; + else { + res = pthread_mutexattr_init(&rec_mtx_attr); + if (res == 0) { + res = SET_REC_MUTEX_ATTR(&rec_mtx_attr); + if (res == 0) + rec_mtx_attr_need_init = 0; + else + (void) pthread_mutexattr_destroy(&rec_mtx_attr); + } + } + + mres = pthread_mutex_unlock(&attrinit_mtx); + if (mres != 0) + return mres; + return res; +} + +#endif /* #if ETHR_HAVE_ETHR_REC_MUTEX_INIT */ + +static ETHR_INLINE void thr_exit_cleanup(void) +{ + run_exit_handlers(); + safe_mutex_lock(&no_ethrs_mtx.pt_mtx); + ASSERT(no_ethreads > 0); + no_ethreads--; + safe_mutex_unlock(&no_ethrs_mtx.pt_mtx); +} + +static void *thr_wrapper(void *vtwd) +{ + void *res; + thr_wrap_data_ *twd = (thr_wrap_data_ *) vtwd; + void *(*thr_func)(void *) = twd->thr_func; + void *arg = twd->arg; + + safe_mutex_lock(&twd->mtx); + + if (thread_create_child_func) + (*thread_create_child_func)(twd->prep_func_res); + + twd->initialized = 1; + + safe_cond_signal(&twd->cnd); + safe_mutex_unlock(&twd->mtx); + + res = (*thr_func)(arg); + thr_exit_cleanup(); + return res; +} + + +/* + * ---------------------------------------------------------------------------- + * Exported functions + * ---------------------------------------------------------------------------- + */ + +int +ethr_init(ethr_init_data *id) +{ + int res; + + if (!ethr_not_inited) + return EINVAL; + + ethr_not_inited = 0; + + res = init_common(id); + if (res != 0) + goto error; + +#if ETHR_HAVE_PTHREAD_ATFORK + init_forksafe(); +#endif + + no_ethreads = 1; + res = ethr_mutex_init(&no_ethrs_mtx); + if (res != 0) + goto error; + res = ethr_mutex_set_forksafe(&no_ethrs_mtx); + if (res != 0 && res != ENOTSUP) + goto error; + +#ifndef ETHR_HAVE_OPTIMIZED_ATOMIC_OPS + { + int i; + for (i = 0; i < (1 << ETHR_ATOMIC_ADDR_BITS); i++) { +#ifdef ETHR_HAVE_PTHREAD_SPIN_LOCK + res = pthread_spin_init(ðr_atomic_protection__[i].u.spnlck, 0); +#else + res = ethr_mutex_init(ðr_atomic_protection__[i].u.mtx); +#endif + if (res != 0) + goto error; + } + } +#endif + + return 0; + + error: + ethr_not_inited = 1; + return res; + +} + +int +ethr_thr_create(ethr_tid *tid, void * (*func)(void *), void *arg, + ethr_thr_opts *opts) +{ + thr_wrap_data_ twd; + pthread_attr_t attr; + int res, dres; + int use_stack_size = (opts && opts->suggested_stack_size >= 0 + ? opts->suggested_stack_size + : -1 /* Use system default */); + +#ifdef ETHR_MODIFIED_DEFAULT_STACK_SIZE + if (use_stack_size < 0) + use_stack_size = ETHR_MODIFIED_DEFAULT_STACK_SIZE; +#endif + + twd.initialized = 0; + twd.thr_func = func; + twd.arg = arg; + +#if ETHR_XCHK + if (ethr_not_inited) { + ASSERT(0); + return EACCES; + } + if (!tid || !func) { + ASSERT(0); + return EINVAL; + } +#endif + + /* Call prepare func if it exist */ + if (thread_create_prepare_func) + twd.prep_func_res = (*thread_create_prepare_func)(); + else + twd.prep_func_res = NULL; + + /* Set som thread attributes */ + res = pthread_attr_init(&attr); + if (res != 0) + goto cleanup_parent_func; + res = pthread_mutex_init(&twd.mtx, NULL); + if (res != 0) + goto cleanup_attr_destroy; + res = pthread_cond_init(&twd.cnd, NULL); + if (res != 0) + goto cleanup_mutex_destroy; + + /* Schedule child thread in system scope (if possible) ... */ + res = pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM); + if (res != 0 && res != ENOTSUP) + goto cleanup_cond_destroy; + + if (use_stack_size >= 0) { + size_t suggested_stack_size = (size_t) use_stack_size; + size_t stack_size; +#ifdef DEBUG + suggested_stack_size /= 2; /* Make sure we got margin */ +#endif +#ifdef ETHR_STACK_GUARD_SIZE + /* The guard is at least on some platforms included in the stack size + passed when creating threads */ + suggested_stack_size += ETHR_B2KW(ETHR_STACK_GUARD_SIZE); +#endif + if (suggested_stack_size < min_stack_size) + stack_size = ETHR_KW2B(min_stack_size); + else if (suggested_stack_size > max_stack_size) + stack_size = ETHR_KW2B(max_stack_size); + else + stack_size = ETHR_PAGE_ALIGN(ETHR_KW2B(suggested_stack_size)); + (void) pthread_attr_setstacksize(&attr, stack_size); + } + +#ifdef ETHR_STACK_GUARD_SIZE + (void) pthread_attr_setguardsize(&attr, ETHR_STACK_GUARD_SIZE); +#endif + + /* Detached or joinable... */ + res = pthread_attr_setdetachstate(&attr, + (opts && opts->detached + ? PTHREAD_CREATE_DETACHED + : PTHREAD_CREATE_JOINABLE)); + if (res != 0) + goto cleanup_cond_destroy; + + res = pthread_mutex_lock(&twd.mtx); + + if (res != 0) + goto cleanup_cond_destroy; + + safe_mutex_lock(&no_ethrs_mtx.pt_mtx); + if (no_ethreads < ETHR_MAX_THREADS) { + no_ethreads++; + safe_mutex_unlock(&no_ethrs_mtx.pt_mtx); + } + else { + res = EAGAIN; + safe_mutex_unlock(&no_ethrs_mtx.pt_mtx); + goto cleanup_mutex_unlock; + } + + res = pthread_create((pthread_t *) tid, &attr, thr_wrapper, (void *) &twd); + + if (res != 0) { + safe_mutex_lock(&no_ethrs_mtx.pt_mtx); + ASSERT(no_ethreads > 0); + no_ethreads--; + safe_mutex_unlock(&no_ethrs_mtx.pt_mtx); + } + else { + + /* Wait for child to initialize... */ + while (!twd.initialized) { + res = pthread_cond_wait(&twd.cnd, &twd.mtx); + if (res != 0 && res != EINTR) + break; + } + + } + + /* Cleanup... */ + cleanup_mutex_unlock: + dres = pthread_mutex_unlock(&twd.mtx); + if (res == 0) + res = dres; + cleanup_cond_destroy: + dres = pthread_cond_destroy(&twd.cnd); + if (res == 0) + res = dres; + cleanup_mutex_destroy: + dres = pthread_mutex_destroy(&twd.mtx); + if (res == 0) + res = dres; + cleanup_attr_destroy: + dres = pthread_attr_destroy(&attr); + if (res == 0) + res = dres; + cleanup_parent_func: + if (thread_create_parent_func) + (*thread_create_parent_func)(twd.prep_func_res); + + return res; +} + +int +ethr_thr_join(ethr_tid tid, void **res) +{ +#if ETHR_XCHK + if (ethr_not_inited) { + ASSERT(0); + return EACCES; + } +#endif + return pthread_join((pthread_t) tid, res); +} + +int +ethr_thr_detach(ethr_tid tid) +{ +#if ETHR_XCHK + if (ethr_not_inited) { + ASSERT(0); + return EACCES; + } +#endif + return pthread_detach((pthread_t) tid); +} + +void +ethr_thr_exit(void *res) +{ +#if ETHR_XCHK + if (ethr_not_inited) { + ASSERT(0); + return; + } +#endif + thr_exit_cleanup(); + pthread_exit(res); +} + +ethr_tid +ethr_self(void) +{ + return (ethr_tid) pthread_self(); +} + +int +ethr_equal_tids(ethr_tid tid1, ethr_tid tid2) +{ + return pthread_equal((pthread_t) tid1, (pthread_t) tid2); +} + + +/* + * Mutex functions + */ + + +int +ethr_mutex_init(ethr_mutex *mtx) +{ +#if ETHR_XCHK + if (ethr_not_inited) { + ASSERT(0); + return EACCES; + } + if (!mtx) { + ASSERT(0); + return EINVAL; + } + mtx->initialized = ETHR_MUTEX_INITIALIZED; +#endif + mtx->prev = NULL; + mtx->next = NULL; + mtx->is_rec_mtx = 0; + return pthread_mutex_init(&mtx->pt_mtx, NULL); +} + +#ifdef ETHR_HAVE_ETHR_REC_MUTEX_INIT + +int +ethr_rec_mutex_init(ethr_mutex *mtx) +{ +#if ETHR_XCHK + if (ethr_not_inited) { + ASSERT(0); + return EACCES; + } + if (!mtx) { + ASSERT(0); + return EINVAL; + } + mtx->initialized = ETHR_MUTEX_INITIALIZED; +#endif + if (rec_mtx_attr_need_init) + init_rec_mtx_attr(); + + mtx->prev = NULL; + mtx->next = NULL; + mtx->is_rec_mtx = 1; + return pthread_mutex_init(&mtx->pt_mtx, &rec_mtx_attr); +} + +#endif /* #if ETHR_HAVE_ETHR_REC_MUTEX_INIT */ + +int +ethr_mutex_destroy(ethr_mutex *mtx) +{ +#if ETHR_XCHK + if (ethr_not_inited) { + ASSERT(0); + return EACCES; + } + if (!mtx || mtx->initialized != ETHR_MUTEX_INITIALIZED) { + ASSERT(0); + return EINVAL; + } +#endif + if (mtx->next) { + ASSERT(mtx->prev); + ethr_mutex_unset_forksafe(mtx); + } +#if ETHR_XCHK + mtx->initialized = 0; +#endif + return pthread_mutex_destroy(&mtx->pt_mtx); +} + +int ethr_mutex_set_forksafe(ethr_mutex *mtx) +{ + int res; +#if ETHR_XCHK + if (ethr_not_inited) { + ASSERT(0); + return EACCES; + } + if (!mtx || mtx->initialized != ETHR_MUTEX_INITIALIZED) { + ASSERT(0); + return EINVAL; + } +#endif +#if ETHR_HAVE_PTHREAD_ATFORK + res = pthread_mutex_lock(&forksafe_mtx.pt_mtx); + if (res != 0) + return res; + if (!forksafe_mtx.next) { + ASSERT(!forksafe_mtx.prev); + init_forksafe(); + } + if (mtx->next) { + /* forksafe already set for this mutex */ + ASSERT(mtx->prev); + } + else { + mtx->next = forksafe_mtx.next; + mtx->prev = &forksafe_mtx; + forksafe_mtx.next->prev = mtx; + forksafe_mtx.next = mtx; + } + + res = pthread_mutex_unlock(&forksafe_mtx.pt_mtx); + +#else /* #if ETHR_HAVE_PTHREAD_ATFORK */ + res = ENOTSUP; +#endif /* #if ETHR_HAVE_PTHREAD_ATFORK */ + return res; +} + +int ethr_mutex_unset_forksafe(ethr_mutex *mtx) +{ + int res; +#if ETHR_XCHK + if (ethr_not_inited) { + ASSERT(0); + return EACCES; + } + if (!mtx || mtx->initialized != ETHR_MUTEX_INITIALIZED) { + ASSERT(0); + return EINVAL; + } +#endif +#if ETHR_HAVE_PTHREAD_ATFORK + res = pthread_mutex_lock(&forksafe_mtx.pt_mtx); + if (res != 0) + return res; + if (!forksafe_mtx.next) { + ASSERT(!forksafe_mtx.prev); + init_forksafe(); + } + if (!mtx->next) { + /* forksafe already unset for this mutex */ + ASSERT(!mtx->prev); + } + else { + mtx->prev->next = mtx->next; + mtx->next->prev = mtx->prev; + mtx->next = NULL; + mtx->prev = NULL; + } + res = pthread_mutex_unlock(&forksafe_mtx.pt_mtx); + +#else /* #if ETHR_HAVE_PTHREAD_ATFORK */ + res = ENOTSUP; +#endif /* #if ETHR_HAVE_PTHREAD_ATFORK */ + return res; +} + +int +ethr_mutex_trylock(ethr_mutex *mtx) +{ +#if ETHR_XCHK + if (ethr_not_inited) { + ASSERT(0); + return EACCES; + } + if (!mtx || mtx->initialized != ETHR_MUTEX_INITIALIZED) { + ASSERT(0); + return EINVAL; + } +#endif + return ethr_mutex_trylock__(mtx); +} + +int +ethr_mutex_lock(ethr_mutex *mtx) +{ +#if ETHR_XCHK + if (ethr_not_inited) { + ASSERT(0); + return EACCES; + } + if (!mtx || mtx->initialized != ETHR_MUTEX_INITIALIZED) { + ASSERT(0); + return EINVAL; + } +#endif + return ethr_mutex_lock__(mtx); +} + +int +ethr_mutex_unlock(ethr_mutex *mtx) +{ +#if ETHR_XCHK + if (ethr_not_inited) { + ASSERT(0); + return EACCES; + } + if (!mtx || mtx->initialized != ETHR_MUTEX_INITIALIZED) { + ASSERT(0); + return EINVAL; + } +#endif + return ethr_mutex_unlock__(mtx); +} + +/* + * Condition variable functions + */ + +int +ethr_cond_init(ethr_cond *cnd) +{ +#if ETHR_XCHK + if (ethr_not_inited) { + ASSERT(0); + return EACCES; + } + if (!cnd) { + ASSERT(0); + return EINVAL; + } + cnd->initialized = ETHR_COND_INITIALIZED; +#endif + return pthread_cond_init(&cnd->pt_cnd, NULL); +} + +int +ethr_cond_destroy(ethr_cond *cnd) +{ +#if ETHR_XCHK + if (ethr_not_inited) { + ASSERT(0); + return EACCES; + } + if (!cnd || cnd->initialized != ETHR_COND_INITIALIZED) { + ASSERT(0); + return EINVAL; + } + cnd->initialized = 0; +#endif + return pthread_cond_destroy(&cnd->pt_cnd); +} + +int +ethr_cond_signal(ethr_cond *cnd) +{ +#if ETHR_XCHK + if (ethr_not_inited) { + ASSERT(0); + return EACCES; + } + if (!cnd || cnd->initialized != ETHR_COND_INITIALIZED) { + ASSERT(0); + return EINVAL; + } +#endif + return pthread_cond_signal(&cnd->pt_cnd); +} + +int +ethr_cond_broadcast(ethr_cond *cnd) +{ +#if ETHR_XCHK + if (ethr_not_inited) { + ASSERT(0); + return EACCES; + } + if (!cnd || cnd->initialized != ETHR_COND_INITIALIZED) { + ASSERT(0); + return EINVAL; + } +#endif + return pthread_cond_broadcast(&cnd->pt_cnd); +} + +int +ethr_cond_wait(ethr_cond *cnd, ethr_mutex *mtx) +{ +#if ETHR_XCHK + if (ethr_not_inited) { + ASSERT(0); + return EACCES; + } + if (!cnd + || cnd->initialized != ETHR_COND_INITIALIZED + || !mtx + || mtx->initialized != ETHR_MUTEX_INITIALIZED) { + ASSERT(0); + return EINVAL; + } +#endif + return pthread_cond_wait(&cnd->pt_cnd, &mtx->pt_mtx); +} + +int +ethr_cond_timedwait(ethr_cond *cnd, ethr_mutex *mtx, ethr_timeval *timeout) +{ + struct timespec to; +#if ETHR_XCHK + if (ethr_not_inited) { + ASSERT(0); + return EACCES; + } + if (!cnd + || cnd->initialized != ETHR_COND_INITIALIZED + || !mtx + || mtx->initialized != ETHR_MUTEX_INITIALIZED + || !timeout) { + ASSERT(0); + return EINVAL; + } +#endif + + to.tv_sec = timeout->tv_sec; + to.tv_nsec = timeout->tv_nsec; + + return pthread_cond_timedwait(&cnd->pt_cnd, &mtx->pt_mtx, &to); +} + + +#ifdef ETHR_HAVE_PTHREAD_RWLOCK_INIT + +int +ethr_rwmutex_init(ethr_rwmutex *rwmtx) +{ +#if ETHR_XCHK + if (ethr_not_inited) { + ASSERT(0); + return EACCES; + } + if (!rwmtx) { + ASSERT(0); + return EINVAL; + } + rwmtx->initialized = ETHR_RWMUTEX_INITIALIZED; +#endif + return pthread_rwlock_init(&rwmtx->pt_rwlock, NULL); +} + +int +ethr_rwmutex_destroy(ethr_rwmutex *rwmtx) +{ + int res; +#if ETHR_XCHK + if (ethr_not_inited) { + ASSERT(0); + return EACCES; + } + if (!rwmtx || rwmtx->initialized != ETHR_RWMUTEX_INITIALIZED) { + ASSERT(0); + return EINVAL; + } +#endif + res = pthread_rwlock_destroy(&rwmtx->pt_rwlock); +#if ETHR_XCHK + rwmtx->initialized = 0; +#endif + return res; +} + +int +ethr_rwmutex_tryrlock(ethr_rwmutex *rwmtx) +{ +#if ETHR_XCHK + if (ethr_not_inited) { + ASSERT(0); + return EACCES; + } + if (!rwmtx || rwmtx->initialized != ETHR_RWMUTEX_INITIALIZED) { + ASSERT(0); + return EINVAL; + } +#endif + return ethr_rwmutex_tryrlock__(rwmtx); +} + +int +ethr_rwmutex_rlock(ethr_rwmutex *rwmtx) +{ +#if ETHR_XCHK + if (ethr_not_inited) { + ASSERT(0); + return EACCES; + } + if (!rwmtx || rwmtx->initialized != ETHR_RWMUTEX_INITIALIZED) { + ASSERT(0); + return EINVAL; + } +#endif + return ethr_rwmutex_rlock__(rwmtx); +} + +int +ethr_rwmutex_runlock(ethr_rwmutex *rwmtx) +{ +#if ETHR_XCHK + if (ethr_not_inited) { + ASSERT(0); + return EACCES; + } + if (!rwmtx || rwmtx->initialized != ETHR_RWMUTEX_INITIALIZED) { + ASSERT(0); + return EINVAL; + } +#endif + return ethr_rwmutex_runlock__(rwmtx); +} + +int +ethr_rwmutex_tryrwlock(ethr_rwmutex *rwmtx) +{ +#if ETHR_XCHK + if (ethr_not_inited) { + ASSERT(0); + return EACCES; + } + if (!rwmtx || rwmtx->initialized != ETHR_RWMUTEX_INITIALIZED) { + ASSERT(0); + return EINVAL; + } +#endif + return ethr_rwmutex_tryrwlock__(rwmtx); +} + +int +ethr_rwmutex_rwlock(ethr_rwmutex *rwmtx) +{ +#if ETHR_XCHK + if (ethr_not_inited) { + ASSERT(0); + return EACCES; + } + if (!rwmtx || rwmtx->initialized != ETHR_RWMUTEX_INITIALIZED) { + ASSERT(0); + return EINVAL; + } +#endif + return ethr_rwmutex_rwlock__(rwmtx); +} + +int +ethr_rwmutex_rwunlock(ethr_rwmutex *rwmtx) +{ +#if ETHR_XCHK + if (ethr_not_inited) { + ASSERT(0); + return EACCES; + } + if (!rwmtx || rwmtx->initialized != ETHR_RWMUTEX_INITIALIZED) { + ASSERT(0); + return EINVAL; + } +#endif + return ethr_rwmutex_rwunlock__(rwmtx); +} + +#endif /* #ifdef ETHR_HAVE_PTHREAD_RWLOCK_INIT */ + +/* + * Current time + */ + +int +ethr_time_now(ethr_timeval *time) +{ + int res; + struct timeval tv; +#if ETHR_XCHK + if (ethr_not_inited) { + ASSERT(0); + return EACCES; + } + if (!time) { + ASSERT(0); + return EINVAL; + } +#endif + + res = gettimeofday(&tv, NULL); + time->tv_sec = (long) tv.tv_sec; + time->tv_nsec = ((long) tv.tv_usec)*1000; + return res; +} + +/* + * Thread specific data + */ + +int +ethr_tsd_key_create(ethr_tsd_key *keyp) +{ +#if ETHR_XCHK + if (ethr_not_inited) { + ASSERT(0); + return EACCES; + } + if (!keyp) { + ASSERT(0); + return EINVAL; + } +#endif + return pthread_key_create((pthread_key_t *) keyp, NULL); +} + +int +ethr_tsd_key_delete(ethr_tsd_key key) +{ +#if ETHR_XCHK + if (ethr_not_inited) { + ASSERT(0); + return EACCES; + } +#endif + return pthread_key_delete((pthread_key_t) key); +} + +int +ethr_tsd_set(ethr_tsd_key key, void *value) +{ +#if ETHR_XCHK + if (ethr_not_inited) { + ASSERT(0); + return EACCES; + } +#endif + return pthread_setspecific((pthread_key_t) key, value); +} + +void * +ethr_tsd_get(ethr_tsd_key key) +{ +#if ETHR_XCHK + if (ethr_not_inited) { + ASSERT(0); + return NULL; + } +#endif + return pthread_getspecific((pthread_key_t) key); +} + +/* + * Signal functions + */ + +#if ETHR_HAVE_ETHR_SIG_FUNCS + +int ethr_sigmask(int how, const sigset_t *set, sigset_t *oset) +{ +#if ETHR_XCHK + if (ethr_not_inited) { + ASSERT(0); + return EACCES; + } + if (!set && !oset) { + ASSERT(0); + return EINVAL; + } +#endif + return pthread_sigmask(how, set, oset); +} + +int ethr_sigwait(const sigset_t *set, int *sig) +{ +#if ETHR_XCHK + if (ethr_not_inited) { + ASSERT(0); + return EACCES; + } + if (!set || !sig) { + ASSERT(0); + return EINVAL; + } +#endif + if (sigwait(set, sig) < 0) + return errno; + return 0; +} + +#endif /* #if ETHR_HAVE_ETHR_SIG_FUNCS */ + +#elif defined(ETHR_WIN32_THREADS) +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\ + * Native win32 threads implementation * +\* */ + +#define INVALID_TID -1 + +/* The spin count values are more or less taken out of the blue */ +#define ETHR_MUTEX_SPIN_COUNT 5000 +#define ETHR_COND_SPIN_COUNT 1000 + +ethr_tid serial_shift; /* Bits to shift serial when constructing a tid */ +ethr_tid last_serial; /* Last thread table serial used */ +ethr_tid last_ix; /* Last thread table index used */ +ethr_tid thr_ix_mask; /* Mask used to mask out thread table index from a tid */ + +/* Event used for conditional variables. On per thread. */ +/*typedef struct cnd_wait_event__ cnd_wait_event_;*/ +struct cnd_wait_event__ { + HANDLE handle; + cnd_wait_event_ *prev; + cnd_wait_event_ *next; + int in_queue; +}; + +/* Thread specific data. Stored in the thread table */ +typedef struct { + ethr_tid thr_id; + HANDLE thr_handle; + ethr_tid joiner; + void *result; + cnd_wait_event_ wait_event; +} thr_data_; + +/* Argument passed to thr_wrapper() */ +typedef struct { + void * (*func)(void *); + void * arg; + thr_data_ *ptd; + thr_data_ *td; + int res; + void *prep_func_res; +} thr_wrap_data_; + + +static CRITICAL_SECTION thr_table_cs; /* Critical section used to protect + the thread table from concurrent + accesses. */ +static CRITICAL_SECTION fake_static_init_cs; /* Critical section used to protect + initialazition of 'statically + initialized' mutexes */ +static thr_data_ * thr_table[ETHR_MAX_THREADS]; /* The thread table */ + +static DWORD tls_own_thr_data; + +static thr_data_ main_thr_data; + +#define THR_IX(TID) ((TID) & thr_ix_mask) +#define OWN_THR_DATA ((thr_data_ *) TlsGetValue(tls_own_thr_data)) + +/* + * ---------------------------------------------------------------------------- + * Static functions + * ---------------------------------------------------------------------------- + */ + +static int +get_errno(void) +{ + switch (GetLastError()) { + case ERROR_INVALID_FUNCTION: return EINVAL; /* 1 */ + case ERROR_FILE_NOT_FOUND: return ENOENT; /* 2 */ + case ERROR_PATH_NOT_FOUND: return ENOENT; /* 3 */ + case ERROR_TOO_MANY_OPEN_FILES: return EMFILE; /* 4 */ + case ERROR_ACCESS_DENIED: return EACCES; /* 5 */ + case ERROR_INVALID_HANDLE: return EBADF; /* 6 */ + case ERROR_ARENA_TRASHED: return ENOMEM; /* 7 */ + case ERROR_NOT_ENOUGH_MEMORY: return ENOMEM; /* 8 */ + case ERROR_INVALID_BLOCK: return ENOMEM; /* 9 */ + case ERROR_BAD_ENVIRONMENT: return E2BIG; /* 10 */ + case ERROR_BAD_FORMAT: return ENOEXEC; /* 11 */ + case ERROR_INVALID_ACCESS: return EINVAL; /* 12 */ + case ERROR_INVALID_DATA: return EINVAL; /* 13 */ + case ERROR_OUTOFMEMORY: return ENOMEM; /* 14 */ + case ERROR_INVALID_DRIVE: return ENOENT; /* 15 */ + case ERROR_CURRENT_DIRECTORY: return EACCES; /* 16 */ + case ERROR_NOT_SAME_DEVICE: return EXDEV; /* 17 */ + case ERROR_NO_MORE_FILES: return ENOENT; /* 18 */ + case ERROR_WRITE_PROTECT: return EACCES; /* 19 */ + case ERROR_BAD_UNIT: return EACCES; /* 20 */ + case ERROR_NOT_READY: return EACCES; /* 21 */ + case ERROR_BAD_COMMAND: return EACCES; /* 22 */ + case ERROR_CRC: return EACCES; /* 23 */ + case ERROR_BAD_LENGTH: return EACCES; /* 24 */ + case ERROR_SEEK: return EACCES; /* 25 */ + case ERROR_NOT_DOS_DISK: return EACCES; /* 26 */ + case ERROR_SECTOR_NOT_FOUND: return EACCES; /* 27 */ + case ERROR_OUT_OF_PAPER: return EACCES; /* 28 */ + case ERROR_WRITE_FAULT: return EACCES; /* 29 */ + case ERROR_READ_FAULT: return EACCES; /* 30 */ + case ERROR_GEN_FAILURE: return EACCES; /* 31 */ + case ERROR_SHARING_VIOLATION: return EACCES; /* 32 */ + case ERROR_LOCK_VIOLATION: return EACCES; /* 33 */ + case ERROR_WRONG_DISK: return EACCES; /* 34 */ + case ERROR_SHARING_BUFFER_EXCEEDED: return EACCES; /* 36 */ + case ERROR_BAD_NETPATH: return ENOENT; /* 53 */ + case ERROR_NETWORK_ACCESS_DENIED: return EACCES; /* 65 */ + case ERROR_BAD_NET_NAME: return ENOENT; /* 67 */ + case ERROR_FILE_EXISTS: return EEXIST; /* 80 */ + case ERROR_CANNOT_MAKE: return EACCES; /* 82 */ + case ERROR_FAIL_I24: return EACCES; /* 83 */ + case ERROR_INVALID_PARAMETER: return EINVAL; /* 87 */ + case ERROR_NO_PROC_SLOTS: return EAGAIN; /* 89 */ + case ERROR_DRIVE_LOCKED: return EACCES; /* 108 */ + case ERROR_BROKEN_PIPE: return EPIPE; /* 109 */ + case ERROR_DISK_FULL: return ENOSPC; /* 112 */ + case ERROR_INVALID_TARGET_HANDLE: return EBADF; /* 114 */ + case ERROR_WAIT_NO_CHILDREN: return ECHILD; /* 128 */ + case ERROR_CHILD_NOT_COMPLETE: return ECHILD; /* 129 */ + case ERROR_DIRECT_ACCESS_HANDLE: return EBADF; /* 130 */ + case ERROR_NEGATIVE_SEEK: return EINVAL; /* 131 */ + case ERROR_SEEK_ON_DEVICE: return EACCES; /* 132 */ + case ERROR_DIR_NOT_EMPTY: return ENOTEMPTY;/* 145 */ + case ERROR_NOT_LOCKED: return EACCES; /* 158 */ + case ERROR_BAD_PATHNAME: return ENOENT; /* 161 */ + case ERROR_MAX_THRDS_REACHED: return EAGAIN; /* 164 */ + case ERROR_LOCK_FAILED: return EACCES; /* 167 */ + case ERROR_ALREADY_EXISTS: return EEXIST; /* 183 */ + case ERROR_INVALID_STARTING_CODESEG: return ENOEXEC; /* 188 */ + case ERROR_INVALID_STACKSEG: return ENOEXEC; /* 189 */ + case ERROR_INVALID_MODULETYPE: return ENOEXEC; /* 190 */ + case ERROR_INVALID_EXE_SIGNATURE: return ENOEXEC; /* 191 */ + case ERROR_EXE_MARKED_INVALID: return ENOEXEC; /* 192 */ + case ERROR_BAD_EXE_FORMAT: return ENOEXEC; /* 193 */ + case ERROR_ITERATED_DATA_EXCEEDS_64k: return ENOEXEC; /* 194 */ + case ERROR_INVALID_MINALLOCSIZE: return ENOEXEC; /* 195 */ + case ERROR_DYNLINK_FROM_INVALID_RING: return ENOEXEC; /* 196 */ + case ERROR_IOPL_NOT_ENABLED: return ENOEXEC; /* 197 */ + case ERROR_INVALID_SEGDPL: return ENOEXEC; /* 198 */ + case ERROR_AUTODATASEG_EXCEEDS_64k: return ENOEXEC; /* 199 */ + case ERROR_RING2SEG_MUST_BE_MOVABLE: return ENOEXEC; /* 200 */ + case ERROR_RELOC_CHAIN_XEEDS_SEGLIM: return ENOEXEC; /* 201 */ + case ERROR_INFLOOP_IN_RELOC_CHAIN: return ENOEXEC; /* 202 */ + case ERROR_FILENAME_EXCED_RANGE: return ENOENT; /* 206 */ + case ERROR_NESTING_NOT_ALLOWED: return EAGAIN; /* 215 */ + case ERROR_NOT_ENOUGH_QUOTA: return ENOMEM; /* 1816 */ + default: return EINVAL; + } +} + +static ETHR_INLINE thr_data_ * +tid2thr(ethr_tid tid) +{ + ethr_tid ix; + thr_data_ *td; + + if (tid < 0) + return NULL; + ix = THR_IX(tid); + if (ix >= ETHR_MAX_THREADS) + return NULL; + td = thr_table[ix]; + if (!td) + return NULL; + if (td->thr_id != tid) + return NULL; + return td; +} + +static ETHR_INLINE void +new_tid(ethr_tid *new_tid, ethr_tid *new_serial, ethr_tid *new_ix) +{ + ethr_tid tmp_serial = last_serial; + ethr_tid tmp_ix = last_ix + 1; + ethr_tid start_ix = tmp_ix; + + + do { + if (tmp_ix >= ETHR_MAX_THREADS) { + tmp_serial++; + if ((tmp_serial << serial_shift) < 0) + tmp_serial = 0; + tmp_ix = 0; + } + if (!thr_table[tmp_ix]) { + *new_tid = (tmp_serial << serial_shift) | tmp_ix; + *new_serial = tmp_serial; + *new_ix = tmp_ix; + return; + } + tmp_ix++; + } while (tmp_ix != start_ix); + + *new_tid = INVALID_TID; + *new_serial = INVALID_TID; + *new_ix = INVALID_TID; + +} + + +static void thr_exit_cleanup(thr_data_ *td, void *res) +{ + + ASSERT(td == OWN_THR_DATA); + + run_exit_handlers(); + + EnterCriticalSection(&thr_table_cs); + CloseHandle(td->wait_event.handle); + if (td->thr_handle == INVALID_HANDLE_VALUE) { + /* We are detached; cleanup thread table */ + ASSERT(td->joiner == INVALID_TID); + ASSERT(td == thr_table[THR_IX(td->thr_id)]); + thr_table[THR_IX(td->thr_id)] = NULL; + if (td != &main_thr_data) + (*freep)((void *) td); + } + else { + /* Save result and let joining thread cleanup */ + td->result = res; + } + LeaveCriticalSection(&thr_table_cs); +} + +static unsigned __stdcall thr_wrapper(LPVOID args) +{ + void *(*func)(void*) = ((thr_wrap_data_ *) args)->func; + void *arg = ((thr_wrap_data_ *) args)->arg; + thr_data_ *td = ((thr_wrap_data_ *) args)->td; + + td->wait_event.handle = CreateEvent(NULL, FALSE, FALSE, NULL); + if (td->wait_event.handle == INVALID_HANDLE_VALUE + || !TlsSetValue(tls_own_thr_data, (LPVOID) td)) { + ((thr_wrap_data_ *) args)->res = get_errno(); + if (td->wait_event.handle != INVALID_HANDLE_VALUE) + CloseHandle(td->wait_event.handle); + SetEvent(((thr_wrap_data_ *) args)->ptd->wait_event.handle); + _endthreadex((unsigned) 0); + ASSERT(0); + } + + td->wait_event.prev = NULL; + td->wait_event.next = NULL; + td->wait_event.in_queue = 0; + + if (thread_create_child_func) + (*thread_create_child_func)(((thr_wrap_data_ *) args)->prep_func_res); + + ASSERT(td == OWN_THR_DATA); + + ((thr_wrap_data_ *) args)->res = 0; + SetEvent(((thr_wrap_data_ *) args)->ptd->wait_event.handle); + + thr_exit_cleanup(td, (*func)(arg)); + return 0; +} + +int +ethr_fake_static_mutex_init(ethr_mutex *mtx) +{ + EnterCriticalSection((CRITICAL_SECTION *) &fake_static_init_cs); + /* Got here under race conditions; check again... */ + if (!mtx->initialized) { + if (!InitializeCriticalSectionAndSpinCount(&mtx->cs, + ETHR_MUTEX_SPIN_COUNT)) + return get_errno(); + mtx->initialized = ETHR_MUTEX_INITIALIZED; + } + LeaveCriticalSection((CRITICAL_SECTION *) &fake_static_init_cs); + return 0; +} + +static int +fake_static_cond_init(ethr_cond *cnd) +{ + EnterCriticalSection((CRITICAL_SECTION *) &fake_static_init_cs); + /* Got here under race conditions; check again... */ + if (!cnd->initialized) { + if (!InitializeCriticalSectionAndSpinCount(&cnd->cs, + ETHR_COND_SPIN_COUNT)) + return get_errno(); + cnd->queue = NULL; + cnd->queue_end = NULL; + cnd->initialized = ETHR_COND_INITIALIZED; + } + LeaveCriticalSection((CRITICAL_SECTION *) &fake_static_init_cs); + return 0; +} + +#ifdef __GNUC__ +#define LL_LITERAL(X) X##LL +#else +#define LL_LITERAL(X) X##i64 +#endif + +#define EPOCH_JULIAN_DIFF LL_LITERAL(11644473600) + +static ETHR_INLINE void +get_curr_time(long *sec, long *nsec) +{ + SYSTEMTIME t; + FILETIME ft; + LONGLONG lft; + + GetSystemTime(&t); + SystemTimeToFileTime(&t, &ft); + memcpy(&lft, &ft, sizeof(lft)); + *nsec = ((long) (lft % LL_LITERAL(10000000)))*100; + *sec = (long) ((lft / LL_LITERAL(10000000)) - EPOCH_JULIAN_DIFF); +} + +static cnd_wait_event_ *cwe_freelist; +static CRITICAL_SECTION cwe_cs; + +static int +alloc_cwe(cnd_wait_event_ **cwe_res) +{ + cnd_wait_event_ *cwe; + EnterCriticalSection(&cwe_cs); + cwe = cwe_freelist; + if (cwe) { + cwe_freelist = cwe->next; + LeaveCriticalSection(&cwe_cs); + } + else { + LeaveCriticalSection(&cwe_cs); + cwe = (*allocp)(sizeof(cnd_wait_event_)); + if (!cwe) + return ENOMEM; + cwe->handle = CreateEvent(NULL, FALSE, FALSE, NULL); + if (cwe->handle == INVALID_HANDLE_VALUE) { + int res = get_errno(); + (*freep)(cwe); + return res; + } + } + *cwe_res = cwe; + return 0; +} + +static +free_cwe(cnd_wait_event_ *cwe) +{ + EnterCriticalSection(&cwe_cs); + cwe->next = cwe_freelist; + cwe_freelist = cwe; + LeaveCriticalSection(&cwe_cs); +} + +static ETHR_INLINE int +condwait(ethr_cond *cnd, + ethr_mutex *mtx, + int with_timeout, + ethr_timeval *timeout) +{ + int res; + thr_data_ *td; + cnd_wait_event_ *cwe; + DWORD code; + long time; /* time until timeout in milli seconds */ + +#if ETHR_XCHK + if (ethr_not_inited) { + ASSERT(0); + return EACCES; + } + + if (!mtx + || mtx->initialized != ETHR_MUTEX_INITIALIZED + || !cnd + || (cnd->initialized && cnd->initialized != ETHR_COND_INITIALIZED) + || (with_timeout && !timeout)) { + ASSERT(0); + return EINVAL; + } +#endif + + td = OWN_THR_DATA; + if (td) + cwe = &td->wait_event; + else { /* A non-ethread thread */ + res = alloc_cwe(&cwe); + if (res != 0) + return res; + } + + if (!cnd->initialized) + fake_static_cond_init(cnd); + EnterCriticalSection(&cnd->cs); + + ASSERT(!cwe->in_queue); + if (cnd->queue_end) { + ASSERT(cnd->queue); + cwe->prev = cnd->queue_end; + cwe->next = NULL; + cnd->queue_end->next = cwe; + cnd->queue_end = cwe; + } + else { + ASSERT(!cnd->queue); + cwe->prev = NULL; + cwe->next = NULL; + cnd->queue = cwe; + cnd->queue_end = cwe; + } + cwe->in_queue = 1; + + LeaveCriticalSection(&cnd->cs); + + LeaveCriticalSection(&mtx->cs); + + if (!with_timeout) + time = INFINITE; + else { + long sec, nsec; + ASSERT(timeout); + get_curr_time(&sec, &nsec); + time = (timeout->tv_sec - sec)*1000; + time += (timeout->tv_nsec - nsec + 500)/1000000; + if (time < 0) + time = 0; + } + + /* wait for event to signal */ + code = WaitForSingleObject(cwe->handle, time); + + EnterCriticalSection(&mtx->cs); + + if (code == WAIT_OBJECT_0) { + /* We were woken by a signal or a broadcast ... */ + res = 0; + + /* ... no need to remove event from wait queue since this was + taken care of by the signal or broadcast */ +#ifdef DEBUG + EnterCriticalSection(&cnd->cs); + ASSERT(!cwe->in_queue); + LeaveCriticalSection(&cnd->cs); +#endif + + } + else { + /* We timed out... */ + res = ETIMEDOUT; + + /* ... probably have to remove event from wait queue ... */ + EnterCriticalSection(&cnd->cs); + + if (cwe->in_queue) { /* ... but we must check that we are in queue + since a signal or broadcast after timeout + may have removed us from the queue */ + if (cwe->prev) { + cwe->prev->next = cwe->next; + } + else { + ASSERT(cnd->queue == cwe); + cnd->queue = cwe->next; + } + + if (cwe->next) { + cwe->next->prev = cwe->prev; + } + else { + ASSERT(cnd->queue_end == cwe); + cnd->queue_end = cwe->prev; + } + cwe->in_queue = 0; + } + + LeaveCriticalSection(&cnd->cs); + + } + + if (!td) + free_cwe(cwe); + + return res; + +} + + +/* + * ---------------------------------------------------------------------------- + * Exported functions + * ---------------------------------------------------------------------------- + */ + +int +ethr_init(ethr_init_data *id) +{ +#ifdef _WIN32_WINNT + DWORD major = (_WIN32_WINNT >> 8) & 0xff; + DWORD minor = _WIN32_WINNT & 0xff; + OSVERSIONINFO os_version; +#endif + int err = 0; + thr_data_ *td = &main_thr_data; + unsigned long i; + + if (!ethr_not_inited) + return EINVAL; + +#ifdef _WIN32_WINNT + os_version.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); + GetVersionEx(&os_version); + if (os_version.dwPlatformId != VER_PLATFORM_WIN32_NT + || os_version.dwMajorVersion < major + || (os_version.dwMajorVersion == major + && os_version.dwMinorVersion < minor)) + return ENOTSUP; +#endif + + ASSERT(ETHR_MAX_THREADS > 0); + for (i = ETHR_MAX_THREADS - 1, serial_shift = 0; + i; + serial_shift++, i >>= 1); + thr_ix_mask = ~(~((ethr_tid) 0) << serial_shift); + + tls_own_thr_data = TlsAlloc(); + if (tls_own_thr_data == TLS_OUT_OF_INDEXES) + goto error; + + last_serial = 0; + last_ix = 0; + + td->thr_id = 0; + td->thr_handle = GetCurrentThread(); + td->joiner = INVALID_TID; + td->result = NULL; + td->wait_event.handle = CreateEvent(NULL, FALSE, FALSE, NULL); + if (td->wait_event.handle == INVALID_HANDLE_VALUE) + goto error; + td->wait_event.prev = NULL; + td->wait_event.next = NULL; + td->wait_event.in_queue = 0; + thr_table[0] = td; + + if (!TlsSetValue(tls_own_thr_data, (LPVOID) td)) + goto error; + + ASSERT(td == OWN_THR_DATA); + + + cwe_freelist = NULL; + if (!InitializeCriticalSectionAndSpinCount(&cwe_cs, + ETHR_MUTEX_SPIN_COUNT)) + goto error; + + for (i = 1; i < ETHR_MAX_THREADS; i++) + thr_table[i] = NULL; + + if (!InitializeCriticalSectionAndSpinCount(&thr_table_cs, + ETHR_MUTEX_SPIN_COUNT)) + goto error; + if (!InitializeCriticalSectionAndSpinCount(&fake_static_init_cs, + ETHR_MUTEX_SPIN_COUNT)) + goto error; + ethr_not_inited = 0; + + err = init_common(id); + if (err) + goto error; + + return 0; + + error: + ethr_not_inited = 1; + if (err == 0) + err = get_errno(); + ASSERT(err != 0); + if (td->thr_handle != INVALID_HANDLE_VALUE) + CloseHandle(td->thr_handle); + if (td->wait_event.handle != INVALID_HANDLE_VALUE) + CloseHandle(td->wait_event.handle); + return err; +} + +/* + * Thread functions. + */ + +int +ethr_thr_create(ethr_tid *tid, void * (*func)(void *), void *arg, + ethr_thr_opts *opts) +{ + int err = 0; + thr_wrap_data_ twd; + thr_data_ *my_td, *child_td = NULL; + ethr_tid child_tid, child_serial, child_ix; + DWORD code; + unsigned ID; + unsigned stack_size = 0; /* 0 = system default */ + int use_stack_size = (opts && opts->suggested_stack_size >= 0 + ? opts->suggested_stack_size + : -1 /* Use system default */); + +#ifdef ETHR_MODIFIED_DEFAULT_STACK_SIZE + if (use_stack_size < 0) + use_stack_size = ETHR_MODIFIED_DEFAULT_STACK_SIZE; +#endif + +#if ETHR_XCHK + if (ethr_not_inited) { + ASSERT(0); + return EACCES; + } + if (!tid || !func) { + ASSERT(0); + return EINVAL; + } +#endif + + my_td = OWN_THR_DATA; + if (!my_td) { + /* Only ethreads are allowed to call this function */ + ASSERT(0); + return EACCES; + } + + if (use_stack_size >= 0) { + size_t suggested_stack_size = (size_t) use_stack_size; +#ifdef DEBUG + suggested_stack_size /= 2; /* Make sure we got margin */ +#endif + if (suggested_stack_size < min_stack_size) + stack_size = (unsigned) ETHR_KW2B(min_stack_size); + else if (suggested_stack_size > max_stack_size) + stack_size = (unsigned) ETHR_KW2B(max_stack_size); + else + stack_size = + (unsigned) ETHR_PAGE_ALIGN(ETHR_KW2B(suggested_stack_size)); + } + + EnterCriticalSection(&thr_table_cs); + + /* Call prepare func if it exist */ + if (thread_create_prepare_func) + twd.prep_func_res = (*thread_create_prepare_func)(); + else + twd.prep_func_res = NULL; + + /* Find a new thread id to use */ + new_tid(&child_tid, &child_serial, &child_ix); + if (child_tid == INVALID_TID) { + err = EAGAIN; + goto error; + } + + ASSERT(child_ix == THR_IX(child_tid)); + + *tid = child_tid; + + ASSERT(!thr_table[child_ix]); + + /* Alloc thread data */ + thr_table[child_ix] = child_td = (thr_data_ *) (*allocp)(sizeof(thr_data_)); + if (!child_td) { + err = ENOMEM; + goto error; + } + + /* Init thread data */ + + child_td->thr_id = child_tid; + child_td->thr_handle = INVALID_HANDLE_VALUE; + child_td->joiner = INVALID_TID; + child_td->result = NULL; + /* 'child_td->wait_event' is initialized by child thread */ + + + /* Init thread wrapper data */ + + twd.func = func; + twd.arg = arg; + twd.ptd = my_td; + twd.td = child_td; + twd.res = 0; + + ASSERT(!my_td->wait_event.in_queue); + + /* spawn the thr_wrapper function */ + child_td->thr_handle = (HANDLE) _beginthreadex(NULL, + stack_size, + thr_wrapper, + (LPVOID) &twd, + 0, + &ID); + if (child_td->thr_handle == (HANDLE) 0) { + child_td->thr_handle = INVALID_HANDLE_VALUE; + goto error; + } + + ASSERT(child_td->thr_handle != INVALID_HANDLE_VALUE); + + /* Wait for child to finish initialization */ + code = WaitForSingleObject(my_td->wait_event.handle, INFINITE); + if (twd.res || code != WAIT_OBJECT_0) { + err = twd.res; + goto error; + } + + if (opts && opts->detached) { + CloseHandle(child_td->thr_handle); + child_td->thr_handle = INVALID_HANDLE_VALUE; + } + + last_serial = child_serial; + last_ix = child_ix; + + ASSERT(thr_table[child_ix] == child_td); + + if (thread_create_parent_func) + (*thread_create_parent_func)(twd.prep_func_res); + + LeaveCriticalSection(&thr_table_cs); + + return 0; + + error: + + if (err == 0) + err = get_errno(); + ASSERT(err != 0); + + if (thread_create_parent_func) + (*thread_create_parent_func)(twd.prep_func_res); + + if (child_ix != INVALID_TID) { + + if (child_td) { + ASSERT(thr_table[child_ix] == child_td); + + if (child_td->thr_handle != INVALID_HANDLE_VALUE) { + WaitForSingleObject(child_td->thr_handle, INFINITE); + CloseHandle(child_td->thr_handle); + } + + (*freep)((void *) child_td); + thr_table[child_ix] = NULL; + } + } + + *tid = INVALID_TID; + + LeaveCriticalSection(&thr_table_cs); + return err; +} + +int ethr_thr_join(ethr_tid tid, void **res) +{ + int err = 0; + DWORD code; + thr_data_ *td; + thr_data_ *my_td; + +#if ETHR_XCHK + if (ethr_not_inited) { + ASSERT(0); + return EACCES; + } +#endif + + my_td = OWN_THR_DATA; + + if (!my_td) { + /* Only ethreads are allowed to call this function */ + ASSERT(0); + return EACCES; + } + + EnterCriticalSection(&thr_table_cs); + + td = tid2thr(tid); + if (!td) + err = ESRCH; + else if (td->thr_handle == INVALID_HANDLE_VALUE /* i.e. detached */ + || td->joiner != INVALID_TID) /* i.e. someone else is joining */ + err = EINVAL; + else if (my_td == td) + err = EDEADLK; + else + td->joiner = my_td->thr_id; + + LeaveCriticalSection(&thr_table_cs); + + if (err) + goto error; + + /* Wait for thread to terminate */ + code = WaitForSingleObject(td->thr_handle, INFINITE); + if (code != WAIT_OBJECT_0) + goto error; + + EnterCriticalSection(&thr_table_cs); + + ASSERT(td == tid2thr(tid)); + ASSERT(td->thr_handle != INVALID_HANDLE_VALUE); + ASSERT(td->joiner == my_td->thr_id); + + if (res) + *res = td->result; + + CloseHandle(td->thr_handle); + ASSERT(td == thr_table[THR_IX(td->thr_id)]); + thr_table[THR_IX(td->thr_id)] = NULL; + if (td != &main_thr_data) + (*freep)((void *) td); + + LeaveCriticalSection(&thr_table_cs); + + return 0; + + error: + if (err == 0) + err = get_errno(); + ASSERT(err != 0); + return err; +} + + +int +ethr_thr_detach(ethr_tid tid) +{ + int res; + DWORD code; + thr_data_ *td; + +#if ETHR_XCHK + if (ethr_not_inited) { + ASSERT(0); + return EACCES; + } +#endif + + if (!OWN_THR_DATA) { + /* Only ethreads are allowed to call this function */ + ASSERT(0); + return EACCES; + } + + EnterCriticalSection(&thr_table_cs); + + td = tid2thr(tid); + if (!td) + res = ESRCH; + if (td->thr_handle == INVALID_HANDLE_VALUE /* i.e. detached */ + || td->joiner != INVALID_TID) /* i.e. someone is joining */ + res = EINVAL; + else { + res = 0; + CloseHandle(td->thr_handle); + td->thr_handle = INVALID_HANDLE_VALUE; + } + + LeaveCriticalSection(&thr_table_cs); + + return res; +} + + +void +ethr_thr_exit(void *res) +{ + thr_data_ *td; +#if ETHR_XCHK + if (ethr_not_inited) { + ASSERT(0); + return; + } +#endif + td = OWN_THR_DATA; + if (!td) { + /* Only ethreads are allowed to call this function */ + ASSERT(0); + return; + } + thr_exit_cleanup(td, res); + _endthreadex((unsigned) 0); +} + +ethr_tid +ethr_self(void) +{ + thr_data_ *td; +#if ETHR_XCHK + if (ethr_not_inited) { + ASSERT(0); + return INVALID_TID; + } +#endif + /* It is okay for non-ethreads (i.e. native win32 threads) to call + ethr_self(). They will however be returned the INVALID_TID. */ + td = OWN_THR_DATA; + if (!td) + return INVALID_TID; + return td->thr_id; +} + +int +ethr_equal_tids(ethr_tid tid1, ethr_tid tid2) +{ + /* INVALID_TID does not equal any tid, not even the INVALID_TID */ + return tid1 == tid2 && tid1 != INVALID_TID; +} + +/* + * Mutex functions. + */ + +int +ethr_mutex_init(ethr_mutex *mtx) +{ +#if ETHR_XCHK + if (ethr_not_inited) { + ASSERT(0); + return EACCES; + } + if (!mtx) { + ASSERT(0); + return EINVAL; + } +#endif + if (!InitializeCriticalSectionAndSpinCount(&mtx->cs, ETHR_MUTEX_SPIN_COUNT)) + return get_errno(); + mtx->initialized = ETHR_MUTEX_INITIALIZED; +#if ETHR_XCHK + mtx->is_rec_mtx = 0; +#endif + return 0; +} + +int +ethr_rec_mutex_init(ethr_mutex *mtx) +{ + int res; + res = ethr_mutex_init(mtx); +#if ETHR_XCHK + mtx->is_rec_mtx = 1; +#endif + return res; +} + +int +ethr_mutex_destroy(ethr_mutex *mtx) +{ +#if ETHR_XCHK + if (ethr_not_inited) { + ASSERT(0); + return EACCES; + } + if (!mtx || mtx->initialized != ETHR_MUTEX_INITIALIZED) { + ASSERT(0); + return EINVAL; + } +#endif + DeleteCriticalSection(&mtx->cs); + mtx->initialized = 0; + return 0; +} + +int ethr_mutex_set_forksafe(ethr_mutex *mtx) +{ +#if ETHR_XCHK + if (ethr_not_inited) { + ASSERT(0); + return EACCES; + } +#endif + return 0; /* No fork() */ +} + +int ethr_mutex_unset_forksafe(ethr_mutex *mtx) +{ +#if ETHR_XCHK + if (ethr_not_inited) { + ASSERT(0); + return EACCES; + } +#endif + return 0; /* No fork() */ +} + +int +ethr_mutex_trylock(ethr_mutex *mtx) +{ +#if ETHR_XCHK + if (ethr_not_inited) { + ASSERT(0); + return EACCES; + } + if (!mtx + || (mtx->initialized && mtx->initialized != ETHR_MUTEX_INITIALIZED)) { + ASSERT(0); + return EINVAL; + } +#endif + if (!mtx->initialized) { + int res = ethr_fake_static_mutex_init(mtx); + if (res != 0) + return res; + } + return ethr_mutex_trylock__(mtx); +} + +int +ethr_mutex_lock(ethr_mutex *mtx) +{ + int res; +#if ETHR_XCHK + if (ethr_not_inited) { + ASSERT(0); + return EACCES; + } + if (!mtx + || (mtx->initialized && mtx->initialized != ETHR_MUTEX_INITIALIZED)) { + ASSERT(0); + return EINVAL; + } +#endif + return ethr_mutex_lock__(mtx); +} + +int +ethr_mutex_unlock(ethr_mutex *mtx) +{ +#if ETHR_XCHK + int res; + if (ethr_not_inited) { + ASSERT(0); + return EACCES; + } + if (!mtx || mtx->initialized != ETHR_MUTEX_INITIALIZED) { + ASSERT(0); + return EINVAL; + } +#endif + return ethr_mutex_unlock__(mtx); +} + +/* + * Condition variable functions. + */ + +int +ethr_cond_init(ethr_cond *cnd) +{ +#if ETHR_XCHK + if (ethr_not_inited) { + ASSERT(0); + return EACCES; + } + if (!cnd) { + ASSERT(0); + return EINVAL; + } +#endif + if (!InitializeCriticalSectionAndSpinCount(&cnd->cs, ETHR_COND_SPIN_COUNT)) + return get_errno(); + cnd->queue = NULL; + cnd->queue_end = NULL; + cnd->initialized = ETHR_COND_INITIALIZED; + return 0; +} + +int +ethr_cond_destroy(ethr_cond *cnd) +{ +#if ETHR_XCHK + if (ethr_not_inited) { + ASSERT(0); + return EACCES; + } + if (!cnd + || (cnd->initialized && cnd->initialized != ETHR_COND_INITIALIZED) + || cnd->queue) { + ASSERT(0); + return EINVAL; + } +#endif + DeleteCriticalSection(&cnd->cs); + cnd->initialized = 0; + return 0; +} + +int +ethr_cond_signal(ethr_cond *cnd) +{ + cnd_wait_event_ *cwe; +#if ETHR_XCHK + if (ethr_not_inited) { + ASSERT(0); + return EACCES; + } + if (!cnd + || (cnd->initialized && cnd->initialized != ETHR_COND_INITIALIZED)) { + ASSERT(0); + return EINVAL; + } +#endif + if (!cnd->initialized) { + int res = fake_static_cond_init(cnd); + if (res != 0) + return res; + } + EnterCriticalSection(&cnd->cs); + cwe = cnd->queue; + if (cwe) { + ASSERT(cwe->in_queue); + SetEvent(cnd->queue->handle); + if (cwe->next) + cwe->next->prev = NULL; + else { + ASSERT(cnd->queue_end == cnd->queue); + cnd->queue_end = NULL; + } + cnd->queue = cwe->next; + cwe->in_queue = 0; + } + LeaveCriticalSection(&cnd->cs); + return 0; +} + +int +ethr_cond_broadcast(ethr_cond *cnd) +{ + cnd_wait_event_ *cwe; + +#if ETHR_XCHK + if (ethr_not_inited) { + ASSERT(0); + return EACCES; + } + if (!cnd + || (cnd->initialized && cnd->initialized != ETHR_COND_INITIALIZED)) { + ASSERT(0); + return EINVAL; + } +#endif + if (!cnd->initialized) { + int res = fake_static_cond_init(cnd); + if (res != 0) + return res; + } + EnterCriticalSection(&cnd->cs); + for (cwe = cnd->queue; cwe; cwe = cwe->next) { + ASSERT(cwe->in_queue); + SetEvent(cwe->handle); + cwe->in_queue = 0; + } + cnd->queue = NULL; + cnd->queue_end = NULL; + LeaveCriticalSection(&cnd->cs); + return 0; + +} + +int +ethr_cond_wait(ethr_cond *cnd, ethr_mutex *mtx) +{ + return condwait(cnd, mtx, 0, NULL); +} + +int +ethr_cond_timedwait(ethr_cond *cnd, ethr_mutex *mtx, ethr_timeval *timeout) +{ + return condwait(cnd, mtx, 1, timeout); +} + +int +ethr_time_now(ethr_timeval *time) +{ +#if ETHR_XCHK + if (ethr_not_inited) { + ASSERT(0); + return EACCES; + } + if (!time) { + ASSERT(0); + return EINVAL; + } +#endif + get_curr_time(&time->tv_sec, &time->tv_nsec); + return 0; +} + +/* + * Thread specific data + */ + +int +ethr_tsd_key_create(ethr_tsd_key *keyp) +{ + DWORD key; +#if ETHR_XCHK + if (ethr_not_inited) { + ASSERT(0); + return EACCES; + } + if (!keyp) { + ASSERT(0); + return EINVAL; + } +#endif + key = TlsAlloc(); + if (key == TLS_OUT_OF_INDEXES) + return get_errno(); + *keyp = (ethr_tsd_key) key; + return 0; +} + +int +ethr_tsd_key_delete(ethr_tsd_key key) +{ +#if ETHR_XCHK + if (ethr_not_inited) { + ASSERT(0); + return EACCES; + } +#endif + if (!TlsFree((DWORD) key)) + return get_errno(); + return 0; +} + +int +ethr_tsd_set(ethr_tsd_key key, void *value) +{ +#if ETHR_XCHK + if (ethr_not_inited) { + ASSERT(0); + return EACCES; + } +#endif + if (!TlsSetValue((DWORD) key, (LPVOID) value)) + return get_errno(); + return 0; +} + +void * +ethr_tsd_get(ethr_tsd_key key) +{ +#if ETHR_XCHK + if (ethr_not_inited) { + ASSERT(0); + return NULL; + } +#endif + return (void *) TlsGetValue((DWORD) key); +} + +/* Misc */ + +#ifndef ETHR_HAVE_OPTIMIZED_LOCKS + +int +ethr_do_spinlock_init(ethr_spinlock_t *lock) +{ +#if ETHR_XCHK + if (ethr_not_inited) { + ASSERT(0); + return EACCES; + } + if (!lock) { + ASSERT(0); + return EINVAL; + } +#endif + if (InitializeCriticalSectionAndSpinCount(&lock->cs, INT_MAX)) + return 0; + else + return get_errno(); +} + +int +ethr_do_rwlock_init(ethr_rwlock_t *lock) +{ +#if ETHR_XCHK + if (ethr_not_inited) { + ASSERT(0); + return EACCES; + } + if (!lock) { + ASSERT(0); + return EINVAL; + } +#endif + lock->counter = 0; + if (InitializeCriticalSectionAndSpinCount(&lock->cs, INT_MAX)) + return 0; + else + return get_errno(); +} + +#endif /* #ifndef ETHR_HAVE_OPTIMIZED_ATOMIC_OPS */ + +#else +#error "Missing thread implementation" +#endif + +/* Atomics */ + +int +ethr_atomic_init(ethr_atomic_t *var, long i) +{ +#if ETHR_XCHK + if (ethr_not_inited) { + ASSERT(0); + return EACCES; + } + if (!var) { + ASSERT(0); + return EINVAL; + } +#endif + return ethr_atomic_init__(var, i); +} + +int +ethr_atomic_set(ethr_atomic_t *var, long i) +{ +#if ETHR_XCHK + if (ethr_not_inited) { + ASSERT(0); + return EACCES; + } + if (!var) { + ASSERT(0); + return EINVAL; + } +#endif + return ethr_atomic_set__(var, i); +} + +int +ethr_atomic_read(ethr_atomic_t *var, long *i) +{ +#if ETHR_XCHK + if (ethr_not_inited) { + ASSERT(0); + return EACCES; + } + if (!var || !i) { + ASSERT(0); + return EINVAL; + } +#endif + return ethr_atomic_read__(var, i); +} + + +int +ethr_atomic_addtest(ethr_atomic_t *var, long incr, long *testp) +{ +#if ETHR_XCHK + if (ethr_not_inited) { + ASSERT(0); + return EACCES; + } + if (!var || !testp) { + ASSERT(0); + return EINVAL; + } +#endif + return ethr_atomic_addtest__(var, incr, testp); +} + +int +ethr_atomic_inctest(ethr_atomic_t *incp, long *testp) +{ +#if ETHR_XCHK + if (ethr_not_inited) { + ASSERT(0); + return EACCES; + } + if (!incp || !testp) { + ASSERT(0); + return EINVAL; + } +#endif + return ethr_atomic_inctest__(incp, testp); +} + +int +ethr_atomic_dectest(ethr_atomic_t *decp, long *testp) +{ +#if ETHR_XCHK + if (ethr_not_inited) { + ASSERT(0); + return EACCES; + } + if (!decp || !testp) { + ASSERT(0); + return EINVAL; + } +#endif + return ethr_atomic_dectest__(decp, testp); +} + +int +ethr_atomic_add(ethr_atomic_t *var, long incr) +{ +#if ETHR_XCHK + if (ethr_not_inited) { + ASSERT(0); + return EACCES; + } + if (!var) { + ASSERT(0); + return EINVAL; + } +#endif + return ethr_atomic_add__(var, incr); +} + +int +ethr_atomic_inc(ethr_atomic_t *incp) +{ +#if ETHR_XCHK + if (ethr_not_inited) { + ASSERT(0); + return EACCES; + } + if (!incp) { + ASSERT(0); + return EINVAL; + } +#endif + return ethr_atomic_inc__(incp); +} + +int +ethr_atomic_dec(ethr_atomic_t *decp) +{ +#if ETHR_XCHK + if (ethr_not_inited) { + ASSERT(0); + return EACCES; + } + if (!decp) { + ASSERT(0); + return EINVAL; + } +#endif + return ethr_atomic_dec__(decp); +} + +int +ethr_atomic_and_old(ethr_atomic_t *var, long mask, long *old) +{ +#if ETHR_XCHK + if (ethr_not_inited) { + ASSERT(0); + return EACCES; + } + if (!var || !old) { + ASSERT(0); + return EINVAL; + } +#endif + return ethr_atomic_and_old__(var, mask, old); +} + +int +ethr_atomic_or_old(ethr_atomic_t *var, long mask, long *old) +{ +#if ETHR_XCHK + if (ethr_not_inited) { + ASSERT(0); + return EACCES; + } + if (!var || !old) { + ASSERT(0); + return EINVAL; + } +#endif + return ethr_atomic_or_old__(var, mask, old); +} + +int +ethr_atomic_xchg(ethr_atomic_t *var, long new, long *old) +{ +#if ETHR_XCHK + if (ethr_not_inited) { + ASSERT(0); + return EACCES; + } + if (!var || !old) { + ASSERT(0); + return EINVAL; + } +#endif + return ethr_atomic_xchg__(var, new, old); +} + +int +ethr_atomic_cmpxchg(ethr_atomic_t *var, long new, long expected, long *old) +{ +#if ETHR_XCHK + if (ethr_not_inited) { + ASSERT(0); + return EACCES; + } + if (!var || !old) { + ASSERT(0); + return EINVAL; + } +#endif + return ethr_atomic_cmpxchg__(var, new, expected, old); +} + +/* Spinlocks and rwspinlocks */ + +int +ethr_spinlock_init(ethr_spinlock_t *lock) +{ +#if ETHR_XCHK + if (ethr_not_inited) { + ASSERT(0); + return EACCES; + } + if (!lock) { + ASSERT(0); + return EINVAL; + } +#endif + return ethr_spinlock_init__(lock); +} + +int +ethr_spinlock_destroy(ethr_spinlock_t *lock) +{ +#if ETHR_XCHK + if (ethr_not_inited) { + ASSERT(0); + return EACCES; + } + if (!lock) { + ASSERT(0); + return EINVAL; + } +#endif + return ethr_spinlock_destroy__(lock); +} + + +int +ethr_spin_unlock(ethr_spinlock_t *lock) +{ +#if ETHR_XCHK + if (ethr_not_inited) { + ASSERT(0); + return EACCES; + } + if (!lock) { + ASSERT(0); + return EINVAL; + } +#endif + return ethr_spin_unlock__(lock); +} + +int +ethr_spin_lock(ethr_spinlock_t *lock) +{ +#if ETHR_XCHK + if (ethr_not_inited) { + ASSERT(0); + return EACCES; + } + if (!lock) { + ASSERT(0); + return EINVAL; + } +#endif + return ethr_spin_lock__(lock); +} + +int +ethr_rwlock_init(ethr_rwlock_t *lock) +{ +#if ETHR_XCHK + if (ethr_not_inited) { + ASSERT(0); + return EACCES; + } + if (!lock) { + ASSERT(0); + return EINVAL; + } +#endif + return ethr_rwlock_init__(lock); +} + +int +ethr_rwlock_destroy(ethr_rwlock_t *lock) +{ +#if ETHR_XCHK + if (ethr_not_inited) { + ASSERT(0); + return EACCES; + } + if (!lock) { + ASSERT(0); + return EINVAL; + } +#endif + return ethr_rwlock_destroy__(lock); +} + +int +ethr_read_unlock(ethr_rwlock_t *lock) +{ +#if ETHR_XCHK + if (ethr_not_inited) { + ASSERT(0); + return EACCES; + } + if (!lock) { + ASSERT(0); + return EINVAL; + } +#endif + return ethr_read_unlock__(lock); +} + +int +ethr_read_lock(ethr_rwlock_t *lock) +{ +#if ETHR_XCHK + if (ethr_not_inited) { + ASSERT(0); + return EACCES; + } + if (!lock) { + ASSERT(0); + return EINVAL; + } +#endif + return ethr_read_lock__(lock); +} + +int +ethr_write_unlock(ethr_rwlock_t *lock) +{ +#if ETHR_XCHK + if (ethr_not_inited) { + ASSERT(0); + return EACCES; + } + if (!lock) { + ASSERT(0); + return EINVAL; + } +#endif + return ethr_write_unlock__(lock); +} + +int +ethr_write_lock(ethr_rwlock_t *lock) +{ +#if ETHR_XCHK + if (ethr_not_inited) { + ASSERT(0); + return EACCES; + } + if (!lock) { + ASSERT(0); + return EINVAL; + } +#endif + return ethr_write_lock__(lock); +} + + +int +ethr_gate_init(ethr_gate *gp) +{ + int res; +#if ETHR_XCHK + if (ethr_not_inited) { + ASSERT(0); + return EACCES; + } + if (!gp) { + ASSERT(0); + return EINVAL; + } +#endif + res = ethr_mutex_init(&gp->mtx); + if (res != 0) + return res; + res = ethr_cond_init(&gp->cnd); + if (res != 0) { + ethr_mutex_destroy(&gp->mtx); + return res; + } + gp->open = 0; + return 0; +} + +int +ethr_gate_destroy(ethr_gate *gp) +{ + int res, dres; +#if ETHR_XCHK + if (ethr_not_inited) { + ASSERT(0); + return EACCES; + } + if (!gp) { + ASSERT(0); + return EINVAL; + } +#endif + res = ethr_mutex_destroy(&gp->mtx); + dres = ethr_cond_destroy(&gp->cnd); + if (res == 0) + res = dres; + gp->open = 0; + return res; +} + +int +ethr_gate_close(ethr_gate *gp) +{ + int res; +#if ETHR_XCHK + if (ethr_not_inited) { + ASSERT(0); + return EACCES; + } + if (!gp) { + ASSERT(0); + return EINVAL; + } +#endif + res = ethr_mutex_lock__(&gp->mtx); + if (res != 0) + return res; + gp->open = 0; + res = ethr_mutex_unlock__(&gp->mtx); + return res; +} + +int +ethr_gate_let_through(ethr_gate *gp, unsigned no) +{ + int res, ures; +#if ETHR_XCHK + if (ethr_not_inited) { + ASSERT(0); + return EACCES; + } + if (!gp) { + ASSERT(0); + return EINVAL; + } +#endif + res = ethr_mutex_lock__(&gp->mtx); + if (res != 0) + return res; + gp->open += no; + res = (gp->open == 1 + ? ethr_cond_signal(&gp->cnd) + : ethr_cond_broadcast(&gp->cnd)); + ures = ethr_mutex_unlock__(&gp->mtx); + if (res != 0) + res = ures; + return res; +} + +int +ethr_gate_swait(ethr_gate *gp, int spincount) +{ + int res, ures, n; +#if ETHR_XCHK + if (ethr_not_inited) { + ASSERT(0); + return EACCES; + } + if (!gp) { + ASSERT(0); + return EINVAL; + } +#endif + n = spincount; + res = ethr_mutex_lock__(&gp->mtx); + if (res != 0) + return res; + while (n >= 0 && !gp->open) { + res = ethr_mutex_unlock__(&gp->mtx); + if (res != 0) + return res; + res = ethr_mutex_lock__(&gp->mtx); + if (res != 0) + return res; + n--; + } + while (!gp->open) { + res = ethr_cond_wait(&gp->cnd, &gp->mtx); + if (res != 0 && res != EINTR) + goto done; + } + gp->open--; + done: + ures = ethr_mutex_unlock__(&gp->mtx); + if (res == 0) + res = ures; + return res; +} + + +int +ethr_gate_wait(ethr_gate *gp) +{ + return ethr_gate_swait(gp, 0); +} + + +/* rwmutex fallback */ +#ifdef ETHR_USE_RWMTX_FALLBACK + +int +ethr_rwmutex_init(ethr_rwmutex *rwmtx) +{ + int res; +#if ETHR_XCHK + if (!rwmtx) { + ASSERT(0); + return EINVAL; + } +#endif + res = ethr_mutex_init(&rwmtx->mtx); + if (res != 0) + return res; + ethr_cond_init(&rwmtx->rcnd); + if (res != 0) + goto error_cleanup1; + res = ethr_cond_init(&rwmtx->wcnd); + if (res != 0) + goto error_cleanup2; + rwmtx->readers = 0; + rwmtx->waiting_readers = 0; + rwmtx->waiting_writers = 0; +#if ETHR_XCHK + rwmtx->initialized = ETHR_RWMUTEX_INITIALIZED; +#endif + return 0; + error_cleanup2: + ethr_cond_destroy(&rwmtx->rcnd); + error_cleanup1: + ethr_mutex_destroy(&rwmtx->mtx); + return res; +} + +int +ethr_rwmutex_destroy(ethr_rwmutex *rwmtx) +{ + int res, pres; +#if ETHR_XCHK + if (!rwmtx || rwmtx->initialized != ETHR_RWMUTEX_INITIALIZED) { + ASSERT(0); + return EINVAL; + } + rwmtx->initialized = 0; +#endif + res = ethr_mutex_destroy(&rwmtx->mtx); + pres = ethr_cond_destroy(&rwmtx->rcnd); + if (res == 0) + res = pres; + pres = ethr_cond_destroy(&rwmtx->wcnd); + if (res == 0) + res = pres; + return res; +} + +int +ethr_rwmutex_tryrlock(ethr_rwmutex *rwmtx) +{ + int res; +#if ETHR_XCHK + if (!rwmtx || rwmtx->initialized != ETHR_RWMUTEX_INITIALIZED) { + ASSERT(0); + return EINVAL; + } +#endif + res = ethr_mutex_trylock__(&rwmtx->mtx); + if (res != 0) + return res; + if (!rwmtx->waiting_writers) { + res = ethr_mutex_unlock__(&rwmtx->mtx); + if (res == 0) + return EBUSY; + return res; + } + rwmtx->readers++; + return ethr_mutex_unlock__(&rwmtx->mtx); +} + +int +ethr_rwmutex_rlock(ethr_rwmutex *rwmtx) +{ + int res; +#if ETHR_XCHK + if (!rwmtx || rwmtx->initialized != ETHR_RWMUTEX_INITIALIZED) { + ASSERT(0); + return EINVAL; + } +#endif + res = ethr_mutex_lock__(&rwmtx->mtx); + if (res != 0) + return res; + while (rwmtx->waiting_writers) { + rwmtx->waiting_readers++; + res = ethr_cond_wait(&rwmtx->rcnd, &rwmtx->mtx); + rwmtx->waiting_readers--; + if (res != 0 && res != EINTR) { + (void) ethr_mutex_unlock__(&rwmtx->mtx); + return res; + } + } + rwmtx->readers++; + return ethr_mutex_unlock__(&rwmtx->mtx); +} + +int +ethr_rwmutex_runlock(ethr_rwmutex *rwmtx) +{ + int res, ures; +#if ETHR_XCHK + if (!rwmtx || rwmtx->initialized != ETHR_RWMUTEX_INITIALIZED) { + ASSERT(0); + return EINVAL; + } +#endif + res = ethr_mutex_lock__(&rwmtx->mtx); + if (res != 0) + return res; + rwmtx->readers--; + if (!rwmtx->readers && rwmtx->waiting_writers) + res = ethr_cond_signal(&rwmtx->wcnd); + ures = ethr_mutex_unlock__(&rwmtx->mtx); + if (res == 0) + res = ures; + return res; +} + +int +ethr_rwmutex_tryrwlock(ethr_rwmutex *rwmtx) +{ + int res; +#if ETHR_XCHK + if (!rwmtx || rwmtx->initialized != ETHR_RWMUTEX_INITIALIZED) { + ASSERT(0); + return EINVAL; + } +#endif + res = ethr_mutex_trylock__(&rwmtx->mtx); + if (res != 0) + return res; + if (!rwmtx->readers && !rwmtx->waiting_writers) + return 0; + else { + res = ethr_mutex_unlock__(&rwmtx->mtx); + if (res == 0) + return EBUSY; + return res; + } +} + +int +ethr_rwmutex_rwlock(ethr_rwmutex *rwmtx) +{ + int res; +#if ETHR_XCHK + if (!rwmtx || rwmtx->initialized != ETHR_RWMUTEX_INITIALIZED) { + ASSERT(0); + return EINVAL; + } +#endif + res = ethr_mutex_lock__(&rwmtx->mtx); + if (res != 0) + return res; + if (!rwmtx->readers && !rwmtx->waiting_writers) + return 0; + + while (rwmtx->readers) { + rwmtx->waiting_writers++; + res = ethr_cond_wait(&rwmtx->wcnd, &rwmtx->mtx); + rwmtx->waiting_writers--; + if (res != 0 && res != EINTR) { + (void) ethr_rwmutex_rwunlock(rwmtx); + return res; + } + } + return 0; +} + +int +ethr_rwmutex_rwunlock(ethr_rwmutex *rwmtx) +{ + int res, ures; +#if ETHR_XCHK + if (!rwmtx || rwmtx->initialized != ETHR_RWMUTEX_INITIALIZED) { + ASSERT(0); + return EINVAL; + } +#endif + res = 0; + if (rwmtx->waiting_writers) + res = ethr_cond_signal(&rwmtx->wcnd); + else if (rwmtx->waiting_readers) + res = ethr_cond_broadcast(&rwmtx->rcnd); + ures = ethr_mutex_unlock__(&rwmtx->mtx); + if (res == 0) + res = ures; + return res; +} + +#endif /* #ifdef ETHR_USE_RWMTX_FALLBACK */ + +void +ethr_compiler_barrier(void) +{ + +} + +#ifdef DEBUG + +#include <stdio.h> +int ethr_assert_failed(char *f, int l, char *a) +{ + fprintf(stderr, "%s:%d: Assertion failed: %s\n", f, l, a); + abort(); + return 0; +} + +#endif + + |