/* * Pop a clean pthread_t struct off the reuse stack. */ pthread_t ptw32_threadReusePop (void) { pthread_t t = {NULL, 0}; ptw32_mcs_local_node_t node; ptw32_mcs_lock_acquire(&ptw32_thread_reuse_lock, &node); if (PTW32_THREAD_REUSE_EMPTY != ptw32_threadReuseTop) { ptw32_thread_t * tp; tp = ptw32_threadReuseTop; ptw32_threadReuseTop = tp->prevReuse; if (PTW32_THREAD_REUSE_EMPTY == ptw32_threadReuseTop) { ptw32_threadReuseBottom = PTW32_THREAD_REUSE_EMPTY; } tp->prevReuse = PTW32_THREAD_REUSE_EMPTY; t = tp->ptHandle; } ptw32_mcs_lock_release(&node); return t; }
static void PTW32_CDECL ptw32_once_on_init_cancel (void * arg) { /* when the initting thread is cancelled we have to release the lock */ ptw32_mcs_local_node_t *node = (ptw32_mcs_local_node_t *)arg; ptw32_mcs_lock_release(node); }
int pthread_getname_np(pthread_t thr, char *name, int len) { ptw32_mcs_local_node_t threadLock; ptw32_thread_t * tp; char * s, * d; int result; /* * Validate the thread id. This method works for pthreads-win32 because * pthread_kill and pthread_t are designed to accommodate it, but the * method is not portable. */ result = pthread_kill (thr, 0); if (0 != result) { return result; } tp = (ptw32_thread_t *) thr.p; ptw32_mcs_lock_acquire (&tp->threadLock, &threadLock); for (s = tp->name, d = name; *s && d < &name[len - 1]; *d++ = *s++) {} *d = '\0'; ptw32_mcs_lock_release (&threadLock); return result; }
int pthread_kill (pthread_t thread, int sig) { int result = 0; ptw32_thread_t * tp; ptw32_mcs_local_node_t node; ptw32_mcs_lock_acquire(&ptw32_thread_reuse_lock, &node); tp = (ptw32_thread_t *) thread.p; if (NULL == tp || thread.x != tp->ptHandle.x || NULL == tp->threadH) { result = ESRCH; } ptw32_mcs_lock_release(&node); if (0 == result && 0 != sig) { result = EINVAL; } return result; }
int pthread_kill (pthread_t thread, int sig) /* * ------------------------------------------------------ * DOCPUBLIC * This function requests that a signal be delivered to the * specified thread. If sig is zero, error checking is * performed but no signal is actually sent such that this * function can be used to check for a valid thread ID. * * PARAMETERS * thread reference to an instances of pthread_t * sig signal. Currently only a value of 0 is supported. * * * DESCRIPTION * This function requests that a signal be delivered to the * specified thread. If sig is zero, error checking is * performed but no signal is actually sent such that this * function can be used to check for a valid thread ID. * * RESULTS * ESRCH the thread is not a valid thread ID, * EINVAL the value of the signal is invalid * or unsupported. * 0 the signal was successfully sent. * * ------------------------------------------------------ */ { int result = 0; ptw32_thread_t * tp; ptw32_mcs_local_node_t node; ptw32_mcs_lock_acquire(&ptw32_thread_reuse_lock, &node); tp = (ptw32_thread_t *) thread.p; if (NULL == tp || thread.x != tp->ptHandle.x || NULL == tp->threadH) { result = ESRCH; } ptw32_mcs_lock_release(&node); if (0 == result && 0 != sig) { /* * Currently does not support any signals. */ result = EINVAL; } return result; } /* pthread_kill */
BOOL pthread_win32_thread_detach_np () { if (ptw32_processInitialized) { /* * Don't use pthread_self() - to avoid creating an implicit POSIX thread handle * unnecessarily. */ ptw32_thread_t * sp = (ptw32_thread_t *) pthread_getspecific (ptw32_selfThreadKey); if (sp != NULL) // otherwise Win32 thread with no implicit POSIX handle. { ptw32_mcs_local_node_t stateLock; ptw32_callUserDestroyRoutines (sp->ptHandle); ptw32_mcs_lock_acquire (&sp->stateLock, &stateLock); sp->state = PThreadStateLast; /* * If the thread is joinable at this point then it MUST be joined * or detached explicitly by the application. */ ptw32_mcs_lock_release (&stateLock); /* * Robust Mutexes */ while (sp->robustMxList != NULL) { pthread_mutex_t mx = sp->robustMxList->mx; ptw32_robust_mutex_remove(&mx, sp); (void) PTW32_INTERLOCKED_EXCHANGE_LONG( (PTW32_INTERLOCKED_LONGPTR)&mx->robustNode->stateInconsistent, (PTW32_INTERLOCKED_LONG)-1); /* * If there are no waiters then the next thread to block will * sleep, wake up immediately and then go back to sleep. * See pthread_mutex_lock.c. */ SetEvent(mx->event); } if (sp->detachState == PTHREAD_CREATE_DETACHED) { ptw32_threadDestroy (sp->ptHandle); if (ptw32_selfThreadKey) { TlsSetValue (ptw32_selfThreadKey->key, NULL); } } } } return TRUE; }
int sem_trywait (sem_t * sem) /* * ------------------------------------------------------ * DOCPUBLIC * This function tries to wait on a semaphore. * * PARAMETERS * sem * pointer to an instance of sem_t * * DESCRIPTION * This function tries to wait on a semaphore. If the * semaphore value is greater than zero, it decreases * its value by one. If the semaphore value is zero, then * this function returns immediately with the error EAGAIN * * RESULTS * 0 successfully decreased semaphore, * -1 failed, error in errno * ERRNO * EAGAIN the semaphore was already locked, * EINVAL 'sem' is not a valid semaphore, * ENOTSUP sem_trywait is not supported, * EINTR the function was interrupted by a signal, * EDEADLK a deadlock condition was detected. * * ------------------------------------------------------ */ { int result = 0; sem_t s = *sem; ptw32_mcs_local_node_t node; ptw32_mcs_lock_acquire(&s->lock, &node); if (s->value > 0) { s->value--; } else { result = EAGAIN; } ptw32_mcs_lock_release(&node); if (result != 0) { PTW32_SET_ERRNO(result); return -1; } return 0; } /* sem_trywait */
int ptw32_setthreadpriority ( ptw32_handle_t thread, int policy, int priority) { int prio; ptw32_mcs_local_node_t threadLock; int result = 0; ptw32_thread_t * tp = (ptw32_thread_t *) thread.p; prio = priority; /* Validate priority level. */ if (prio < sched_get_priority_min (policy) || prio > sched_get_priority_max (policy)) { return EINVAL; } #if (THREAD_PRIORITY_LOWEST > THREAD_PRIORITY_NORMAL) /* WinCE */ #else /* Everything else */ if (THREAD_PRIORITY_IDLE < prio && THREAD_PRIORITY_LOWEST > prio) { prio = THREAD_PRIORITY_LOWEST; } else if (THREAD_PRIORITY_TIME_CRITICAL > prio && THREAD_PRIORITY_HIGHEST < prio) { prio = THREAD_PRIORITY_HIGHEST; } #endif ptw32_mcs_lock_acquire (&tp->threadLock, &threadLock); /* If this fails, the current priority is unchanged. */ if (0 == SetThreadPriority (tp->threadH, prio)) { result = EINVAL; } else { /* * Must record the thread's sched_priority as given, * not as finally adjusted. */ tp->sched_priority = priority; } ptw32_mcs_lock_release (&threadLock); return result; }
int pthread_barrier_wait (pthread_barrier_t * barrier) { int result; pthread_barrier_t b; ptw32_mcs_local_node_t node; if (barrier == NULL || *barrier == (pthread_barrier_t) PTW32_OBJECT_INVALID) { return EINVAL; } ptw32_mcs_lock_acquire(&(*barrier)->lock, &node); b = *barrier; if (--b->nCurrentBarrierHeight == 0) { ptw32_mcs_node_transfer(&b->proxynode, &node); result = (b->nInitialBarrierHeight > 1 ? sem_post_multiple (&(b->semBarrierBreeched), b->nInitialBarrierHeight - 1) : 0); } else { ptw32_mcs_lock_release(&node); result = ptw32_semwait (&(b->semBarrierBreeched)); } if ((PTW32_INTERLOCKED_LONG)PTW32_INTERLOCKED_INCREMENT_LONG((PTW32_INTERLOCKED_LONGPTR)&b->nCurrentBarrierHeight) == (PTW32_INTERLOCKED_LONG)b->nInitialBarrierHeight) { ptw32_mcs_lock_release(&b->proxynode); if (0 == result) { result = PTHREAD_BARRIER_SERIAL_THREAD; } } return (result); }
int pthread_setcanceltype (int type, int *oldtype) { ptw32_mcs_local_node_t stateLock; int result = 0; pthread_t self = pthread_self (); ptw32_thread_t * sp = (ptw32_thread_t *) self.p; if (sp == NULL || (type != PTHREAD_CANCEL_DEFERRED && type != PTHREAD_CANCEL_ASYNCHRONOUS)) { return EINVAL; } ptw32_mcs_lock_acquire (&sp->stateLock, &stateLock); if (oldtype != NULL) { *oldtype = sp->cancelType; } sp->cancelType = type; if (sp->cancelState == PTHREAD_CANCEL_ENABLE && type == PTHREAD_CANCEL_ASYNCHRONOUS && WaitForSingleObject (sp->cancelEvent, 0) == WAIT_OBJECT_0) { sp->state = PThreadStateCanceling; sp->cancelState = PTHREAD_CANCEL_DISABLE; ResetEvent (sp->cancelEvent); ptw32_mcs_lock_release (&stateLock); ptw32_throw (PTW32_EPS_CANCEL); } ptw32_mcs_lock_release (&stateLock); return (result); }
int ptw32_setthreadpriority (pthread_t thread, int policy, int priority) { int prio; ptw32_mcs_local_node_t threadLock; int result = 0; ptw32_thread_t * tp = (ptw32_thread_t *) thread.p; prio = priority; if (prio < sched_get_priority_min (policy) || prio > sched_get_priority_max (policy)) { return EINVAL; } #if (THREAD_PRIORITY_LOWEST > THREAD_PRIORITY_NORMAL) #else if (THREAD_PRIORITY_IDLE < prio && THREAD_PRIORITY_LOWEST > prio) { prio = THREAD_PRIORITY_LOWEST; } else if (THREAD_PRIORITY_TIME_CRITICAL > prio && THREAD_PRIORITY_HIGHEST < prio) { prio = THREAD_PRIORITY_HIGHEST; } #endif ptw32_mcs_lock_acquire (&tp->threadLock, &threadLock); if (0 == SetThreadPriority (tp->threadH, prio)) { result = EINVAL; } else { tp->sched_priority = priority; } ptw32_mcs_lock_release (&threadLock); return result; }
void * pthread_timechange_handler_np (void *arg) /* * ------------------------------------------------------ * DOCPUBLIC * Broadcasts all CVs to force re-evaluation and * new timeouts if required. * * PARAMETERS * NONE * * * DESCRIPTION * Broadcasts all CVs to force re-evaluation and * new timeouts if required. * * This routine may be passed directly to pthread_create() * as a new thread in order to run asynchronously. * * * RESULTS * 0 successfully broadcast all CVs * EAGAIN Not all CVs were broadcast * * ------------------------------------------------------ */ { int result = 0; pthread_cond_t cv; ptw32_mcs_local_node_t node; ptw32_mcs_lock_acquire(&ptw32_cond_list_lock, &node); cv = ptw32_cond_list_head; while (cv != NULL && 0 == result) { result = pthread_cond_broadcast (&cv); cv = cv->next; } ptw32_mcs_lock_release(&node); return (void *) (size_t) (result != 0 ? EAGAIN : 0); }
INLINE int ptw32_mutex_check_need_init (pthread_mutex_t * mutex) { register int result = 0; register pthread_mutex_t mtx; ptw32_mcs_local_node_t node; ptw32_mcs_lock_acquire(&ptw32_mutex_test_init_lock, &node); /* * We got here possibly under race * conditions. Check again inside the critical section * and only initialise if the mutex is valid (not been destroyed). * If a static mutex has been destroyed, the application can * re-initialise it only by calling pthread_mutex_init() * explicitly. */ mtx = *mutex; if (mtx == PTHREAD_MUTEX_INITIALIZER) { result = pthread_mutex_init (mutex, NULL); } else if (mtx == PTHREAD_RECURSIVE_MUTEX_INITIALIZER) { result = pthread_mutex_init (mutex, &ptw32_recursive_mutexattr); } else if (mtx == PTHREAD_ERRORCHECK_MUTEX_INITIALIZER) { result = pthread_mutex_init (mutex, &ptw32_errorcheck_mutexattr); } else if (mtx == NULL) { /* * The mutex has been destroyed while we were waiting to * initialise it, so the operation that caused the * auto-initialisation should fail. */ result = EINVAL; } ptw32_mcs_lock_release(&node); return (result); }
INLINE int ptw32_cond_check_need_init (pthread_cond_t * cond) { int result = 0; ptw32_mcs_local_node_t node; ptw32_mcs_lock_acquire(&ptw32_cond_test_init_lock, &node); if (*cond == PTHREAD_COND_INITIALIZER) { result = pthread_cond_init (cond, NULL); } else if (*cond == NULL) { result = EINVAL; } ptw32_mcs_lock_release(&node); return result; }
int pthread_once (pthread_once_t * once_control, void (PTW32_CDECL *init_routine) (void)) { if (once_control == NULL || init_routine == NULL) { return EINVAL; } if ((PTW32_INTERLOCKED_LONG)PTW32_FALSE == (PTW32_INTERLOCKED_LONG)PTW32_INTERLOCKED_EXCHANGE_ADD_LONG((PTW32_INTERLOCKED_LONGPTR)&once_control->done, (PTW32_INTERLOCKED_LONG)0)) /* MBR fence */ { ptw32_mcs_local_node_t node; ptw32_mcs_lock_acquire((ptw32_mcs_lock_t *)&once_control->lock, &node); if (!once_control->done) { #if defined(_MSC_VER) && _MSC_VER < 1400 #pragma inline_depth(0) #endif pthread_cleanup_push(ptw32_mcs_lock_release, &node); (*init_routine)(); pthread_cleanup_pop(0); #if defined(_MSC_VER) && _MSC_VER < 1400 #pragma inline_depth() #endif once_control->done = PTW32_TRUE; } ptw32_mcs_lock_release(&node); } return 0; } /* pthread_once */
static void PTW32_CDECL ptw32_sem_timedwait_cleanup (void * args) { ptw32_mcs_local_node_t node; sem_timedwait_cleanup_args_t * a = (sem_timedwait_cleanup_args_t *)args; sem_t s = a->sem; ptw32_mcs_lock_acquire(&s->lock, &node); /* * We either timed out or were cancelled. * If someone has posted between then and now we try to take the semaphore. * Otherwise the semaphore count may be wrong after we * return. In the case of a cancellation, it is as if we * were cancelled just before we return (after taking the semaphore) * which is ok. */ if (WaitForSingleObject(s->sem, 0) == WAIT_OBJECT_0) { /* We got the semaphore on the second attempt */ *(a->resultPtr) = 0; } else { /* Indicate we're no longer waiting */ s->value++; #if defined(NEED_SEM) if (s->value > 0) { s->leftToUnblock = 0; } #else /* * Don't release the W32 sema, it doesn't need adjustment * because it doesn't record the number of waiters. */ #endif } ptw32_mcs_lock_release(&node); }
/* * Push a clean pthread_t struct onto the reuse stack. * Must be re-initialised when reused. * All object elements (mutexes, events etc) must have been either * destroyed before this, or never initialised. */ void ptw32_threadReusePush (pthread_t thread) { ptw32_thread_t * tp = (ptw32_thread_t *) thread.p; pthread_t t; ptw32_mcs_local_node_t node; ptw32_mcs_lock_acquire(&ptw32_thread_reuse_lock, &node); t = tp->ptHandle; memset(tp, 0, sizeof(ptw32_thread_t)); /* Must restore the original POSIX handle that we just wiped. */ tp->ptHandle = t; /* Bump the reuse counter now */ #if defined(PTW32_THREAD_ID_REUSE_INCREMENT) tp->ptHandle.x += PTW32_THREAD_ID_REUSE_INCREMENT; #else tp->ptHandle.x++; #endif tp->state = PThreadStateReuse; tp->prevReuse = PTW32_THREAD_REUSE_EMPTY; if (PTW32_THREAD_REUSE_EMPTY != ptw32_threadReuseBottom) { ptw32_threadReuseBottom->prevReuse = tp; } else { ptw32_threadReuseTop = tp; } ptw32_threadReuseBottom = tp; ptw32_mcs_lock_release(&node); }
int pthread_once (pthread_once_t * once_control, void (*init_routine) (void)) { if (once_control == NULL || init_routine == NULL) { return EINVAL; } if (InterlockedExchangeAdd((LPLONG)&once_control->init, 0L)) /* MBR fence */ { ptw32_mcs_local_node_t node; ptw32_mcs_lock_acquire((ptw32_mcs_lock_t *)&once_control->lock, &node); if (once_control->init) { #ifdef _MSC_VER #pragma inline_depth(0) #endif pthread_cleanup_push(ptw32_once_on_init_cancel, (void *)&node); (*init_routine)(); pthread_cleanup_pop(0); #ifdef _MSC_VER #pragma inline_depth() #endif once_control->init = 0; } ptw32_mcs_lock_release(&node); } return 0; } /* pthread_once */
int ptw32_rwlock_check_need_init (pthread_rwlock_t * rwlock) { int result = 0; ptw32_mcs_local_node_t node; /* * The following guarded test is specifically for statically * initialised rwlocks (via PTHREAD_RWLOCK_INITIALIZER). */ ptw32_mcs_lock_acquire(&ptw32_rwlock_test_init_lock, &node); /* * We got here possibly under race * conditions. Check again inside the critical section * and only initialise if the rwlock is valid (not been destroyed). * If a static rwlock has been destroyed, the application can * re-initialise it only by calling pthread_rwlock_init() * explicitly. */ if (*rwlock == PTHREAD_RWLOCK_INITIALIZER) { result = pthread_rwlock_init (rwlock, NULL); } else if (*rwlock == NULL) { /* * The rwlock has been destroyed while we were waiting to * initialise it, so the operation that caused the * auto-initialisation should fail. */ result = EINVAL; } ptw32_mcs_lock_release(&node); return result; }
int ptw32_cond_check_need_init (pthread_cond_t * cond) { int result = 0; ptw32_mcs_local_node_t node; /* * The following guarded test is specifically for statically * initialised condition variables (via PTHREAD_OBJECT_INITIALIZER). */ ptw32_mcs_lock_acquire(&ptw32_cond_test_init_lock, &node); /* * We got here possibly under race * conditions. Check again inside the critical section. * If a static cv has been destroyed, the application can * re-initialise it only by calling pthread_cond_init() * explicitly. */ if (*cond == PTHREAD_COND_INITIALIZER) { result = pthread_cond_init (cond, NULL); } else if (*cond == NULL) { /* * The cv has been destroyed while we were waiting to * initialise it, so the operation that caused the * auto-initialisation should fail. */ result = EINVAL; } ptw32_mcs_lock_release(&node); return result; }
int pthread_cond_destroy (pthread_cond_t * cond) /* * ------------------------------------------------------ * DOCPUBLIC * This function destroys a condition variable * * * PARAMETERS * cond * pointer to an instance of pthread_cond_t * * * DESCRIPTION * This function destroys a condition variable. * * NOTES: * 1) A condition variable can be destroyed * immediately after all the threads that * are blocked on it are awakened. e.g. * * struct list { * pthread_mutex_t lm; * ... * } * * struct elt { * key k; * int busy; * pthread_cond_t notbusy; * ... * } * * * struct elt * * list_find(struct list *lp, key k) * { * struct elt *ep; * * pthread_mutex_lock(&lp->lm); * while ((ep = find_elt(l,k) != NULL) && ep->busy) * pthread_cond_wait(&ep->notbusy, &lp->lm); * if (ep != NULL) * ep->busy = 1; * pthread_mutex_unlock(&lp->lm); * return(ep); * } * * delete_elt(struct list *lp, struct elt *ep) * { * pthread_mutex_lock(&lp->lm); * assert(ep->busy); * ... remove ep from list ... * ep->busy = 0; * (A) pthread_cond_broadcast(&ep->notbusy); * pthread_mutex_unlock(&lp->lm); * (B) pthread_cond_destroy(&rp->notbusy); * free(ep); * } * * In this example, the condition variable * and its list element may be freed (line B) * immediately after all threads waiting for * it are awakened (line A), since the mutex * and the code ensure that no other thread * can touch the element to be deleted. * * RESULTS * 0 successfully released condition variable, * EINVAL 'cond' is invalid, * EBUSY 'cond' is in use, * * ------------------------------------------------------ */ { pthread_cond_t cv; int result = 0, result1 = 0, result2 = 0; /* * Assuming any race condition here is harmless. */ if (cond == NULL || *cond == NULL) { return EINVAL; } if (*cond != PTHREAD_COND_INITIALIZER) { ptw32_mcs_local_node_t node; ptw32_mcs_lock_acquire(&ptw32_cond_list_lock, &node); cv = *cond; /* * Close the gate; this will synchronize this thread with * all already signaled waiters to let them retract their * waiter status - SEE NOTE 1 ABOVE!!! */ if (ptw32_semwait (&(cv->semBlockLock)) != 0) /* Non-cancelable */ { result = PTW32_GET_ERRNO(); } else { /* * !TRY! lock mtxUnblockLock; try will detect busy condition * and will not cause a deadlock with respect to concurrent * signal/broadcast. */ if ((result = pthread_mutex_trylock (&(cv->mtxUnblockLock))) != 0) { (void) sem_post (&(cv->semBlockLock)); } } if (result != 0) { ptw32_mcs_lock_release(&node); return result; } /* * Check whether cv is still busy (still has waiters) */ if (cv->nWaitersBlocked > cv->nWaitersGone) { if (sem_post (&(cv->semBlockLock)) != 0) { result = PTW32_GET_ERRNO(); } result1 = pthread_mutex_unlock (&(cv->mtxUnblockLock)); result2 = EBUSY; } else { /* * Now it is safe to destroy */ *cond = NULL; if (sem_destroy (&(cv->semBlockLock)) != 0) { result = PTW32_GET_ERRNO(); } if (sem_destroy (&(cv->semBlockQueue)) != 0) { result1 = PTW32_GET_ERRNO(); } if ((result2 = pthread_mutex_unlock (&(cv->mtxUnblockLock))) == 0) { result2 = pthread_mutex_destroy (&(cv->mtxUnblockLock)); } /* Unlink the CV from the list */ if (ptw32_cond_list_head == cv) { ptw32_cond_list_head = cv->next; } else { cv->prev->next = cv->next; } if (ptw32_cond_list_tail == cv) { ptw32_cond_list_tail = cv->prev; } else { cv->next->prev = cv->prev; } (void) free (cv); } ptw32_mcs_lock_release(&node); } else { ptw32_mcs_local_node_t node; /* * See notes in ptw32_cond_check_need_init() above also. */ ptw32_mcs_lock_acquire(&ptw32_cond_test_init_lock, &node); /* * Check again. */ if (*cond == PTHREAD_COND_INITIALIZER) { /* * This is all we need to do to destroy a statically * initialised cond that has not yet been used (initialised). * If we get to here, another thread waiting to initialise * this cond will get an EINVAL. That's OK. */ *cond = NULL; } else { /* * The cv has been initialised while we were waiting * so assume it's in use. */ result = EBUSY; } ptw32_mcs_lock_release(&node); } return ((result != 0) ? result : ((result1 != 0) ? result1 : result2)); }
int pthread_timedjoin_np (pthread_t thread, void **value_ptr, const struct timespec *abstime) /* * ------------------------------------------------------ * DOCPUBLIC * This function waits for 'thread' to terminate and * returns the thread's exit value if 'value_ptr' is not * NULL or until 'abstime' passes and returns an * error. If 'abstime' is NULL then the function waits * forever, i.e. reverts to pthread_join behaviour. * This function detaches the thread on successful * completion. * * PARAMETERS * thread * an instance of pthread_t * * value_ptr * pointer to an instance of pointer to void * * abstime * pointer to an instance of struct timespec * representing an absolute time value * * * DESCRIPTION * This function waits for 'thread' to terminate and * returns the thread's exit value if 'value_ptr' is not * NULL or until 'abstime' passes and returns an * error. If 'abstime' is NULL then the function waits * forever, i.e. reverts to pthread_join behaviour. * This function detaches the thread on successful * completion. * NOTE: Detached threads cannot be joined or canceled. * In this implementation 'abstime' will be * resolved to the nearest millisecond. * * RESULTS * 0 'thread' has completed * ETIMEDOUT abstime passed * EINVAL thread is not a joinable thread, * ESRCH no thread could be found with ID 'thread', * ENOENT thread couldn't find it's own valid handle, * EDEADLK attempt to join thread with self * * ------------------------------------------------------ */ { int result; pthread_t self; DWORD milliseconds; ptw32_thread_t * tp = (ptw32_thread_t *) thread.p; ptw32_mcs_local_node_t node; if (abstime == NULL) { milliseconds = INFINITE; } else { /* * Calculate timeout as milliseconds from current system time. */ milliseconds = ptw32_relmillisecs (abstime); } ptw32_mcs_lock_acquire(&ptw32_thread_reuse_lock, &node); if (NULL == tp || thread.x != tp->ptHandle.x) { result = ESRCH; } else if (PTHREAD_CREATE_DETACHED == tp->detachState) { result = EINVAL; } else { result = 0; } ptw32_mcs_lock_release(&node); if (result == 0) { /* * The target thread is joinable and can't be reused before we join it. */ self = pthread_self(); if (NULL == self.p) { result = ENOENT; } else if (pthread_equal (self, thread)) { result = EDEADLK; } else { /* * Pthread_join is a cancellation point. * If we are canceled then our target thread must not be * detached (destroyed). This is guaranteed because * pthreadCancelableTimedWait will not return if we * are canceled. */ result = pthreadCancelableTimedWait (tp->threadH, milliseconds); if (0 == result) { if (value_ptr != NULL) { *value_ptr = tp->exitStatus; } /* * The result of making multiple simultaneous calls to * pthread_join() or pthread_timedjoin_np() or pthread_detach() * specifying the same target is undefined. */ result = pthread_detach (thread); } else if (ETIMEDOUT != result) { result = ESRCH; } } } return (result); }
int pthread_barrier_wait (pthread_barrier_t * barrier) { int result; pthread_barrier_t b; ptw32_mcs_local_node_t node; if (barrier == NULL || *barrier == (pthread_barrier_t) PTW32_OBJECT_INVALID) { return EINVAL; } ptw32_mcs_lock_acquire(&(*barrier)->lock, &node); b = *barrier; if (--b->nCurrentBarrierHeight == 0) { /* * We are the last thread to arrive at the barrier before it releases us. * Move our MCS local node to the global scope barrier handle so that the * last thread out (not necessarily us) can release the lock. */ ptw32_mcs_node_transfer(&b->proxynode, &node); /* * Any threads that have not quite entered sem_wait below when the * multiple_post has completed will nevertheless continue through * the semaphore (barrier). */ result = (b->nInitialBarrierHeight > 1 ? sem_post_multiple (&(b->semBarrierBreeched), b->nInitialBarrierHeight - 1) : 0); } else { ptw32_mcs_lock_release(&node); /* * Use the non-cancelable version of sem_wait(). * * It is possible that all nInitialBarrierHeight-1 threads are * at this point when the last thread enters the barrier, resets * nCurrentBarrierHeight = nInitialBarrierHeight and leaves. * If pthread_barrier_destroy is called at that moment then the * barrier will be destroyed along with the semas. */ result = ptw32_semwait (&(b->semBarrierBreeched)); } if ((PTW32_INTERLOCKED_LONG)PTW32_INTERLOCKED_INCREMENT_LONG((PTW32_INTERLOCKED_LONGPTR)&b->nCurrentBarrierHeight) == (PTW32_INTERLOCKED_LONG)b->nInitialBarrierHeight) { /* * We are the last thread to cross this barrier */ ptw32_mcs_lock_release(&b->proxynode); if (0 == result) { result = PTHREAD_BARRIER_SERIAL_THREAD; } } return (result); }
static INLINE int ptw32_cancelable_wait (HANDLE waitHandle, DWORD timeout) /* * ------------------------------------------------------------------- * This provides an extra hook into the pthread_cancel * mechanism that will allow you to wait on a Windows handle and make it a * cancellation point. This function blocks until the given WIN32 handle is * signaled or pthread_cancel has been called. It is implemented using * WaitForMultipleObjects on 'waitHandle' and a manually reset WIN32 * event used to implement pthread_cancel. * * Given this hook it would be possible to implement more of the cancellation * points. * ------------------------------------------------------------------- */ { int result; pthread_t self; ptw32_thread_t * sp; HANDLE handles[2]; DWORD nHandles = 1; DWORD status; handles[0] = waitHandle; self = pthread_self(); sp = (ptw32_thread_t *) to_internal(self).p; if (sp != NULL) { /* * Get cancelEvent handle */ if (sp->cancelState == PTHREAD_CANCEL_ENABLE) { if ((handles[1] = sp->cancelEvent) != NULL) { nHandles++; } } } else { handles[1] = NULL; } status = WaitForMultipleObjects (nHandles, handles, PTW32_FALSE, timeout); switch (status - WAIT_OBJECT_0) { case 0: /* * Got the handle. * In the event that both handles are signalled, the smallest index * value (us) is returned. As it has been arranged, this ensures that * we don't drop a signal that we should act on (i.e. semaphore, * mutex, or condition variable etc). */ result = 0; break; case 1: /* * Got cancel request. * In the event that both handles are signaled, the cancel will * be ignored (see case 0 comment). */ ResetEvent (handles[1]); if (sp != NULL) { ptw32_mcs_local_node_t stateLock; /* * Should handle POSIX and implicit POSIX threads.. * Make sure we haven't been async-canceled in the meantime. */ ptw32_mcs_lock_acquire (&sp->stateLock, &stateLock); if (sp->state < PThreadStateCanceling) { sp->state = PThreadStateCanceling; sp->cancelState = PTHREAD_CANCEL_DISABLE; ptw32_mcs_lock_release (&stateLock); ptw32_throw (PTW32_EPS_CANCEL); /* Never reached */ } ptw32_mcs_lock_release (&stateLock); } /* Should never get to here. */ result = EINVAL; break; default: if (status == WAIT_TIMEOUT) { result = ETIMEDOUT; } else { result = EINVAL; } break; } return (result); } /* CancelableWait */
int pthread_cancel (pthread_t thread) { int result; int cancel_self; pthread_t self; ptw32_thread_t * tp; ptw32_mcs_local_node_t stateLock; result = pthread_kill (thread, 0); if (0 != result) { return result; } if ((self = pthread_self ()).p == NULL) { return ENOMEM; }; cancel_self = pthread_equal (thread, self); tp = (ptw32_thread_t *) thread.p; ptw32_mcs_lock_acquire (&tp->stateLock, &stateLock); if (tp->cancelType == PTHREAD_CANCEL_ASYNCHRONOUS && tp->cancelState == PTHREAD_CANCEL_ENABLE && tp->state < PThreadStateCanceling) { if (cancel_self) { tp->state = PThreadStateCanceling; tp->cancelState = PTHREAD_CANCEL_DISABLE; ptw32_mcs_lock_release (&stateLock); ptw32_throw (PTW32_EPS_CANCEL); } else { HANDLE threadH = tp->threadH; SuspendThread (threadH); if (WaitForSingleObject (threadH, 0) == WAIT_TIMEOUT) { tp->state = PThreadStateCanceling; tp->cancelState = PTHREAD_CANCEL_DISABLE; ptw32_register_cancelation ((PAPCFUNC)ptw32_cancel_callback, threadH, 0); ptw32_mcs_lock_release (&stateLock); ResumeThread (threadH); } } } else { if (tp->state < PThreadStateCancelPending) { tp->state = PThreadStateCancelPending; if (!SetEvent (tp->cancelEvent)) { result = ESRCH; } } else if (tp->state >= PThreadStateCanceling) { result = ESRCH; } ptw32_mcs_lock_release (&stateLock); } return (result); }
int pthread_barrier_destroy (pthread_barrier_t * barrier) { int result = 0; pthread_barrier_t b; ptw32_mcs_local_node_t node; if (barrier == NULL || *barrier == (pthread_barrier_t) PTW32_OBJECT_INVALID) { return EINVAL; } if (0 != ptw32_mcs_lock_try_acquire(&(*barrier)->lock, &node)) { return EBUSY; } b = *barrier; if (b->nCurrentBarrierHeight < b->nInitialBarrierHeight) { result = EBUSY; } else { if (0 == (result = sem_destroy (&(b->semBarrierBreeched)))) { *barrier = (pthread_barrier_t) PTW32_OBJECT_INVALID; /* * Release the lock before freeing b. * * FIXME: There may be successors which, when we release the lock, * will be linked into b->lock, which will be corrupted at some * point with undefined results for the application. To fix this * will require changing pthread_barrier_t from a pointer to * pthread_barrier_t_ to an instance. This is a change to the ABI * and will require a major version number increment. */ ptw32_mcs_lock_release(&node); (void) free (b); return 0; } else { /* * This should not ever be reached. * Restore the barrier to working condition before returning. */ (void) sem_init (&(b->semBarrierBreeched), b->pshared, 0); } if (result != 0) { /* * The barrier still exists and is valid * in the event of any error above. */ result = EBUSY; } } ptw32_mcs_lock_release(&node); return (result); }
int pthread_setcancelstate (int state, int *oldstate) /* * ------------------------------------------------------ * DOCPUBLIC * This function atomically sets the calling thread's * cancelability state to 'state' and returns the previous * cancelability state at the location referenced by * 'oldstate' * * PARAMETERS * state, * oldstate * PTHREAD_CANCEL_ENABLE * cancellation is enabled, * * PTHREAD_CANCEL_DISABLE * cancellation is disabled * * * DESCRIPTION * This function atomically sets the calling thread's * cancelability state to 'state' and returns the previous * cancelability state at the location referenced by * 'oldstate'. * * NOTES: * 1) Use to disable cancellation around 'atomic' code that * includes cancellation points * * COMPATIBILITY ADDITIONS * If 'oldstate' is NULL then the previous state is not returned * but the function still succeeds. (Solaris) * * RESULTS * 0 successfully set cancelability type, * EINVAL 'state' is invalid * * ------------------------------------------------------ */ { ptw32_mcs_local_node_t stateLock; int result = 0; pthread_t self = pthread_self (); ptw32_thread_t * sp = (ptw32_thread_t *) self.p; if (sp == NULL || (state != PTHREAD_CANCEL_ENABLE && state != PTHREAD_CANCEL_DISABLE)) { return EINVAL; } /* * Lock for async-cancel safety. */ ptw32_mcs_lock_acquire (&sp->stateLock, &stateLock); if (oldstate != NULL) { *oldstate = sp->cancelState; } sp->cancelState = state; /* * Check if there is a pending asynchronous cancel */ if (state == PTHREAD_CANCEL_ENABLE && sp->cancelType == PTHREAD_CANCEL_ASYNCHRONOUS && WaitForSingleObject (sp->cancelEvent, 0) == WAIT_OBJECT_0) { sp->state = PThreadStateCanceling; sp->cancelState = PTHREAD_CANCEL_DISABLE; ResetEvent (sp->cancelEvent); ptw32_mcs_lock_release (&stateLock); ptw32_throw (PTW32_EPS_CANCEL); /* Never reached */ } ptw32_mcs_lock_release (&stateLock); return (result); } /* pthread_setcancelstate */
int pthread_detach (pthread_t thread) /* * ------------------------------------------------------ * DOCPUBLIC * This function detaches the given thread. * * PARAMETERS * thread * an instance of a pthread_t * * * DESCRIPTION * This function detaches the given thread. You may use it to * detach the main thread or to detach a joinable thread. * NOTE: detached threads cannot be joined; * storage is freed immediately on termination. * * RESULTS * 0 successfully detached the thread, * EINVAL thread is not a joinable thread, * ENOSPC a required resource has been exhausted, * ESRCH no thread could be found for 'thread', * * ------------------------------------------------------ */ { int result; BOOL destroyIt = PTW32_FALSE; ptw32_thread_t * tp = (ptw32_thread_t *) thread.p; ptw32_mcs_local_node_t node; ptw32_mcs_lock_acquire(&ptw32_thread_reuse_lock, &node); if (NULL == tp || thread.x != tp->ptHandle.x) { result = ESRCH; } else if (PTHREAD_CREATE_DETACHED == tp->detachState) { result = EINVAL; } else { ptw32_mcs_local_node_t stateLock; /* * Joinable ptw32_thread_t structs are not scavenged until * a join or detach is done. The thread may have exited already, * but all of the state and locks etc are still there. */ result = 0; ptw32_mcs_lock_acquire (&tp->stateLock, &stateLock); if (tp->state != PThreadStateLast) { tp->detachState = PTHREAD_CREATE_DETACHED; } else if (tp->detachState != PTHREAD_CREATE_DETACHED) { /* * Thread is joinable and has exited or is exiting. */ destroyIt = PTW32_TRUE; } ptw32_mcs_lock_release (&stateLock); } ptw32_mcs_lock_release(&node); if (result == 0) { /* Thread is joinable */ if (destroyIt) { /* The thread has exited or is exiting but has not been joined or * detached. Need to wait in case it's still exiting. */ (void) WaitForSingleObject(tp->threadH, INFINITE); ptw32_threadDestroy (thread); } } return (result); } /* pthread_detach */
int pthread_setspecific (pthread_key_t key, const void *value) /* * ------------------------------------------------------ * DOCPUBLIC * This function sets the value of the thread specific * key in the calling thread. * * PARAMETERS * key * an instance of pthread_key_t * value * the value to set key to * * * DESCRIPTION * This function sets the value of the thread specific * key in the calling thread. * * RESULTS * 0 successfully set value * EAGAIN could not set value * ENOENT SERIOUS!! * * ------------------------------------------------------ */ { pthread_t self; int result = 0; if (key != ptw32_selfThreadKey) { /* * Using pthread_self will implicitly create * an instance of pthread_t for the current * thread if one wasn't explicitly created */ self = pthread_self (); if (self.p == NULL) { return ENOENT; } } else { /* * Resolve catch-22 of registering thread with selfThread * key */ ptw32_thread_t * sp = (ptw32_thread_t *) pthread_getspecific (ptw32_selfThreadKey); if (sp == NULL) { if (value == NULL) { return ENOENT; } self = *((pthread_t *) value); } else { self = sp->ptHandle; } } result = 0; if (key != NULL) { if (self.p != NULL && key->destructor != NULL && value != NULL) { ptw32_mcs_local_node_t keyLock; ptw32_mcs_local_node_t threadLock; ptw32_thread_t * sp = (ptw32_thread_t *) self.p; /* * Only require associations if we have to * call user destroy routine. * Don't need to locate an existing association * when setting data to NULL for WIN32 since the * data is stored with the operating system; not * on the association; setting assoc to NULL short * circuits the search. */ ThreadKeyAssoc *assoc; ptw32_mcs_lock_acquire(&(key->keyLock), &keyLock); ptw32_mcs_lock_acquire(&(sp->threadLock), &threadLock); assoc = (ThreadKeyAssoc *) sp->keys; /* * Locate existing association */ while (assoc != NULL) { if (assoc->key == key) { /* * Association already exists */ break; } assoc = assoc->nextKey; } /* * create an association if not found */ if (assoc == NULL) { result = ptw32_tkAssocCreate (sp, key); } ptw32_mcs_lock_release(&threadLock); ptw32_mcs_lock_release(&keyLock); } if (result == 0) { if (!TlsSetValue (key->key, (LPVOID) value)) { result = EAGAIN; } } } return (result); } /* pthread_setspecific */
int sem_timedwait (sem_t * sem, const struct timespec *abstime) /* * ------------------------------------------------------ * DOCPUBLIC * This function waits on a semaphore possibly until * 'abstime' time. * * PARAMETERS * sem * pointer to an instance of sem_t * * abstime * pointer to an instance of struct timespec * * DESCRIPTION * This function waits on a semaphore. If the * semaphore value is greater than zero, it decreases * its value by one. If the semaphore value is zero, then * the calling thread (or process) is blocked until it can * successfully decrease the value or until interrupted by * a signal. * * If 'abstime' is a NULL pointer then this function will * block until it can successfully decrease the value or * until interrupted by a signal. * * RESULTS * 0 successfully decreased semaphore, * -1 failed, error in errno * ERRNO * EINVAL 'sem' is not a valid semaphore, * ENOSYS semaphores are not supported, * EINTR the function was interrupted by a signal, * EDEADLK a deadlock condition was detected. * ETIMEDOUT abstime elapsed before success. * * ------------------------------------------------------ */ { ptw32_mcs_local_node_t node; DWORD milliseconds; int v; int result = 0; sem_t s = *sem; pthread_testcancel(); if (abstime == NULL) { milliseconds = INFINITE; } else { /* * Calculate timeout as milliseconds from current system time. */ milliseconds = ptw32_relmillisecs (abstime); } ptw32_mcs_lock_acquire(&s->lock, &node); v = --s->value; ptw32_mcs_lock_release(&node); if (v < 0) { #if defined(NEED_SEM) int timedout; #endif sem_timedwait_cleanup_args_t cleanup_args; cleanup_args.sem = s; cleanup_args.resultPtr = &result; #if defined(PTW32_CONFIG_MSVC7) #pragma inline_depth(0) #endif /* Must wait */ pthread_cleanup_push(ptw32_sem_timedwait_cleanup, (void *) &cleanup_args); #if defined(NEED_SEM) timedout = #endif result = pthreadCancelableTimedWait (s->sem, milliseconds); pthread_cleanup_pop(result); #if defined(PTW32_CONFIG_MSVC7) #pragma inline_depth() #endif #if defined(NEED_SEM) if (!timedout) { ptw32_mcs_lock_acquire(&s->lock, &node); if (s->leftToUnblock > 0) { --s->leftToUnblock; SetEvent(s->sem); } ptw32_mcs_lock_release(&node); } #endif /* NEED_SEM */ } if (result != 0) { PTW32_SET_ERRNO(result); return -1; } return 0; } /* sem_timedwait */