summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ANNOUNCE2
-rw-r--r--ChangeLog9
-rw-r--r--NEWS17
-rw-r--r--implement.h10
-rw-r--r--manual/pthread_key_create.html2
-rw-r--r--pthread_key_delete.c47
-rw-r--r--pthread_mutex_lock.c4
-rw-r--r--pthread_setspecific.c2
-rw-r--r--ptw32_callUserDestroyRoutines.c156
-rw-r--r--ptw32_tkAssocCreate.c23
-rw-r--r--ptw32_tkAssocDestroy.c24
-rw-r--r--tests/Bmakefile3
-rw-r--r--tests/GNUmakefile6
-rw-r--r--tests/Makefile3
-rw-r--r--tests/Wmakefile2
-rw-r--r--tests/semaphore1.c6
-rw-r--r--tests/tsd2.c215
17 files changed, 426 insertions, 105 deletions
diff --git a/ANNOUNCE b/ANNOUNCE
index 6db7c41..4c2eea4 100644
--- a/ANNOUNCE
+++ b/ANNOUNCE
@@ -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
diff --git a/ChangeLog b/ChangeLog
index b1ae8c3..074b340 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -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
diff --git a/NEWS b/NEWS
index a4b3809..82358ed 100644
--- a/NEWS
+++ b/NEWS
@@ -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);
+}