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); +} |