diff options
author | rpj <rpj> | 1998-10-14 03:06:39 +0000 |
---|---|---|
committer | rpj <rpj> | 1998-10-14 03:06:39 +0000 |
commit | 4ed54ca07b8115bd9e7813a1484d4c7936a25e70 (patch) | |
tree | dce1f613f881a7fa06b692ea316922473e3d3690 | |
parent | 7522a5a3b4f87f79534ae134a087d80e3e4bfa8a (diff) |
Mon Oct 12 00:00:44 1998 Ross Johnson <rpj@ixobrychus.canberra.edu.au>
* implement.h (_pthread_tsd_key_table): New.
* create.c (_pthread_start_call): Initialise per-thread TSD keys
to NULL.
* misc.c (pthread_once): Correct typo in comment.
* implement.h (_pthread_destructor_push): Remove.
(_pthread_destructor_pop): Remove.
(_pthread_destructor_run_all): Rename from _pthread_destructor_pop_all.
(_PTHREAD_TSD_KEY_DELETED): Add enum.
(_PTHREAD_TSD_KEY_INUSE): Add enum.
* cleanup.c (_pthread_destructor_push): Remove.
(_pthread_destructor_pop): Remove.
(_pthread_destructor_run_all): Totally revamped TSD.
* dll.c (_pthread_TSD_keys_TlsIndex): Initialise.
* tsd.c (pthread_setspecific): Totally revamped TSD.
(pthread_getspecific): Ditto.
(pthread_create): Ditto.
(pthread_delete): Ditto.
Sun Oct 11 22:44:55 1998 Ross Johnson <rpj@ixobrychus.canberra.edu.au>
* global.c (_pthread_tsd_key_table): Add new global.
* implement.h (_pthread_tsd_key_t and struct _pthread_tsd_key):
Add.
(struct _pthread): Remove destructorstack.
* cleanup.c (_pthread_destructor_run_all): Rename from
_pthread_destructor_pop_all. The key destructor stack was made
global rather than per-thread. No longer removes destructor nodes
from the stack. Comments updated.
-rw-r--r-- | ChangeLog | 39 | ||||
-rw-r--r-- | cleanup.c | 102 | ||||
-rw-r--r-- | create.c | 4 | ||||
-rw-r--r-- | dll.c | 13 | ||||
-rw-r--r-- | global.c | 5 | ||||
-rw-r--r-- | implement.h | 24 | ||||
-rw-r--r-- | misc.c | 2 | ||||
-rw-r--r-- | tsd.c | 86 |
8 files changed, 155 insertions, 120 deletions
@@ -1,3 +1,42 @@ +Mon Oct 12 00:00:44 1998 Ross Johnson <rpj@ixobrychus.canberra.edu.au> + + * implement.h (_pthread_tsd_key_table): New. + + * create.c (_pthread_start_call): Initialise per-thread TSD keys + to NULL. + + * misc.c (pthread_once): Correct typo in comment. + + * implement.h (_pthread_destructor_push): Remove. + (_pthread_destructor_pop): Remove. + (_pthread_destructor_run_all): Rename from _pthread_destructor_pop_all. + (_PTHREAD_TSD_KEY_DELETED): Add enum. + (_PTHREAD_TSD_KEY_INUSE): Add enum. + + * cleanup.c (_pthread_destructor_push): Remove. + (_pthread_destructor_pop): Remove. + (_pthread_destructor_run_all): Totally revamped TSD. + + * dll.c (_pthread_TSD_keys_TlsIndex): Initialise. + + * tsd.c (pthread_setspecific): Totally revamped TSD. + (pthread_getspecific): Ditto. + (pthread_create): Ditto. + (pthread_delete): Ditto. + +Sun Oct 11 22:44:55 1998 Ross Johnson <rpj@ixobrychus.canberra.edu.au> + + * global.c (_pthread_tsd_key_table): Add new global. + + * implement.h (_pthread_tsd_key_t and struct _pthread_tsd_key): + Add. + (struct _pthread): Remove destructorstack. + + * cleanup.c (_pthread_destructor_run_all): Rename from + _pthread_destructor_pop_all. The key destructor stack was made + global rather than per-thread. No longer removes destructor nodes + from the stack. Comments updated. + 1998-10-06 Ben Elliston <bje@cygnus.com> * condvar.c (cond_wait): Use POSIX, not Win32 mutex calls. @@ -120,112 +120,36 @@ _pthread_handler_pop_all(int stack, int execute) } } - -int -_pthread_destructor_push(void (* routine)(void *), pthread_key_t key) -{ - return _pthread_handler_push(_PTHREAD_DESTRUCTOR_STACK, - _PTHREAD_HANDLER_POP_LIFO, - routine, - (void *) key); -} - - -/* Remove all of the destructors associated with the key. */ -void -_pthread_destructor_pop(pthread_key_t key) -{ - _pthread_handler_node_t ** head; - _pthread_handler_node_t * current; - _pthread_handler_node_t * next; - - head = _PTHREAD_STACK(_PTHREAD_DESTRUCTOR_STACK); - current = *head; - - while (current != NULL) - { - next = current->next; - - /* The destructors associated key is in current->arg. */ - if (current->arg == (void *) key) - { - if (current == *head) - { - *head = next; - } - free(current); - } - current = next; - } -} - - -/* Run destructors for all non-NULL key values. - - FIXME: Currently we only run the destructors on the calling - thread's key values. The way I interpret POSIX semantics is that, - for each key that the calling thread has a destructor for, we need - to look at the key values of every thread and run the destructor on - it if the key value is non-NULL. - - The question is: how do we access the key associated values which - are private to other threads? - +/* Run destructors for all non-NULL key values for the calling thread. */ void -_pthread_destructor_pop_all() +_pthread_destructor_run_all() { - _pthread_handler_node_t ** head; - _pthread_handler_node_t * current; - _pthread_handler_node_t * next; - void (* func)(void *); + _pthread_tsd_key_t * k; void * arg; int count; - head = _PTHREAD_STACK(_PTHREAD_DESTRUCTOR_STACK); + k = _pthread_tsd_key_table; /* Stop destructor execution at a finite time. POSIX allows us to ignore this if we like, even at the risk of an infinite loop. */ for (count = 0; count < PTHREAD_DESTRUCTOR_ITERATIONS; count++) { - /* Loop through all destructors for this thread. */ - while (current != NULL) + /* Loop through all keys. */ + for (key = 0; key < _POSIX_THREAD_KEYS_MAX; key++) { - func = current->routine; - - /* Get the key value using the key which is in current->arg. */ - arg = pthread_getspecific((int) current->arg); + if (k->in_use != 1) + continue; - next = current->next; + arg = pthread_getspecific(key); - /* If the key value is non-NULL run the destructor, otherwise - unlink it from the list. - */ - if (arg != NULL) - { - if (func != NULL) - { - (void) func(arg); - } - } - else + if (arg != NULL && k->destructor != NULL) { - if (current == *head) - { - *head = next; - } - free(current); + (void) (k->destructor)(arg); } - current = next; - } - } - /* Free the destructor list even if we still have non-NULL key values. */ - while (*head != NULL) - { - next = (*head)->next; - free(*head); - *head = next; + k++; + } } } @@ -22,13 +22,17 @@ STDCALL _pthread_start_call(void * us_arg) this thread's private stack so we're safe to leave data in them until we leave. */ pthread_t us; + void * keys[PTHREAD_KEYS_MAX]; unsigned (*func)(void *); void * arg; unsigned ret; us = (pthread_t) us_arg; + memset(keys, 0, sizeof(keys)); + (void) TlsSetValue(_pthread_threadID_TlsIndex, (LPVOID) us); + (void) TlsSetValue(_pthread_TSD_keys_TlsIndex, (LPVOID) keys); /* FIXME: For now, if priority setting fails then at least ensure that our records reflect true reality. */ @@ -22,6 +22,10 @@ /* Global index for TLS data. */ DWORD _pthread_threadID_TlsIndex; +/* Global index for thread TSD key array. */ +DWORD _pthread_TSD_keys_TlsIndex; + + BOOL WINAPI PthreadsEntryPoint(HINSTANCE dllHandle, DWORD reason, LPVOID situation) @@ -42,9 +46,18 @@ BOOL WINAPI PthreadsEntryPoint(HINSTANCE dllHandle, { return FALSE; } + + /* Set up per thread TSD key array pointer. */ + _pthread_TSD_keys_TlsIndex = TlsAlloc(); + + if (_pthread_TSD_keys_TlsIndex == 0xFFFFFFFF) + { + return FALSE; + } break; case DLL_PROCESS_DETACH: + (void) TlsFree(_pthread_TSD_keys_TlsIndex); (void) TlsFree(_pthread_threadID_TlsIndex); break; @@ -57,3 +57,8 @@ pthread_t _pthread_win32handle_map[_PTHREAD_MAX_THREADS]; /* Per thread mutex locks. */ pthread_mutex_t _pthread_threads_mutex_table[_PTHREAD_MAX_THREADS]; +/* Global TSD key array. */ +_pthread_tsd_key_t _pthread_tsd_key_table[_POSIX_THREAD_KEYS_MAX]; + +/* Index to the next available TSD key. */ +int _pthread_tsd_key_next = 0; diff --git a/implement.h b/implement.h index bf55e91..a87edb2 100644 --- a/implement.h +++ b/implement.h @@ -19,6 +19,11 @@ enum { _PTHREAD_REUSE }; +enum { + _PTHREAD_TSD_KEY_DELETED, + _PTHREAD_TSD_KEY_INUSE +}; + #define _PTHREAD_VALID(T) \ ((T) != NULL \ && ((T)->ptstatus == _PTHREAD_NEW \ @@ -40,6 +45,14 @@ struct _pthread_handler_node { void * arg; }; +/* TSD key element. */ +typedef struct _pthread_tsd_key _pthread_tsd_key_t; + +struct _pthread_tsd_key { + int in_use; + void (* destructor)(void *); +}; + /* Stores a thread call routine and argument. */ typedef struct { unsigned (*routine)(void *); @@ -84,7 +97,6 @@ struct _pthread { /* These must be kept in this order and together. */ _pthread_handler_node_t * cleanupstack; - _pthread_handler_node_t * destructorstack; _pthread_handler_node_t * forkpreparestack; _pthread_handler_node_t * forkparentstack; _pthread_handler_node_t * forkchildstack; @@ -107,12 +119,7 @@ void _pthread_handler_pop(int stack, void _pthread_handler_pop_all(int stack, int execute); -int _pthread_destructor_push(void (*routine)(void *), - pthread_key_t key); - -void _pthread_destructor_pop(pthread_key_t key); - -void _pthread_destructor_pop_all(); +void _pthread_destructor_run_all(); /* Primitives to manage threads table entries. */ @@ -161,6 +168,9 @@ extern pthread_t _pthread_win32handle_map[]; /* Per thread mutex locks. */ extern pthread_mutex_t _pthread_threads_mutex_table[]; +/* Global TSD key array. */ +extern _pthread_tsd_key_t _pthread_tsd_key_table[]; + #endif /* _IMPLEMENT_H */ @@ -13,7 +13,7 @@ int pthread_once(pthread_once_t *once_control, void (*init_routine)(void)) { - /* A flag, allocated per invocation, that indicates if the amotic + /* A flag, allocated per invocation, that indicates if the atomic test-and-set occured. */ unsigned short flag = 0; @@ -12,14 +12,40 @@ * In a word: Destructors * * POSIX 1003.1 1996, Section 17 allows for optional destructor functions - * to be associated with each key value. The destructors are called from - * the creating thread, which means that the calling thread must have access - * to the TSD keys of all active threads. + * to be associated with each key value. * - * If we use Win32 TLS then this is not possible since Tls*Value() - * functions don't allow us to access other than our own [thread's] key. + * This is my (revised) understanding of how destructors work: * - * As a result, these routines need to be redesigned. + * 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> @@ -30,21 +56,17 @@ int pthread_key_create(pthread_key_t *key, void (*destructor)(void *)) { - DWORD index; + pthread_key_t k; + + if (_pthread_tsd_key_next >= PTHREAD_KEYS_MAX) + return EAGAIN; - index = TlsAlloc(); - if (index == 0xFFFFFFFF) - { - return EAGAIN; - } + k = _pthread_tsd_key_next++; - /* Only modify the `key' parameter if allocation was successful. */ - *key = index; + _pthread_tsd_key_table[k].in_use = _PTHREAD_TSD_KEY_INUSE; + _pthread_tsd_key_table[k].destructor = destructor; - if (destructor != NULL) - { - return (_pthread_destructor_push(destructor, *key)); - } + *key = k; return 0; } @@ -52,20 +74,38 @@ pthread_key_create(pthread_key_t *key, void (*destructor)(void *)) int pthread_setspecific(pthread_key_t key, void *value) { - return (TlsSetValue(key, value) == FALSE) ? EINVAL : 0; + void ** keys; + + if (_pthread_tsd_key_table[key].in_use != _PTHREAD_TSD_KEY_INUSE) + return EINVAL; + + keys = (void **) TlsGetValue(_pthread_TSD_keys_TlsIndex); + keys[key] = value; + + return 0; } void * pthread_getspecific(pthread_key_t key) { - return TlsGetValue(key); + void ** keys; + + if (_pthread_tsd_key_table[key].in_use != _PTHREAD_TSD_KEY_INUSE) + return EINVAL; + + keys = (void **) TlsGetValue(_pthread_TSD_keys_TlsIndex); + return keys[key]; } int pthread_key_delete(pthread_key_t key) { - /* Remove this key's destructors. */ - _pthread_destructor_pop(key); + if (_pthread_tsd_key_table[key].in_use != _PTHREAD_TSD_KEY_INUSE) + return EINVAL; - return (TlsFree(key) == FALSE) ? EINVAL : 0; + _pthread_tsd_key_table[key].in_use = _PTHREAD_TSD_KEY_DELETED; + _pthread_tsd_key_table[key].destructor = NULL; + + return 0; } + |