diff options
| -rw-r--r-- | ANNOUNCE | 9 | ||||
| -rw-r--r-- | ChangeLog | 36 | ||||
| -rw-r--r-- | NEWS | 43 | ||||
| -rw-r--r-- | create.c | 22 | ||||
| -rw-r--r-- | implement.h | 89 | ||||
| -rw-r--r-- | pthread.h | 4 | ||||
| -rw-r--r-- | pthread_key_create.c | 2 | ||||
| -rw-r--r-- | pthread_key_delete.c | 33 | ||||
| -rw-r--r-- | pthread_setspecific.c | 69 | ||||
| -rw-r--r-- | ptw32_callUserDestroyRoutines.c | 114 | ||||
| -rw-r--r-- | ptw32_tkAssocCreate.c | 60 | ||||
| -rw-r--r-- | ptw32_tkAssocDestroy.c | 41 | ||||
| -rw-r--r-- | sem_init.c | 7 | ||||
| -rw-r--r-- | sem_post.c | 27 | ||||
| -rw-r--r-- | sem_post_multiple.c | 46 | ||||
| -rw-r--r-- | signal.c | 2 | ||||
| -rw-r--r-- | tests/Bmakefile | 10 | ||||
| -rw-r--r-- | tests/GNUmakefile | 10 | ||||
| -rw-r--r-- | tests/Makefile | 10 | ||||
| -rw-r--r-- | tests/cancel4.c | 4 | ||||
| -rw-r--r-- | tests/condvar2.c | 2 | ||||
| -rw-r--r-- | tests/semaphore1.c | 4 | ||||
| -rw-r--r-- | tests/tsd1.c | 38 | 
23 files changed, 435 insertions, 247 deletions
@@ -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: @@ -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 @@ -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) @@ -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); @@ -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);      } @@ -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)); @@ -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);      } @@ -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  | 
