diff options
author | Corinna Vinschen <corinna@vinschen.de> | 2022-08-09 23:48:43 +0300 |
---|---|---|
committer | Corinna Vinschen <corinna@vinschen.de> | 2022-08-09 23:48:43 +0300 |
commit | 34872ce1a11e3c24154dc2d2ab90c5eb6da47edb (patch) | |
tree | 1fccd97cc4685385fbda077b07df86e472d7ebbd /winsup | |
parent | 1556b96b1b3b03112f271dfe30e043ed538354fd (diff) |
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 <corinna@vinschen.de>
Diffstat (limited to 'winsup')
-rw-r--r-- | winsup/cygwin/Makefile.am | 5 | ||||
-rw-r--r-- | winsup/cygwin/pthread.cc | 196 | ||||
-rw-r--r-- | winsup/cygwin/thread.cc | 2830 |
3 files changed, 1511 insertions, 1520 deletions
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 <marco@ddi.nl> - - 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 <typename T> 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,764 +2340,911 @@ 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) +int +pthread_cond::init (pthread_cond_t *cond, const pthread_condattr_t *attr) { - LARGE_INTEGER timeout = { QuadPart:0LL }; + pthread_cond_t new_cond; - return pthread::join (&thread, (void **) return_val, &timeout); -} + if (attr && !pthread_condattr::is_good_object (attr)) + return EINVAL; -extern "C" int -pthread_timedjoin_np (pthread_t thread, void **return_val, - const struct timespec *abstime) -{ - LARGE_INTEGER timeout; + cond_initialization_lock.lock (); - int err = pthread_convert_abstime (CLOCK_REALTIME, abstime, &timeout); - if (err) - return err; - return pthread::join (&thread, (void **) return_val, &timeout); -} + new_cond = new pthread_cond (attr ? (*attr) : NULL); + if (!is_good_object (&new_cond)) + { + delete new_cond; + cond_initialization_lock.unlock (); + return EAGAIN; + } -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; + int ret = 0; - return sched_get_thread_affinity (thread->win32_obj_id, sizeof_set, set); + __try + { + *cond = new_cond; + } + __except (NO_ERROR) + { + delete new_cond; + ret = EINVAL; + } + __endtry + cond_initialization_lock.unlock (); + return ret; } -extern "C" int -pthread_setaffinity_np (pthread_t thread, size_t sizeof_set, const cpu_set_t *set) +int +pthread_rwlock::init (pthread_rwlock_t *rwlock, const pthread_rwlockattr_t *attr) { - if (!pthread::is_good_object (&thread)) - return ESRCH; - - return sched_set_thread_affinity (thread->win32_obj_id, sizeof_set, set); -} + pthread_rwlock_t new_rwlock; -extern "C" int -pthread_getattr_np (pthread_t thread, pthread_attr_t *attr) -{ - THREAD_BASIC_INFORMATION tbi; - NTSTATUS status; + if (attr && !pthread_rwlockattr::is_good_object (attr)) + return EINVAL; - if (!pthread::is_good_object (&thread)) - return ESRCH; + rwlock_initialization_lock.lock (); - /* attr may not be pre-initialized */ - if (!pthread_attr::is_good_object (attr)) - { - int rv = pthread_attr_init (attr); - if (rv != 0) - return rv; - } + new_rwlock = new pthread_rwlock (attr ? (*attr) : NULL); + if (!is_good_object (&new_rwlock)) + { + delete new_rwlock; + rwlock_initialization_lock.unlock (); + return EAGAIN; + } - (*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; + int ret = 0; - status = NtQueryInformationThread (thread->win32_obj_id, - ThreadBasicInformation, - &tbi, sizeof (tbi), NULL); - if (NT_SUCCESS (status)) + __try { - 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); + *rwlock = new_rwlock; } - else + __except (NO_ERROR) { - debug_printf ("NtQueryInformationThread(ThreadBasicInformation), " - "status %y", status); - (*attr)->stackaddr = thread->attr.stackaddr; - (*attr)->stacksize = thread->attr.stacksize; + delete new_rwlock; + ret = EINVAL; } - - return 0; + __endtry + rwlock_initialization_lock.unlock (); + return ret; } -/* For Linux compatibility, the length of a thread name is 16 characters. */ -#define THRNAMELEN 16 +/* Mutexes */ -extern "C" int -pthread_getname_np (pthread_t thread, char *buf, size_t buflen) +int +pthread_mutex::init (pthread_mutex_t *mutex, + const pthread_mutexattr_t *attr, + const pthread_mutex_t initializer) { - char *name; + if (attr && !pthread_mutexattr::is_good_object (attr)) + return EINVAL; - if (!pthread::is_good_object (&thread)) - return ESRCH; + mutex_initialization_lock.lock (); + if (initializer == NULL || pthread_mutex::is_initializer (mutex)) + { + pthread_mutex_t new_mutex = new pthread_mutex (attr ? (*attr) : NULL); + if (!is_good_object (&new_mutex)) + { + delete new_mutex; + mutex_initialization_lock.unlock (); + return EAGAIN; + } - if (!thread->attr.name) - name = program_invocation_short_name; - else - name = thread->attr.name; + if (!attr && initializer) + { + if (initializer == PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP) + new_mutex->type = PTHREAD_MUTEX_RECURSIVE; + else if (initializer == PTHREAD_NORMAL_MUTEX_INITIALIZER_NP) + new_mutex->type = PTHREAD_MUTEX_NORMAL; + else if (initializer == PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP) + new_mutex->type = PTHREAD_MUTEX_ERRORCHECK; + } - /* 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; + __try + { + *mutex = new_mutex; + } + __except (NO_ERROR) + { + delete new_mutex; + mutex_initialization_lock.unlock (); + return EINVAL; + } + __endtry + } + mutex_initialization_lock.unlock (); + pthread_printf ("*mutex %p, attr %p, initializer %p", *mutex, attr, initializer); + + return 0; +} + +/* Spinlocks */ + +int +pthread_spinlock::init (pthread_spinlock_t *spinlock, int pshared) +{ + pthread_spinlock_t new_spinlock = new pthread_spinlock (pshared); + if (!is_good_object (&new_spinlock)) + { + delete new_spinlock; + return EAGAIN; + } - int ret = 0; __try { - strlcpy (buf, name, buflen); + *spinlock = new_spinlock; } __except (NO_ERROR) { - ret = EFAULT; + delete new_spinlock; + return EINVAL; } __endtry - - return ret; + pthread_printf ("*spinlock %p, pshared %d", *spinlock, pshared); + return 0; } -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; +/* Semaphores */ - SetThreadName (GetThreadId (thread->win32_obj_id), thread->attr.name); +List<semaphore> semaphore::semaphores; - if (oldname) - free (oldname); +semaphore::semaphore (int pshared, unsigned int value) +: verifyable_object (SEM_MAGIC), + shared (pshared), + currentvalue (-1), + startvalue (value), + fd (-1), + hash (0ULL), + sem (NULL) +{ + SECURITY_ATTRIBUTES sa = (pshared != PTHREAD_PROCESS_PRIVATE) + ? sec_all : sec_none_nih; + this->win32_obj_id = ::CreateSemaphore (&sa, value, INT32_MAX, NULL); + if (!this->win32_obj_id) + magic = 0; - return 0; + semaphores.insert (this); } -/* Returns running thread's name; works for both cygthreads and pthreads */ -char * -mythreadname (void) +semaphore::semaphore (unsigned long long shash, LUID sluid, int sfd, + sem_t *ssem, int oflag, mode_t mode, unsigned int value) +: verifyable_object (SEM_MAGIC), + shared (PTHREAD_PROCESS_SHARED), + currentvalue (-1), /* Unused for named semaphores. */ + startvalue (value), + fd (sfd), + hash (shash), + luid (sluid), + sem (ssem) { - char *result = (char *) cygthread::name (); + char name[MAX_PATH]; - if (result == _my_tls.locals.unknown_thread_name) + __small_sprintf (name, "semaphore/%016X%08x%08x", + hash, luid.HighPart, luid.LowPart); + this->win32_obj_id = ::CreateSemaphore (&sec_all, value, INT32_MAX, name); + if (!this->win32_obj_id) + magic = 0; + if (GetLastError () == ERROR_ALREADY_EXISTS && (oflag & O_EXCL)) { - result[0] = '\0'; - pthread_getname_np (pthread_self (), result, (size_t) THRNAMELEN); + __seterrno (); + CloseHandle (this->win32_obj_id); + magic = 0; } - return result; + semaphores.insert (this); } -#undef THRNAMELEN -/* provided for source level compatability. - See http://www.opengroup.org/onlinepubs/007908799/xsh/pthread_getconcurrency.html -*/ -extern "C" int -pthread_getconcurrency () +semaphore::~semaphore () { - return MT_INTERFACE->concurrency; -} + if (win32_obj_id) + CloseHandle (win32_obj_id); -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; + semaphores.remove (this); } -/* keep this in sync with sched.cc */ -extern "C" int -pthread_getschedparam (pthread_t thread, int *policy, - struct sched_param *param) +void +semaphore::_post () { - if (!pthread::is_good_object (&thread)) - return ESRCH; - *policy = SCHED_FIFO; - param->sched_priority = sched_get_thread_priority (thread->win32_obj_id); - return 0; + LONG dummy; + ReleaseSemaphore (win32_obj_id, 1, &dummy); } -/* Thread Specific Data */ -extern "C" int -pthread_key_create (pthread_key_t *key, void (*destructor) (void *)) +int +semaphore::_getvalue (int *sval) { - *key = new pthread_key (destructor); + NTSTATUS status; + SEMAPHORE_BASIC_INFORMATION sbi; - if (!pthread_key::is_good_object (key)) + status = NtQuerySemaphore (win32_obj_id, SemaphoreBasicInformation, &sbi, + sizeof sbi, NULL); + int res; + if (NT_SUCCESS (status)) { - delete (*key); - *key = NULL; - return EAGAIN; + *sval = sbi.CurrentCount; + res = 0; } - return 0; + else + { + *sval = startvalue; + __seterrno_from_nt_status (status); + res = -1; + } + return res; } -extern "C" int -pthread_key_delete (pthread_key_t key) +int +semaphore::_trywait () { - if (!pthread_key::is_good_object (&key)) - return EINVAL; - - delete (key); + /* FIXME: signals should be able to interrupt semaphores... + We probably need WaitForMultipleObjects here. */ + if (WaitForSingleObject (win32_obj_id, 0) == WAIT_TIMEOUT) + { + set_errno (EAGAIN); + return -1; + } 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) +int +semaphore::_wait (PLARGE_INTEGER timeout) { - if (new_level < 0) - return EINVAL; - MT_INTERFACE->concurrency = new_level; + __try + { + switch (cygwait (win32_obj_id, timeout, + cw_cancel | cw_cancel_self | cw_sig_eintr)) + { + case WAIT_OBJECT_0: + break; + case WAIT_SIGNALED: + set_errno (EINTR); + return -1; + case WAIT_TIMEOUT: + set_errno (ETIMEDOUT); + return -1; + default: + pthread_printf ("cygwait failed. %E"); + __seterrno (); + return -1; + } + } + __except (NO_ERROR) {} + __endtry return 0; } -/* keep syncronised with sched.cc */ -extern "C" int -pthread_setschedparam (pthread_t thread, int policy, - const struct sched_param *param) +void +semaphore::_fixup_before_fork () { - 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; + NTSTATUS status; + SEMAPHORE_BASIC_INFORMATION sbi; + + status = NtQuerySemaphore (win32_obj_id, SemaphoreBasicInformation, &sbi, + sizeof sbi, NULL); + if (NT_SUCCESS (status)) + currentvalue = sbi.CurrentCount; + else + currentvalue = startvalue; } -extern "C" int -pthread_setschedprio (pthread_t thread, int priority) +void +semaphore::_fixup_after_fork () { - 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; + if (shared == PTHREAD_PROCESS_PRIVATE) + { + pthread_printf ("sem %p", this); + win32_obj_id = ::CreateSemaphore (&sec_none_nih, currentvalue, + INT32_MAX, NULL); + if (!win32_obj_id) + api_fatal ("failed to create new win32 semaphore, " + "currentvalue %ld, %E", currentvalue); + } } -extern "C" int -pthread_setspecific (pthread_key_t key, const void *value) +void +semaphore::_terminate () { - if (!pthread_key::is_good_object (&key)) - return EINVAL; - (key)->set (value); - return 0; + int _sem_close (sem_t *, bool); + + if (sem) + _sem_close (sem, false); } -extern "C" void * -pthread_getspecific (pthread_key_t key) +/* static members */ + +int +semaphore::init (sem_t *sem, int pshared, unsigned int value) { - if (!pthread_key::is_good_object (&key)) - return NULL; + /* + We can't tell the difference between reinitialising an + existing semaphore and initialising a semaphore who's + contents happen to be a valid pointer + */ + if (is_good_object (sem)) + paranoid_printf ("potential attempt to reinitialise a semaphore"); - return (key)->get (); + if (value > SEM_VALUE_MAX) + { + set_errno(EINVAL); + return -1; + } + *sem = new semaphore (pshared, value); + + if (!is_good_object (sem)) + { + delete (*sem); + *sem = NULL; + set_errno(EAGAIN); + return -1; + } + return 0; } -extern "C" int -pthread_cond_destroy (pthread_cond_t *cond) +int +semaphore::destroy (sem_t *sem) { - if (pthread_cond::is_initializer (cond)) - return 0; - if (!pthread_cond::is_good_object (cond)) - return EINVAL; + if (!is_good_object (sem)) + { + set_errno(EINVAL); + return -1; + } - /* reads are atomic */ - if ((*cond)->waiting) - return EBUSY; + /* It's invalid to destroy a semaphore not opened with sem_init. */ + if ((*sem)->fd != -1) + { + set_errno(EINVAL); + return -1; + } - delete (*cond); - *cond = NULL; + /* FIXME - new feature - test for busy against threads... */ + delete (*sem); + *sem = NULL; return 0; } int -pthread_cond::init (pthread_cond_t *cond, const pthread_condattr_t *attr) +semaphore::close (sem_t *sem) { - pthread_cond_t new_cond; + if (!is_good_object (sem)) + { + set_errno(EINVAL); + return -1; + } - if (attr && !pthread_condattr::is_good_object (attr)) - return EINVAL; + /* It's invalid to close a semaphore not opened with sem_open. */ + if ((*sem)->fd == -1) + { + set_errno(EINVAL); + return -1; + } - cond_initialization_lock.lock (); + delete (*sem); + delete sem; + return 0; +} - new_cond = new pthread_cond (attr ? (*attr) : NULL); - if (!is_good_object (&new_cond)) +sem_t * +semaphore::open (unsigned long long hash, LUID luid, int fd, int oflag, + mode_t mode, unsigned int value, bool &wasopen) +{ + if (value > SEM_VALUE_MAX) { - delete new_cond; - cond_initialization_lock.unlock (); - return EAGAIN; + set_errno (EINVAL); + return NULL; } - int ret = 0; + /* sem_open is supposed to return the same pointer, if the same named + semaphore is opened multiple times in the same process, as long as + the semaphore hasn't been closed or unlinked in the meantime. */ + semaphores.mx.lock (); + for (semaphore *sema = semaphores.head; sema; sema = sema->next) + if (sema->fd >= 0 && sema->hash == hash + && sema->luid.HighPart == luid.HighPart + && sema->luid.LowPart == luid.LowPart) + { + wasopen = true; + semaphores.mx.unlock (); + return sema->sem; + } + semaphores.mx.unlock (); - __try + wasopen = false; + sem_t *sem = new sem_t; + if (!sem) { - *cond = new_cond; + set_errno (ENOMEM); + return NULL; } - __except (NO_ERROR) + + *sem = new semaphore (hash, luid, fd, sem, oflag, mode, value); + + if (!is_good_object (sem)) { - delete new_cond; - ret = EINVAL; + delete *sem; + delete sem; + return NULL; } - __endtry - cond_initialization_lock.unlock (); - return ret; + return sem; } -extern "C" int -pthread_cond_broadcast (pthread_cond_t *cond) +int +semaphore::wait (sem_t *sem) { - if (pthread_cond::is_initializer (cond)) - return 0; - if (!pthread_cond::is_good_object (cond)) - return EINVAL; + pthread_testcancel (); - (*cond)->unblock (true); + if (!is_good_object (sem)) + { + set_errno (EINVAL); + return -1; + } - return 0; + return (*sem)->_wait (); } -extern "C" int -pthread_cond_signal (pthread_cond_t *cond) +int +semaphore::trywait (sem_t *sem) { - if (pthread_cond::is_initializer (cond)) - return 0; - if (!pthread_cond::is_good_object (cond)) - return EINVAL; - - (*cond)->unblock (false); + if (!is_good_object (sem)) + { + set_errno (EINVAL); + return -1; + } - return 0; + return (*sem)->_trywait (); } -static int -__pthread_cond_wait_init (pthread_cond_t *cond, pthread_mutex_t *mutex) +int +semaphore::clockwait (sem_t *sem, clockid_t clock_id, + const struct timespec *abstime) { - 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; + LARGE_INTEGER timeout; - return 0; -} + if (!is_good_object (sem)) + { + set_errno (EINVAL); + return -1; + } -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; + /* According to SUSv3, abstime need not be checked for validity, + if the semaphore can be locked immediately. */ + if (!(*sem)->_trywait ()) + return 0; - do + __try { - err = pthread_convert_abstime (clock_id, abstime, &timeout); + int err = pthread_convert_abstime (clock_id, abstime, &timeout); if (err) - break; + return err; - err = (*cond)->wait (*mutex, &timeout); + return (*sem)->_wait (&timeout); } - while (err == ETIMEDOUT); - return err; + __except (NO_ERROR) {} + __endtry + return EINVAL; } -extern "C" int -pthread_cond_clockwait (pthread_cond_t *cond, pthread_mutex_t *mutex, - clockid_t clock_id, const struct timespec *abstime) +int +semaphore::post (sem_t *sem) { - int err = 0; + if (!is_good_object (sem)) + { + set_errno (EINVAL); + return -1; + } - pthread_testcancel (); + (*sem)->_post (); + return 0; +} +int +semaphore::getvalue (sem_t *sem, int *sval) +{ __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; + if (is_good_object (sem)) + return (*sem)->_getvalue (sval); } + __except (NO_ERROR) {} __endtry - return err; + set_errno (EINVAL); + return -1; } -extern "C" int -pthread_cond_timedwait (pthread_cond_t *cond, pthread_mutex_t *mutex, - const struct timespec *abstime) +int +semaphore::getinternal (sem_t *sem, int *sfd, unsigned long long *shash, + LUID *sluid, unsigned int *sval) { - int err = 0; - - pthread_testcancel (); - __try { - err = __pthread_cond_wait_init (cond, mutex); - if (err) + if (!is_good_object (sem)) __leave; - err = __pthread_cond_clockwait (cond, mutex, (*cond)->clock_id, abstime); - } - __except (NO_ERROR) - { - return EINVAL; + if ((*sfd = (*sem)->fd) < 0) + __leave; + *shash = (*sem)->hash; + *sluid = (*sem)->luid; + /* POSIX defines the value in calls to sem_init/sem_open as unsigned, + but the sem_getvalue gets a pointer to int to return the value. + Go figure! */ + return (*sem)->_getvalue ((int *)sval); } + __except (NO_ERROR) {} __endtry - return err; + set_errno (EINVAL); + return -1; } -extern "C" int -pthread_cond_wait (pthread_cond_t *cond, pthread_mutex_t *mutex) +/* pthread_null */ +pthread * +pthread_null::get_null_pthread () { - pthread_testcancel (); + /* because of weird entry points */ + _instance.magic = 0; + return &_instance; +} - int err = __pthread_cond_wait_init (cond, mutex); - if (err) - return err; - return (*cond)->wait (*mutex, NULL); +pthread_null::pthread_null () +{ + attr.joinable = PTHREAD_CREATE_DETACHED; + /* Mark ourselves as invalid */ + magic = 0; } -extern "C" int -pthread_condattr_init (pthread_condattr_t *condattr) +pthread_null::~pthread_null () { - *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) +bool +pthread_null::create (void *(*)(void *), pthread_attr *, void *) { - if (!pthread_condattr::is_good_object (attr)) - return EINVAL; - *pshared = (*attr)->shared; - return 0; + return true; } -extern "C" int -pthread_condattr_setpshared (pthread_condattr_t *attr, int pshared) +void +pthread_null::exit (void *value_ptr) { - 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; + _my_tls.remove (INFINITE); + ExitThread (0); } -extern "C" int -pthread_condattr_getclock (const pthread_condattr_t *attr, clockid_t *clock_id) +int +pthread_null::cancel () { - 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) +void +pthread_null::testcancel () { - 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) +int +pthread_null::setcancelstate (int state, int *oldstate) { - if (!pthread_condattr::is_good_object (condattr)) - return EINVAL; - delete (*condattr); - *condattr = NULL; - return 0; + return EINVAL; } -extern "C" int -pthread_rwlock_destroy (pthread_rwlock_t *rwlock) +int +pthread_null::setcanceltype (int type, int *oldtype) { - if (pthread_rwlock::is_initializer (rwlock)) - return 0; - if (!pthread_rwlock::is_good_object (rwlock)) - return EINVAL; + return EINVAL; +} - if ((*rwlock)->writer || (*rwlock)->readers || - (*rwlock)->waiting_readers || (*rwlock)->waiting_writers) - return EBUSY; +void +pthread_null::push_cleanup_handler (__pthread_cleanup_handler *handler) +{ +} - delete (*rwlock); - *rwlock = NULL; +void +pthread_null::pop_cleanup_handler (int const execute) +{ +} +unsigned long +pthread_null::getsequence_np () +{ return 0; } +pthread_null pthread_null::_instance; + int -pthread_rwlock::init (pthread_rwlock_t *rwlock, const pthread_rwlockattr_t *attr) +pthread_barrier::init (const pthread_barrierattr_t * attr, unsigned count) { - pthread_rwlock_t new_rwlock; + pthread_mutex_t * mutex = NULL; - if (attr && !pthread_rwlockattr::is_good_object (attr)) + if (unlikely ((attr != NULL + && (! pthread_barrierattr::is_good_object (attr) + || (*attr)->shared == PTHREAD_PROCESS_SHARED)) + || count == 0)) return EINVAL; - rwlock_initialization_lock.lock (); + int retval = pthread_mutex_init (&mtx, NULL); + if (unlikely (retval != 0)) + return retval; - new_rwlock = new pthread_rwlock (attr ? (*attr) : NULL); - if (!is_good_object (&new_rwlock)) + retval = pthread_cond_init (&cond, NULL); + if (unlikely (retval != 0)) { - delete new_rwlock; - rwlock_initialization_lock.unlock (); - return EAGAIN; + int ret = pthread_mutex_destroy (mutex); + if (ret != 0) + api_fatal ("pthread_mutex_destroy (%p) = %d", mutex, ret); + + mtx = NULL; + return retval; } - int ret = 0; + cnt = count; + cyc = 0; + wt = 0; - __try - { - *rwlock = new_rwlock; - } - __except (NO_ERROR) - { - delete new_rwlock; - ret = EINVAL; - } - __endtry - rwlock_initialization_lock.unlock (); - return ret; + return 0; } -extern "C" int -pthread_rwlock_rdlock (pthread_rwlock_t *rwlock) +int +pthread_barrier::destroy () { - pthread_testcancel (); + if (unlikely (wt != 0)) + return EBUSY; - if (pthread_rwlock::is_initializer (rwlock)) - pthread_rwlock::init (rwlock, NULL); - if (!pthread_rwlock::is_good_object (rwlock)) - return EINVAL; + int retval = pthread_cond_destroy (&cond); + if (unlikely (retval != 0)) + return retval; + else + cond = NULL; - return (*rwlock)->rdlock (); + retval = pthread_mutex_destroy (&mtx); + if (unlikely (retval != 0)) + return retval; + else + mtx = NULL; + + cnt = 0; + cyc = 0; + wt = 0; + + return 0; } -extern "C" int -pthread_rwlock_clockrdlock (pthread_rwlock_t *rwlock, clockid_t clock_id, - const struct timespec *abstime) +int +pthread_barrier::wait () { - LARGE_INTEGER timeout; + int retval = pthread_mutex_lock (&mtx); + if (unlikely (retval != 0)) + return retval; - pthread_testcancel (); + if (unlikely (wt >= cnt)) + { + api_fatal ("wt >= cnt (%u >= %u)", wt, cnt); + return EINVAL; + } - if (pthread_rwlock::is_initializer (rwlock)) - pthread_rwlock::init (rwlock, NULL); - if (!pthread_rwlock::is_good_object (rwlock)) - return EINVAL; + if (unlikely (++wt == cnt)) + { + ++cyc; + /* This is the last thread to reach the barrier. Signal the waiting + threads to wake up and continue. */ + retval = pthread_cond_broadcast (&cond); + if (unlikely (retval != 0)) + goto cond_error; - /* According to SUSv3, abstime need not be checked for validity, - if the rwlock can be locked immediately. */ - if (!(*rwlock)->tryrdlock ()) - return 0; + wt = 0; + retval = pthread_mutex_unlock (&mtx); + if (unlikely (retval != 0)) + abort (); - __try + return PTHREAD_BARRIER_SERIAL_THREAD; + } + else { - int err = pthread_convert_abstime (clock_id, abstime, &timeout); - if (err) - return err; + uint64_t cycle = cyc; + do + { + retval = pthread_cond_wait (&cond, &mtx); + if (unlikely (retval != 0)) + goto cond_error; + } + while (unlikely (cycle == cyc)); - return (*rwlock)->rdlock (&timeout); + retval = pthread_mutex_unlock (&mtx); + if (unlikely (retval != 0)) + api_fatal ("pthread_mutex_unlock (%p) = %d", &mtx, retval); + + return 0; } - __except (NO_ERROR) {} - __endtry - return EINVAL; + + cond_error: + { + --wt; + int ret = pthread_mutex_unlock (&mtx); + if (unlikely (ret != 0)) + api_fatal ("pthread_mutex_unlock (%p) = %d", &mtx, ret); + + return retval; + } } -extern "C" int -pthread_rwlock_timedrdlock (pthread_rwlock_t *rwlock, - const struct timespec *abstime) +/* Returns running thread's name; works for both cygthreads and pthreads */ +char * +mythreadname (void) { - return pthread_rwlock_clockrdlock (rwlock, CLOCK_REALTIME, abstime); + 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" int -pthread_rwlock_tryrdlock (pthread_rwlock_t *rwlock) +extern "C" { - if (pthread_rwlock::is_initializer (rwlock)) - pthread_rwlock::init (rwlock, NULL); - if (!pthread_rwlock::is_good_object (rwlock)) - return EINVAL; - return (*rwlock)->tryrdlock (); -} +/* Thread creation */ -extern "C" int -pthread_rwlock_wrlock (pthread_rwlock_t *rwlock) +int +pthread_create (pthread_t *thread, const pthread_attr_t *attr, + void *(*start_routine) (void *), void *arg) { - pthread_testcancel (); + return pthread::create (thread, attr, start_routine, arg); +} - if (pthread_rwlock::is_initializer (rwlock)) - pthread_rwlock::init (rwlock, NULL); - if (!pthread_rwlock::is_good_object (rwlock)) - return EINVAL; +int +pthread_once (pthread_once_t * once_control, void (*init_routine) (void)) +{ + return pthread::once (once_control, init_routine); +} - return (*rwlock)->wrlock (); +int +pthread_atfork (void (*prepare)(void), void (*parent)(void), void (*child)(void)) +{ + return pthread::atfork (prepare, parent, child); } -extern "C" int -pthread_rwlock_clockwrlock (pthread_rwlock_t *rwlock, clockid_t clock_id, - const struct timespec *abstime) +/* ID */ + +pthread_t pthread_self () { - LARGE_INTEGER timeout; + return pthread::self (); +} - pthread_testcancel (); +int +pthread_equal (pthread_t t1, pthread_t t2) +{ + return pthread::equal (t1, t2); +} - if (pthread_rwlock::is_initializer (rwlock)) - pthread_rwlock::init (rwlock, NULL); - if (!pthread_rwlock::is_good_object (rwlock)) +unsigned long +pthread_getsequence_np (pthread_t * thread) +{ + if (!pthread::is_good_object (thread)) return EINVAL; + return (*thread)->getsequence_np (); +} - /* According to SUSv3, abstime need not be checked for validity, - if the rwlock can be locked immediately. */ - if (!(*rwlock)->trywrlock ()) - return 0; +/* 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 { - int err = pthread_convert_abstime (clock_id, abstime, &timeout); - if (err) - return err; - - return (*rwlock)->wrlock (&timeout); + strlcpy (buf, name, buflen); + } + __except (NO_ERROR) + { + ret = EFAULT; } - __except (NO_ERROR) {} __endtry - return EINVAL; + + return ret; } -extern "C" int -pthread_rwlock_timedwrlock (pthread_rwlock_t *rwlock, - const struct timespec *abstime) +int +pthread_setname_np (pthread_t thread, const char *name) { - return pthread_rwlock_clockwrlock (rwlock, CLOCK_REALTIME, abstime); + 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; } -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; +/* Thread exit */ - return (*rwlock)->trywrlock (); +void +pthread_exit (void *value_ptr) +{ + pthread::self ()->exit (value_ptr); + __builtin_unreachable (); /* FIXME: don't know why this is necessary */ } -extern "C" int -pthread_rwlock_unlock (pthread_rwlock_t *rwlock) +int +pthread_detach (pthread_t thread) { - if (pthread_rwlock::is_initializer (rwlock)) - return 0; - if (!pthread_rwlock::is_good_object (rwlock)) - return EINVAL; + return pthread::detach (&thread); +} - return (*rwlock)->unlock (); +int +pthread_join (pthread_t thread, void **return_val) +{ + return pthread::join (&thread, (void **) return_val, NULL); } -extern "C" int -pthread_rwlockattr_init (pthread_rwlockattr_t *rwlockattr) +int +pthread_tryjoin_np (pthread_t thread, void **return_val) { - *rwlockattr = new pthread_rwlockattr; - if (!pthread_rwlockattr::is_good_object (rwlockattr)) - { - delete (*rwlockattr); - *rwlockattr = NULL; - return ENOMEM; - } - return 0; + LARGE_INTEGER timeout = { QuadPart:0LL }; + + return pthread::join (&thread, (void **) return_val, &timeout); } -extern "C" int -pthread_rwlockattr_getpshared (const pthread_rwlockattr_t *attr, int *pshared) +int +pthread_timedjoin_np (pthread_t thread, void **return_val, + const struct timespec *abstime) { - if (!pthread_rwlockattr::is_good_object (attr)) - return EINVAL; - *pshared = (*attr)->shared; - return 0; + 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_rwlockattr_setpshared (pthread_rwlockattr_t *attr, int pshared) +/* Thread suspend/resume */ + +/* This isn't a posix call... should we keep it? */ +int +pthread_suspend (pthread_t thread) { - 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; + return pthread::suspend (&thread); } -extern "C" int -pthread_rwlockattr_destroy (pthread_rwlockattr_t *rwlockattr) +/* same */ +int +pthread_continue (pthread_t thread) { - if (!pthread_rwlockattr::is_good_object (rwlockattr)) - return EINVAL; - delete (*rwlockattr); - *rwlockattr = NULL; - return 0; + return pthread::resume (&thread); } /* Thread signal */ -extern "C" int + +int pthread_kill (pthread_t thread, int sig) { // lock myself, for the use of thread2signal @@ -3380,7 +3286,7 @@ pthread_kill (pthread_t thread, int sig) return rval; } -extern "C" int +int pthread_sigmask (int operation, const sigset_t *set, sigset_t *old_set) { int res = handle_sigprocmask (operation, set, old_set, _my_tls.sigmask); @@ -3389,7 +3295,7 @@ pthread_sigmask (int operation, const sigset_t *set, sigset_t *old_set) return res; } -extern "C" int +int pthread_sigqueue (pthread_t *thread, int sig, const union sigval value) { siginfo_t si = {0}; @@ -3407,64 +3313,470 @@ pthread_sigqueue (pthread_t *thread, int sig, const union sigval value) return (int) sig_send (NULL, si, (*thread)->cygtls); } -/* ID */ +/* Cancelability */ -extern "C" int -pthread_equal (pthread_t t1, pthread_t t2) +int +pthread_cancel (pthread_t thread) { - return pthread::equal (t1, t2); + return pthread::cancel (thread); } -/* Mutexes */ +int +pthread_setcancelstate (int state, int *oldstate) +{ + return pthread::self ()->setcancelstate (state, oldstate); +} int -pthread_mutex::init (pthread_mutex_t *mutex, - const pthread_mutexattr_t *attr, - const pthread_mutex_t initializer) +pthread_setcanceltype (int type, int *oldtype) { - if (attr && !pthread_mutexattr::is_good_object (attr)) + 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; +} - mutex_initialization_lock.lock (); - if (initializer == NULL || pthread_mutex::is_initializer (mutex)) +/* 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)) { - pthread_mutex_t new_mutex = new pthread_mutex (attr ? (*attr) : NULL); - if (!is_good_object (&new_mutex)) - { - delete new_mutex; - mutex_initialization_lock.unlock (); - return EAGAIN; - } + delete (*attr); + *attr = NULL; + return ENOMEM; + } + return 0; +} - if (!attr && initializer) - { - if (initializer == PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP) - new_mutex->type = PTHREAD_MUTEX_RECURSIVE; - else if (initializer == PTHREAD_NORMAL_MUTEX_INITIALIZER_NP) - new_mutex->type = PTHREAD_MUTEX_NORMAL; - else if (initializer == PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP) - new_mutex->type = PTHREAD_MUTEX_ERRORCHECK; - } +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; +} - __try - { - *mutex = new_mutex; - } - __except (NO_ERROR) - { - delete new_mutex; - mutex_initialization_lock.unlock (); - return EINVAL; - } - __endtry +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; } - mutex_initialization_lock.unlock (); - pthread_printf ("*mutex %p, attr %p, initializer %p", *mutex, attr, initializer); return 0; } -extern "C" int +/* 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) { @@ -3478,7 +3790,7 @@ pthread_mutex_getprioceiling (const pthread_mutex_t *mutex, return ENOSYS; } -extern "C" int +int pthread_mutex_lock (pthread_mutex_t *mutex) { if (pthread_mutex::is_initializer (mutex)) @@ -3488,7 +3800,7 @@ pthread_mutex_lock (pthread_mutex_t *mutex) return (*mutex)->lock (); } -extern "C" int +int pthread_mutex_clocklock (pthread_mutex_t *mutex, clockid_t clock_id, const struct timespec *abstime) { @@ -3517,13 +3829,13 @@ pthread_mutex_clocklock (pthread_mutex_t *mutex, clockid_t clock_id, return EINVAL; } -extern "C" int +int pthread_mutex_timedlock (pthread_mutex_t *mutex, const struct timespec *abstime) { return pthread_mutex_clocklock (mutex, CLOCK_REALTIME, abstime); } -extern "C" int +int pthread_mutex_trylock (pthread_mutex_t *mutex) { if (pthread_mutex::is_initializer (mutex)) @@ -3533,7 +3845,7 @@ pthread_mutex_trylock (pthread_mutex_t *mutex) return (*mutex)->trylock (); } -extern "C" int +int pthread_mutex_unlock (pthread_mutex_t *mutex) { if (pthread_mutex::is_initializer (mutex)) @@ -3543,7 +3855,7 @@ pthread_mutex_unlock (pthread_mutex_t *mutex) return (*mutex)->unlock (); } -extern "C" int +int pthread_mutex_destroy (pthread_mutex_t *mutex) { int rv; @@ -3561,74 +3873,18 @@ pthread_mutex_destroy (pthread_mutex_t *mutex) return 0; } -extern "C" int +int pthread_mutex_setprioceiling (pthread_mutex_t *mutex, int prioceiling, int *old_ceiling) { return ENOSYS; } -/* Spinlocks */ - -int -pthread_spinlock::init (pthread_spinlock_t *spinlock, int pshared) -{ - pthread_spinlock_t new_spinlock = new pthread_spinlock (pshared); - if (!is_good_object (&new_spinlock)) - { - delete new_spinlock; - return EAGAIN; - } - - __try - { - *spinlock = new_spinlock; - } - __except (NO_ERROR) - { - delete new_spinlock; - return EINVAL; - } - __endtry - pthread_printf ("*spinlock %p, pshared %d", *spinlock, 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 (); -} +/* Mutex attributes */ /* Win32 doesn't support mutex priorities - see __pthread_mutex_getprioceiling for more detail */ -extern "C" int +int pthread_mutexattr_getprotocol (const pthread_mutexattr_t *attr, int *protocol) { @@ -3637,7 +3893,7 @@ pthread_mutexattr_getprotocol (const pthread_mutexattr_t *attr, return ENOSYS; } -extern "C" int +int pthread_mutexattr_getpshared (const pthread_mutexattr_t *attr, int *pshared) { @@ -3647,7 +3903,7 @@ pthread_mutexattr_getpshared (const pthread_mutexattr_t *attr, return 0; } -extern "C" int +int pthread_mutexattr_gettype (const pthread_mutexattr_t *attr, int *type) { if (!pthread_mutexattr::is_good_object (attr)) @@ -3657,7 +3913,7 @@ pthread_mutexattr_gettype (const pthread_mutexattr_t *attr, int *type) } /* FIXME: write and test process shared mutex's. */ -extern "C" int +int pthread_mutexattr_init (pthread_mutexattr_t *attr) { *attr = new pthread_mutexattr (); @@ -3670,7 +3926,7 @@ pthread_mutexattr_init (pthread_mutexattr_t *attr) return 0; } -extern "C" int +int pthread_mutexattr_destroy (pthread_mutexattr_t *attr) { if (!pthread_mutexattr::is_good_object (attr)) @@ -3682,7 +3938,7 @@ pthread_mutexattr_destroy (pthread_mutexattr_t *attr) /* Win32 doesn't support mutex priorities */ -extern "C" int +int pthread_mutexattr_setprotocol (pthread_mutexattr_t *attr, int protocol) { if (!pthread_mutexattr::is_good_object (attr)) @@ -3691,7 +3947,7 @@ pthread_mutexattr_setprotocol (pthread_mutexattr_t *attr, int protocol) } /* Win32 doesn't support mutex priorities */ -extern "C" int +int pthread_mutexattr_setprioceiling (pthread_mutexattr_t *attr, int prioceiling) { @@ -3700,7 +3956,7 @@ pthread_mutexattr_setprioceiling (pthread_mutexattr_t *attr, return ENOSYS; } -extern "C" int +int pthread_mutexattr_getprioceiling (const pthread_mutexattr_t *attr, int *prioceiling) { @@ -3709,7 +3965,7 @@ pthread_mutexattr_getprioceiling (const pthread_mutexattr_t *attr, return ENOSYS; } -extern "C" int +int pthread_mutexattr_setpshared (pthread_mutexattr_t *attr, int pshared) { if (!pthread_mutexattr::is_good_object (attr)) @@ -3724,7 +3980,7 @@ pthread_mutexattr_setpshared (pthread_mutexattr_t *attr, int pshared) } /* see pthread_mutex_gettype */ -extern "C" int +int pthread_mutexattr_settype (pthread_mutexattr_t *attr, int type) { if (!pthread_mutexattr::is_good_object (attr)) @@ -3744,331 +4000,314 @@ pthread_mutexattr_settype (pthread_mutexattr_t *attr, int type) return 0; } -/* Semaphores */ - -List<semaphore> semaphore::semaphores; +/* Spinlocks */ -semaphore::semaphore (int pshared, unsigned int value) -: verifyable_object (SEM_MAGIC), - shared (pshared), - currentvalue (-1), - startvalue (value), - fd (-1), - hash (0ULL), - sem (NULL) +int +pthread_spin_init (pthread_spinlock_t *spinlock, int pshared) { - SECURITY_ATTRIBUTES sa = (pshared != PTHREAD_PROCESS_PRIVATE) - ? sec_all : sec_none_nih; - this->win32_obj_id = ::CreateSemaphore (&sa, value, INT32_MAX, NULL); - if (!this->win32_obj_id) - magic = 0; - - semaphores.insert (this); + return pthread_spinlock::init (spinlock, pshared); } -semaphore::semaphore (unsigned long long shash, LUID sluid, int sfd, - sem_t *ssem, int oflag, mode_t mode, unsigned int value) -: verifyable_object (SEM_MAGIC), - shared (PTHREAD_PROCESS_SHARED), - currentvalue (-1), /* Unused for named semaphores. */ - startvalue (value), - fd (sfd), - hash (shash), - luid (sluid), - sem (ssem) +int +pthread_spin_lock (pthread_spinlock_t *spinlock) { - char name[MAX_PATH]; - - __small_sprintf (name, "semaphore/%016X%08x%08x", - hash, luid.HighPart, luid.LowPart); - this->win32_obj_id = ::CreateSemaphore (&sec_all, value, INT32_MAX, name); - if (!this->win32_obj_id) - magic = 0; - if (GetLastError () == ERROR_ALREADY_EXISTS && (oflag & O_EXCL)) - { - __seterrno (); - CloseHandle (this->win32_obj_id); - magic = 0; - } - - semaphores.insert (this); + if (!pthread_spinlock::is_good_object (spinlock)) + return EINVAL; + return (*spinlock)->lock (); } -semaphore::~semaphore () +int +pthread_spin_trylock (pthread_spinlock_t *spinlock) { - if (win32_obj_id) - CloseHandle (win32_obj_id); - - semaphores.remove (this); + if (!pthread_spinlock::is_good_object (spinlock)) + return EINVAL; + return (*spinlock)->trylock (); } -void -semaphore::_post () +int +pthread_spin_unlock (pthread_spinlock_t *spinlock) { - LONG dummy; - ReleaseSemaphore (win32_obj_id, 1, &dummy); + if (!pthread_spinlock::is_good_object (spinlock)) + return EINVAL; + return (*spinlock)->unlock (); } int -semaphore::_getvalue (int *sval) +pthread_spin_destroy (pthread_spinlock_t *spinlock) { - NTSTATUS status; - SEMAPHORE_BASIC_INFORMATION sbi; + if (!pthread_spinlock::is_good_object (spinlock)) + return EINVAL; + return (*spinlock)->destroy (); +} - status = NtQuerySemaphore (win32_obj_id, SemaphoreBasicInformation, &sbi, - sizeof sbi, NULL); - int res; - if (NT_SUCCESS (status)) - { - *sval = sbi.CurrentCount; - res = 0; - } - else - { - *sval = startvalue; - __seterrno_from_nt_status (status); - res = -1; - } - return res; +/* Synchronisation */ + +int +pthread_cond_init (pthread_cond_t * cond, const pthread_condattr_t * attr) +{ + return pthread_cond::init (cond, attr); } int -semaphore::_trywait () +pthread_cond_destroy (pthread_cond_t *cond) { - /* FIXME: signals should be able to interrupt semaphores... - We probably need WaitForMultipleObjects here. */ - if (WaitForSingleObject (win32_obj_id, 0) == WAIT_TIMEOUT) - { - set_errno (EAGAIN); - return -1; - } + 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 -semaphore::_wait (PLARGE_INTEGER timeout) +pthread_cond_broadcast (pthread_cond_t *cond) { - __try - { - switch (cygwait (win32_obj_id, timeout, - cw_cancel | cw_cancel_self | cw_sig_eintr)) - { - case WAIT_OBJECT_0: - break; - case WAIT_SIGNALED: - set_errno (EINTR); - return -1; - case WAIT_TIMEOUT: - set_errno (ETIMEDOUT); - return -1; - default: - pthread_printf ("cygwait failed. %E"); - __seterrno (); - return -1; - } - } - __except (NO_ERROR) {} - __endtry + if (pthread_cond::is_initializer (cond)) + return 0; + if (!pthread_cond::is_good_object (cond)) + return EINVAL; + + (*cond)->unblock (true); + return 0; } -void -semaphore::_fixup_before_fork () +int +pthread_cond_signal (pthread_cond_t *cond) { - NTSTATUS status; - SEMAPHORE_BASIC_INFORMATION sbi; + if (pthread_cond::is_initializer (cond)) + return 0; + if (!pthread_cond::is_good_object (cond)) + return EINVAL; - status = NtQuerySemaphore (win32_obj_id, SemaphoreBasicInformation, &sbi, - sizeof sbi, NULL); - if (NT_SUCCESS (status)) - currentvalue = sbi.CurrentCount; - else - currentvalue = startvalue; + (*cond)->unblock (false); + + return 0; } -void -semaphore::_fixup_after_fork () +static int +__pthread_cond_wait_init (pthread_cond_t *cond, pthread_mutex_t *mutex) { - if (shared == PTHREAD_PROCESS_PRIVATE) - { - pthread_printf ("sem %p", this); - win32_obj_id = ::CreateSemaphore (&sec_none_nih, currentvalue, - INT32_MAX, NULL); - if (!win32_obj_id) - api_fatal ("failed to create new win32 semaphore, " - "currentvalue %ld, %E", currentvalue); - } + 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; } -void -semaphore::_terminate () +static int +__pthread_cond_clockwait (pthread_cond_t *cond, pthread_mutex_t *mutex, + clockid_t clock_id, const struct timespec *abstime) { - int _sem_close (sem_t *, bool); + int err = 0; + LARGE_INTEGER timeout; - if (sem) - _sem_close (sem, false); -} + do + { + err = pthread_convert_abstime (clock_id, abstime, &timeout); + if (err) + break; -/* static members */ + err = (*cond)->wait (*mutex, &timeout); + } + while (err == ETIMEDOUT); + return err; +} int -semaphore::init (sem_t *sem, int pshared, unsigned int value) +pthread_cond_clockwait (pthread_cond_t *cond, pthread_mutex_t *mutex, + clockid_t clock_id, const struct timespec *abstime) { - /* - We can't tell the difference between reinitialising an - existing semaphore and initialising a semaphore who's - contents happen to be a valid pointer - */ - if (is_good_object (sem)) - paranoid_printf ("potential attempt to reinitialise a semaphore"); + int err = 0; - if (value > SEM_VALUE_MAX) + pthread_testcancel (); + + __try { - set_errno(EINVAL); - return -1; + err = __pthread_cond_wait_init (cond, mutex); + if (err) + __leave; + err = __pthread_cond_clockwait (cond, mutex, clock_id, abstime); } - - *sem = new semaphore (pshared, value); - - if (!is_good_object (sem)) + __except (NO_ERROR) { - delete (*sem); - *sem = NULL; - set_errno(EAGAIN); - return -1; + return EINVAL; } - return 0; + __endtry + return err; } int -semaphore::destroy (sem_t *sem) +pthread_cond_timedwait (pthread_cond_t *cond, pthread_mutex_t *mutex, + const struct timespec *abstime) { - if (!is_good_object (sem)) + int err = 0; + + pthread_testcancel (); + + __try { - set_errno(EINVAL); - return -1; + err = __pthread_cond_wait_init (cond, mutex); + if (err) + __leave; + err = __pthread_cond_clockwait (cond, mutex, (*cond)->clock_id, abstime); } - - /* It's invalid to destroy a semaphore not opened with sem_init. */ - if ((*sem)->fd != -1) + __except (NO_ERROR) { - set_errno(EINVAL); - return -1; + return EINVAL; } + __endtry + return err; +} - /* FIXME - new feature - test for busy against threads... */ +int +pthread_cond_wait (pthread_cond_t *cond, pthread_mutex_t *mutex) +{ + pthread_testcancel (); - delete (*sem); - *sem = NULL; - return 0; + int err = __pthread_cond_wait_init (cond, mutex); + if (err) + return err; + return (*cond)->wait (*mutex, NULL); } +/* Thread cond attributes */ + int -semaphore::close (sem_t *sem) +pthread_condattr_init (pthread_condattr_t *condattr) { - if (!is_good_object (sem)) + *condattr = new pthread_condattr; + if (!pthread_condattr::is_good_object (condattr)) { - set_errno(EINVAL); - return -1; + delete (*condattr); + *condattr = NULL; + return ENOMEM; } + return 0; +} - /* It's invalid to close a semaphore not opened with sem_open. */ - if ((*sem)->fd == -1) - { - set_errno(EINVAL); - return -1; - } +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; +} - delete (*sem); - delete sem; +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; } -sem_t * -semaphore::open (unsigned long long hash, LUID luid, int fd, int oflag, - mode_t mode, unsigned int value, bool &wasopen) +int +pthread_condattr_getclock (const pthread_condattr_t *attr, clockid_t *clock_id) { - if (value > SEM_VALUE_MAX) - { - set_errno (EINVAL); - return NULL; - } + if (!pthread_condattr::is_good_object (attr)) + return EINVAL; + *clock_id = (*attr)->clock_id; + return 0; +} - /* sem_open is supposed to return the same pointer, if the same named - semaphore is opened multiple times in the same process, as long as - the semaphore hasn't been closed or unlinked in the meantime. */ - semaphores.mx.lock (); - for (semaphore *sema = semaphores.head; sema; sema = sema->next) - if (sema->fd >= 0 && sema->hash == hash - && sema->luid.HighPart == luid.HighPart - && sema->luid.LowPart == luid.LowPart) - { - wasopen = true; - semaphores.mx.unlock (); - return sema->sem; - } - semaphores.mx.unlock (); +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; +} - wasopen = false; - sem_t *sem = new sem_t; - if (!sem) - { - set_errno (ENOMEM); - return NULL; - } +int +pthread_condattr_destroy (pthread_condattr_t *condattr) +{ + if (!pthread_condattr::is_good_object (condattr)) + return EINVAL; + delete (*condattr); + *condattr = NULL; + return 0; +} - *sem = new semaphore (hash, luid, fd, sem, oflag, mode, value); +/* RW Locks */ - if (!is_good_object (sem)) - { - delete *sem; - delete sem; - return NULL; - } - return sem; +int +pthread_rwlock_init (pthread_rwlock_t *rwlock, const pthread_rwlockattr_t *attr) +{ + return pthread_rwlock::init (rwlock, attr); } int -semaphore::wait (sem_t *sem) +pthread_rwlock_destroy (pthread_rwlock_t *rwlock) { - pthread_testcancel (); + if (pthread_rwlock::is_initializer (rwlock)) + return 0; + if (!pthread_rwlock::is_good_object (rwlock)) + return EINVAL; - if (!is_good_object (sem)) - { - set_errno (EINVAL); - return -1; - } + if ((*rwlock)->writer || (*rwlock)->readers || + (*rwlock)->waiting_readers || (*rwlock)->waiting_writers) + return EBUSY; - return (*sem)->_wait (); + delete (*rwlock); + *rwlock = NULL; + + return 0; } int -semaphore::trywait (sem_t *sem) +pthread_rwlock_rdlock (pthread_rwlock_t *rwlock) { - if (!is_good_object (sem)) - { - set_errno (EINVAL); - return -1; - } + pthread_testcancel (); - return (*sem)->_trywait (); + if (pthread_rwlock::is_initializer (rwlock)) + pthread_rwlock::init (rwlock, NULL); + if (!pthread_rwlock::is_good_object (rwlock)) + return EINVAL; + + return (*rwlock)->rdlock (); } int -semaphore::clockwait (sem_t *sem, clockid_t clock_id, - const struct timespec *abstime) +pthread_rwlock_clockrdlock (pthread_rwlock_t *rwlock, clockid_t clock_id, + const struct timespec *abstime) { LARGE_INTEGER timeout; - if (!is_good_object (sem)) - { - set_errno (EINVAL); - return -1; - } + 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 semaphore can be locked immediately. */ - if (!(*sem)->_trywait ()) + if the rwlock can be locked immediately. */ + if (!(*rwlock)->tryrdlock ()) return 0; __try @@ -4077,7 +4316,7 @@ semaphore::clockwait (sem_t *sem, clockid_t clock_id, if (err) return err; - return (*sem)->_wait (&timeout); + return (*rwlock)->rdlock (&timeout); } __except (NO_ERROR) {} __endtry @@ -4085,131 +4324,182 @@ semaphore::clockwait (sem_t *sem, clockid_t clock_id, } int -semaphore::post (sem_t *sem) +pthread_rwlock_timedrdlock (pthread_rwlock_t *rwlock, + const struct timespec *abstime) { - if (!is_good_object (sem)) - { - set_errno (EINVAL); - return -1; - } + return pthread_rwlock_clockrdlock (rwlock, CLOCK_REALTIME, abstime); +} - (*sem)->_post (); - return 0; +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 -semaphore::getvalue (sem_t *sem, int *sval) +pthread_rwlock_wrlock (pthread_rwlock_t *rwlock) { - __try - { - if (is_good_object (sem)) - return (*sem)->_getvalue (sval); - } - __except (NO_ERROR) {} - __endtry - set_errno (EINVAL); - return -1; + 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 -semaphore::getinternal (sem_t *sem, int *sfd, unsigned long long *shash, - LUID *sluid, unsigned int *sval) +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 { - if (!is_good_object (sem)) - __leave; - if ((*sfd = (*sem)->fd) < 0) - __leave; - *shash = (*sem)->hash; - *sluid = (*sem)->luid; - /* POSIX defines the value in calls to sem_init/sem_open as unsigned, - but the sem_getvalue gets a pointer to int to return the value. - Go figure! */ - return (*sem)->_getvalue ((int *)sval); + int err = pthread_convert_abstime (clock_id, abstime, &timeout); + if (err) + return err; + + return (*rwlock)->wrlock (&timeout); } __except (NO_ERROR) {} __endtry - set_errno (EINVAL); - return -1; + return EINVAL; } -/* pthread_null */ -pthread * -pthread_null::get_null_pthread () +int +pthread_rwlock_timedwrlock (pthread_rwlock_t *rwlock, + const struct timespec *abstime) { - /* because of weird entry points */ - _instance.magic = 0; - return &_instance; + return pthread_rwlock_clockwrlock (rwlock, CLOCK_REALTIME, abstime); } -pthread_null::pthread_null () +int +pthread_rwlock_trywrlock (pthread_rwlock_t *rwlock) { - attr.joinable = PTHREAD_CREATE_DETACHED; - /* Mark ourselves as invalid */ - magic = 0; -} + if (pthread_rwlock::is_initializer (rwlock)) + pthread_rwlock::init (rwlock, NULL); + if (!pthread_rwlock::is_good_object (rwlock)) + return EINVAL; -pthread_null::~pthread_null () -{ + return (*rwlock)->trywrlock (); } -bool -pthread_null::create (void *(*)(void *), pthread_attr *, void *) +int +pthread_rwlock_unlock (pthread_rwlock_t *rwlock) { - return true; -} + if (pthread_rwlock::is_initializer (rwlock)) + return 0; + if (!pthread_rwlock::is_good_object (rwlock)) + return EINVAL; -void -pthread_null::exit (void *value_ptr) -{ - _my_tls.remove (INFINITE); - ExitThread (0); + return (*rwlock)->unlock (); } +/* RW Lock attributes */ + int -pthread_null::cancel () +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; } -void -pthread_null::testcancel () +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_null::setcancelstate (int state, int *oldstate) +pthread_rwlockattr_setpshared (pthread_rwlockattr_t *attr, int pshared) { - return EINVAL; + 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_null::setcanceltype (int type, int *oldtype) +pthread_rwlockattr_destroy (pthread_rwlockattr_t *rwlockattr) { - return EINVAL; + if (!pthread_rwlockattr::is_good_object (rwlockattr)) + return EINVAL; + delete (*rwlockattr); + *rwlockattr = NULL; + return 0; } -void -pthread_null::push_cleanup_handler (__pthread_cleanup_handler *handler) +/* 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); } -void -pthread_null::pop_cleanup_handler (int const execute) +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; } -unsigned long -pthread_null::getsequence_np () +int +pthread_barrier_wait (pthread_barrier_t * bar) { - return 0; -} + if (unlikely (! pthread_barrier::is_good_object (bar))) + return EINVAL; -pthread_null pthread_null::_instance; + return (*bar)->wait (); +} +/* Barrier attributes */ -extern "C" int pthread_barrierattr_init (pthread_barrierattr_t * battr) { @@ -4222,8 +4512,6 @@ pthread_barrierattr_init (pthread_barrierattr_t * battr) return 0; } - -extern "C" int pthread_barrierattr_setpshared (pthread_barrierattr_t * battr, int shared) { @@ -4238,8 +4526,6 @@ pthread_barrierattr_setpshared (pthread_barrierattr_t * battr, int shared) return 0; } - -extern "C" int pthread_barrierattr_getpshared (const pthread_barrierattr_t * battr, int * shared) @@ -4252,8 +4538,6 @@ pthread_barrierattr_getpshared (const pthread_barrierattr_t * battr, return 0; } - -extern "C" int pthread_barrierattr_destroy (pthread_barrierattr_t * battr) { @@ -4264,161 +4548,65 @@ pthread_barrierattr_destroy (pthread_barrierattr_t * battr) return 0; } +/* Thread clock ID */ -extern "C" int -pthread_barrier_init (pthread_barrier_t * bar, - const pthread_barrierattr_t * attr, unsigned count) +pthread_getcpuclockid (pthread_t thread, clockid_t *clk_id) { - if (unlikely (bar == NULL)) - return EINVAL; - - *bar = new pthread_barrier; - return (*bar)->init (attr, count); + if (!pthread::is_good_object (&thread)) + return (ESRCH); + *clk_id = (clockid_t) THREADID_TO_CLOCKID (thread->getsequence_np ()); + return 0; } +/* Semaphores */ int -pthread_barrier::init (const pthread_barrierattr_t * attr, unsigned count) +sem_init (sem_t * sem, int pshared, unsigned int value) { - pthread_mutex_t * mutex = NULL; - - if (unlikely ((attr != NULL - && (! pthread_barrierattr::is_good_object (attr) - || (*attr)->shared == PTHREAD_PROCESS_SHARED)) - || count == 0)) - return EINVAL; - - int retval = pthread_mutex_init (&mtx, NULL); - if (unlikely (retval != 0)) - return retval; - - retval = pthread_cond_init (&cond, NULL); - if (unlikely (retval != 0)) - { - int ret = pthread_mutex_destroy (mutex); - if (ret != 0) - api_fatal ("pthread_mutex_destroy (%p) = %d", mutex, ret); - - mtx = NULL; - return retval; - } - - cnt = count; - cyc = 0; - wt = 0; - - return 0; + return semaphore::init (sem, pshared, value); } - -extern "C" int -pthread_barrier_destroy (pthread_barrier_t * bar) +sem_destroy (sem_t * sem) { - if (unlikely (! pthread_barrier::is_good_object (bar))) - return EINVAL; - - int ret; - ret = (*bar)->destroy (); - if (ret == 0) - delete_and_clear (bar); - - return ret; + return semaphore::destroy (sem); } - int -pthread_barrier::destroy () +sem_wait (sem_t * sem) { - if (unlikely (wt != 0)) - return EBUSY; - - int retval = pthread_cond_destroy (&cond); - if (unlikely (retval != 0)) - return retval; - else - cond = NULL; - - retval = pthread_mutex_destroy (&mtx); - if (unlikely (retval != 0)) - return retval; - else - mtx = NULL; - - cnt = 0; - cyc = 0; - wt = 0; - - return 0; + return semaphore::wait (sem); } - -extern "C" int -pthread_barrier_wait (pthread_barrier_t * bar) +sem_trywait (sem_t * sem) { - if (unlikely (! pthread_barrier::is_good_object (bar))) - return EINVAL; - - return (*bar)->wait (); + return semaphore::trywait (sem); } - int -pthread_barrier::wait () +sem_clockwait (sem_t * sem, clockid_t clock_id, const struct timespec *abstime) { - int retval = pthread_mutex_lock (&mtx); - if (unlikely (retval != 0)) - return retval; - - if (unlikely (wt >= cnt)) - { - api_fatal ("wt >= cnt (%u >= %u)", wt, cnt); - return EINVAL; - } - - if (unlikely (++wt == cnt)) - { - ++cyc; - /* This is the last thread to reach the barrier. Signal the waiting - threads to wake up and continue. */ - retval = pthread_cond_broadcast (&cond); - if (unlikely (retval != 0)) - goto cond_error; - - wt = 0; - retval = pthread_mutex_unlock (&mtx); - if (unlikely (retval != 0)) - abort (); - - return PTHREAD_BARRIER_SERIAL_THREAD; - } - else - { - uint64_t cycle = cyc; - do - { - retval = pthread_cond_wait (&cond, &mtx); - if (unlikely (retval != 0)) - goto cond_error; - } - while (unlikely (cycle == cyc)); + return semaphore::clockwait (sem, clock_id, abstime); +} - retval = pthread_mutex_unlock (&mtx); - if (unlikely (retval != 0)) - api_fatal ("pthread_mutex_unlock (%p) = %d", &mtx, retval); +int +sem_timedwait (sem_t * sem, const struct timespec *abstime) +{ + return semaphore::clockwait (sem, CLOCK_REALTIME, abstime); +} - return 0; - } +int +sem_post (sem_t *sem) +{ + return semaphore::post (sem); +} - cond_error: - { - --wt; - int ret = pthread_mutex_unlock (&mtx); - if (unlikely (ret != 0)) - api_fatal ("pthread_mutex_unlock (%p) = %d", &mtx, ret); +int +sem_getvalue (sem_t * sem, int *sval) +{ + return semaphore::getvalue (sem, sval); +} - return retval; - } } |