From 4ed54ca07b8115bd9e7813a1484d4c7936a25e70 Mon Sep 17 00:00:00 2001 From: rpj Date: Wed, 14 Oct 1998 03:06:39 +0000 Subject: Mon Oct 12 00:00:44 1998 Ross Johnson * 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 * 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. --- tsd.c | 86 +++++++++++++++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 63 insertions(+), 23 deletions(-) (limited to 'tsd.c') diff --git a/tsd.c b/tsd.c index 015af85..8e53b37 100644 --- a/tsd.c +++ b/tsd.c @@ -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 @@ -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; } + -- cgit v1.2.3