From 78f83cfa240ec14874b22c7302ff8d306c130aaf Mon Sep 17 00:00:00 2001 From: rpj Date: Fri, 6 May 2005 07:31:28 +0000 Subject: '' --- ANNOUNCE | 9 +++- ChangeLog | 36 +++++++++++++ NEWS | 43 +++++++++++++++ create.c | 22 +++++--- implement.h | 89 +++++++++++++++++++++++-------- pthread.h | 4 +- pthread_key_create.c | 2 +- pthread_key_delete.c | 33 ++++++------ pthread_setspecific.c | 69 +++++++++++++----------- ptw32_callUserDestroyRoutines.c | 114 +++++++++++++++++----------------------- ptw32_tkAssocCreate.c | 60 ++++++++------------- ptw32_tkAssocDestroy.c | 41 ++++++++++++++- sem_init.c | 7 ++- sem_post.c | 27 ++++++---- sem_post_multiple.c | 46 ++++++++++------ signal.c | 2 + tests/Bmakefile | 10 ++-- tests/GNUmakefile | 10 ++-- tests/Makefile | 10 ++-- tests/cancel4.c | 4 +- tests/condvar2.c | 2 +- tests/semaphore1.c | 4 +- tests/tsd1.c | 38 ++++++++------ 23 files changed, 435 insertions(+), 247 deletions(-) diff --git a/ANNOUNCE b/ANNOUNCE index 51f2b4f..4123b49 100644 --- a/ANNOUNCE +++ b/ANNOUNCE @@ -1,4 +1,4 @@ - PTHREADS-WIN32 RELEASE 2.4.0 (2005-04-26) + PTHREADS-WIN32 RELEASE 2.5.0 (2005-05-06) ----------------------------------------- Web Site: http://sources.redhat.com/pthreads-win32/ FTP Site: ftp://sources.redhat.com/pub/pthreads-win32 @@ -40,6 +40,13 @@ Aurelio Medina and improved by Alexander Terekhov. Many others have contributed significant time and effort to solve crutial problems in order to make the library workable, robust and reliable. +Thanks to Xavier Leroy for granting permission to use and modify his +LinuxThreads manual pages. + +Thanks to The Open Group for making the Single Unix Specification +publicly available - many of the manual pages included in the package +were extracted from it. + There is also a separate CONTRIBUTORS file. This file and others are on the web site: diff --git a/ChangeLog b/ChangeLog index baa6a14..fd6baf8 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,39 @@ +2005-05-06 Ross Johnson + + * signal.c (sigwait): Add a cancellation point to this otherwise + no-op. + * sem_init.c (sem_init): Check for and return ERANGE error. + * sem_post.c (sem_post): Likewise. + * sem_post_multiple.c (sem_post_multiple): Likewise. + * manual (directory): Added; see ChangeLog inside. + +2005-05-02 Ross Johnson + + * implement.h (struct pthread_key_t_): Change threadsLock to keyLock + so as not to be confused with the per thread lock 'threadlock'; + change all references to it. + * implement.h (struct ThreadKeyAssoc): Remove lock; add prevKey + and prevThread pointers; re-implemented all routines that use this + struct. The effect of this is to save one handle per association, + which could potentially equal the number of keys multiplied by the + number of threads, accumulating over time - and to free the + association memory as soon as it is no longer referenced by either + the key or the thread. Previously, the handle and memory were + released only after BOTH key and thread no longer referenced the + association. That is, often no association resources were released + until the process itself exited. In addition, at least one race + condition has been removed - where two threads could attempt to + release the association resources simultaneously - one via + ptw32_callUserDestroyRoutines and the other via + pthread_key_delete. + - thanks to Richard Hughes at Aculab for discovering the problem. + * pthread_key_create.c: See above. + * pthread_key_delete.c: See above. + * pthread_setspecific.c: See above. + * ptw32_callUserDestroyRoutines.c: See above. + * ptw32_tkAssocCreate.c: See above. + * ptw32_tkAssocDestroy.c: See above. + 2005-04-27 Ross Johnson * sem_wait.c (ptw32_sem_wait_cleanup): after cancellation re-attempt diff --git a/NEWS b/NEWS index 1f8be3a..c637c96 100644 --- a/NEWS +++ b/NEWS @@ -1,3 +1,46 @@ +RELEASE 2.5.0 +------------- +(2005-05-06) + +General +------- + +The package now includes a reference documentation set consisting of +HTML formatted Unix-style manual pages that have been edited for +consistency with Pthreads-w32. The set can also be read online at: +http://sources.redhat.com/pthreads-win32/manual/index.html + +All of the bug fixes and new features in this release have been +back-ported in release 1.9.0. + +Bugs fixed +---------- + +* Thread Specific Data (TSD) key management has been altered to +eliminate a source of resource leakage (HANDLEs plus memory). +Thanks to Richard Hughes at Aculab for identifying the leak. + +* Fix a semaphore accounting race between sem_post/sem_post_multiple +and sem_wait cancellation. This is the same issue as with +sem_timedwait that was fixed in the last release. + +* sem_init, sem_post, and sem_post_multiple now check that the +semaphore count never exceeds _POSIX_SEM_VALUE_MAX. + +* Although sigwait() is nothing more than a no-op, it should at least +be a cancellation point to be consistent with the standard. + +New tests +--------- + +* stress1.c - attempts to expose problems in condition variable +and semaphore timed wait logic. This test was inspired by Stephan +Mueller's sample test code used to identify the sem_timedwait bug +from the last release. It's not a part of the regular test suite +because it can take awhile to run. To run it: +nmake clean VC-stress + + RELEASE 2.4.0 ------------- (2005-04-26) diff --git a/create.c b/create.c index 919db2e..9e9388b 100644 --- a/create.c +++ b/create.c @@ -92,6 +92,7 @@ pthread_create (pthread_t * tid, ThreadParms *parms = NULL; long stackSize; int priority; + pthread_t self; /* * Before doing anything, check that tid can be stored through @@ -128,18 +129,23 @@ pthread_create (pthread_t * tid, parms->start = start; parms->arg = arg; +#if defined(HAVE_SIGSET_T) + + /* + * Threads inherit their initial sigmask from their creator thread. + */ + self = pthread_self(); + tp->sigmask = ((ptw32_thread_t *)self.p)->sigmask; + +#endif /* HAVE_SIGSET_T */ + + if (a != NULL) { stackSize = a->stacksize; tp->detachState = a->detachstate; priority = a->param.sched_priority; -#if HAVE_SIGSET_T - - tp->sigmask = a->sigmask; - -#endif /* HAVE_SIGSET_T */ - #if (THREAD_PRIORITY_LOWEST > THREAD_PRIORITY_NORMAL) /* WinCE */ #else @@ -164,7 +170,9 @@ pthread_create (pthread_t * tid, * then the inherited priority could be the result of a temporary * system adjustment. This is not the case for POSIX threads. */ - pthread_t self = pthread_self (); +#if ! defined(HAVE_SIGSET_T) + self = pthread_self (); +#endif priority = ((ptw32_thread_t *) self.p)->sched_priority; } diff --git a/implement.h b/implement.h index b723ef3..d4cbbe5 100644 --- a/implement.h +++ b/implement.h @@ -266,7 +266,7 @@ struct pthread_key_t_ { DWORD key; void (*destructor) (void *); - pthread_mutex_t threadsLock; + pthread_mutex_t keyLock; void *threads; }; @@ -339,26 +339,67 @@ struct ThreadKeyAssoc { /* * Purpose: - * This structure creates an association between a - * thread and a key. - * It is used to implement the implicit invocation - * of a user defined destroy routine for thread - * specific data registered by a user upon exiting a - * thread. + * This structure creates an association between a thread and a key. + * It is used to implement the implicit invocation of a user defined + * destroy routine for thread specific data registered by a user upon + * exiting a thread. * - * Attributes: - * lock - * protects access to the rest of the structure + * Graphically, the arrangement is as follows, where: + * + * K - Key with destructor + * (head of chain is key->threads) + * T - Thread that has called pthread_setspecific(Kn) + * (head of chain is thread->keys) + * A - Association. Each association is a node at the + * intersection of two doubly-linked lists. + * + * T1 T2 T3 + * | | | + * | | | + * K1 -----+-----A-----A-----> + * | | | + * | | | + * K2 -----A-----A-----+-----> + * | | | + * | | | + * K3 -----A-----+-----A-----> + * | | | + * | | | + * V V V + * + * Access to the association is guarded by two locks: the key's + * general lock (guarding the row) and the thread's general + * lock (guarding the column). This avoids the need for a + * dedicated lock for each association, which not only consumes + * more handles but requires that: before the lock handle can + * be released - both the key must be deleted and the thread + * must have called the destructor. The two-lock arrangement + * allows the resources to be freed as soon as either thread or + * key is concluded. + * + * To avoid deadlock: whenever both locks are required, the key + * and thread locks are always applied in the order: key lock + * then thread lock. * + * An association is created when a thread first calls + * pthread_setspecific() on a key that has a specified + * destructor. + * + * An association is destroyed either immediately after the + * thread calls the key destructor function on thread exit, or + * when the key is deleted. + * + * Attributes: * thread - * reference to the thread that owns the association. - * As long as this is not NULL, the association remains - * referenced by the pthread_t. + * reference to the thread that owns the + * association. This is actually the pointer to the + * thread struct itself. Since the association is + * destroyed before the thread exits, this can never + * point to a different logical thread to the one that + * created the assoc, i.e. after thread struct reuse. * * key * reference to the key that owns the association. - * As long as this is not NULL, the association remains - * referenced by the pthread_key_t. * * nextKey * The pthread_t->keys attribute is the head of a @@ -367,6 +408,9 @@ struct ThreadKeyAssoc * between a pthread_t and all pthread_key_t on which * it called pthread_setspecific. * + * prevKey + * Similarly. + * * nextThread * The pthread_key_t->threads attribute is the head of * a chain of assoctiations that runs through the @@ -375,11 +419,13 @@ struct ThreadKeyAssoc * PThreads that have called pthread_setspecific for * this pthread_key_t. * + * prevThread + * Similarly. * * Notes: - * 1) As long as one of the attributes, thread or key, is - * not NULL, the association is being referenced; once - * both are NULL, the association must be released. + * 1) As soon as either the key or the thread is no longer + * referencing the association, it can be destroyed. The + * association will be removed from both chains. * * 2) Under WIN32, an association is only created by * pthread_setspecific if the user provided a @@ -387,11 +433,12 @@ struct ThreadKeyAssoc * * */ - pthread_mutex_t lock; - pthread_t thread; + ptw32_thread_t * thread; pthread_key_t key; ThreadKeyAssoc *nextKey; ThreadKeyAssoc *nextThread; + ThreadKeyAssoc *prevKey; + ThreadKeyAssoc *prevThread; }; @@ -555,7 +602,7 @@ extern "C" void ptw32_callUserDestroyRoutines (pthread_t thread); int ptw32_tkAssocCreate (ThreadKeyAssoc ** assocP, - pthread_t thread, pthread_key_t key); + ptw32_thread_t * thread, pthread_key_t key); void ptw32_tkAssocDestroy (ThreadKeyAssoc * assoc); diff --git a/pthread.h b/pthread.h index 1e0f46a..e347a24 100644 --- a/pthread.h +++ b/pthread.h @@ -37,8 +37,8 @@ * See the README file for an explanation of the pthreads-win32 version * numbering scheme and how the DLL is named etc. */ -#define PTW32_VERSION 2,4,0,0 -#define PTW32_VERSION_STRING "2, 4, 0, 0\0" +#define PTW32_VERSION 2,5,0,0 +#define PTW32_VERSION_STRING "2, 5, 0, 0\0" /* There are three implementations of cancel cleanup. * Note that pthread.h is included in both application diff --git a/pthread_key_create.c b/pthread_key_create.c index b5b00ce..5e278c2 100644 --- a/pthread_key_create.c +++ b/pthread_key_create.c @@ -98,7 +98,7 @@ pthread_key_create (pthread_key_t * key, void (*destructor) (void *)) * * The mutex will only be created when it is first locked. */ - newkey->threadsLock = PTHREAD_MUTEX_INITIALIZER; + newkey->keyLock = PTHREAD_MUTEX_INITIALIZER; newkey->destructor = destructor; } diff --git a/pthread_key_delete.c b/pthread_key_delete.c index eacab03..99b00b9 100644 --- a/pthread_key_delete.c +++ b/pthread_key_delete.c @@ -44,7 +44,7 @@ pthread_key_delete (pthread_key_t key) * ------------------------------------------------------ * DOCPUBLIC * This function deletes a thread-specific data key. This - * does not change the value of the thread spcific data key + * does not change the value of the thread specific data key * for any thread and does not run the key's destructor * in any thread so it should be used with caution. * @@ -55,7 +55,7 @@ pthread_key_delete (pthread_key_t key) * * DESCRIPTION * This function deletes a thread-specific data key. This - * does not change the value of the thread spcific data key + * does not change the value of the thread specific data key * for any thread and does not run the key's destructor * in any thread so it should be used with caution. * @@ -72,7 +72,7 @@ pthread_key_delete (pthread_key_t key) { if (key->threads != NULL && key->destructor != NULL && - pthread_mutex_lock (&(key->threadsLock)) == 0) + pthread_mutex_lock (&(key->keyLock)) == 0) { /* * Run through all Thread<-->Key associations @@ -93,28 +93,31 @@ pthread_key_delete (pthread_key_t key) while (assoc != NULL) { - if (pthread_mutex_lock (&(assoc->lock)) == 0) - { - ThreadKeyAssoc *next; + ThreadKeyAssoc *next; + ptw32_thread_t * thread = assoc->thread; - assoc->key = NULL; + if (thread != NULL + && pthread_mutex_lock (&(thread->threadLock)) == 0) + { + next = assoc->nextThread; + ptw32_tkAssocDestroy (assoc); + (void) pthread_mutex_unlock (&(thread->threadLock)); + } + else + { + /* Thread or lock is no longer valid */ next = assoc->nextThread; - assoc->nextThread = NULL; - - pthread_mutex_unlock (&(assoc->lock)); - ptw32_tkAssocDestroy (assoc); - - assoc = next; } + assoc = next; } - pthread_mutex_unlock (&(key->threadsLock)); + pthread_mutex_unlock (&(key->keyLock)); } TlsFree (key->key); if (key->destructor != NULL) { - pthread_mutex_destroy (&(key->threadsLock)); + pthread_mutex_destroy (&(key->keyLock)); } #if defined( _DEBUG ) diff --git a/pthread_setspecific.c b/pthread_setspecific.c index 7ac1d73..3c630cf 100644 --- a/pthread_setspecific.c +++ b/pthread_setspecific.c @@ -87,9 +87,8 @@ pthread_setspecific (pthread_key_t key, const void *value) * Resolve catch-22 of registering thread with selfThread * key */ - ptw32_thread_t * sp; + ptw32_thread_t * sp = (ptw32_thread_t *) pthread_getspecific (ptw32_selfThreadKey); - sp = (ptw32_thread_t *) pthread_getspecific (ptw32_selfThreadKey); if (sp == NULL) { if (value == NULL) @@ -108,12 +107,8 @@ pthread_setspecific (pthread_key_t key, const void *value) if (key != NULL) { - ThreadKeyAssoc *assoc; - if (self.p != NULL && key->destructor != NULL && value != NULL) { - ptw32_thread_t * sp = (ptw32_thread_t *) self.p; - /* * Only require associations if we have to * call user destroy routine. @@ -123,38 +118,50 @@ pthread_setspecific (pthread_key_t key, const void *value) * on the association; setting assoc to NULL short * circuits the search. */ - assoc = (ThreadKeyAssoc *) sp->keys; - /* - * Locate existing association - */ - while (assoc != NULL) + ThreadKeyAssoc *assoc; + + if (pthread_mutex_lock(&(key->keyLock)) == 0) { - if (assoc->key == key) + ptw32_thread_t * sp = (ptw32_thread_t *) self.p; + + (void) pthread_mutex_lock(&(sp->threadLock)); + + assoc = (ThreadKeyAssoc *) sp->keys; + /* + * Locate existing association + */ + while (assoc != NULL) { - /* - * Association already exists - */ - break; + if (assoc->key == key) + { + /* + * Association already exists + */ + break; + } + assoc = assoc->nextKey; } - assoc = assoc->nextKey; - } - /* - * create an association if not found - */ - if (assoc == NULL) - { - result = ptw32_tkAssocCreate (&assoc, self, key); - } - } + /* + * create an association if not found + */ + if (assoc == NULL) + { + result = ptw32_tkAssocCreate (&assoc, sp, key); + } - if (result == 0) - { - if (!TlsSetValue (key->key, (LPVOID) value)) - { - result = EAGAIN; + (void) pthread_mutex_unlock(&(sp->threadLock)); } + (void) pthread_mutex_unlock(&(key->keyLock)); } + + if (result == 0) + { + if (!TlsSetValue (key->key, (LPVOID) value)) + { + result = EAGAIN; + } + } } return (result); diff --git a/ptw32_callUserDestroyRoutines.c b/ptw32_callUserDestroyRoutines.c index 410d714..226c1d8 100644 --- a/ptw32_callUserDestroyRoutines.c +++ b/ptw32_callUserDestroyRoutines.c @@ -64,100 +64,84 @@ ptw32_callUserDestroyRoutines (pthread_t thread) * ------------------------------------------------------------------- */ { - ThreadKeyAssoc ** - nextP; - ThreadKeyAssoc * - assoc; + ThreadKeyAssoc * next; + ThreadKeyAssoc * assoc; if (thread.p != NULL) { + ptw32_thread_t * sp = (ptw32_thread_t *) thread.p; + /* * Run through all Thread<-->Key associations * for the current thread. - * If the pthread_key_t still exists (ie the assoc->key - * is not NULL) then call the user's TSD destroy routine. - * Notes: - * If assoc->key is NULL, then the user previously called - * PThreadKeyDestroy. The association is now only referenced - * by the current thread and must be released; otherwise - * the assoc will be destroyed when the key is destroyed. */ - nextP = (ThreadKeyAssoc **) &((ptw32_thread_t *)thread.p)->keys; - assoc = *nextP; + assoc = next = (ThreadKeyAssoc *) sp->keys; while (assoc != NULL) { + pthread_key_t k; - if (pthread_mutex_lock (&(assoc->lock)) == 0) + if ((k = assoc->key) != NULL + && pthread_mutex_lock(&(k->keyLock)) == 0) { - pthread_key_t k; - pthread_t nil = {NULL, 0}; + /* + * Key still active; pthread_key_delete + * will block on this same mutex before + * it can release actual key; therefore, + * key is valid and we can call the destroy + * routine; + */ + void * value = NULL; - if ((k = assoc->key) != NULL) + value = pthread_getspecific (k); + if (value != NULL && k->destructor != NULL) { - /* - * Key still active; pthread_key_delete - * will block on this same mutex before - * it can release actual key; therefore, - * key is valid and we can call the destroy - * routine; - */ - void * value = NULL; - - value = pthread_getspecific (k); - if (value != NULL && k->destructor != NULL) - { #ifdef __cplusplus - try - { - /* - * Run the caller's cleanup routine. - */ - (*(k->destructor)) (value); - } - catch (...) - { - /* - * A system unexpected exception has occurred - * running the user's destructor. - * We get control back within this block in case - * the application has set up it's own terminate - * handler. Since we are leaving the thread we - * should not get any internal pthreads - * exceptions. - */ - terminate (); - } - -#else /* __cplusplus */ - + try + { /* * Run the caller's cleanup routine. */ (*(k->destructor)) (value); + } + catch (...) + { + /* + * A system unexpected exception has occurred + * running the user's destructor. + * We get control back within this block in case + * the application has set up it's own terminate + * handler. Since we are leaving the thread we + * should not get any internal pthreads + * exceptions. + */ + (void) pthread_mutex_unlock(&(k->keyLock)); + terminate (); + } + +#else /* __cplusplus */ + + /* + * Run the caller's cleanup routine. + */ + (*(k->destructor)) (value); #endif /* __cplusplus */ - } } /* - * mark assoc->thread as NULL to indicate the - * thread no longer references this association - */ - assoc->thread = nil; - - /* - * Remove association from the pthread_t chain + * Remove association from both the key and thread chains */ - *nextP = assoc->nextKey; - - pthread_mutex_unlock (&(assoc->lock)); - + (void) pthread_mutex_lock(&(sp->threadLock)); + next = assoc->nextKey; ptw32_tkAssocDestroy (assoc); + (void) pthread_mutex_unlock(&(sp->threadLock)); + + assoc = next; - assoc = *nextP; + (void) pthread_mutex_unlock(&(k->keyLock)); } } } diff --git a/ptw32_tkAssocCreate.c b/ptw32_tkAssocCreate.c index f159408..10d6527 100644 --- a/ptw32_tkAssocCreate.c +++ b/ptw32_tkAssocCreate.c @@ -41,7 +41,7 @@ int ptw32_tkAssocCreate (ThreadKeyAssoc ** assocP, - pthread_t thread, pthread_key_t key) + ptw32_thread_t * sp, pthread_key_t key) /* * ------------------------------------------------------------------- * This routine creates an association that @@ -57,16 +57,14 @@ ptw32_tkAssocCreate (ThreadKeyAssoc ** assocP, * 1) New associations are pushed to the beginning of the * chain so that the internal ptw32_selfThreadKey association * is always last, thus allowing selfThreadExit to - * be implicitly called by pthread_exit last. + * be implicitly called last by pthread_exit. + * 2) * * Parameters: * assocP * address into which the association is returned. * thread - * current running thread. If NULL, then association - * is only added to the key. A NULL thread indicates - * that the user called pthread_setspecific prior - * to starting a thread. That's ok. + * current running thread. * key * key on which to create an association. * Returns: @@ -77,65 +75,49 @@ ptw32_tkAssocCreate (ThreadKeyAssoc ** assocP, * ------------------------------------------------------------------- */ { - int result; ThreadKeyAssoc *assoc; /* * Have to create an association and add it * to both the key and the thread. + * + * Both key->keyLock and thread->threadLock are locked on + * entry to this routine. */ assoc = (ThreadKeyAssoc *) calloc (1, sizeof (*assoc)); if (assoc == NULL) { - result = ENOMEM; - goto FAIL0; + return ENOMEM; } - /* - * Initialise only when used for the first time. - */ - assoc->lock = PTHREAD_MUTEX_INITIALIZER; - assoc->thread = thread; + assoc->thread = sp; assoc->key = key; /* * Register assoc with key */ - if ((result = pthread_mutex_lock (&(key->threadsLock))) != 0) + if (key->threads != NULL) { - goto FAIL2; + ((ThreadKeyAssoc *)key->threads)->prevThread = assoc; } - assoc->nextThread = (ThreadKeyAssoc *) key->threads; + assoc->prevThread = NULL; key->threads = (void *) assoc; - pthread_mutex_unlock (&(key->threadsLock)); - - if (thread.p != NULL) + /* + * Register assoc with thread + */ + if (sp->keys != NULL) { - /* - * Register assoc with thread - */ - assoc->nextKey = (ThreadKeyAssoc *) ((ptw32_thread_t *)thread.p)->keys; - ((ptw32_thread_t *)thread.p)->keys = (void *) assoc; + ((ThreadKeyAssoc *)sp->keys)->prevKey = assoc; } + assoc->nextKey = (ThreadKeyAssoc *) sp->keys; + assoc->prevKey = NULL; + sp->keys = (void *) assoc; *assocP = assoc; - return (result); - - /* - * ------------- - * Failure Code - * ------------- - */ -FAIL2: - pthread_mutex_destroy (&(assoc->lock)); - free (assoc); - -FAIL0: - - return (result); + return (0); } /* ptw32_tkAssocCreate */ diff --git a/ptw32_tkAssocDestroy.c b/ptw32_tkAssocDestroy.c index d80cd3f..f529e3c 100644 --- a/ptw32_tkAssocDestroy.c +++ b/ptw32_tkAssocDestroy.c @@ -56,10 +56,47 @@ ptw32_tkAssocDestroy (ThreadKeyAssoc * assoc) */ { - if ((assoc != NULL) && (assoc->key == NULL && assoc->thread.p == NULL)) + /* + * Both key->keyLock and thread->threadLock are locked on + * entry to this routine. + */ + if (assoc != NULL) { + ThreadKeyAssoc * prev, * next; - pthread_mutex_destroy (&(assoc->lock)); + prev = assoc->prevKey; + next = assoc->nextKey; + if (prev != NULL) + { + prev->nextKey = next; + } + if (next != NULL) + { + next->prevKey = prev; + } + + if (assoc->key->threads == assoc) + { + /* We're at the head of the threads chain */ + assoc->key->threads = next; + } + + prev = assoc->prevThread; + next = assoc->nextThread; + if (prev != NULL) + { + prev->nextThread = next; + } + if (next != NULL) + { + next->prevThread = prev; + } + + if (assoc->thread->keys == assoc) + { + /* We're at the head of the keys chain */ + assoc->thread->keys = next; + } free (assoc); } diff --git a/sem_init.c b/sem_init.c index 3bb2a45..647ba18 100644 --- a/sem_init.c +++ b/sem_init.c @@ -72,7 +72,8 @@ sem_init (sem_t * sem, int pshared, unsigned int value) * 0 successfully created semaphore, * -1 failed, error in errno * ERRNO - * EINVAL 'sem' is not a valid semaphore, + * EINVAL 'sem' is not a valid semaphore, or + * 'value' >= _POSIX_SEM_VALUE_MAX * ENOMEM out of memory, * ENOSPC a required resource has been exhausted, * ENOSYS semaphores are not supported, @@ -92,6 +93,10 @@ sem_init (sem_t * sem, int pshared, unsigned int value) */ result = EPERM; } + else if (value > (unsigned int)_POSIX_SEM_VALUE_MAX) + { + result = EINVAL; + } else { s = (sem_t) calloc (1, sizeof (*s)); diff --git a/sem_post.c b/sem_post.c index f4f48e1..2edaecf 100644 --- a/sem_post.c +++ b/sem_post.c @@ -68,6 +68,7 @@ sem_post (sem_t * sem) * ERRNO * EINVAL 'sem' is not a valid semaphore, * ENOSYS semaphores are not supported, + * ERANGE semaphore count is too big * * ------------------------------------------------------ */ @@ -81,23 +82,29 @@ sem_post (sem_t * sem) } else if ((result = pthread_mutex_lock (&s->lock)) == 0) { -#ifdef NEED_SEM - - if (++s->value <= 0) + if (s->value < _POSIX_SEM_VALUE_MAX) { - if (!SetEvent(s->sem)) +#ifdef NEED_SEM + if (++s->value <= 0 + && !SetEvent(s->sem)) { + s->value--; result = EINVAL; } - } #else - if (++s->value <= 0 - && !ReleaseSemaphore (s->sem, 1, NULL)) + if (++s->value <= 0 + && !ReleaseSemaphore (s->sem, 1, NULL)) + { + s->value--; + result = EINVAL; + } +#endif /* NEED_SEM */ + } + else { - result = EINVAL; + result = ERANGE; } -#endif /* NEED_SEM */ - + (void) pthread_mutex_unlock (&s->lock); } diff --git a/sem_post_multiple.c b/sem_post_multiple.c index 9dab861..42ea2f4 100644 --- a/sem_post_multiple.c +++ b/sem_post_multiple.c @@ -71,6 +71,7 @@ sem_post_multiple (sem_t * sem, int count) * ERRNO * EINVAL 'sem' is not a valid semaphore * or count is less than or equal to zero. + * ERANGE semaphore count is too big * * ------------------------------------------------------ */ @@ -85,28 +86,39 @@ sem_post_multiple (sem_t * sem, int count) } else if ((result = pthread_mutex_lock (&s->lock)) == 0) { - waiters = -s->value; - s->value += count; - if (waiters > 0) - { -#ifdef NEED_SEM - if (SetEvent(s->sem)) + if (s->value <= (_POSIX_SEM_VALUE_MAX - count)) + { + waiters = -s->value; + s->value += count; + if (waiters > 0) { - waiters--; - s->leftToUnblock += count - 1; - if (s->leftToUnblock > waiters) +#ifdef NEED_SEM + if (SetEvent(s->sem)) { - s->leftToUnblock = waiters; + waiters--; + s->leftToUnblock += count - 1; + if (s->leftToUnblock > waiters) + { + s->leftToUnblock = waiters; + } } - } - else #else - if (!ReleaseSemaphore (s->sem, (waiters<=count)?waiters:count, 0)) + if (ReleaseSemaphore (s->sem, (waiters<=count)?waiters:count, 0)) + { + /* No action */ + } #endif - { - result = EINVAL; - } - } + else + { + s->value -= count; + result = EINVAL; + } + } + } + else + { + result = ERANGE; + } (void) pthread_mutex_unlock (&s->lock); } diff --git a/signal.c b/signal.c index e72e49f..8f56c48 100644 --- a/signal.c +++ b/signal.c @@ -167,6 +167,8 @@ pthread_sigmask (int how, sigset_t const *set, sigset_t * oset) int sigwait (const sigset_t * set, int *sig) { + /* This routine is a cancellation point */ + pthread_test_cancel(); } int diff --git a/tests/Bmakefile b/tests/Bmakefile index ff8c95b..7e18d56 100644 --- a/tests/Bmakefile +++ b/tests/Bmakefile @@ -91,11 +91,12 @@ PASSES= loadfree.pass \ mutex6s.pass mutex6es.pass mutex6rs.pass \ mutex7.pass mutex7n.pass mutex7e.pass mutex7r.pass \ mutex8.pass mutex8n.pass mutex8e.pass mutex8r.pass \ - count1.pass once1.pass once2.pass once3.pass once4.pass tsd1.pass \ + count1.pass once1.pass once2.pass once3.pass once4.pass \ self2.pass \ cancel1.pass cancel2.pass \ semaphore4.pass semaphore4t.pass \ - delay1.pass delay2.pass eyal1.pass \ + barrier1.pass barrier2.pass barrier3.pass barrier4.pass barrier5.pass \ + tsd1.pass delay1.pass delay2.pass eyal1.pass \ condvar3.pass condvar3_1.pass condvar3_2.pass condvar3_3.pass \ condvar4.pass condvar5.pass condvar6.pass \ condvar7.pass condvar8.pass condvar9.pass \ @@ -108,7 +109,6 @@ PASSES= loadfree.pass \ cleanup0.pass cleanup1.pass cleanup2.pass cleanup3.pass \ priority1.pass priority2.pass inherit1.pass \ spin1.pass spin2.pass spin3.pass spin4.pass \ - barrier1.pass barrier2.pass barrier3.pass barrier4.pass barrier5.pass \ exception1.pass exception2.pass exception3.pass \ cancel9.pass create3.pass stress1.pass @@ -220,7 +220,7 @@ benchtest2.bench: benchtest3.bench: benchtest4.bench: benchtest5.bench: -barrier1.pass: +barrier1.pass: semaphore4.pass barrier2.pass: barrier1.pass barrier3.pass: barrier2.pass barrier4.pass: barrier3.pass @@ -341,6 +341,6 @@ spin2.pass: spin1.pass spin3.pass: spin2.pass spin4.pass: spin3.pass stress1.pass: barrier5.pass -tsd1.pass: join1.pass +tsd1.pass: barrier5.pass join1.pass valid1.pass: join1.pass valid2.pass: valid1.pass diff --git a/tests/GNUmakefile b/tests/GNUmakefile index fc36c32..15d8dcc 100644 --- a/tests/GNUmakefile +++ b/tests/GNUmakefile @@ -86,10 +86,11 @@ TESTS = sizes loadfree \ mutex4 mutex6 mutex6n mutex6e mutex6r \ mutex6s mutex6es mutex6rs \ mutex7 mutex7n mutex7e mutex7r mutex8 mutex8n mutex8e mutex8r \ - count1 once1 once2 once3 once4 tsd1 self2 \ + count1 once1 once2 once3 once4 self2 \ cancel1 cancel2 \ semaphore4 semaphore4t \ - delay1 delay2 eyal1 \ + barrier1 barrier2 barrier3 barrier4 barrier5 \ + tsd1 delay1 delay2 eyal1 \ condvar3 condvar3_1 condvar3_2 condvar3_3 \ condvar4 condvar5 condvar6 condvar7 condvar8 condvar9 \ errno1 \ @@ -100,7 +101,6 @@ TESTS = sizes loadfree \ cleanup0 cleanup1 cleanup2 cleanup3 \ priority1 priority2 inherit1 \ spin1 spin2 spin3 spin4 \ - barrier1 barrier2 barrier3 barrier4 barrier5 \ exception1 exception2 exception3 \ cancel9 create3 @@ -183,7 +183,7 @@ benchtest5.bench: stress1.pass: -barrier1.pass: +barrier1.pass: semaphore4.pass barrier2.pass: barrier1.pass barrier3.pass: barrier2.pass barrier4.pass: barrier3.pass @@ -304,7 +304,7 @@ spin1.pass: spin2.pass: spin1.pass spin3.pass: spin2.pass spin4.pass: spin3.pass -tsd1.pass: join1.pass +tsd1.pass: barrier5.pass join1.pass valid1.pass: join1.pass valid2.pass: valid1.pass diff --git a/tests/Makefile b/tests/Makefile index f227914..0a7738a 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -95,11 +95,12 @@ PASSES= sizes.pass loadfree.pass \ mutex6s.pass mutex6es.pass mutex6rs.pass \ mutex7.pass mutex7n.pass mutex7e.pass mutex7r.pass \ mutex8.pass mutex8n.pass mutex8e.pass mutex8r.pass \ - count1.pass once1.pass once2.pass once3.pass once4.pass tsd1.pass \ + count1.pass once1.pass once2.pass once3.pass once4.pass \ self2.pass \ cancel1.pass cancel2.pass \ semaphore4.pass semaphore4t.pass \ - delay1.pass delay2.pass eyal1.pass \ + barrier1.pass barrier2.pass barrier3.pass barrier4.pass barrier5.pass \ + tsd1.pass delay1.pass delay2.pass eyal1.pass \ condvar3.pass condvar3_1.pass condvar3_2.pass condvar3_3.pass \ condvar4.pass condvar5.pass condvar6.pass \ condvar7.pass condvar8.pass condvar9.pass \ @@ -113,7 +114,6 @@ PASSES= sizes.pass loadfree.pass \ cleanup0.pass cleanup1.pass cleanup2.pass cleanup3.pass \ priority1.pass priority2.pass inherit1.pass \ spin1.pass spin2.pass spin3.pass spin4.pass \ - barrier1.pass barrier2.pass barrier3.pass barrier4.pass barrier5.pass \ exception1.pass exception2.pass exception3.pass \ cancel9.pass create3.pass @@ -276,7 +276,7 @@ benchtest5.bench: stress1.pass: -barrier1.pass: +barrier1.pass: semaphore4.pass barrier2.pass: barrier1.pass barrier3.pass: barrier2.pass barrier4.pass: barrier3.pass @@ -396,6 +396,6 @@ spin1.pass: spin2.pass: spin1.pass spin3.pass: spin2.pass spin4.pass: spin3.pass -tsd1.pass: join1.pass +tsd1.pass: barrier5.pass join1.pass valid1.pass: join1.pass valid2.pass: valid1.pass diff --git a/tests/cancel4.c b/tests/cancel4.c index 1561ea6..6d6d3dc 100644 --- a/tests/cancel4.c +++ b/tests/cancel4.c @@ -112,10 +112,10 @@ mythread(void * arg) assert(pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL) == 0); /* - * We wait up to 10 seconds, waking every 0.1 seconds, + * We wait up to 2 seconds, waking every 0.1 seconds, * for a cancelation to be applied to us. */ - for (bag->count = 0; bag->count < 100; bag->count++) + for (bag->count = 0; bag->count < 20; bag->count++) Sleep(100); return (void *) result; diff --git a/tests/condvar2.c b/tests/condvar2.c index cfc68af..33f1d3f 100644 --- a/tests/condvar2.c +++ b/tests/condvar2.c @@ -102,7 +102,7 @@ main() abstime.tv_sec = currSysTime.time; abstime.tv_nsec = NANOSEC_PER_MILLISEC * currSysTime.millitm; - abstime.tv_sec += 5; + abstime.tv_sec += 1; assert(pthread_cond_timedwait(&cv, &mutex, &abstime) == ETIMEDOUT); diff --git a/tests/semaphore1.c b/tests/semaphore1.c index b5b2050..4327e73 100644 --- a/tests/semaphore1.c +++ b/tests/semaphore1.c @@ -86,7 +86,7 @@ thr(void * arg) if ( result == -1 ) { int err = errno; - perror("thread: sem_trywait 1: expected error"); // No error + perror("thread: sem_trywait 1: expect an EAGAIN error"); // No error assert(err == EAGAIN); } else @@ -131,7 +131,7 @@ main() if ( result == -1 ) { int err = errno; - perror("main: sem_trywait 1: expected error"); // No error + perror("main: sem_trywait 1: expect an EAGAIN error"); // No error assert(err == EAGAIN); } else diff --git a/tests/tsd1.c b/tests/tsd1.c index 6e21d2a..4d89165 100644 --- a/tests/tsd1.c +++ b/tests/tsd1.c @@ -34,6 +34,8 @@ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA * * + * -------------------------------------------------------------------------- + * * Description: * - * @@ -77,10 +79,15 @@ #include #include "test.h" +enum { + NUM_THREADS = 100 +}; + static pthread_key_t key = NULL; -static int accesscount[10]; -static int thread_set[10]; -static int thread_destroyed[10]; +static int accesscount[NUM_THREADS]; +static int thread_set[NUM_THREADS]; +static int thread_destroyed[NUM_THREADS]; +static pthread_barrier_t startBarrier; static void destroy_key(void * arg) @@ -117,10 +124,7 @@ setkey(void * arg) static void * mythread(void * arg) { - while (key == NULL) - { - sched_yield(); - } + (void) pthread_barrier_wait(&startBarrier); setkey(arg); @@ -134,22 +138,24 @@ main() { int i; int fail = 0; - pthread_t thread[10]; + pthread_t thread[NUM_THREADS]; - for (i = 1; i < 5; i++) + assert(pthread_barrier_init(&startBarrier, NULL, NUM_THREADS/2) == 0); + + for (i = 1; i < NUM_THREADS/2; i++) { accesscount[i] = thread_set[i] = thread_destroyed[i] = 0; assert(pthread_create(&thread[i], NULL, mythread, (void *)&accesscount[i]) == 0); } - Sleep(2000); - /* * Here we test that existing threads will get a key created * for them. */ assert(pthread_key_create(&key, destroy_key) == 0); + (void) pthread_barrier_wait(&startBarrier); + /* * Test main thread key. */ @@ -160,16 +166,16 @@ main() * Here we test that new threads will get a key created * for them. */ - for (i = 5; i < 10; i++) + for (i = NUM_THREADS/2; i < NUM_THREADS; i++) { - accesscount[i] = thread_set[i] = thread_destroyed[i] = 0; + accesscount[i] = thread_set[i] = thread_destroyed[i] = 0; assert(pthread_create(&thread[i], NULL, mythread, (void *)&accesscount[i]) == 0); } /* * Wait for all threads to complete. */ - for (i = 1; i < 10; i++) + for (i = 1; i < NUM_THREADS; i++) { int result = 0; @@ -178,7 +184,9 @@ main() assert(pthread_key_delete(key) == 0); - for (i = 1; i < 10; i++) + assert(pthread_barrier_destroy(&startBarrier) == 0); + + for (i = 1; i < NUM_THREADS; i++) { /* * The counter is incremented once when the key is set to -- cgit v1.2.3