summaryrefslogtreecommitdiff
path: root/tsd.c
diff options
context:
space:
mode:
authorrpj <rpj>1999-01-03 18:47:50 +0000
committerrpj <rpj>1999-01-03 18:47:50 +0000
commit36f0ed4155fdab7b12c5c5ddf4252170fac0a77e (patch)
tree2d8ed62df0b72d42a74b383d389ee7c28a0324da /tsd.c
parent4650bcf1f1efd88a0c8f502c28945bfabd7ef6db (diff)
Merge John Bossom's code into the main trunk. See ChangeLog for details.snapshot-1999-01-04-1305
This will be tagged as snapshot-1999-01-04-1305
Diffstat (limited to 'tsd.c')
-rw-r--r--tsd.c436
1 files changed, 270 insertions, 166 deletions
diff --git a/tsd.c b/tsd.c
index cc0b98e..72a03ea 100644
--- a/tsd.c
+++ b/tsd.c
@@ -5,202 +5,306 @@
* POSIX thread functions which implement thread-specific data (TSD).
*/
-/*
- * Why we can't use Win32 TLS
- * --------------------------
- *
- * In a word: Destructors
- *
- * POSIX 1003.1 1996, Section 17 allows for optional destructor functions
- * to be associated with each key value.
- *
- * This is my (revised) understanding of how destructors work:
- *
- * A key is created by a single thread, which then provides in every
- * existing thread a TSD matching the same key, but initialised
- * to NULL. Each new thread will also get a matching key with value NULL.
- * The creating thread can optionally associate a function, called a
- * destructor, with the key.
- *
- * When each thread exits, it calls the destructor function, which
- * will then perform an action on that threads key value
- * only. (Previously I thought that only the key creating thread ran
- * the destructor on the key in all threads. That proposition is
- * sounding scarier by the minute.)
- *
- * SOME APPROACHES TO MANAGING TSD MEMORY
- *
- * We could simply allocate enough memory on process startup to hold
- * all possible data for all possible threads.
- *
- * We could allocate memory for just a table to hold a single pointer
- * for each of POSIX_THREAD_KEYS_MAX keys. pthread_key_create() could then
- * allocate space for POSIX_THREADS_MAX key values in one hit and store
- * the location of the array in the first table.
- *
- * The standard also suggests that each thread might store key/value pairs
- * on its private stack. This seems like a good idea. I had concerns about
- * memory leaks and key re-use if a key was deleted, but the standard talks
- * at length on this and basically says it's up to the application to
- * make sure everything goes smoothly here, making sure that proper cleanup
- * is done before a key is deleted. (section B.17.1.3 in particular)
- *
- * One more thing to note: destructors must never be called on deleted keys.
- */
-
-#include <errno.h>
-
#include "pthread.h"
#include "implement.h"
+/*
+ * Code contributed by John E. Bossom <JEB>.
+ */
+
int
-pthread_key_create(pthread_key_t *key, void (*destructor)(void *))
+pthread_key_create (pthread_key_t * key, void (*destructor) (void *))
+ /*
+ * ------------------------------------------------------
+ * DOCPUBLIC
+ * This function creates a thread-specific data key visible
+ * to all threads. All existing and new threads have a value
+ * NULL for key until set using pthread_setspecific. When any
+ * thread with a non-NULL value for key terminates, 'destructor'
+ * is called with key's current value for that thread.
+ *
+ * PARAMETERS
+ * key
+ * pointer to an instance of pthread_key_t
+ *
+ *
+ * DESCRIPTION
+ * This function creates a thread-specific data key visible
+ * to all threads. All existing and new threads have a value
+ * NULL for key until set using pthread_setspecific. When any
+ * thread with a non-NULL value for key terminates, 'destructor'
+ * is called with key's current value for that thread.
+ *
+ * RESULTS
+ * 0 successfully created semaphore,
+ * EAGAIN insufficient resources or PTHREAD_KEYS_MAX
+ * exceeded,
+ * ENOMEM insufficient memory to create the key,
+ *
+ * ------------------------------------------------------
+ */
{
- pthread_key_t k;
- int ret = 0;
-
- /* CRITICAL SECTION */
- pthread_mutex_lock(&_pthread_tsd_mutex);
-
- if (_pthread_key_reuse_top >= 0)
+ int result = 0;
+
+ if ((*key = (pthread_key_t) calloc (1, sizeof (**key))) == NULL)
{
- k = _pthread_key_reuse[_pthread_key_reuse_top--];
+ result = ENOMEM;
}
- else
+ else if (((*key)->key = TlsAlloc ()) == TLS_OUT_OF_INDEXES)
{
- if (_pthread_key_virgin_next < PTHREAD_KEYS_MAX)
- {
- k = _pthread_key_virgins[_pthread_key_virgin_next++];
- }
- else
- {
- return EAGAIN;
- }
- }
-
- /* FIXME: This needs to be implemented as a list plus a re-use stack as for
- thread IDs. _pthread_destructor_run_all() then needs to be changed
- to push keys onto the re-use stack.
- */
-
- _pthread_tsd_key_table[k].in_use = 0;
- _pthread_tsd_key_table[k].status = _PTHREAD_TSD_KEY_INUSE;
- _pthread_tsd_key_table[k].destructor = destructor;
-
- pthread_mutex_unlock(&_pthread_tsd_mutex);
- /* END CRITICAL SECTION */
-
- *key = k;
-
- return ret;
-}
-
-int
-pthread_setspecific(pthread_key_t key, void *value)
-{
- void ** keys;
- int inuse;
-
- /* CRITICAL SECTION */
- pthread_mutex_lock(&_pthread_tsd_mutex);
-
- inuse = (_pthread_tsd_key_table[key].status == _PTHREAD_TSD_KEY_INUSE);
-
- pthread_mutex_unlock(&_pthread_tsd_mutex);
- /* END CRITICAL SECTION */
-
- if (! inuse)
- return EINVAL;
-
- keys = (void **) TlsGetValue(_pthread_TSD_keys_TlsIndex);
+ /*
+ * Create system key
+ */
+ result = EAGAIN;
- if (keys[key] != NULL)
- {
- if (value == NULL)
- {
- /* Key is no longer in use by this thread. */
- _pthread_tsd_key_table[key].in_use--;
- }
+ free (*key);
+ *key = NULL;
}
- else
+ else if (destructor != NULL)
{
- if (value != NULL)
- {
- /* Key is now in use by this thread. */
- _pthread_tsd_key_table[key].in_use++;
- }
+ /*
+ * Have to manage associations between thread and key;
+ * Therefore, need a lock that allows multiple threads
+ * to gain exclusive access to the key->threads list
+ */
+ result = pthread_mutex_init (&((*key)->threadsLock), NULL);
+
+ if (result != 0)
+ {
+ TlsFree ((*key)->key);
+
+ free (*key);
+ *key = NULL;
+ }
+ (*key)->destructor = destructor;
}
- keys[key] = value;
-
- return 0;
+ return (result);
}
-void *
-pthread_getspecific(pthread_key_t key)
+int
+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
+ * for any thread and does not run the key's destructor
+ * in any thread so it should be used with caution.
+ *
+ * PARAMETERS
+ * key
+ * pointer to an instance of pthread_key_t
+ *
+ *
+ * DESCRIPTION
+ * This function deletes a thread-specific data key. This
+ * does not change the value of the thread spcific data key
+ * for any thread and does not run the key's destructor
+ * in any thread so it should be used with caution.
+ *
+ * RESULTS
+ * 0 successfully deleted the key,
+ * EINVAL key is invalid,
+ *
+ * ------------------------------------------------------
+ */
{
- void ** keys;
- int inuse;
-
- /* CRITICAL SECTION */
- pthread_mutex_lock(&_pthread_tsd_mutex);
-
- inuse = (_pthread_tsd_key_table[key].status == _PTHREAD_TSD_KEY_INUSE);
+ int result = 0;
- pthread_mutex_unlock(&_pthread_tsd_mutex);
- /* END CRITICAL SECTION */
-
- if (! inuse)
- return (void *) NULL;
+ if (key != NULL)
+ {
+ if (key->threads != NULL &&
+ pthread_mutex_lock (&(key->threadsLock)) == 0)
+ {
+ /*
+ * Run through all Thread<-->Key associations
+ * for this key.
+ * If the pthread_t still exits (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.
+ */
+ ThreadKeyAssoc *assoc;
+
+ assoc = (ThreadKeyAssoc *) key->threads;
+
+ while (assoc != NULL)
+ {
+ if (pthread_mutex_lock (&(assoc->lock)) == 0)
+ {
+ ThreadKeyAssoc *next;
+
+ assoc->key = NULL;
+ next = assoc->nextThread;
+ assoc->nextThread = NULL;
+
+ pthread_mutex_unlock (&(assoc->lock));
+
+ _pthread_tkAssocDestroy (assoc);
+
+ assoc = next;
+ }
+ }
+ pthread_mutex_unlock (&(key->threadsLock));
+ }
+
+ TlsFree (key->key);
+ if (key->destructor != NULL)
+ {
+ pthread_mutex_destroy (&(key->threadsLock));
+ }
+
+#if defined( _DEBUG )
+ memset ((char *) key, 0, sizeof (*key));
+#endif
+ free (key);
+ }
- keys = (void **) TlsGetValue(_pthread_TSD_keys_TlsIndex);
- return keys[key];
+ return (result);
}
-/*
- pthread_key_delete:
-
- ANSI/IEEE Std 1003.1, 1996 Edition
-
- Section 17.1.3.2
-
- This function deletes a thread-specific data key previously returned by
- pthread_key_create(). The thread specific data values associated with
- "key" need not be NULL at the time pthread_key_delete() is called. It is
- the responsibility of the application to free any application storage
- or perform any cleanup actions for data structures related to the deleted
- key or associated thread-specific data in any threads; this cleanup
- can be done either before or after pthread_key_delete() is called. Any
- attempt to use "key" following the call to pthread_key_delete()
- results in undefined behaviour.
-
- The pthread_key_delete() function shall be callable from within
- destructor functions. No destructor functions shall be invoked by
- pthread_key_delete(). Any destructor function that may have been associated
- with "key" shall no longer be called upon thread exit.
- */
int
-pthread_key_delete(pthread_key_t key)
+pthread_setspecific (pthread_key_t key, const void *value)
+ /*
+ * ------------------------------------------------------
+ * DOCPUBLIC
+ * This function initializes an unnamed semaphore. the
+ * initial value of the semaphore is 'value'
+ *
+ * PARAMETERS
+ * sem
+ * pointer to an instance of sem_t
+ *
+ *
+ * DESCRIPTION
+ * This function initializes an unnamed semaphore. The
+ * initial value of the semaphore is set to 'value'.
+ *
+ * RESULTS
+ * 0 successfully created semaphore,
+ * EINVAL 'sem' is not a valid semaphore,
+ * ENOSPC a required resource has been exhausted,
+ * ENOSYS semaphores are not supported,
+ * EPERM the process lacks appropriate privilege
+ *
+ * ------------------------------------------------------
+ */
{
- int ret = 0;
-
- /* CRITICAL SECTION */
- pthread_mutex_lock(&_pthread_tsd_mutex);
+ pthread_t self;
+ int result = 0;
- if (_pthread_tsd_key_table[key].status != _PTHREAD_TSD_KEY_INUSE)
+ if (key != _pthread_selfThreadKey)
{
- ret = EINVAL;
+ /*
+ * Using pthread_self will implicitly create
+ * an instance of pthread_t for the current
+ * thread if one wasn't explicitly created
+ */
+ self = pthread_self ();
}
else
{
- _pthread_tsd_key_table[key].status = _PTHREAD_TSD_KEY_DELETED;
- _pthread_tsd_key_table[key].destructor = NULL;
+ /*
+ * Resolve catch-22 of registering thread with threadSelf
+ * key
+ */
+ self = pthread_getspecific (_pthread_selfThreadKey);
+ if (self == NULL)
+ {
+ self = (pthread_t) value;
+ }
+ }
+
+ result = 0;
+
+ if (key != NULL)
+ {
+ ThreadKeyAssoc *assoc;
+
+ if (self != NULL &&
+ key->destructor != NULL &&
+ value != NULL)
+ {
+ /*
+ * Only require associations if we have to
+ * call user destroy routine.
+ * Don't need to locate an existing association
+ * when setting data to NULL for WIN32 since the
+ * data is stored with the operating system; not
+ * on the association; setting assoc to NULL short
+ * circuits the search.
+ */
+ assoc = (ThreadKeyAssoc *) self->keys;
+ /*
+ * Locate existing association
+ */
+ while (assoc != NULL)
+ {
+ if (assoc->key == key)
+ {
+ /*
+ * Association already exists
+ */
+ break;
+ }
+ assoc = assoc->nextKey;
+ }
+
+ /*
+ * create an association if not found
+ */
+ result = (assoc == NULL)
+ ? _pthread_tkAssocCreate (&assoc, self, key)
+ : 0;
+ }
+ else
+ {
+ result = 0;
+ }
+
+ if (result == 0)
+ {
+ TlsSetValue (key->key, (LPVOID) value);
+ }
}
+ return (result);
+} /* pthread_setspecific */
- pthread_mutex_unlock(&_pthread_tsd_mutex);
- /* END CRITICAL SECTION */
- return ret;
+void *
+pthread_getspecific (pthread_key_t key)
+ /*
+ * ------------------------------------------------------
+ * DOCPUBLIC
+ * This function returns the current value of key in the
+ * calling thread. If no value has been set for 'key' in
+ * the thread, NULL is returned.
+ *
+ * PARAMETERS
+ * key
+ * an instance of pthread_key_t
+ *
+ *
+ * DESCRIPTION
+ * This function returns the current value of key in the
+ * calling thread. If no value has been set for 'key' in
+ * the thread, NULL is returned.
+ *
+ * RESULTS
+ * key value
+ *
+ * ------------------------------------------------------
+ */
+{
+ return (TlsGetValue (key->key));
}
+/* </JEB> */
+