diff options
| -rw-r--r-- | ANNOUNCE | 2 | ||||
| -rw-r--r-- | ChangeLog | 9 | ||||
| -rw-r--r-- | NEWS | 17 | ||||
| -rw-r--r-- | implement.h | 10 | ||||
| -rw-r--r-- | manual/pthread_key_create.html | 2 | ||||
| -rw-r--r-- | pthread_key_delete.c | 47 | ||||
| -rw-r--r-- | pthread_mutex_lock.c | 4 | ||||
| -rw-r--r-- | pthread_setspecific.c | 2 | ||||
| -rw-r--r-- | ptw32_callUserDestroyRoutines.c | 156 | ||||
| -rw-r--r-- | ptw32_tkAssocCreate.c | 23 | ||||
| -rw-r--r-- | ptw32_tkAssocDestroy.c | 24 | ||||
| -rw-r--r-- | tests/Bmakefile | 3 | ||||
| -rw-r--r-- | tests/GNUmakefile | 6 | ||||
| -rw-r--r-- | tests/Makefile | 3 | ||||
| -rw-r--r-- | tests/Wmakefile | 2 | ||||
| -rw-r--r-- | tests/semaphore1.c | 6 | ||||
| -rw-r--r-- | tests/tsd2.c | 215 | 
17 files changed, 426 insertions, 105 deletions
| @@ -1,4 +1,4 @@ -	     PTHREADS-WIN32 RELEASE 1.9.0 (2005-05-06) +	     PTHREADS-WIN32 RELEASE 1.9.0 (2005-05-09)  	     -----------------------------------------  	 Web Site: http://sources.redhat.com/pthreads-win32/  	FTP Site: ftp://sources.redhat.com/pub/pthreads-win32 @@ -1,3 +1,12 @@ +2005-05-09  Ross Johnson  <ross at callisto.canberra.edu.au>^M + +        * ptw32_callUserDestroyRoutines.c: Run destructor process (i.e. +        loop over all keys calling destructors) up to  +        PTHREAD_DESTRUCTOR_ITERATIONS times if TSD value isn't NULL yet; +        modify assoc management. +        * pthread_key_delete.c: Modify assoc management. +        * ptw32_tkAssocDestroy.c: Fix error in assoc removal from chains. +  2005-05-06  Ross Johnson  <ross at callisto.canberra.edu.au>  	* signal.c (sigwait): Add a cancellation point to this otherwise @@ -1,6 +1,6 @@  RELEASE 1.9.0  ------------- -(2005-05-06) +(2005-05-09)  General  ------- @@ -16,9 +16,13 @@ http://sources.redhat.com/pthreads-win32/manual/index.html  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. +* Thread Specific Data (TSD) key management has been ammended to +eliminate a source of (what was effectively) resource leakage (HANDLEs +plus memory). +Thanks to Richard Hughes at Aculab for identifying and locating the leak. + +* TSD key destructors are now processed up to PTHREAD_DESTRUCTOR_ITERATIONS +times (was always in pthread.h) instead of just once.  * Fix a semaphore accounting race between sem_post/sem_post_multiple  and sem_wait cancellation. This is the same issue as with @@ -40,6 +44,11 @@ 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 +* tsd2.c - tests that key destructors are re-run if the tsd key value is +not NULL after the destructor routine has run. Also tests that  +pthread_setspecific() and pthread_getspecific() are callable from +destructors. +  RELEASE 1.8.0  ------------- diff --git a/implement.h b/implement.h index 994a768..2e883a8 100644 --- a/implement.h +++ b/implement.h @@ -150,6 +150,7 @@ struct ptw32_thread_t_  #endif				/* HAVE_SIGSET_T */    int implicit:1;    void *keys; +  void *nextAssoc;  }; @@ -386,8 +387,10 @@ struct ThreadKeyAssoc     *      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. +   *      and thread locks are always acquired in the order: key lock +   *      then thread lock. An exception to this exists when a thread +   *      calls the destructors, however this is done carefully to +   *      avoid deadlock.     *     *      An association is created when a thread first calls     *      pthread_setspecific() on a key that has a specified @@ -610,8 +613,7 @@ extern "C"    void ptw32_callUserDestroyRoutines (pthread_t thread); -  int ptw32_tkAssocCreate (ThreadKeyAssoc ** assocP, -			   ptw32_thread_t * thread, pthread_key_t key); +  int ptw32_tkAssocCreate (ptw32_thread_t * thread, pthread_key_t key);    void ptw32_tkAssocDestroy (ThreadKeyAssoc * assoc); diff --git a/manual/pthread_key_create.html b/manual/pthread_key_create.html index 2d2a183..9d9e004 100644 --- a/manual/pthread_key_create.html +++ b/manual/pthread_key_create.html @@ -70,7 +70,7 @@ might, however, re-associate non- <B>NULL</B> values to that key or  some other key. To deal with this, if after all the destructors have  been called for all non- <B>NULL</B> values, there are still some  non- <B>NULL</B> values with associated destructors, then the process -is repeated. The LinuxThreads implementation stops the process after +is repeated. The <B>Pthreads-w32</B> implementation stops the process after  <B>PTHREAD_DESTRUCTOR_ITERATIONS</B> iterations, even if some non-  <B>NULL</B> values with associated descriptors remain. Other  implementations may loop indefinitely.  diff --git a/pthread_key_delete.c b/pthread_key_delete.c index 99b00b9..7da9b2f 100644 --- a/pthread_key_delete.c +++ b/pthread_key_delete.c @@ -74,42 +74,41 @@ pthread_key_delete (pthread_key_t key)  	  key->destructor != NULL &&  	  pthread_mutex_lock (&(key->keyLock)) == 0)  	{ +	  ThreadKeyAssoc *assoc;  	  /*  	   * Run through all Thread<-->Key associations  	   * for this key. -	   * If the pthread_t still exists (ie the assoc->thread -	   * is not NULL) then leave the assoc for the thread to -	   * destroy. -	   * Notes: -	   *      If assoc->thread is NULL, then the associated thread -	   *      is no longer referencing this assoc. -	   *      The association is only referenced -	   *      by this key and must be released; otherwise -	   *      the assoc will be destroyed when the thread is destroyed. +	   * +	   * While we hold at least one of the locks guarding +	   * the assoc, we know that the assoc pointed to by +	   * key->threads is valid.  	   */ -	  ThreadKeyAssoc *assoc; - -	  assoc = (ThreadKeyAssoc *) key->threads; - -	  while (assoc != NULL) +	  while ((assoc = (ThreadKeyAssoc *) key->threads) != NULL)  	    { -	      ThreadKeyAssoc *next;  	      ptw32_thread_t * thread = assoc->thread; -	      if (thread != NULL -		  && pthread_mutex_lock (&(thread->threadLock)) == 0) +	      if (assoc == NULL)  		{ -		  next = assoc->nextThread; +		  /* Finished */ +		  break; +		} + +	      if (pthread_mutex_lock (&(thread->threadLock)) == 0) +		{ +		  /* +		   * Since we are starting at the head of the key's threads +		   * chain, this will also point key->threads at the next assoc. +		   * While we hold key->keyLock, no other thread can insert +		   * a new assoc via pthread_setspecific. +		   */  		  ptw32_tkAssocDestroy (assoc);  		  (void) pthread_mutex_unlock (&(thread->threadLock));  		}  	      else  		{ -		  /* Thread or lock is no longer valid */ -		  next = assoc->nextThread; +		  /* Thread or lock is no longer valid? */  		  ptw32_tkAssocDestroy (assoc);  		} -	      assoc = next;  	    }  	  pthread_mutex_unlock (&(key->keyLock));  	} @@ -117,7 +116,11 @@ pthread_key_delete (pthread_key_t key)        TlsFree (key->key);        if (key->destructor != NULL)  	{ -	  pthread_mutex_destroy (&(key->keyLock)); +	  /* A thread could be holding the keyLock */ +	  while (EBUSY == pthread_mutex_destroy (&(key->keyLock))) +	    { +	      Sleep(1); // Ugly. +	    }  	}  #if defined( _DEBUG ) diff --git a/pthread_mutex_lock.c b/pthread_mutex_lock.c index 301f7bf..4ca5c25 100644 --- a/pthread_mutex_lock.c +++ b/pthread_mutex_lock.c @@ -49,6 +49,10 @@ pthread_mutex_lock (pthread_mutex_t * mutex)    /*     * Let the system deal with invalid pointers.     */ +  if (*mutex == NULL) +    { +      return EINVAL; +    }    /*     * We do a quick check to see if we need to do more work diff --git a/pthread_setspecific.c b/pthread_setspecific.c index 3c630cf..f06b696 100644 --- a/pthread_setspecific.c +++ b/pthread_setspecific.c @@ -147,7 +147,7 @@ pthread_setspecific (pthread_key_t key, const void *value)  	       */  	      if (assoc == NULL)  		{ -		  result = ptw32_tkAssocCreate (&assoc, sp, key); +		  result = ptw32_tkAssocCreate (sp, key);  		}  	      (void) pthread_mutex_unlock(&(sp->threadLock)); diff --git a/ptw32_callUserDestroyRoutines.c b/ptw32_callUserDestroyRoutines.c index 7edce08..cdec87e 100644 --- a/ptw32_callUserDestroyRoutines.c +++ b/ptw32_callUserDestroyRoutines.c @@ -64,85 +64,151 @@ ptw32_callUserDestroyRoutines (pthread_t thread)        * -------------------------------------------------------------------        */  { -  ThreadKeyAssoc * next;    ThreadKeyAssoc * assoc;    if (thread.p != NULL)      { +      int assocsRemaining; +      int iterations = 0;        ptw32_thread_t * sp = (ptw32_thread_t *) thread.p;        /*         * Run through all Thread<-->Key associations         * for the current thread. +       * +       * Do this process at most PTHREAD_DESTRUCTOR_ITERATIONS times. +       * +       * First we need to serialise with pthread_key_delete by locking +       * both assoc guards, but in the reverse order to normal, and so +       * we must be careful to avoid deadlock.         */ -      assoc = next = (ThreadKeyAssoc *) sp->keys; - -      while (assoc != NULL) +      do +	{ +	  assocsRemaining = 0; +	  iterations++; + +	  (void) pthread_mutex_lock(&(sp->threadLock)); +	  /* +	   * The next assoc pointer is stored in the thread struct so that +	   * the assoc destructor in pthread_key_delete can adjust it +	   * if it deletes this assoc. This can happen if we fail to acquire +	   * both locks below, and are forced to release all of our locks, +	   * leaving open the opportunity. +	   */ +	  sp->nextAssoc = sp->keys; +	  (void) pthread_mutex_unlock(&(sp->threadLock)); + +      for (;;)  	{ +	  void * value;  	  pthread_key_t k; +	  void (*destructor) (void *); + +	  (void) pthread_mutex_lock(&(sp->threadLock)); -	  if ((k = assoc->key) != NULL -	      && pthread_mutex_lock(&(k->keyLock)) == 0) +	  if ((assoc = (ThreadKeyAssoc *)sp->nextAssoc) == NULL) +	    { +	      /* Finished */ +	      pthread_mutex_unlock(&(sp->threadLock)); +	      break; +	    } +	  else  	    {  	      /* -	       * 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; +	       * assoc->key must be valid because assoc can't change or be +	       * removed from our chain while we hold at least one lock. If +	       * the assoc was on our key chain then the key has not been +	       * deleted yet. +	       * +	       * Try to acquire both locks without deadlocking.  	       */ -	      void * value = NULL; - -	      value = pthread_getspecific (k); -	      if (value != NULL && k->destructor != NULL) +	      if (pthread_mutex_trylock(&(assoc->key->keyLock)) == EBUSY)  		{ +		  pthread_mutex_unlock(&(sp->threadLock)); +		  Sleep(1); // Ugly. +		  /* +		   * Go around again. +		   * If pthread_key_delete has removed this assoc in the meantime, +		   * sp->keys will point to a new assoc. +		   */ +		  continue; +		} +	    } -#ifdef __cplusplus +	  /* We now hold both locks */ -		  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 (); -		    } +	   sp->nextAssoc = assoc->nextKey; -#else /* __cplusplus */ +	  /* +	   * Key still active; pthread_key_delete +	   * will block on these same mutexes before +	   * it can release actual key; therefore, +	   * key is valid and we can call the destroy +	   * routine; +	   */ +	  k = assoc->key; +	  destructor = k->destructor; +	  value = TlsGetValue(k->key); +	  TlsSetValue (k->key, NULL); + +	  // Every assoc->key exists and has a destructor +	  if (value != NULL && iterations <= PTHREAD_DESTRUCTOR_ITERATIONS) +	    { +	      /* +	       * Unlock both locks before the destructor runs. +	       * POSIX says pthread_key_delete can be run from destructors, +	       * and that probably includes with this key as target. +	       */ +	      (void) pthread_mutex_unlock(&(sp->threadLock)); +	      (void) pthread_mutex_unlock(&(k->keyLock)); + +	      assocsRemaining++; +#ifdef __cplusplus + +	      try +		{  		  /*  		   * Run the caller's cleanup routine.  		   */ -		  (*(k->destructor)) (value); +		  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 */ + +	      /* +	       * Run the caller's cleanup routine. +	       */ +	      destructor (value);  #endif /* __cplusplus */ -		} +	    } +	  else +	    {  	      /*  	       * Remove association from both the key and thread chains +	       * and reclaim it's memory resources.  	       */ -	      (void) pthread_mutex_lock(&(sp->threadLock)); -	      next = assoc->nextKey;  	      ptw32_tkAssocDestroy (assoc);  	      (void) pthread_mutex_unlock(&(sp->threadLock)); - -	      assoc = next; -  	      (void) pthread_mutex_unlock(&(k->keyLock));  	    }  	} +	} +      while (assocsRemaining);      }  }				/* ptw32_callUserDestroyRoutines */ diff --git a/ptw32_tkAssocCreate.c b/ptw32_tkAssocCreate.c index 10d6527..5ba24bb 100644 --- a/ptw32_tkAssocCreate.c +++ b/ptw32_tkAssocCreate.c @@ -40,8 +40,7 @@  int -ptw32_tkAssocCreate (ThreadKeyAssoc ** assocP, -		     ptw32_thread_t * sp, pthread_key_t key) +ptw32_tkAssocCreate (ptw32_thread_t * sp, pthread_key_t key)       /*        * -------------------------------------------------------------------        * This routine creates an association that @@ -61,8 +60,6 @@ ptw32_tkAssocCreate (ThreadKeyAssoc ** assocP,        *      2)              *        * Parameters: -      *              assocP -      *                      address into which the association is returned.        *              thread        *                      current running thread.        *              key @@ -97,27 +94,25 @@ ptw32_tkAssocCreate (ThreadKeyAssoc ** assocP,    /*     * Register assoc with key     */ -  if (key->threads != NULL) +  assoc->prevThread = NULL; +  assoc->nextThread = (ThreadKeyAssoc *) key->threads; +  if (assoc->nextThread != NULL)      { -      ((ThreadKeyAssoc *)key->threads)->prevThread = assoc; +      assoc->nextThread->prevThread = assoc;      } -  assoc->nextThread = (ThreadKeyAssoc *) key->threads; -  assoc->prevThread = NULL;    key->threads = (void *) assoc;    /*     * Register assoc with thread     */ -  if (sp->keys != NULL) +  assoc->prevKey = NULL; +  assoc->nextKey = (ThreadKeyAssoc *) sp->keys; +  if (assoc->nextKey != NULL)      { -      ((ThreadKeyAssoc *)sp->keys)->prevKey = assoc; +      assoc->nextKey->prevKey = assoc;      } -  assoc->nextKey = (ThreadKeyAssoc *) sp->keys; -  assoc->prevKey = NULL;    sp->keys = (void *) assoc; -  *assocP = assoc; -    return (0);  }				/* ptw32_tkAssocCreate */ diff --git a/ptw32_tkAssocDestroy.c b/ptw32_tkAssocDestroy.c index f529e3c..a7842ea 100644 --- a/ptw32_tkAssocDestroy.c +++ b/ptw32_tkAssocDestroy.c @@ -45,7 +45,7 @@ ptw32_tkAssocDestroy (ThreadKeyAssoc * assoc)        * -------------------------------------------------------------------        * This routine releases all resources for the given ThreadKeyAssoc        * once it is no longer being referenced -      * ie) both the key and thread have stopped referencing it. +      * ie) either the key or thread has stopped referencing it.        *        * Parameters:        *              assoc @@ -64,6 +64,7 @@ ptw32_tkAssocDestroy (ThreadKeyAssoc * assoc)      {        ThreadKeyAssoc * prev, * next; +      /* Remove assoc from thread's keys chain */        prev = assoc->prevKey;        next = assoc->nextKey;        if (prev != NULL) @@ -75,12 +76,21 @@ ptw32_tkAssocDestroy (ThreadKeyAssoc * assoc)  	  next->prevKey = prev;  	} -      if (assoc->key->threads == assoc) +      if (assoc->thread->keys == assoc)  	{ -	  /* We're at the head of the threads chain */ -	  assoc->key->threads = next; +	  /* We're at the head of the thread's keys chain */ +	  assoc->thread->keys = next; +	} +      if (assoc->thread->nextAssoc == assoc) +	{ +	  /* +	   * Thread is exiting and we're deleting the assoc to be processed next. +	   * Hand thread the assoc after this one. +	   */ +	  assoc->thread->nextAssoc = next;  	} +      /* Remove assoc from key's threads chain */        prev = assoc->prevThread;        next = assoc->nextThread;        if (prev != NULL) @@ -92,10 +102,10 @@ ptw32_tkAssocDestroy (ThreadKeyAssoc * assoc)  	  next->prevThread = prev;  	} -      if (assoc->thread->keys == assoc) +      if (assoc->key->threads == assoc)  	{ -	  /* We're at the head of the keys chain */ -	  assoc->thread->keys = next; +	  /* We're at the head of the key's threads chain */ +	  assoc->key->threads = next;  	}        free (assoc); diff --git a/tests/Bmakefile b/tests/Bmakefile index 60b0a75..6141ffe 100644 --- a/tests/Bmakefile +++ b/tests/Bmakefile @@ -96,7 +96,7 @@ PASSES=   loadfree.pass \  	  cancel1.pass  cancel2.pass  \  	  semaphore4.pass  semaphore4t.pass  \  	  barrier1.pass  barrier2.pass  barrier3.pass  barrier4.pass  barrier5.pass  \ -	  tsd1.pass  delay1.pass  delay2.pass  eyal1.pass  \ +	  tsd1.pass  tsd2.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  \ @@ -342,5 +342,6 @@ spin3.pass: spin2.pass  spin4.pass: spin3.pass  stress1.pass: barrier5.pass  tsd1.pass: barrier5.pass join1.pass +tsd2.pass: tsd1.pass  valid1.pass: join1.pass  valid2.pass: valid1.pass diff --git a/tests/GNUmakefile b/tests/GNUmakefile index 641164d..e9df21a 100644 --- a/tests/GNUmakefile +++ b/tests/GNUmakefile @@ -90,7 +90,7 @@ TESTS	= sizes loadfree \  	  cancel1 cancel2 \  	  semaphore4 semaphore4t \  	  barrier1 barrier2 barrier3 barrier4 barrier5 \ -	  tsd1 delay1 delay2 eyal1 \ +	  tsd1 tsd2 delay1 delay2 eyal1 \  	  condvar3 condvar3_1 condvar3_2 condvar3_3 \  	  condvar4 condvar5 condvar6 condvar7 condvar8 condvar9 \  	  errno1 \ @@ -149,6 +149,9 @@ GC-bench:  GCE-bench:  	$(MAKE) TEST=GCE  CC=g++ XXCFLAGS="-mthreads -D__CLEANUP_CXX" XXLIBS="benchlib." all-bench +GC-debug: +	$(MAKE) TEST=GC CC=gcc XXCFLAGS="-D__CLEANUP_C" DLL_VER="$(DLL_VER)d" all-pass +  GC-static:  	$(MAKE) TEST=GC CC=gcc XXCFLAGS="-D__CLEANUP_C -DPTW32_STATIC_LIB" DLL="" all-static @@ -305,6 +308,7 @@ spin2.pass: spin1.pass  spin3.pass: spin2.pass  spin4.pass: spin3.pass  tsd1.pass: barrier5.pass join1.pass +tsd2.pass: tsd1.pass  valid1.pass: join1.pass  valid2.pass: valid1.pass diff --git a/tests/Makefile b/tests/Makefile index 8fa72b4..d12a1f1 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -100,7 +100,7 @@ PASSES= sizes.pass  loadfree.pass \  	  cancel1.pass  cancel2.pass  \  	  semaphore4.pass  semaphore4t.pass  \  	  barrier1.pass  barrier2.pass  barrier3.pass  barrier4.pass  barrier5.pass  \ -	  tsd1.pass  delay1.pass  delay2.pass  eyal1.pass  \ +	  tsd1.pass  tsd2.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  \ @@ -397,5 +397,6 @@ spin2.pass: spin1.pass  spin3.pass: spin2.pass  spin4.pass: spin3.pass  tsd1.pass: barrier5.pass join1.pass +tsd2.pass: tsd1.pass  valid1.pass: join1.pass  valid2.pass: valid1.pass diff --git a/tests/Wmakefile b/tests/Wmakefile index c8db27a..4f003df 100644 --- a/tests/Wmakefile +++ b/tests/Wmakefile @@ -32,7 +32,7 @@  # -DLL_VER	= 1 +DLL_VER	= 2  .EXTENSIONS: diff --git a/tests/semaphore1.c b/tests/semaphore1.c index 4327e73..f89a430 100644 --- a/tests/semaphore1.c +++ b/tests/semaphore1.c @@ -86,7 +86,8 @@ thr(void * arg)    if ( result == -1 )    {      int err = errno; -    perror("thread: sem_trywait 1: expect an EAGAIN error"); // No error +    printf("thread: sem_trywait 1: expecting error %s: got %s\n", +	   error_string[EAGAIN], error_string[err]); fflush(stdout);      assert(err == EAGAIN);    }    else @@ -131,7 +132,8 @@ main()    if ( result == -1 )    {      int err = errno; -    perror("main: sem_trywait 1: expect an EAGAIN error"); // No error +    printf("main: sem_trywait 1: expecting error %s: got %s\n", +	   error_string[EAGAIN], error_string[err]); fflush(stdout);      assert(err == EAGAIN);    }    else diff --git a/tests/tsd2.c b/tests/tsd2.c new file mode 100644 index 0000000..d1f50cd --- /dev/null +++ b/tests/tsd2.c @@ -0,0 +1,215 @@ +/* + * tsd2.c + * + * Test Thread Specific Data (TSD) key creation and destruction. + * + * + * -------------------------------------------------------------------------- + * + *      Pthreads-win32 - POSIX Threads Library for Win32 + *      Copyright(C) 1998 John E. Bossom + *      Copyright(C) 1999,2005 Pthreads-win32 contributors + *  + *      Contact Email: rpj@callisto.canberra.edu.au + *  + *      The current list of contributors is contained + *      in the file CONTRIBUTORS included with the source + *      code distribution. The list can also be seen at the + *      following World Wide Web location: + *      http://sources.redhat.com/pthreads-win32/contributors.html + *  + *      This library is free software; you can redistribute it and/or + *      modify it under the terms of the GNU Lesser General Public + *      License as published by the Free Software Foundation; either + *      version 2 of the License, or (at your option) any later version. + *  + *      This library is distributed in the hope that it will be useful, + *      but WITHOUT ANY WARRANTY; without even the implied warranty of + *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + *      Lesser General Public License for more details. + *  + *      You should have received a copy of the GNU Lesser General Public + *      License along with this library in the file COPYING.LIB; + *      if not, write to the Free Software Foundation, Inc., + *      59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + * + * + * -------------------------------------------------------------------------- + * + * Description: + * -  + * + * Test Method (validation or falsification): + * - validation + * + * Requirements Tested: + * - keys are created for each existing thread including the main thread + * - keys are created for newly created threads + * - keys are thread specific + * - destroy routine is called on each thread exit including the main thread + * + * Features Tested: + * -  + * + * Cases Tested: + * -  + * + * Environment: + * -  + * + * Input: + * - none + * + * Output: + * - text to stdout + * + * Assumptions: + * - already validated:     pthread_create() + *                          pthread_once() + * - main thread also has a POSIX thread identity + * + * Pass Criteria: + * - stdout matches file reference/tsd1.out + * + * Fail Criteria: + * - fails to match file reference/tsd1.out + * - output identifies failed component + */ + +#include <sched.h> +#include "test.h" + +enum { +  NUM_THREADS = 100 +}; + +static pthread_key_t key = NULL; +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) +{ +  int * j = (int *) arg; + +  (*j)++; + +  /* Set TSD key from the destructor to test destructor iteration */ +  if (*j == 2) +    assert(pthread_setspecific(key, arg) == 0); +  else +    assert(*j == 3); + +  thread_destroyed[j - accesscount] = 1; +} + +static void +setkey(void * arg) +{ +  int * j = (int *) arg; + +  thread_set[j - accesscount] = 1; + +  assert(*j == 0); + +  assert(pthread_getspecific(key) == NULL); + +  assert(pthread_setspecific(key, arg) == 0); +  assert(pthread_setspecific(key, arg) == 0); +  assert(pthread_setspecific(key, arg) == 0); + +  assert(pthread_getspecific(key) == arg); + +  (*j)++; + +  assert(*j == 1); +} + +static void * +mythread(void * arg) +{ +  (void) pthread_barrier_wait(&startBarrier); + +  setkey(arg); + +  return 0; + +  /* Exiting the thread will call the key destructor. */ +} + +int +main() +{ +  int i; +  int fail = 0; +  pthread_t thread[NUM_THREADS]; + +  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); +    } + +  /* +   * 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. +   */ +  accesscount[0] = 0; +  setkey((void *) &accesscount[0]); + +  /* +   * Here we test that new threads will get a key created +   * for them. +   */ +  for (i = NUM_THREADS/2; i < NUM_THREADS; i++) +    { +      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 < NUM_THREADS; i++) +    { +	int result = 0; + +	assert(pthread_join(thread[i], (void **) &result) == 0); +    } + +  assert(pthread_key_delete(key) == 0); + +  assert(pthread_barrier_destroy(&startBarrier) == 0); + +  for (i = 1; i < NUM_THREADS; i++) +    { +	/* +	 * The counter is incremented once when the key is set to +	 * a value, and again when the key is destroyed. If the key +	 * doesn't get set for some reason then it will still be +	 * NULL and the destroy function will not be called, and +	 * hence accesscount will not equal 2. +	 */ +	if (accesscount[i] != 3) +	  { +	    fail++; +	    fprintf(stderr, "Thread %d key, set = %d, destroyed = %d\n", +			i, thread_set[i], thread_destroyed[i]); +	  } +    } + +  fflush(stderr); + +  return (fail); +} | 
