From 34872ce1a11e3c24154dc2d2ab90c5eb6da47edb Mon Sep 17 00:00:00 2001 From: Corinna Vinschen Date: Tue, 9 Aug 2022 22:48:43 +0200 Subject: [PATCH] Cygwin: pthreads: merge pthread.cc into thread.cc provide entire internal and external pthread API from inside the same file. While I dislike to have another even larger file, this is basically cleaning up the source and grouping the external API into useful chunks. Splitting the file cleanly is tricky due to usage of inline methods is_good_object and verifyable_object_isvalid. Signed-off-by: Corinna Vinschen --- winsup/cygwin/Makefile.am | 5 +- winsup/cygwin/pthread.cc | 196 --- winsup/cygwin/thread.cc | 2922 ++++++++++++++++++++----------------- 3 files changed, 1557 insertions(+), 1566 deletions(-) delete mode 100644 winsup/cygwin/pthread.cc diff --git a/winsup/cygwin/Makefile.am b/winsup/cygwin/Makefile.am index 6debcc4f2..ff5d4bb4e 100644 --- a/winsup/cygwin/Makefile.am +++ b/winsup/cygwin/Makefile.am @@ -314,7 +314,7 @@ DLL_FILES= \ posix_ipc.cc \ posix_timer.cc \ pseudo-reloc.cc \ - pthread.cc \ + thread.cc \ quotactl.cc \ random.cc \ registry.cc \ @@ -339,7 +339,6 @@ DLL_FILES= \ sysconf.cc \ syslog.cc \ termios.cc \ - thread.cc \ timerfd.cc \ times.cc \ tls_pbuf.cc \ @@ -653,7 +652,7 @@ libc.a: $(LIB_NAME) libm.a libpthread.a libutil.a libm.a: $(LIB_NAME) $(newlib_build)/libm.a $(addsuffix .o,$(basename $(MATH_FILES))) $(AM_V_GEN)$(speclib) $^ $(@F) -libpthread.a: $(LIB_NAME) pthread.o thread.o libc/call_once.o libc/cnd.o \ +libpthread.a: $(LIB_NAME) thread.o libc/call_once.o libc/cnd.o \ libc/mtx.o libc/thrd.o libc/tss.o $(AM_V_GEN)$(speclib) $^ $(@F) diff --git a/winsup/cygwin/pthread.cc b/winsup/cygwin/pthread.cc deleted file mode 100644 index 5d0c0eeaa..000000000 --- a/winsup/cygwin/pthread.cc +++ /dev/null @@ -1,196 +0,0 @@ -/* pthread.cc: posix pthread interface for Cygwin - - Originally written by Marco Fuykschot - - This file is part of Cygwin. - - This software is a copyrighted work licensed under the terms of the - Cygwin license. Please consult the file "CYGWIN_LICENSE" for - details. */ - -#include "winsup.h" -#include "thread.h" - -extern "C" -{ -/* ThreadCreation */ -int -pthread_create (pthread_t *thread, const pthread_attr_t *attr, - void *(*start_routine) (void *), void *arg) -{ - return pthread::create (thread, attr, start_routine, arg); -} - -int -pthread_once (pthread_once_t * once_control, void (*init_routine) (void)) -{ - return pthread::once (once_control, init_routine); -} - -int -pthread_atfork (void (*prepare)(void), void (*parent)(void), void (*child)(void)) -{ - return pthread::atfork (prepare, parent, child); -} - -/* Thread Exit */ -void -pthread_exit (void *value_ptr) -{ - pthread::self ()->exit (value_ptr); - __builtin_unreachable (); /* FIXME: don't know why this is necessary */ -} - -int -pthread_detach (pthread_t thread) -{ - return pthread::detach (&thread); -} - - -/* This isn't a posix call... should we keep it? */ -int -pthread_suspend (pthread_t thread) -{ - return pthread::suspend (&thread); -} - -/* same */ -int -pthread_continue (pthread_t thread) -{ - return pthread::resume (&thread); -} - -unsigned long -pthread_getsequence_np (pthread_t * thread) -{ - if (!pthread::is_good_object (thread)) - return EINVAL; - return (*thread)->getsequence_np (); -} - -/* ID */ - -pthread_t pthread_self () -{ - return pthread::self (); -} - -/* Mutexes */ -int -pthread_mutex_init (pthread_mutex_t * mutex, const pthread_mutexattr_t * attr) -{ - return pthread_mutex::init (mutex, attr, NULL); -} - -/* Spinlocks */ -int -pthread_spin_init (pthread_spinlock_t *spinlock, int pshared) -{ - return pthread_spinlock::init (spinlock, pshared); -} - - -/* Synchronisation */ -int -pthread_cond_init (pthread_cond_t * cond, const pthread_condattr_t * attr) -{ - return pthread_cond::init (cond, attr); -} - -/* RW Locks */ -int -pthread_rwlock_init (pthread_rwlock_t *rwlock, const pthread_rwlockattr_t *attr) -{ - return pthread_rwlock::init (rwlock, attr); -} - -/* Cancelability */ - -int -pthread_cancel (pthread_t thread) -{ - return pthread::cancel (thread); -} - -int -pthread_setcancelstate (int state, int *oldstate) -{ - return pthread::self ()->setcancelstate (state, oldstate); -} - -int -pthread_setcanceltype (int type, int *oldtype) -{ - return pthread::self ()->setcanceltype (type, oldtype); -} - -void -pthread_testcancel () -{ - pthread::self ()->testcancel (); -} - -void -_pthread_cleanup_push (__pthread_cleanup_handler *handler) -{ - pthread::self ()->push_cleanup_handler (handler); -} - -void -_pthread_cleanup_pop (int execute) -{ - pthread::self ()->pop_cleanup_handler (execute); -} - -/* Semaphores */ -int -sem_init (sem_t * sem, int pshared, unsigned int value) -{ - return semaphore::init (sem, pshared, value); -} - -int -sem_destroy (sem_t * sem) -{ - return semaphore::destroy (sem); -} - -int -sem_wait (sem_t * sem) -{ - return semaphore::wait (sem); -} - -int -sem_trywait (sem_t * sem) -{ - return semaphore::trywait (sem); -} - -int -sem_clockwait (sem_t * sem, clockid_t clock_id, const struct timespec *abstime) -{ - return semaphore::clockwait (sem, clock_id, abstime); -} - -int -sem_timedwait (sem_t * sem, const struct timespec *abstime) -{ - return semaphore::clockwait (sem, CLOCK_REALTIME, abstime); -} - -int -sem_post (sem_t *sem) -{ - return semaphore::post (sem); -} - -int -sem_getvalue (sem_t * sem, int *sval) -{ - return semaphore::getvalue (sem, sval); -} - -} diff --git a/winsup/cygwin/thread.cc b/winsup/cygwin/thread.cc index dc59294e7..6f1853bce 100644 --- a/winsup/cygwin/thread.cc +++ b/winsup/cygwin/thread.cc @@ -33,16 +33,14 @@ details. */ #include "cygwait.h" #include "exception.h" +/* For Linux compatibility, the length of a thread name is 16 characters. */ +#define THRNAMELEN 16 + extern "C" void __fp_lock_all (); extern "C" void __fp_unlock_all (); extern "C" bool valid_sched_parameters(const struct sched_param *); extern "C" int sched_get_thread_priority(HANDLE thread); extern "C" int sched_set_thread_priority(HANDLE thread, int priority); -static inline verifyable_object_state - verifyable_object_isvalid (void const * objectptr, thread_magic_t magic, - void *static_ptr1 = NULL, - void *static_ptr2 = NULL, - void *static_ptr3 = NULL); extern int threadsafe; @@ -50,7 +48,6 @@ const pthread_t pthread_mutex::_new_mutex = (pthread_t) 1; const pthread_t pthread_mutex::_unlocked_mutex = (pthread_t) 2; const pthread_t pthread_mutex::_destroyed_mutex = (pthread_t) 3; - template static inline void @@ -60,7 +57,6 @@ delete_and_clear (T * * const ptr) *ptr = 0; } - inline bool pthread_mutex::no_owner() { @@ -129,8 +125,10 @@ __cygwin_lock_unlock (_LOCK_T *lock) } static inline verifyable_object_state -verifyable_object_isvalid (void const *objectptr, thread_magic_t magic, void *static_ptr1, - void *static_ptr2, void *static_ptr3) +verifyable_object_isvalid (void const *objectptr, thread_magic_t magic, + void *static_ptr1 = NULL, + void *static_ptr2 = NULL, + void *static_ptr3 = NULL) { verifyable_object_state state = INVALID_OBJECT; @@ -2184,245 +2182,6 @@ pthread::atfork (void (*prepare)(void), void (*parent)(void), void (*child)(void return 0; } -extern "C" int -pthread_attr_init (pthread_attr_t *attr) -{ - *attr = new pthread_attr; - if (!pthread_attr::is_good_object (attr)) - { - delete (*attr); - *attr = NULL; - return ENOMEM; - } - return 0; -} - -extern "C" int -pthread_attr_getinheritsched (const pthread_attr_t *attr, - int *inheritsched) -{ - if (!pthread_attr::is_good_object (attr)) - return EINVAL; - *inheritsched = (*attr)->inheritsched; - return 0; -} - -extern "C" int -pthread_attr_getschedparam (const pthread_attr_t *attr, - struct sched_param *param) -{ - if (!pthread_attr::is_good_object (attr)) - return EINVAL; - *param = (*attr)->schedparam; - return 0; -} - -/* From a pure code point of view, this should call a helper in sched.cc, - to allow for someone adding scheduler policy changes to win32 in the future. - However that's extremely unlikely, so short and sweet will do us */ -extern "C" int -pthread_attr_getschedpolicy (const pthread_attr_t *attr, int *policy) -{ - if (!pthread_attr::is_good_object (attr)) - return EINVAL; - *policy = SCHED_FIFO; - return 0; -} - - -extern "C" int -pthread_attr_getscope (const pthread_attr_t *attr, int *contentionscope) -{ - if (!pthread_attr::is_good_object (attr)) - return EINVAL; - *contentionscope = (*attr)->contentionscope; - return 0; -} - -extern "C" int -pthread_attr_setdetachstate (pthread_attr_t *attr, int detachstate) -{ - if (!pthread_attr::is_good_object (attr)) - return EINVAL; - if (detachstate < 0 || detachstate > 1) - return EINVAL; - (*attr)->joinable = detachstate; - return 0; -} - -extern "C" int -pthread_attr_getdetachstate (const pthread_attr_t *attr, int *detachstate) -{ - if (!pthread_attr::is_good_object (attr)) - return EINVAL; - *detachstate = (*attr)->joinable; - return 0; -} - -extern "C" int -pthread_attr_setinheritsched (pthread_attr_t *attr, int inheritsched) -{ - if (!pthread_attr::is_good_object (attr)) - return EINVAL; - if (inheritsched != PTHREAD_INHERIT_SCHED - && inheritsched != PTHREAD_EXPLICIT_SCHED) - return ENOTSUP; - (*attr)->inheritsched = inheritsched; - return 0; -} - -extern "C" int -pthread_attr_setschedparam (pthread_attr_t *attr, - const struct sched_param *param) -{ - if (!pthread_attr::is_good_object (attr)) - return EINVAL; - if (!valid_sched_parameters (param)) - return ENOTSUP; - (*attr)->schedparam = *param; - return 0; -} - -/* See __pthread_attr_getschedpolicy for some notes */ -extern "C" int -pthread_attr_setschedpolicy (pthread_attr_t *attr, int policy) -{ - if (!pthread_attr::is_good_object (attr)) - return EINVAL; - if (policy != SCHED_FIFO) - return ENOTSUP; - return 0; -} - -extern "C" int -pthread_attr_setscope (pthread_attr_t *attr, int contentionscope) -{ - if (!pthread_attr::is_good_object (attr)) - return EINVAL; - if (contentionscope != PTHREAD_SCOPE_SYSTEM - && contentionscope != PTHREAD_SCOPE_PROCESS) - return EINVAL; - /* In future, we may be able to support system scope by escalating the thread - priority to exceed the priority class. For now we only support PROCESS scope. */ - if (contentionscope != PTHREAD_SCOPE_PROCESS) - return ENOTSUP; - (*attr)->contentionscope = contentionscope; - return 0; -} - -extern "C" int -pthread_attr_setstack (pthread_attr_t *attr, void *addr, size_t size) -{ - if (!pthread_attr::is_good_object (attr)) - return EINVAL; - if (addr == NULL) - return EINVAL; - if (size < PTHREAD_STACK_MIN) - return EINVAL; - /* The incoming address addr points to the lowest addressable byte of a - buffer of size bytes. Due to the way pthread_attr_setstackaddr is defined - on Linux, the lowest address ot the stack can't be reliably computed when - using pthread_attr_setstackaddr/pthread_attr_setstacksize. Therefore we - store the uppermost address of the stack in stackaddr. See also the - comment in pthread_attr_setstackaddr. */ - (*attr)->stackaddr = (caddr_t) addr + size; - (*attr)->stacksize = size; - return 0; -} - -extern "C" int -pthread_attr_getstack (const pthread_attr_t *attr, void **addr, size_t *size) -{ - if (!pthread_attr::is_good_object (attr)) - return EINVAL; - /* stackaddr holds the uppermost stack address. See the comment in - pthread_attr_setstack. */ - *addr = (caddr_t) (*attr)->stackaddr - (*attr)->stacksize; - *size = (*attr)->stacksize; - return 0; -} - -extern "C" int -pthread_attr_setstackaddr (pthread_attr_t *attr, void *addr) -{ - if (!pthread_attr::is_good_object (attr)) - return EINVAL; - if (addr == NULL) - return EINVAL; - /* This function is deprecated in SUSv4, but SUSv3 didn't define - if the incoming stack address is the lowest address of the memory - area defined as stack, or if it's the start address of the stack - at which it begins its growth. On Linux it's the latter which - means the uppermost stack address on x86 based systems. See comment - in pthread_attr_setstack as well. */ - (*attr)->stackaddr = addr; - return 0; -} - -extern "C" int -pthread_attr_getstackaddr (const pthread_attr_t *attr, void **addr) -{ - if (!pthread_attr::is_good_object (attr)) - return EINVAL; - /* See comment in pthread_attr_setstackaddr. */ - *addr = (*attr)->stackaddr; - return 0; -} - -extern "C" int -pthread_attr_setstacksize (pthread_attr_t *attr, size_t size) -{ - if (!pthread_attr::is_good_object (attr)) - return EINVAL; - if (size < PTHREAD_STACK_MIN) - return EINVAL; - (*attr)->stacksize = size; - return 0; -} - -extern "C" int -pthread_attr_getstacksize (const pthread_attr_t *attr, size_t *size) -{ - if (!pthread_attr::is_good_object (attr)) - return EINVAL; - /* If the stacksize has not been set by the application, return the - default stacksize. Note that this is different from what - pthread_attr_getstack returns. */ - *size = (*attr)->stacksize ?: get_rlimit_stack (); - return 0; -} - -extern "C" int -pthread_attr_setguardsize (pthread_attr_t *attr, size_t size) -{ - if (!pthread_attr::is_good_object (attr)) - return EINVAL; - /* We don't support a guardsize of more than 1 Meg. */ - if (size > 1024 * 1024) - return EINVAL; - (*attr)->guardsize = size; - return 0; -} - -extern "C" int -pthread_attr_getguardsize (const pthread_attr_t *attr, size_t *size) -{ - if (!pthread_attr::is_good_object (attr)) - return EINVAL; - *size = (*attr)->guardsize; - return 0; -} - -extern "C" int -pthread_attr_destroy (pthread_attr_t *attr) -{ - if (!pthread_attr::is_good_object (attr)) - return EINVAL; - delete (*attr); - *attr = NULL; - return 0; -} - int pthread::join (pthread_t *thread, void **return_val, PLARGE_INTEGER timeout) { @@ -2581,310 +2340,6 @@ pthread_convert_abstime (clockid_t clock_id, const struct timespec *abstime, return 0; } -extern "C" int -pthread_join (pthread_t thread, void **return_val) -{ - return pthread::join (&thread, (void **) return_val, NULL); -} - -extern "C" int -pthread_tryjoin_np (pthread_t thread, void **return_val) -{ - LARGE_INTEGER timeout = { QuadPart:0LL }; - - return pthread::join (&thread, (void **) return_val, &timeout); -} - -extern "C" int -pthread_timedjoin_np (pthread_t thread, void **return_val, - const struct timespec *abstime) -{ - LARGE_INTEGER timeout; - - int err = pthread_convert_abstime (CLOCK_REALTIME, abstime, &timeout); - if (err) - return err; - return pthread::join (&thread, (void **) return_val, &timeout); -} - -extern "C" int -pthread_getaffinity_np (pthread_t thread, size_t sizeof_set, cpu_set_t *set) -{ - if (!pthread::is_good_object (&thread)) - return ESRCH; - - return sched_get_thread_affinity (thread->win32_obj_id, sizeof_set, set); -} - -extern "C" int -pthread_setaffinity_np (pthread_t thread, size_t sizeof_set, const cpu_set_t *set) -{ - if (!pthread::is_good_object (&thread)) - return ESRCH; - - return sched_set_thread_affinity (thread->win32_obj_id, sizeof_set, set); -} - -extern "C" int -pthread_getattr_np (pthread_t thread, pthread_attr_t *attr) -{ - THREAD_BASIC_INFORMATION tbi; - NTSTATUS status; - - if (!pthread::is_good_object (&thread)) - return ESRCH; - - /* attr may not be pre-initialized */ - if (!pthread_attr::is_good_object (attr)) - { - int rv = pthread_attr_init (attr); - if (rv != 0) - return rv; - } - - (*attr)->joinable = thread->attr.joinable; - (*attr)->contentionscope = thread->attr.contentionscope; - (*attr)->inheritsched = thread->attr.inheritsched; - (*attr)->schedparam = thread->attr.schedparam; - (*attr)->guardsize = thread->attr.guardsize; - - status = NtQueryInformationThread (thread->win32_obj_id, - ThreadBasicInformation, - &tbi, sizeof (tbi), NULL); - if (NT_SUCCESS (status)) - { - PTEB teb = (PTEB) tbi.TebBaseAddress; - /* stackaddr holds the uppermost stack address. See the comments - in pthread_attr_setstack and pthread_attr_setstackaddr for a - description. */ - (*attr)->stackaddr = teb->Tib.StackBase; - (*attr)->stacksize = (uintptr_t) teb->Tib.StackBase - - (uintptr_t) (teb->DeallocationStack ?: teb->Tib.StackLimit); - } - else - { - debug_printf ("NtQueryInformationThread(ThreadBasicInformation), " - "status %y", status); - (*attr)->stackaddr = thread->attr.stackaddr; - (*attr)->stacksize = thread->attr.stacksize; - } - - return 0; -} - -/* For Linux compatibility, the length of a thread name is 16 characters. */ -#define THRNAMELEN 16 - -extern "C" int -pthread_getname_np (pthread_t thread, char *buf, size_t buflen) -{ - char *name; - - if (!pthread::is_good_object (&thread)) - return ESRCH; - - if (!thread->attr.name) - name = program_invocation_short_name; - else - name = thread->attr.name; - - /* Return ERANGE if the provided buffer is less than THRNAMELEN. Truncate - and zero-terminate the name to fit in buf. This means we always return - something if the buffer is THRNAMELEN or larger, but there is no way to - tell if we have the whole name. */ - if (buflen < THRNAMELEN) - return ERANGE; - - int ret = 0; - __try - { - strlcpy (buf, name, buflen); - } - __except (NO_ERROR) - { - ret = EFAULT; - } - __endtry - - return ret; -} - -extern "C" int -pthread_setname_np (pthread_t thread, const char *name) -{ - char *oldname, *cp; - - if (!pthread::is_good_object (&thread)) - return ESRCH; - - if (strlen (name) > THRNAMELEN) - return ERANGE; - - cp = strdup (name); - if (!cp) - return ENOMEM; - - oldname = thread->attr.name; - thread->attr.name = cp; - - SetThreadName (GetThreadId (thread->win32_obj_id), thread->attr.name); - - if (oldname) - free (oldname); - - return 0; -} - -/* Returns running thread's name; works for both cygthreads and pthreads */ -char * -mythreadname (void) -{ - char *result = (char *) cygthread::name (); - - if (result == _my_tls.locals.unknown_thread_name) - { - result[0] = '\0'; - pthread_getname_np (pthread_self (), result, (size_t) THRNAMELEN); - } - - return result; -} -#undef THRNAMELEN - -/* provided for source level compatability. - See http://www.opengroup.org/onlinepubs/007908799/xsh/pthread_getconcurrency.html -*/ -extern "C" int -pthread_getconcurrency () -{ - return MT_INTERFACE->concurrency; -} - -extern "C" int -pthread_getcpuclockid (pthread_t thread, clockid_t *clk_id) -{ - if (!pthread::is_good_object (&thread)) - return (ESRCH); - *clk_id = (clockid_t) THREADID_TO_CLOCKID (thread->getsequence_np ()); - return 0; -} - -/* keep this in sync with sched.cc */ -extern "C" int -pthread_getschedparam (pthread_t thread, int *policy, - struct sched_param *param) -{ - if (!pthread::is_good_object (&thread)) - return ESRCH; - *policy = SCHED_FIFO; - param->sched_priority = sched_get_thread_priority (thread->win32_obj_id); - return 0; -} - -/* Thread Specific Data */ -extern "C" int -pthread_key_create (pthread_key_t *key, void (*destructor) (void *)) -{ - *key = new pthread_key (destructor); - - if (!pthread_key::is_good_object (key)) - { - delete (*key); - *key = NULL; - return EAGAIN; - } - return 0; -} - -extern "C" int -pthread_key_delete (pthread_key_t key) -{ - if (!pthread_key::is_good_object (&key)) - return EINVAL; - - delete (key); - return 0; -} - -/* provided for source level compatability. See -http://www.opengroup.org/onlinepubs/007908799/xsh/pthread_getconcurrency.html -*/ -extern "C" int -pthread_setconcurrency (int new_level) -{ - if (new_level < 0) - return EINVAL; - MT_INTERFACE->concurrency = new_level; - return 0; -} - -/* keep syncronised with sched.cc */ -extern "C" int -pthread_setschedparam (pthread_t thread, int policy, - const struct sched_param *param) -{ - if (!pthread::is_good_object (&thread)) - return ESRCH; - if (policy != SCHED_FIFO) - return ENOTSUP; - if (!param) - return EINVAL; - int rv = - sched_set_thread_priority (thread->win32_obj_id, param->sched_priority); - if (!rv) - thread->attr.schedparam.sched_priority = param->sched_priority; - return rv; -} - -extern "C" int -pthread_setschedprio (pthread_t thread, int priority) -{ - if (!pthread::is_good_object (&thread)) - return ESRCH; - int rv = - sched_set_thread_priority (thread->win32_obj_id, priority); - if (!rv) - thread->attr.schedparam.sched_priority = priority; - return rv; -} - -extern "C" int -pthread_setspecific (pthread_key_t key, const void *value) -{ - if (!pthread_key::is_good_object (&key)) - return EINVAL; - (key)->set (value); - return 0; -} - -extern "C" void * -pthread_getspecific (pthread_key_t key) -{ - if (!pthread_key::is_good_object (&key)) - return NULL; - - return (key)->get (); - -} - -extern "C" int -pthread_cond_destroy (pthread_cond_t *cond) -{ - if (pthread_cond::is_initializer (cond)) - return 0; - if (!pthread_cond::is_good_object (cond)) - return EINVAL; - - /* reads are atomic */ - if ((*cond)->waiting) - return EBUSY; - - delete (*cond); - *cond = NULL; - - return 0; -} - int pthread_cond::init (pthread_cond_t *cond, const pthread_condattr_t *attr) { @@ -2919,209 +2374,6 @@ pthread_cond::init (pthread_cond_t *cond, const pthread_condattr_t *attr) return ret; } -extern "C" int -pthread_cond_broadcast (pthread_cond_t *cond) -{ - if (pthread_cond::is_initializer (cond)) - return 0; - if (!pthread_cond::is_good_object (cond)) - return EINVAL; - - (*cond)->unblock (true); - - return 0; -} - -extern "C" int -pthread_cond_signal (pthread_cond_t *cond) -{ - if (pthread_cond::is_initializer (cond)) - return 0; - if (!pthread_cond::is_good_object (cond)) - return EINVAL; - - (*cond)->unblock (false); - - return 0; -} - -static int -__pthread_cond_wait_init (pthread_cond_t *cond, pthread_mutex_t *mutex) -{ - if (!pthread_mutex::is_good_object (mutex)) - return EINVAL; - if (!(*mutex)->can_be_unlocked ()) - return EPERM; - - if (pthread_cond::is_initializer (cond)) - pthread_cond::init (cond, NULL); - if (!pthread_cond::is_good_object (cond)) - return EINVAL; - - return 0; -} - -static int -__pthread_cond_clockwait (pthread_cond_t *cond, pthread_mutex_t *mutex, - clockid_t clock_id, const struct timespec *abstime) -{ - int err = 0; - LARGE_INTEGER timeout; - - do - { - err = pthread_convert_abstime (clock_id, abstime, &timeout); - if (err) - break; - - err = (*cond)->wait (*mutex, &timeout); - } - while (err == ETIMEDOUT); - return err; -} - -extern "C" int -pthread_cond_clockwait (pthread_cond_t *cond, pthread_mutex_t *mutex, - clockid_t clock_id, const struct timespec *abstime) -{ - int err = 0; - - pthread_testcancel (); - - __try - { - err = __pthread_cond_wait_init (cond, mutex); - if (err) - __leave; - err = __pthread_cond_clockwait (cond, mutex, clock_id, abstime); - } - __except (NO_ERROR) - { - return EINVAL; - } - __endtry - return err; -} - -extern "C" int -pthread_cond_timedwait (pthread_cond_t *cond, pthread_mutex_t *mutex, - const struct timespec *abstime) -{ - int err = 0; - - pthread_testcancel (); - - __try - { - err = __pthread_cond_wait_init (cond, mutex); - if (err) - __leave; - err = __pthread_cond_clockwait (cond, mutex, (*cond)->clock_id, abstime); - } - __except (NO_ERROR) - { - return EINVAL; - } - __endtry - return err; -} - -extern "C" int -pthread_cond_wait (pthread_cond_t *cond, pthread_mutex_t *mutex) -{ - pthread_testcancel (); - - int err = __pthread_cond_wait_init (cond, mutex); - if (err) - return err; - return (*cond)->wait (*mutex, NULL); -} - -extern "C" int -pthread_condattr_init (pthread_condattr_t *condattr) -{ - *condattr = new pthread_condattr; - if (!pthread_condattr::is_good_object (condattr)) - { - delete (*condattr); - *condattr = NULL; - return ENOMEM; - } - return 0; -} - -extern "C" int -pthread_condattr_getpshared (const pthread_condattr_t *attr, int *pshared) -{ - if (!pthread_condattr::is_good_object (attr)) - return EINVAL; - *pshared = (*attr)->shared; - return 0; -} - -extern "C" int -pthread_condattr_setpshared (pthread_condattr_t *attr, int pshared) -{ - if (!pthread_condattr::is_good_object (attr)) - return EINVAL; - if ((pshared < 0) || (pshared > 1)) - return EINVAL; - /* shared cond vars not currently supported */ - if (pshared != PTHREAD_PROCESS_PRIVATE) - return EINVAL; - (*attr)->shared = pshared; - return 0; -} - -extern "C" int -pthread_condattr_getclock (const pthread_condattr_t *attr, clockid_t *clock_id) -{ - if (!pthread_condattr::is_good_object (attr)) - return EINVAL; - *clock_id = (*attr)->clock_id; - return 0; -} - -extern "C" int -pthread_condattr_setclock (pthread_condattr_t *attr, clockid_t clock_id) -{ - if (!pthread_condattr::is_good_object (attr)) - return EINVAL; - if (CLOCKID_IS_PROCESS (clock_id) || CLOCKID_IS_THREAD (clock_id) - || clock_id >= MAX_CLOCKS) - return EINVAL; - (*attr)->clock_id = clock_id; - return 0; -} - -extern "C" int -pthread_condattr_destroy (pthread_condattr_t *condattr) -{ - if (!pthread_condattr::is_good_object (condattr)) - return EINVAL; - delete (*condattr); - *condattr = NULL; - return 0; -} - -extern "C" int -pthread_rwlock_destroy (pthread_rwlock_t *rwlock) -{ - if (pthread_rwlock::is_initializer (rwlock)) - return 0; - if (!pthread_rwlock::is_good_object (rwlock)) - return EINVAL; - - if ((*rwlock)->writer || (*rwlock)->readers || - (*rwlock)->waiting_readers || (*rwlock)->waiting_writers) - return EBUSY; - - delete (*rwlock); - *rwlock = NULL; - - return 0; -} - int pthread_rwlock::init (pthread_rwlock_t *rwlock, const pthread_rwlockattr_t *attr) { @@ -3156,265 +2408,6 @@ pthread_rwlock::init (pthread_rwlock_t *rwlock, const pthread_rwlockattr_t *attr return ret; } -extern "C" int -pthread_rwlock_rdlock (pthread_rwlock_t *rwlock) -{ - pthread_testcancel (); - - if (pthread_rwlock::is_initializer (rwlock)) - pthread_rwlock::init (rwlock, NULL); - if (!pthread_rwlock::is_good_object (rwlock)) - return EINVAL; - - return (*rwlock)->rdlock (); -} - -extern "C" int -pthread_rwlock_clockrdlock (pthread_rwlock_t *rwlock, clockid_t clock_id, - const struct timespec *abstime) -{ - LARGE_INTEGER timeout; - - pthread_testcancel (); - - if (pthread_rwlock::is_initializer (rwlock)) - pthread_rwlock::init (rwlock, NULL); - if (!pthread_rwlock::is_good_object (rwlock)) - return EINVAL; - - /* According to SUSv3, abstime need not be checked for validity, - if the rwlock can be locked immediately. */ - if (!(*rwlock)->tryrdlock ()) - return 0; - - __try - { - int err = pthread_convert_abstime (clock_id, abstime, &timeout); - if (err) - return err; - - return (*rwlock)->rdlock (&timeout); - } - __except (NO_ERROR) {} - __endtry - return EINVAL; -} - -extern "C" int -pthread_rwlock_timedrdlock (pthread_rwlock_t *rwlock, - const struct timespec *abstime) -{ - return pthread_rwlock_clockrdlock (rwlock, CLOCK_REALTIME, abstime); -} - -extern "C" int -pthread_rwlock_tryrdlock (pthread_rwlock_t *rwlock) -{ - if (pthread_rwlock::is_initializer (rwlock)) - pthread_rwlock::init (rwlock, NULL); - if (!pthread_rwlock::is_good_object (rwlock)) - return EINVAL; - - return (*rwlock)->tryrdlock (); -} - -extern "C" int -pthread_rwlock_wrlock (pthread_rwlock_t *rwlock) -{ - pthread_testcancel (); - - if (pthread_rwlock::is_initializer (rwlock)) - pthread_rwlock::init (rwlock, NULL); - if (!pthread_rwlock::is_good_object (rwlock)) - return EINVAL; - - return (*rwlock)->wrlock (); -} - -extern "C" int -pthread_rwlock_clockwrlock (pthread_rwlock_t *rwlock, clockid_t clock_id, - const struct timespec *abstime) -{ - LARGE_INTEGER timeout; - - pthread_testcancel (); - - if (pthread_rwlock::is_initializer (rwlock)) - pthread_rwlock::init (rwlock, NULL); - if (!pthread_rwlock::is_good_object (rwlock)) - return EINVAL; - - /* According to SUSv3, abstime need not be checked for validity, - if the rwlock can be locked immediately. */ - if (!(*rwlock)->trywrlock ()) - return 0; - - __try - { - int err = pthread_convert_abstime (clock_id, abstime, &timeout); - if (err) - return err; - - return (*rwlock)->wrlock (&timeout); - } - __except (NO_ERROR) {} - __endtry - return EINVAL; -} - -extern "C" int -pthread_rwlock_timedwrlock (pthread_rwlock_t *rwlock, - const struct timespec *abstime) -{ - return pthread_rwlock_clockwrlock (rwlock, CLOCK_REALTIME, abstime); -} - -extern "C" int -pthread_rwlock_trywrlock (pthread_rwlock_t *rwlock) -{ - if (pthread_rwlock::is_initializer (rwlock)) - pthread_rwlock::init (rwlock, NULL); - if (!pthread_rwlock::is_good_object (rwlock)) - return EINVAL; - - return (*rwlock)->trywrlock (); -} - -extern "C" int -pthread_rwlock_unlock (pthread_rwlock_t *rwlock) -{ - if (pthread_rwlock::is_initializer (rwlock)) - return 0; - if (!pthread_rwlock::is_good_object (rwlock)) - return EINVAL; - - return (*rwlock)->unlock (); -} - -extern "C" int -pthread_rwlockattr_init (pthread_rwlockattr_t *rwlockattr) -{ - *rwlockattr = new pthread_rwlockattr; - if (!pthread_rwlockattr::is_good_object (rwlockattr)) - { - delete (*rwlockattr); - *rwlockattr = NULL; - return ENOMEM; - } - return 0; -} - -extern "C" int -pthread_rwlockattr_getpshared (const pthread_rwlockattr_t *attr, int *pshared) -{ - if (!pthread_rwlockattr::is_good_object (attr)) - return EINVAL; - *pshared = (*attr)->shared; - return 0; -} - -extern "C" int -pthread_rwlockattr_setpshared (pthread_rwlockattr_t *attr, int pshared) -{ - if (!pthread_rwlockattr::is_good_object (attr)) - return EINVAL; - if ((pshared < 0) || (pshared > 1)) - return EINVAL; - /* shared rwlock vars not currently supported */ - if (pshared != PTHREAD_PROCESS_PRIVATE) - return EINVAL; - (*attr)->shared = pshared; - return 0; -} - -extern "C" int -pthread_rwlockattr_destroy (pthread_rwlockattr_t *rwlockattr) -{ - if (!pthread_rwlockattr::is_good_object (rwlockattr)) - return EINVAL; - delete (*rwlockattr); - *rwlockattr = NULL; - return 0; -} - -/* Thread signal */ -extern "C" int -pthread_kill (pthread_t thread, int sig) -{ - // lock myself, for the use of thread2signal - // two different kills might clash: FIXME - - if (!pthread::is_good_object (&thread)) - return EINVAL; - - /* check that sig is in right range */ - if (sig < 0 || sig >= _NSIG) - return EINVAL; - - siginfo_t si = {0}; - si.si_signo = sig; - si.si_code = SI_USER; - si.si_pid = myself->pid; - si.si_uid = myself->uid; - int rval; - if (!thread->valid) - rval = ESRCH; - else if (sig) - { - rval = (int) sig_send (NULL, si, thread->cygtls); - if (rval == -1) - rval = get_errno (); - } - else - switch (WaitForSingleObject (thread->win32_obj_id, 0)) - { - case WAIT_TIMEOUT: - rval = 0; - break; - default: - rval = ESRCH; - break; - } - - // unlock myself - return rval; -} - -extern "C" int -pthread_sigmask (int operation, const sigset_t *set, sigset_t *old_set) -{ - int res = handle_sigprocmask (operation, set, old_set, _my_tls.sigmask); - syscall_printf ("%d = pthread_sigmask(%d, %p, %p)", - res, operation, set, old_set); - return res; -} - -extern "C" int -pthread_sigqueue (pthread_t *thread, int sig, const union sigval value) -{ - siginfo_t si = {0}; - - if (!pthread::is_good_object (thread)) - return EINVAL; - if (!(*thread)->valid) - return ESRCH; - - si.si_signo = sig; - si.si_code = SI_QUEUE; - si.si_value = value; - si.si_pid = myself->pid; - si.si_uid = myself->uid; - return (int) sig_send (NULL, si, (*thread)->cygtls); -} - -/* ID */ - -extern "C" int -pthread_equal (pthread_t t1, pthread_t t2) -{ - return pthread::equal (t1, t2); -} - /* Mutexes */ int @@ -3464,110 +2457,6 @@ pthread_mutex::init (pthread_mutex_t *mutex, return 0; } -extern "C" int -pthread_mutex_getprioceiling (const pthread_mutex_t *mutex, - int *prioceiling) -{ - /* We don't define _POSIX_THREAD_PRIO_PROTECT because we do't currently support - mutex priorities. - - We can support mutex priorities in the future though: - Store a priority with each mutex. - When the mutex is optained, set the thread priority as appropriate - When the mutex is released, reset the thread priority. */ - return ENOSYS; -} - -extern "C" int -pthread_mutex_lock (pthread_mutex_t *mutex) -{ - if (pthread_mutex::is_initializer (mutex)) - pthread_mutex::init (mutex, NULL, *mutex); - if (!pthread_mutex::is_good_object (mutex)) - return EINVAL; - return (*mutex)->lock (); -} - -extern "C" int -pthread_mutex_clocklock (pthread_mutex_t *mutex, clockid_t clock_id, - const struct timespec *abstime) -{ - LARGE_INTEGER timeout; - - if (pthread_mutex::is_initializer (mutex)) - pthread_mutex::init (mutex, NULL, *mutex); - if (!pthread_mutex::is_good_object (mutex)) - return EINVAL; - - /* According to SUSv3, abstime need not be checked for validity, - if the mutex can be locked immediately. */ - if (!(*mutex)->trylock ()) - return 0; - - __try - { - int err = pthread_convert_abstime (clock_id, abstime, &timeout); - if (err) - return err; - - return (*mutex)->lock (&timeout); - } - __except (NO_ERROR) {} - __endtry - return EINVAL; -} - -extern "C" int -pthread_mutex_timedlock (pthread_mutex_t *mutex, const struct timespec *abstime) -{ - return pthread_mutex_clocklock (mutex, CLOCK_REALTIME, abstime); -} - -extern "C" int -pthread_mutex_trylock (pthread_mutex_t *mutex) -{ - if (pthread_mutex::is_initializer (mutex)) - pthread_mutex::init (mutex, NULL, *mutex); - if (!pthread_mutex::is_good_object (mutex)) - return EINVAL; - return (*mutex)->trylock (); -} - -extern "C" int -pthread_mutex_unlock (pthread_mutex_t *mutex) -{ - if (pthread_mutex::is_initializer (mutex)) - pthread_mutex::init (mutex, NULL, *mutex); - if (!pthread_mutex::is_good_object (mutex)) - return EINVAL; - return (*mutex)->unlock (); -} - -extern "C" int -pthread_mutex_destroy (pthread_mutex_t *mutex) -{ - int rv; - - if (pthread_mutex::is_initializer (mutex)) - return 0; - if (!pthread_mutex::is_good_object (mutex)) - return EINVAL; - - rv = (*mutex)->destroy (); - if (rv) - return rv; - - *mutex = NULL; - return 0; -} - -extern "C" int -pthread_mutex_setprioceiling (pthread_mutex_t *mutex, int prioceiling, - int *old_ceiling) -{ - return ENOSYS; -} - /* Spinlocks */ int @@ -3594,156 +2483,6 @@ pthread_spinlock::init (pthread_spinlock_t *spinlock, int pshared) return 0; } -extern "C" int -pthread_spin_lock (pthread_spinlock_t *spinlock) -{ - if (!pthread_spinlock::is_good_object (spinlock)) - return EINVAL; - return (*spinlock)->lock (); -} - -extern "C" int -pthread_spin_trylock (pthread_spinlock_t *spinlock) -{ - if (!pthread_spinlock::is_good_object (spinlock)) - return EINVAL; - return (*spinlock)->trylock (); -} - -extern "C" int -pthread_spin_unlock (pthread_spinlock_t *spinlock) -{ - if (!pthread_spinlock::is_good_object (spinlock)) - return EINVAL; - return (*spinlock)->unlock (); -} - -extern "C" int -pthread_spin_destroy (pthread_spinlock_t *spinlock) -{ - if (!pthread_spinlock::is_good_object (spinlock)) - return EINVAL; - return (*spinlock)->destroy (); -} - -/* Win32 doesn't support mutex priorities - see __pthread_mutex_getprioceiling - for more detail */ -extern "C" int -pthread_mutexattr_getprotocol (const pthread_mutexattr_t *attr, - int *protocol) -{ - if (!pthread_mutexattr::is_good_object (attr)) - return EINVAL; - return ENOSYS; -} - -extern "C" int -pthread_mutexattr_getpshared (const pthread_mutexattr_t *attr, - int *pshared) -{ - if (!pthread_mutexattr::is_good_object (attr)) - return EINVAL; - *pshared = (*attr)->pshared; - return 0; -} - -extern "C" int -pthread_mutexattr_gettype (const pthread_mutexattr_t *attr, int *type) -{ - if (!pthread_mutexattr::is_good_object (attr)) - return EINVAL; - *type = (*attr)->mutextype; - return 0; -} - -/* FIXME: write and test process shared mutex's. */ -extern "C" int -pthread_mutexattr_init (pthread_mutexattr_t *attr) -{ - *attr = new pthread_mutexattr (); - if (!pthread_mutexattr::is_good_object (attr)) - { - delete (*attr); - *attr = NULL; - return ENOMEM; - } - return 0; -} - -extern "C" int -pthread_mutexattr_destroy (pthread_mutexattr_t *attr) -{ - if (!pthread_mutexattr::is_good_object (attr)) - return EINVAL; - delete (*attr); - *attr = NULL; - return 0; -} - - -/* Win32 doesn't support mutex priorities */ -extern "C" int -pthread_mutexattr_setprotocol (pthread_mutexattr_t *attr, int protocol) -{ - if (!pthread_mutexattr::is_good_object (attr)) - return EINVAL; - return ENOSYS; -} - -/* Win32 doesn't support mutex priorities */ -extern "C" int -pthread_mutexattr_setprioceiling (pthread_mutexattr_t *attr, - int prioceiling) -{ - if (!pthread_mutexattr::is_good_object (attr)) - return EINVAL; - return ENOSYS; -} - -extern "C" int -pthread_mutexattr_getprioceiling (const pthread_mutexattr_t *attr, - int *prioceiling) -{ - if (!pthread_mutexattr::is_good_object (attr)) - return EINVAL; - return ENOSYS; -} - -extern "C" int -pthread_mutexattr_setpshared (pthread_mutexattr_t *attr, int pshared) -{ - if (!pthread_mutexattr::is_good_object (attr)) - return EINVAL; - /* we don't use pshared for anything as yet. We need to test PROCESS_SHARED - *functionality - */ - if (pshared != PTHREAD_PROCESS_PRIVATE) - return EINVAL; - (*attr)->pshared = pshared; - return 0; -} - -/* see pthread_mutex_gettype */ -extern "C" int -pthread_mutexattr_settype (pthread_mutexattr_t *attr, int type) -{ - if (!pthread_mutexattr::is_good_object (attr)) - return EINVAL; - - switch (type) - { - case PTHREAD_MUTEX_ERRORCHECK: - case PTHREAD_MUTEX_RECURSIVE: - case PTHREAD_MUTEX_NORMAL: - (*attr)->mutextype = type; - break; - default: - return EINVAL; - } - - return 0; -} - /* Semaphores */ List semaphore::semaphores; @@ -4208,76 +2947,6 @@ pthread_null::getsequence_np () pthread_null pthread_null::_instance; - -extern "C" -int -pthread_barrierattr_init (pthread_barrierattr_t * battr) -{ - if (unlikely (battr == NULL)) - return EINVAL; - - *battr = new pthread_barrierattr; - (*battr)->shared = PTHREAD_PROCESS_PRIVATE; - - return 0; -} - - -extern "C" -int -pthread_barrierattr_setpshared (pthread_barrierattr_t * battr, int shared) -{ - if (unlikely (! pthread_barrierattr::is_good_object (battr))) - return EINVAL; - - if (unlikely (shared != PTHREAD_PROCESS_SHARED - && shared != PTHREAD_PROCESS_PRIVATE)) - return EINVAL; - - (*battr)->shared = shared; - return 0; -} - - -extern "C" -int -pthread_barrierattr_getpshared (const pthread_barrierattr_t * battr, - int * shared) -{ - if (unlikely (! pthread_barrierattr::is_good_object (battr) - || shared == NULL)) - return EINVAL; - - *shared = (*battr)->shared; - return 0; -} - - -extern "C" -int -pthread_barrierattr_destroy (pthread_barrierattr_t * battr) -{ - if (unlikely (! pthread_barrierattr::is_good_object (battr))) - return EINVAL; - - delete_and_clear (battr); - return 0; -} - - -extern "C" -int -pthread_barrier_init (pthread_barrier_t * bar, - const pthread_barrierattr_t * attr, unsigned count) -{ - if (unlikely (bar == NULL)) - return EINVAL; - - *bar = new pthread_barrier; - return (*bar)->init (attr, count); -} - - int pthread_barrier::init (const pthread_barrierattr_t * attr, unsigned count) { @@ -4311,23 +2980,6 @@ pthread_barrier::init (const pthread_barrierattr_t * attr, unsigned count) return 0; } - -extern "C" -int -pthread_barrier_destroy (pthread_barrier_t * bar) -{ - if (unlikely (! pthread_barrier::is_good_object (bar))) - return EINVAL; - - int ret; - ret = (*bar)->destroy (); - if (ret == 0) - delete_and_clear (bar); - - return ret; -} - - int pthread_barrier::destroy () { @@ -4353,18 +3005,6 @@ pthread_barrier::destroy () return 0; } - -extern "C" -int -pthread_barrier_wait (pthread_barrier_t * bar) -{ - if (unlikely (! pthread_barrier::is_good_object (bar))) - return EINVAL; - - return (*bar)->wait (); -} - - int pthread_barrier::wait () { @@ -4422,3 +3062,1551 @@ pthread_barrier::wait () return retval; } } + +/* Returns running thread's name; works for both cygthreads and pthreads */ +char * +mythreadname (void) +{ + char *result = (char *) cygthread::name (); + + if (result == _my_tls.locals.unknown_thread_name) + { + result[0] = '\0'; + pthread_getname_np (pthread_self (), result, (size_t) THRNAMELEN); + } + + return result; +} + +extern "C" +{ + +/* Thread creation */ + +int +pthread_create (pthread_t *thread, const pthread_attr_t *attr, + void *(*start_routine) (void *), void *arg) +{ + return pthread::create (thread, attr, start_routine, arg); +} + +int +pthread_once (pthread_once_t * once_control, void (*init_routine) (void)) +{ + return pthread::once (once_control, init_routine); +} + +int +pthread_atfork (void (*prepare)(void), void (*parent)(void), void (*child)(void)) +{ + return pthread::atfork (prepare, parent, child); +} + +/* ID */ + +pthread_t pthread_self () +{ + return pthread::self (); +} + +int +pthread_equal (pthread_t t1, pthread_t t2) +{ + return pthread::equal (t1, t2); +} + +unsigned long +pthread_getsequence_np (pthread_t * thread) +{ + if (!pthread::is_good_object (thread)) + return EINVAL; + return (*thread)->getsequence_np (); +} + +/* Thread name */ + +int +pthread_getname_np (pthread_t thread, char *buf, size_t buflen) +{ + char *name; + + if (!pthread::is_good_object (&thread)) + return ESRCH; + + if (!thread->attr.name) + name = program_invocation_short_name; + else + name = thread->attr.name; + + /* Return ERANGE if the provided buffer is less than THRNAMELEN. Truncate + and zero-terminate the name to fit in buf. This means we always return + something if the buffer is THRNAMELEN or larger, but there is no way to + tell if we have the whole name. */ + if (buflen < THRNAMELEN) + return ERANGE; + + int ret = 0; + __try + { + strlcpy (buf, name, buflen); + } + __except (NO_ERROR) + { + ret = EFAULT; + } + __endtry + + return ret; +} + +int +pthread_setname_np (pthread_t thread, const char *name) +{ + char *oldname, *cp; + + if (!pthread::is_good_object (&thread)) + return ESRCH; + + if (strlen (name) > THRNAMELEN) + return ERANGE; + + cp = strdup (name); + if (!cp) + return ENOMEM; + + oldname = thread->attr.name; + thread->attr.name = cp; + + SetThreadName (GetThreadId (thread->win32_obj_id), thread->attr.name); + + if (oldname) + free (oldname); + + return 0; +} + +/* Thread exit */ + +void +pthread_exit (void *value_ptr) +{ + pthread::self ()->exit (value_ptr); + __builtin_unreachable (); /* FIXME: don't know why this is necessary */ +} + +int +pthread_detach (pthread_t thread) +{ + return pthread::detach (&thread); +} + +int +pthread_join (pthread_t thread, void **return_val) +{ + return pthread::join (&thread, (void **) return_val, NULL); +} + +int +pthread_tryjoin_np (pthread_t thread, void **return_val) +{ + LARGE_INTEGER timeout = { QuadPart:0LL }; + + return pthread::join (&thread, (void **) return_val, &timeout); +} + +int +pthread_timedjoin_np (pthread_t thread, void **return_val, + const struct timespec *abstime) +{ + LARGE_INTEGER timeout; + + int err = pthread_convert_abstime (CLOCK_REALTIME, abstime, &timeout); + if (err) + return err; + return pthread::join (&thread, (void **) return_val, &timeout); +} + +/* Thread suspend/resume */ + +/* This isn't a posix call... should we keep it? */ +int +pthread_suspend (pthread_t thread) +{ + return pthread::suspend (&thread); +} + +/* same */ +int +pthread_continue (pthread_t thread) +{ + return pthread::resume (&thread); +} + +/* Thread signal */ + +int +pthread_kill (pthread_t thread, int sig) +{ + // lock myself, for the use of thread2signal + // two different kills might clash: FIXME + + if (!pthread::is_good_object (&thread)) + return EINVAL; + + /* check that sig is in right range */ + if (sig < 0 || sig >= _NSIG) + return EINVAL; + + siginfo_t si = {0}; + si.si_signo = sig; + si.si_code = SI_USER; + si.si_pid = myself->pid; + si.si_uid = myself->uid; + int rval; + if (!thread->valid) + rval = ESRCH; + else if (sig) + { + rval = (int) sig_send (NULL, si, thread->cygtls); + if (rval == -1) + rval = get_errno (); + } + else + switch (WaitForSingleObject (thread->win32_obj_id, 0)) + { + case WAIT_TIMEOUT: + rval = 0; + break; + default: + rval = ESRCH; + break; + } + + // unlock myself + return rval; +} + +int +pthread_sigmask (int operation, const sigset_t *set, sigset_t *old_set) +{ + int res = handle_sigprocmask (operation, set, old_set, _my_tls.sigmask); + syscall_printf ("%d = pthread_sigmask(%d, %p, %p)", + res, operation, set, old_set); + return res; +} + +int +pthread_sigqueue (pthread_t *thread, int sig, const union sigval value) +{ + siginfo_t si = {0}; + + if (!pthread::is_good_object (thread)) + return EINVAL; + if (!(*thread)->valid) + return ESRCH; + + si.si_signo = sig; + si.si_code = SI_QUEUE; + si.si_value = value; + si.si_pid = myself->pid; + si.si_uid = myself->uid; + return (int) sig_send (NULL, si, (*thread)->cygtls); +} + +/* Cancelability */ + +int +pthread_cancel (pthread_t thread) +{ + return pthread::cancel (thread); +} + +int +pthread_setcancelstate (int state, int *oldstate) +{ + return pthread::self ()->setcancelstate (state, oldstate); +} + +int +pthread_setcanceltype (int type, int *oldtype) +{ + return pthread::self ()->setcanceltype (type, oldtype); +} + +void +pthread_testcancel () +{ + pthread::self ()->testcancel (); +} + +void +_pthread_cleanup_push (__pthread_cleanup_handler *handler) +{ + pthread::self ()->push_cleanup_handler (handler); +} + +void +_pthread_cleanup_pop (int execute) +{ + pthread::self ()->pop_cleanup_handler (execute); +} + +/* provided for source level compatability. + See http://www.opengroup.org/onlinepubs/007908799/xsh/pthread_getconcurrency.html +*/ +int +pthread_getconcurrency () +{ + return MT_INTERFACE->concurrency; +} + +/* provided for source level compatability. See +http://www.opengroup.org/onlinepubs/007908799/xsh/pthread_getconcurrency.html +*/ +int +pthread_setconcurrency (int new_level) +{ + if (new_level < 0) + return EINVAL; + MT_INTERFACE->concurrency = new_level; + return 0; +} + +/* Thread scheduling */ + +/* keep this in sync with sched.cc */ +int +pthread_getschedparam (pthread_t thread, int *policy, + struct sched_param *param) +{ + if (!pthread::is_good_object (&thread)) + return ESRCH; + *policy = SCHED_FIFO; + param->sched_priority = sched_get_thread_priority (thread->win32_obj_id); + return 0; +} + +/* keep this in sync with sched.cc */ +int +pthread_setschedparam (pthread_t thread, int policy, + const struct sched_param *param) +{ + if (!pthread::is_good_object (&thread)) + return ESRCH; + if (policy != SCHED_FIFO) + return ENOTSUP; + if (!param) + return EINVAL; + int rv = + sched_set_thread_priority (thread->win32_obj_id, param->sched_priority); + if (!rv) + thread->attr.schedparam.sched_priority = param->sched_priority; + return rv; +} + +int +pthread_setschedprio (pthread_t thread, int priority) +{ + if (!pthread::is_good_object (&thread)) + return ESRCH; + int rv = + sched_set_thread_priority (thread->win32_obj_id, priority); + if (!rv) + thread->attr.schedparam.sched_priority = priority; + return rv; +} + +/* Thread affinity */ + +int +pthread_getaffinity_np (pthread_t thread, size_t sizeof_set, cpu_set_t *set) +{ + if (!pthread::is_good_object (&thread)) + return ESRCH; + + return sched_get_thread_affinity (thread->win32_obj_id, sizeof_set, set); +} + +int +pthread_setaffinity_np (pthread_t thread, size_t sizeof_set, const cpu_set_t *set) +{ + if (!pthread::is_good_object (&thread)) + return ESRCH; + + return sched_set_thread_affinity (thread->win32_obj_id, sizeof_set, set); +} + +/* pthread_attr */ + +int +pthread_attr_init (pthread_attr_t *attr) +{ + *attr = new pthread_attr; + if (!pthread_attr::is_good_object (attr)) + { + delete (*attr); + *attr = NULL; + return ENOMEM; + } + return 0; +} + +int +pthread_attr_getinheritsched (const pthread_attr_t *attr, + int *inheritsched) +{ + if (!pthread_attr::is_good_object (attr)) + return EINVAL; + *inheritsched = (*attr)->inheritsched; + return 0; +} + +int +pthread_attr_getschedparam (const pthread_attr_t *attr, + struct sched_param *param) +{ + if (!pthread_attr::is_good_object (attr)) + return EINVAL; + *param = (*attr)->schedparam; + return 0; +} + +/* From a pure code point of view, this should call a helper in sched.cc, + to allow for someone adding scheduler policy changes to win32 in the future. + However that's extremely unlikely, so short and sweet will do us */ +int +pthread_attr_getschedpolicy (const pthread_attr_t *attr, int *policy) +{ + if (!pthread_attr::is_good_object (attr)) + return EINVAL; + *policy = SCHED_FIFO; + return 0; +} + + +int +pthread_attr_getscope (const pthread_attr_t *attr, int *contentionscope) +{ + if (!pthread_attr::is_good_object (attr)) + return EINVAL; + *contentionscope = (*attr)->contentionscope; + return 0; +} + +int +pthread_attr_setdetachstate (pthread_attr_t *attr, int detachstate) +{ + if (!pthread_attr::is_good_object (attr)) + return EINVAL; + if (detachstate < 0 || detachstate > 1) + return EINVAL; + (*attr)->joinable = detachstate; + return 0; +} + +int +pthread_attr_getdetachstate (const pthread_attr_t *attr, int *detachstate) +{ + if (!pthread_attr::is_good_object (attr)) + return EINVAL; + *detachstate = (*attr)->joinable; + return 0; +} + +int +pthread_attr_setinheritsched (pthread_attr_t *attr, int inheritsched) +{ + if (!pthread_attr::is_good_object (attr)) + return EINVAL; + if (inheritsched != PTHREAD_INHERIT_SCHED + && inheritsched != PTHREAD_EXPLICIT_SCHED) + return ENOTSUP; + (*attr)->inheritsched = inheritsched; + return 0; +} + +int +pthread_attr_setschedparam (pthread_attr_t *attr, + const struct sched_param *param) +{ + if (!pthread_attr::is_good_object (attr)) + return EINVAL; + if (!valid_sched_parameters (param)) + return ENOTSUP; + (*attr)->schedparam = *param; + return 0; +} + +/* See __pthread_attr_getschedpolicy for some notes */ +int +pthread_attr_setschedpolicy (pthread_attr_t *attr, int policy) +{ + if (!pthread_attr::is_good_object (attr)) + return EINVAL; + if (policy != SCHED_FIFO) + return ENOTSUP; + return 0; +} + +int +pthread_attr_setscope (pthread_attr_t *attr, int contentionscope) +{ + if (!pthread_attr::is_good_object (attr)) + return EINVAL; + if (contentionscope != PTHREAD_SCOPE_SYSTEM + && contentionscope != PTHREAD_SCOPE_PROCESS) + return EINVAL; + /* In future, we may be able to support system scope by escalating the thread + priority to exceed the priority class. For now we only support PROCESS scope. */ + if (contentionscope != PTHREAD_SCOPE_PROCESS) + return ENOTSUP; + (*attr)->contentionscope = contentionscope; + return 0; +} + +int +pthread_attr_setstack (pthread_attr_t *attr, void *addr, size_t size) +{ + if (!pthread_attr::is_good_object (attr)) + return EINVAL; + if (addr == NULL) + return EINVAL; + if (size < PTHREAD_STACK_MIN) + return EINVAL; + /* The incoming address addr points to the lowest addressable byte of a + buffer of size bytes. Due to the way pthread_attr_setstackaddr is defined + on Linux, the lowest address ot the stack can't be reliably computed when + using pthread_attr_setstackaddr/pthread_attr_setstacksize. Therefore we + store the uppermost address of the stack in stackaddr. See also the + comment in pthread_attr_setstackaddr. */ + (*attr)->stackaddr = (caddr_t) addr + size; + (*attr)->stacksize = size; + return 0; +} + +int +pthread_attr_getstack (const pthread_attr_t *attr, void **addr, size_t *size) +{ + if (!pthread_attr::is_good_object (attr)) + return EINVAL; + /* stackaddr holds the uppermost stack address. See the comment in + pthread_attr_setstack. */ + *addr = (caddr_t) (*attr)->stackaddr - (*attr)->stacksize; + *size = (*attr)->stacksize; + return 0; +} + +int +pthread_attr_setstackaddr (pthread_attr_t *attr, void *addr) +{ + if (!pthread_attr::is_good_object (attr)) + return EINVAL; + if (addr == NULL) + return EINVAL; + /* This function is deprecated in SUSv4, but SUSv3 didn't define + if the incoming stack address is the lowest address of the memory + area defined as stack, or if it's the start address of the stack + at which it begins its growth. On Linux it's the latter which + means the uppermost stack address on x86 based systems. See comment + in pthread_attr_setstack as well. */ + (*attr)->stackaddr = addr; + return 0; +} + +int +pthread_attr_getstackaddr (const pthread_attr_t *attr, void **addr) +{ + if (!pthread_attr::is_good_object (attr)) + return EINVAL; + /* See comment in pthread_attr_setstackaddr. */ + *addr = (*attr)->stackaddr; + return 0; +} + +int +pthread_attr_setstacksize (pthread_attr_t *attr, size_t size) +{ + if (!pthread_attr::is_good_object (attr)) + return EINVAL; + if (size < PTHREAD_STACK_MIN) + return EINVAL; + (*attr)->stacksize = size; + return 0; +} + +int +pthread_attr_getstacksize (const pthread_attr_t *attr, size_t *size) +{ + if (!pthread_attr::is_good_object (attr)) + return EINVAL; + /* If the stacksize has not been set by the application, return the + default stacksize. Note that this is different from what + pthread_attr_getstack returns. */ + *size = (*attr)->stacksize ?: get_rlimit_stack (); + return 0; +} + +int +pthread_attr_setguardsize (pthread_attr_t *attr, size_t size) +{ + if (!pthread_attr::is_good_object (attr)) + return EINVAL; + /* We don't support a guardsize of more than 1 Meg. */ + if (size > 1024 * 1024) + return EINVAL; + (*attr)->guardsize = size; + return 0; +} + +int +pthread_attr_getguardsize (const pthread_attr_t *attr, size_t *size) +{ + if (!pthread_attr::is_good_object (attr)) + return EINVAL; + *size = (*attr)->guardsize; + return 0; +} + +int +pthread_attr_destroy (pthread_attr_t *attr) +{ + if (!pthread_attr::is_good_object (attr)) + return EINVAL; + delete (*attr); + *attr = NULL; + return 0; +} + +int +pthread_getattr_np (pthread_t thread, pthread_attr_t *attr) +{ + THREAD_BASIC_INFORMATION tbi; + NTSTATUS status; + + if (!pthread::is_good_object (&thread)) + return ESRCH; + + /* attr may not be pre-initialized */ + if (!pthread_attr::is_good_object (attr)) + { + int rv = pthread_attr_init (attr); + if (rv != 0) + return rv; + } + + (*attr)->joinable = thread->attr.joinable; + (*attr)->contentionscope = thread->attr.contentionscope; + (*attr)->inheritsched = thread->attr.inheritsched; + (*attr)->schedparam = thread->attr.schedparam; + (*attr)->guardsize = thread->attr.guardsize; + + status = NtQueryInformationThread (thread->win32_obj_id, + ThreadBasicInformation, + &tbi, sizeof (tbi), NULL); + if (NT_SUCCESS (status)) + { + PTEB teb = (PTEB) tbi.TebBaseAddress; + /* stackaddr holds the uppermost stack address. See the comments + in pthread_attr_setstack and pthread_attr_setstackaddr for a + description. */ + (*attr)->stackaddr = teb->Tib.StackBase; + (*attr)->stacksize = (uintptr_t) teb->Tib.StackBase + - (uintptr_t) (teb->DeallocationStack ?: teb->Tib.StackLimit); + } + else + { + debug_printf ("NtQueryInformationThread(ThreadBasicInformation), " + "status %y", status); + (*attr)->stackaddr = thread->attr.stackaddr; + (*attr)->stacksize = thread->attr.stacksize; + } + + return 0; +} + +/* Thread Specific Data */ + +int +pthread_key_create (pthread_key_t *key, void (*destructor) (void *)) +{ + *key = new pthread_key (destructor); + + if (!pthread_key::is_good_object (key)) + { + delete (*key); + *key = NULL; + return EAGAIN; + } + return 0; +} + +int +pthread_key_delete (pthread_key_t key) +{ + if (!pthread_key::is_good_object (&key)) + return EINVAL; + + delete (key); + return 0; +} + +void * +pthread_getspecific (pthread_key_t key) +{ + if (!pthread_key::is_good_object (&key)) + return NULL; + + return (key)->get (); +} + +int +pthread_setspecific (pthread_key_t key, const void *value) +{ + if (!pthread_key::is_good_object (&key)) + return EINVAL; + (key)->set (value); + return 0; +} + +/* Mutexes */ + +int +pthread_mutex_init (pthread_mutex_t * mutex, const pthread_mutexattr_t * attr) +{ + return pthread_mutex::init (mutex, attr, NULL); +} + +int +pthread_mutex_getprioceiling (const pthread_mutex_t *mutex, + int *prioceiling) +{ + /* We don't define _POSIX_THREAD_PRIO_PROTECT because we do't currently support + mutex priorities. + + We can support mutex priorities in the future though: + Store a priority with each mutex. + When the mutex is optained, set the thread priority as appropriate + When the mutex is released, reset the thread priority. */ + return ENOSYS; +} + +int +pthread_mutex_lock (pthread_mutex_t *mutex) +{ + if (pthread_mutex::is_initializer (mutex)) + pthread_mutex::init (mutex, NULL, *mutex); + if (!pthread_mutex::is_good_object (mutex)) + return EINVAL; + return (*mutex)->lock (); +} + +int +pthread_mutex_clocklock (pthread_mutex_t *mutex, clockid_t clock_id, + const struct timespec *abstime) +{ + LARGE_INTEGER timeout; + + if (pthread_mutex::is_initializer (mutex)) + pthread_mutex::init (mutex, NULL, *mutex); + if (!pthread_mutex::is_good_object (mutex)) + return EINVAL; + + /* According to SUSv3, abstime need not be checked for validity, + if the mutex can be locked immediately. */ + if (!(*mutex)->trylock ()) + return 0; + + __try + { + int err = pthread_convert_abstime (clock_id, abstime, &timeout); + if (err) + return err; + + return (*mutex)->lock (&timeout); + } + __except (NO_ERROR) {} + __endtry + return EINVAL; +} + +int +pthread_mutex_timedlock (pthread_mutex_t *mutex, const struct timespec *abstime) +{ + return pthread_mutex_clocklock (mutex, CLOCK_REALTIME, abstime); +} + +int +pthread_mutex_trylock (pthread_mutex_t *mutex) +{ + if (pthread_mutex::is_initializer (mutex)) + pthread_mutex::init (mutex, NULL, *mutex); + if (!pthread_mutex::is_good_object (mutex)) + return EINVAL; + return (*mutex)->trylock (); +} + +int +pthread_mutex_unlock (pthread_mutex_t *mutex) +{ + if (pthread_mutex::is_initializer (mutex)) + pthread_mutex::init (mutex, NULL, *mutex); + if (!pthread_mutex::is_good_object (mutex)) + return EINVAL; + return (*mutex)->unlock (); +} + +int +pthread_mutex_destroy (pthread_mutex_t *mutex) +{ + int rv; + + if (pthread_mutex::is_initializer (mutex)) + return 0; + if (!pthread_mutex::is_good_object (mutex)) + return EINVAL; + + rv = (*mutex)->destroy (); + if (rv) + return rv; + + *mutex = NULL; + return 0; +} + +int +pthread_mutex_setprioceiling (pthread_mutex_t *mutex, int prioceiling, + int *old_ceiling) +{ + return ENOSYS; +} + +/* Mutex attributes */ + +/* Win32 doesn't support mutex priorities - see __pthread_mutex_getprioceiling + for more detail */ +int +pthread_mutexattr_getprotocol (const pthread_mutexattr_t *attr, + int *protocol) +{ + if (!pthread_mutexattr::is_good_object (attr)) + return EINVAL; + return ENOSYS; +} + +int +pthread_mutexattr_getpshared (const pthread_mutexattr_t *attr, + int *pshared) +{ + if (!pthread_mutexattr::is_good_object (attr)) + return EINVAL; + *pshared = (*attr)->pshared; + return 0; +} + +int +pthread_mutexattr_gettype (const pthread_mutexattr_t *attr, int *type) +{ + if (!pthread_mutexattr::is_good_object (attr)) + return EINVAL; + *type = (*attr)->mutextype; + return 0; +} + +/* FIXME: write and test process shared mutex's. */ +int +pthread_mutexattr_init (pthread_mutexattr_t *attr) +{ + *attr = new pthread_mutexattr (); + if (!pthread_mutexattr::is_good_object (attr)) + { + delete (*attr); + *attr = NULL; + return ENOMEM; + } + return 0; +} + +int +pthread_mutexattr_destroy (pthread_mutexattr_t *attr) +{ + if (!pthread_mutexattr::is_good_object (attr)) + return EINVAL; + delete (*attr); + *attr = NULL; + return 0; +} + + +/* Win32 doesn't support mutex priorities */ +int +pthread_mutexattr_setprotocol (pthread_mutexattr_t *attr, int protocol) +{ + if (!pthread_mutexattr::is_good_object (attr)) + return EINVAL; + return ENOSYS; +} + +/* Win32 doesn't support mutex priorities */ +int +pthread_mutexattr_setprioceiling (pthread_mutexattr_t *attr, + int prioceiling) +{ + if (!pthread_mutexattr::is_good_object (attr)) + return EINVAL; + return ENOSYS; +} + +int +pthread_mutexattr_getprioceiling (const pthread_mutexattr_t *attr, + int *prioceiling) +{ + if (!pthread_mutexattr::is_good_object (attr)) + return EINVAL; + return ENOSYS; +} + +int +pthread_mutexattr_setpshared (pthread_mutexattr_t *attr, int pshared) +{ + if (!pthread_mutexattr::is_good_object (attr)) + return EINVAL; + /* we don't use pshared for anything as yet. We need to test PROCESS_SHARED + *functionality + */ + if (pshared != PTHREAD_PROCESS_PRIVATE) + return EINVAL; + (*attr)->pshared = pshared; + return 0; +} + +/* see pthread_mutex_gettype */ +int +pthread_mutexattr_settype (pthread_mutexattr_t *attr, int type) +{ + if (!pthread_mutexattr::is_good_object (attr)) + return EINVAL; + + switch (type) + { + case PTHREAD_MUTEX_ERRORCHECK: + case PTHREAD_MUTEX_RECURSIVE: + case PTHREAD_MUTEX_NORMAL: + (*attr)->mutextype = type; + break; + default: + return EINVAL; + } + + return 0; +} + +/* Spinlocks */ + +int +pthread_spin_init (pthread_spinlock_t *spinlock, int pshared) +{ + return pthread_spinlock::init (spinlock, pshared); +} + +int +pthread_spin_lock (pthread_spinlock_t *spinlock) +{ + if (!pthread_spinlock::is_good_object (spinlock)) + return EINVAL; + return (*spinlock)->lock (); +} + +int +pthread_spin_trylock (pthread_spinlock_t *spinlock) +{ + if (!pthread_spinlock::is_good_object (spinlock)) + return EINVAL; + return (*spinlock)->trylock (); +} + +int +pthread_spin_unlock (pthread_spinlock_t *spinlock) +{ + if (!pthread_spinlock::is_good_object (spinlock)) + return EINVAL; + return (*spinlock)->unlock (); +} + +int +pthread_spin_destroy (pthread_spinlock_t *spinlock) +{ + if (!pthread_spinlock::is_good_object (spinlock)) + return EINVAL; + return (*spinlock)->destroy (); +} + +/* Synchronisation */ + +int +pthread_cond_init (pthread_cond_t * cond, const pthread_condattr_t * attr) +{ + return pthread_cond::init (cond, attr); +} + +int +pthread_cond_destroy (pthread_cond_t *cond) +{ + if (pthread_cond::is_initializer (cond)) + return 0; + if (!pthread_cond::is_good_object (cond)) + return EINVAL; + + /* reads are atomic */ + if ((*cond)->waiting) + return EBUSY; + + delete (*cond); + *cond = NULL; + + return 0; +} + +int +pthread_cond_broadcast (pthread_cond_t *cond) +{ + if (pthread_cond::is_initializer (cond)) + return 0; + if (!pthread_cond::is_good_object (cond)) + return EINVAL; + + (*cond)->unblock (true); + + return 0; +} + +int +pthread_cond_signal (pthread_cond_t *cond) +{ + if (pthread_cond::is_initializer (cond)) + return 0; + if (!pthread_cond::is_good_object (cond)) + return EINVAL; + + (*cond)->unblock (false); + + return 0; +} + +static int +__pthread_cond_wait_init (pthread_cond_t *cond, pthread_mutex_t *mutex) +{ + if (!pthread_mutex::is_good_object (mutex)) + return EINVAL; + if (!(*mutex)->can_be_unlocked ()) + return EPERM; + + if (pthread_cond::is_initializer (cond)) + pthread_cond::init (cond, NULL); + if (!pthread_cond::is_good_object (cond)) + return EINVAL; + + return 0; +} + +static int +__pthread_cond_clockwait (pthread_cond_t *cond, pthread_mutex_t *mutex, + clockid_t clock_id, const struct timespec *abstime) +{ + int err = 0; + LARGE_INTEGER timeout; + + do + { + err = pthread_convert_abstime (clock_id, abstime, &timeout); + if (err) + break; + + err = (*cond)->wait (*mutex, &timeout); + } + while (err == ETIMEDOUT); + return err; +} + +int +pthread_cond_clockwait (pthread_cond_t *cond, pthread_mutex_t *mutex, + clockid_t clock_id, const struct timespec *abstime) +{ + int err = 0; + + pthread_testcancel (); + + __try + { + err = __pthread_cond_wait_init (cond, mutex); + if (err) + __leave; + err = __pthread_cond_clockwait (cond, mutex, clock_id, abstime); + } + __except (NO_ERROR) + { + return EINVAL; + } + __endtry + return err; +} + +int +pthread_cond_timedwait (pthread_cond_t *cond, pthread_mutex_t *mutex, + const struct timespec *abstime) +{ + int err = 0; + + pthread_testcancel (); + + __try + { + err = __pthread_cond_wait_init (cond, mutex); + if (err) + __leave; + err = __pthread_cond_clockwait (cond, mutex, (*cond)->clock_id, abstime); + } + __except (NO_ERROR) + { + return EINVAL; + } + __endtry + return err; +} + +int +pthread_cond_wait (pthread_cond_t *cond, pthread_mutex_t *mutex) +{ + pthread_testcancel (); + + int err = __pthread_cond_wait_init (cond, mutex); + if (err) + return err; + return (*cond)->wait (*mutex, NULL); +} + +/* Thread cond attributes */ + +int +pthread_condattr_init (pthread_condattr_t *condattr) +{ + *condattr = new pthread_condattr; + if (!pthread_condattr::is_good_object (condattr)) + { + delete (*condattr); + *condattr = NULL; + return ENOMEM; + } + return 0; +} + +int +pthread_condattr_getpshared (const pthread_condattr_t *attr, int *pshared) +{ + if (!pthread_condattr::is_good_object (attr)) + return EINVAL; + *pshared = (*attr)->shared; + return 0; +} + +int +pthread_condattr_setpshared (pthread_condattr_t *attr, int pshared) +{ + if (!pthread_condattr::is_good_object (attr)) + return EINVAL; + if ((pshared < 0) || (pshared > 1)) + return EINVAL; + /* shared cond vars not currently supported */ + if (pshared != PTHREAD_PROCESS_PRIVATE) + return EINVAL; + (*attr)->shared = pshared; + return 0; +} + +int +pthread_condattr_getclock (const pthread_condattr_t *attr, clockid_t *clock_id) +{ + if (!pthread_condattr::is_good_object (attr)) + return EINVAL; + *clock_id = (*attr)->clock_id; + return 0; +} + +int +pthread_condattr_setclock (pthread_condattr_t *attr, clockid_t clock_id) +{ + if (!pthread_condattr::is_good_object (attr)) + return EINVAL; + if (CLOCKID_IS_PROCESS (clock_id) || CLOCKID_IS_THREAD (clock_id) + || clock_id >= MAX_CLOCKS) + return EINVAL; + (*attr)->clock_id = clock_id; + return 0; +} + +int +pthread_condattr_destroy (pthread_condattr_t *condattr) +{ + if (!pthread_condattr::is_good_object (condattr)) + return EINVAL; + delete (*condattr); + *condattr = NULL; + return 0; +} + +/* RW Locks */ + +int +pthread_rwlock_init (pthread_rwlock_t *rwlock, const pthread_rwlockattr_t *attr) +{ + return pthread_rwlock::init (rwlock, attr); +} + +int +pthread_rwlock_destroy (pthread_rwlock_t *rwlock) +{ + if (pthread_rwlock::is_initializer (rwlock)) + return 0; + if (!pthread_rwlock::is_good_object (rwlock)) + return EINVAL; + + if ((*rwlock)->writer || (*rwlock)->readers || + (*rwlock)->waiting_readers || (*rwlock)->waiting_writers) + return EBUSY; + + delete (*rwlock); + *rwlock = NULL; + + return 0; +} + +int +pthread_rwlock_rdlock (pthread_rwlock_t *rwlock) +{ + pthread_testcancel (); + + if (pthread_rwlock::is_initializer (rwlock)) + pthread_rwlock::init (rwlock, NULL); + if (!pthread_rwlock::is_good_object (rwlock)) + return EINVAL; + + return (*rwlock)->rdlock (); +} + +int +pthread_rwlock_clockrdlock (pthread_rwlock_t *rwlock, clockid_t clock_id, + const struct timespec *abstime) +{ + LARGE_INTEGER timeout; + + pthread_testcancel (); + + if (pthread_rwlock::is_initializer (rwlock)) + pthread_rwlock::init (rwlock, NULL); + if (!pthread_rwlock::is_good_object (rwlock)) + return EINVAL; + + /* According to SUSv3, abstime need not be checked for validity, + if the rwlock can be locked immediately. */ + if (!(*rwlock)->tryrdlock ()) + return 0; + + __try + { + int err = pthread_convert_abstime (clock_id, abstime, &timeout); + if (err) + return err; + + return (*rwlock)->rdlock (&timeout); + } + __except (NO_ERROR) {} + __endtry + return EINVAL; +} + +int +pthread_rwlock_timedrdlock (pthread_rwlock_t *rwlock, + const struct timespec *abstime) +{ + return pthread_rwlock_clockrdlock (rwlock, CLOCK_REALTIME, abstime); +} + +int +pthread_rwlock_tryrdlock (pthread_rwlock_t *rwlock) +{ + if (pthread_rwlock::is_initializer (rwlock)) + pthread_rwlock::init (rwlock, NULL); + if (!pthread_rwlock::is_good_object (rwlock)) + return EINVAL; + + return (*rwlock)->tryrdlock (); +} + +int +pthread_rwlock_wrlock (pthread_rwlock_t *rwlock) +{ + pthread_testcancel (); + + if (pthread_rwlock::is_initializer (rwlock)) + pthread_rwlock::init (rwlock, NULL); + if (!pthread_rwlock::is_good_object (rwlock)) + return EINVAL; + + return (*rwlock)->wrlock (); +} + +int +pthread_rwlock_clockwrlock (pthread_rwlock_t *rwlock, clockid_t clock_id, + const struct timespec *abstime) +{ + LARGE_INTEGER timeout; + + pthread_testcancel (); + + if (pthread_rwlock::is_initializer (rwlock)) + pthread_rwlock::init (rwlock, NULL); + if (!pthread_rwlock::is_good_object (rwlock)) + return EINVAL; + + /* According to SUSv3, abstime need not be checked for validity, + if the rwlock can be locked immediately. */ + if (!(*rwlock)->trywrlock ()) + return 0; + + __try + { + int err = pthread_convert_abstime (clock_id, abstime, &timeout); + if (err) + return err; + + return (*rwlock)->wrlock (&timeout); + } + __except (NO_ERROR) {} + __endtry + return EINVAL; +} + +int +pthread_rwlock_timedwrlock (pthread_rwlock_t *rwlock, + const struct timespec *abstime) +{ + return pthread_rwlock_clockwrlock (rwlock, CLOCK_REALTIME, abstime); +} + +int +pthread_rwlock_trywrlock (pthread_rwlock_t *rwlock) +{ + if (pthread_rwlock::is_initializer (rwlock)) + pthread_rwlock::init (rwlock, NULL); + if (!pthread_rwlock::is_good_object (rwlock)) + return EINVAL; + + return (*rwlock)->trywrlock (); +} + +int +pthread_rwlock_unlock (pthread_rwlock_t *rwlock) +{ + if (pthread_rwlock::is_initializer (rwlock)) + return 0; + if (!pthread_rwlock::is_good_object (rwlock)) + return EINVAL; + + return (*rwlock)->unlock (); +} + +/* RW Lock attributes */ + +int +pthread_rwlockattr_init (pthread_rwlockattr_t *rwlockattr) +{ + *rwlockattr = new pthread_rwlockattr; + if (!pthread_rwlockattr::is_good_object (rwlockattr)) + { + delete (*rwlockattr); + *rwlockattr = NULL; + return ENOMEM; + } + return 0; +} + +int +pthread_rwlockattr_getpshared (const pthread_rwlockattr_t *attr, int *pshared) +{ + if (!pthread_rwlockattr::is_good_object (attr)) + return EINVAL; + *pshared = (*attr)->shared; + return 0; +} + +int +pthread_rwlockattr_setpshared (pthread_rwlockattr_t *attr, int pshared) +{ + if (!pthread_rwlockattr::is_good_object (attr)) + return EINVAL; + if ((pshared < 0) || (pshared > 1)) + return EINVAL; + /* shared rwlock vars not currently supported */ + if (pshared != PTHREAD_PROCESS_PRIVATE) + return EINVAL; + (*attr)->shared = pshared; + return 0; +} + +int +pthread_rwlockattr_destroy (pthread_rwlockattr_t *rwlockattr) +{ + if (!pthread_rwlockattr::is_good_object (rwlockattr)) + return EINVAL; + delete (*rwlockattr); + *rwlockattr = NULL; + return 0; +} + +/* Barriers */ + +int +pthread_barrier_init (pthread_barrier_t * bar, + const pthread_barrierattr_t * attr, unsigned count) +{ + if (unlikely (bar == NULL)) + return EINVAL; + + *bar = new pthread_barrier; + return (*bar)->init (attr, count); +} + +int +pthread_barrier_destroy (pthread_barrier_t * bar) +{ + if (unlikely (! pthread_barrier::is_good_object (bar))) + return EINVAL; + + int ret; + ret = (*bar)->destroy (); + if (ret == 0) + delete_and_clear (bar); + + return ret; +} + +int +pthread_barrier_wait (pthread_barrier_t * bar) +{ + if (unlikely (! pthread_barrier::is_good_object (bar))) + return EINVAL; + + return (*bar)->wait (); +} + +/* Barrier attributes */ + +int +pthread_barrierattr_init (pthread_barrierattr_t * battr) +{ + if (unlikely (battr == NULL)) + return EINVAL; + + *battr = new pthread_barrierattr; + (*battr)->shared = PTHREAD_PROCESS_PRIVATE; + + return 0; +} + +int +pthread_barrierattr_setpshared (pthread_barrierattr_t * battr, int shared) +{ + if (unlikely (! pthread_barrierattr::is_good_object (battr))) + return EINVAL; + + if (unlikely (shared != PTHREAD_PROCESS_SHARED + && shared != PTHREAD_PROCESS_PRIVATE)) + return EINVAL; + + (*battr)->shared = shared; + return 0; +} + +int +pthread_barrierattr_getpshared (const pthread_barrierattr_t * battr, + int * shared) +{ + if (unlikely (! pthread_barrierattr::is_good_object (battr) + || shared == NULL)) + return EINVAL; + + *shared = (*battr)->shared; + return 0; +} + +int +pthread_barrierattr_destroy (pthread_barrierattr_t * battr) +{ + if (unlikely (! pthread_barrierattr::is_good_object (battr))) + return EINVAL; + + delete_and_clear (battr); + return 0; +} + +/* Thread clock ID */ + +int +pthread_getcpuclockid (pthread_t thread, clockid_t *clk_id) +{ + if (!pthread::is_good_object (&thread)) + return (ESRCH); + *clk_id = (clockid_t) THREADID_TO_CLOCKID (thread->getsequence_np ()); + return 0; +} + +/* Semaphores */ + +int +sem_init (sem_t * sem, int pshared, unsigned int value) +{ + return semaphore::init (sem, pshared, value); +} + +int +sem_destroy (sem_t * sem) +{ + return semaphore::destroy (sem); +} + +int +sem_wait (sem_t * sem) +{ + return semaphore::wait (sem); +} + +int +sem_trywait (sem_t * sem) +{ + return semaphore::trywait (sem); +} + +int +sem_clockwait (sem_t * sem, clockid_t clock_id, const struct timespec *abstime) +{ + return semaphore::clockwait (sem, clock_id, abstime); +} + +int +sem_timedwait (sem_t * sem, const struct timespec *abstime) +{ + return semaphore::clockwait (sem, CLOCK_REALTIME, abstime); +} + +int +sem_post (sem_t *sem) +{ + return semaphore::post (sem); +} + +int +sem_getvalue (sem_t * sem, int *sval) +{ + return semaphore::getvalue (sem, sval); +} + +}