summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorrpj <rpj>2005-05-06 07:31:28 +0000
committerrpj <rpj>2005-05-06 07:31:28 +0000
commit78f83cfa240ec14874b22c7302ff8d306c130aaf (patch)
treebf29a117aecb99e9ecb2d30bad398d8b20392b49
parente3845d9b6b0a18ffdfdf0cefd811a17fce62f1ac (diff)
''
-rw-r--r--ANNOUNCE9
-rw-r--r--ChangeLog36
-rw-r--r--NEWS43
-rw-r--r--create.c22
-rw-r--r--implement.h89
-rw-r--r--pthread.h4
-rw-r--r--pthread_key_create.c2
-rw-r--r--pthread_key_delete.c33
-rw-r--r--pthread_setspecific.c69
-rw-r--r--ptw32_callUserDestroyRoutines.c114
-rw-r--r--ptw32_tkAssocCreate.c60
-rw-r--r--ptw32_tkAssocDestroy.c41
-rw-r--r--sem_init.c7
-rw-r--r--sem_post.c27
-rw-r--r--sem_post_multiple.c46
-rw-r--r--signal.c2
-rw-r--r--tests/Bmakefile10
-rw-r--r--tests/GNUmakefile10
-rw-r--r--tests/Makefile10
-rw-r--r--tests/cancel4.c4
-rw-r--r--tests/condvar2.c2
-rw-r--r--tests/semaphore1.c4
-rw-r--r--tests/tsd1.c38
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 <ross at callisto.canberra.edu.au>
+
+ * 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 <ross at callisto.canberra.edu.au>
+
+ * 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 <ross at callisto.canberra.edu.au>
* 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 <sched.h>
#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