diff options
| author | rpj <rpj> | 1999-01-03 18:47:50 +0000 | 
|---|---|---|
| committer | rpj <rpj> | 1999-01-03 18:47:50 +0000 | 
| commit | 36f0ed4155fdab7b12c5c5ddf4252170fac0a77e (patch) | |
| tree | 2d8ed62df0b72d42a74b383d389ee7c28a0324da /tsd.c | |
| parent | 4650bcf1f1efd88a0c8f502c28945bfabd7ef6db (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.c | 436 | 
1 files changed, 270 insertions, 166 deletions
| @@ -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> */ + | 
