diff options
Diffstat (limited to 'generic/tclThreadStorage.c')
-rw-r--r-- | generic/tclThreadStorage.c | 987 |
1 files changed, 544 insertions, 443 deletions
diff --git a/generic/tclThreadStorage.c b/generic/tclThreadStorage.c index 81c1e61..bf41adb 100644 --- a/generic/tclThreadStorage.c +++ b/generic/tclThreadStorage.c @@ -8,7 +8,7 @@ * See the file "license.terms" for information on usage and redistribution * of this file, and for a DISCLAIMER OF ALL WARRANTIES. * - * RCS: @(#) $Id: tclThreadStorage.c,v 1.2 2004/06/24 04:13:30 dgp Exp $ + * RCS: @(#) $Id: tclThreadStorage.c,v 1.3 2004/06/24 08:58:40 dkf Exp $ */ #include "tclInt.h" @@ -23,21 +23,24 @@ * the number of hash table lookups in the master thread storage hash * table. */ + static Tcl_Mutex threadStorageLock; /* * This is the struct used for a thread storage cache slot. It contains * the owning thread Id and the associated hash table pointer. */ + typedef struct ThreadStorage { - Tcl_ThreadId id; /* the owning thread id */ - Tcl_HashTable *hashTablePtr; /* the hash table for the thread */ + Tcl_ThreadId id; /* the owning thread id */ + Tcl_HashTable *hashTablePtr; /* the hash table for the thread */ } ThreadStorage; /* * These are the prototypes for the custom hash table allocation * functions used by the thread storage subsystem. */ + static Tcl_HashEntry * AllocThreadStorageEntry _ANSI_ARGS_(( Tcl_HashTable *tablePtr, void *keyPtr)); static void FreeThreadStorageEntry _ANSI_ARGS_(( @@ -49,41 +52,46 @@ static void FreeThreadStorageEntry _ANSI_ARGS_(( * because these hash tables MAY be used by the threaded memory allocator. */ Tcl_HashKeyType tclThreadStorageHashKeyType = { - TCL_HASH_KEY_TYPE_VERSION, /* version */ - TCL_HASH_KEY_SYSTEM_HASH, /* flags */ - NULL, /* hashKeyProc */ - NULL, /* compareKeysProc */ - AllocThreadStorageEntry, /* allocEntryProc */ - FreeThreadStorageEntry /* freeEntryProc */ + TCL_HASH_KEY_TYPE_VERSION, /* version */ + TCL_HASH_KEY_SYSTEM_HASH, /* flags */ + NULL, /* hashKeyProc */ + NULL, /* compareKeysProc */ + AllocThreadStorageEntry, /* allocEntryProc */ + FreeThreadStorageEntry /* freeEntryProc */ }; /* * This is an invalid thread value. */ + #define STORAGE_INVALID_THREAD (Tcl_ThreadId)0 /* * This is the value for an invalid thread storage key. */ -#define STORAGE_INVALID_KEY 0 + +#define STORAGE_INVALID_KEY 0 /* * This is the first valid key for use by external callers. * All the values below this are RESERVED for future use. */ -#define STORAGE_FIRST_KEY 101 + +#define STORAGE_FIRST_KEY 101 /* * This is the default number of thread storage cache slots. * This define may need to be fine tuned for maximum performance. */ -#define STORAGE_CACHE_SLOTS 97 + +#define STORAGE_CACHE_SLOTS 97 /* * This is the master thread storage hash table. It is keyed on * thread Id and contains values that are hash tables for each thread. * The thread specific hash tables contain the actual thread storage. */ + static Tcl_HashTable *threadStorageHashTablePtr = NULL; /* @@ -91,11 +99,13 @@ static Tcl_HashTable *threadStorageHashTablePtr = NULL; * everytime we "allocate" one. It is initially set to 1 in * TclThreadStorageInit. */ + static int nextThreadStorageKey = STORAGE_INVALID_KEY; /* * Have we initialized the thread storage mutex yet? */ + static int initThreadStorage = 0; /* @@ -103,23 +113,23 @@ static int initThreadStorage = 0; * prevents unnecessary lookups for threads that use a lot of thread * storage. */ -static volatile ThreadStorage threadStorageCache[STORAGE_CACHE_SLOTS]; +static volatile ThreadStorage threadStorageCache[STORAGE_CACHE_SLOTS]; /* *---------------------------------------------------------------------- * * TclThreadStorageLockInit * - * This procedure is used to initialize the lock that serializes - * creation of thread storage. + * This procedure is used to initialize the lock that serializes + * creation of thread storage. * * Results: - * None. + * None. * * Side effects: - * The master lock is acquired and possibly initialized for the - * first time. + * The master lock is acquired and possibly initialized for the + * first time. * *---------------------------------------------------------------------- */ @@ -128,12 +138,12 @@ void TclThreadStorageLockInit() { if (!initThreadStorage) { - /* - * Mutexes in Tcl are self initializing, and we are taking - * advantage of that fact since this file cannot contain - * platform specific calls. - */ - initThreadStorage = 1; + /* + * Mutexes in Tcl are self initializing, and we are taking + * advantage of that fact since this file cannot contain + * platform specific calls. + */ + initThreadStorage = 1; } } @@ -142,17 +152,17 @@ TclThreadStorageLockInit() * * TclThreadStorageLock * - * This procedure is used to grab a lock that serializes creation - * of thread storage. + * This procedure is used to grab a lock that serializes creation + * of thread storage. * - * This lock must be different than the initLock because the - * initLock is held during creation of syncronization objects. + * This lock must be different than the initLock because the + * initLock is held during creation of syncronization objects. * * Results: - * None. + * None. * * Side effects: - * Acquire the thread storage mutex. + * Acquire the thread storage mutex. * *---------------------------------------------------------------------- */ @@ -169,14 +179,14 @@ TclThreadStorageLock() * * TclThreadStorageUnlock * - * This procedure is used to release a lock that serializes creation - * of thread storage. + * This procedure is used to release a lock that serializes creation + * of thread storage. * * Results: - * None. + * None. * * Side effects: - * Release the thread storage mutex. + * Release the thread storage mutex. * *---------------------------------------------------------------------- */ @@ -192,15 +202,15 @@ TclThreadStorageUnlock() * * AllocThreadStorageEntry -- * - * Allocate space for a Tcl_HashEntry using TclpSysAlloc (not - * ckalloc). We do this because the threaded memory allocator MAY - * use the thread storage hash tables. + * Allocate space for a Tcl_HashEntry using TclpSysAlloc (not + * ckalloc). We do this because the threaded memory allocator MAY + * use the thread storage hash tables. * * Results: - * The return value is a pointer to the created entry. + * The return value is a pointer to the created entry. * * Side effects: - * None. + * None. * *---------------------------------------------------------------------- */ @@ -223,15 +233,15 @@ AllocThreadStorageEntry(tablePtr, keyPtr) * * FreeThreadStorageEntry -- * - * Frees space for a Tcl_HashEntry using TclpSysFree (not ckfree). - * We do this because the threaded memory allocator MAY use the - * thread storage hash tables. + * Frees space for a Tcl_HashEntry using TclpSysFree (not ckfree). + * We do this because the threaded memory allocator MAY use the + * thread storage hash tables. * * Results: - * None. + * None. * * Side effects: - * None. + * None. * *---------------------------------------------------------------------- */ @@ -248,88 +258,96 @@ FreeThreadStorageEntry(hPtr) * * TclThreadStoragePrint -- * - * This procedure prints out the contents of the master thread - * storage hash table, the thread storage cache, and the next key - * value to the specified file. + * This procedure prints out the contents of the master thread + * storage hash table, the thread storage cache, and the next key + * value to the specified file. * - * This assumes that thread storage lock is held. + * This assumes that thread storage lock is held. * * Results: - * None. + * None. * * Side effects: - * The thread storage lock is acquired and released. + * The thread storage lock is acquired and released. * *---------------------------------------------------------------------- */ void TclThreadStoragePrint(outFile, flags) - FILE *outFile; /* The file to print the information to. */ - int flags; /* Reserved for future use. */ + FILE *outFile; /* The file to print the information to. */ + int flags; /* Reserved for future use. */ { Tcl_HashEntry *hPtr; Tcl_HashSearch search; -#if 0 - /* Please see comment regarding Tcl_HashStats below. */ - CONST char *stats; -#endif - int header; - int index; + int header, index; if (threadStorageHashTablePtr != NULL) { - hPtr = Tcl_FirstHashEntry(threadStorageHashTablePtr, &search); - - if (hPtr != NULL) { - fprintf(outFile, "master thread storage hash table:\n"); - for (; hPtr != NULL; hPtr = Tcl_NextHashEntry(&search)) { - fprintf(outFile, "master entry ptr %p, thread %p, thread table ptr %p\n", - hPtr, Tcl_GetHashKey(threadStorageHashTablePtr, hPtr), Tcl_GetHashValue(hPtr)); - } - } else { - fprintf(outFile, "master thread storage hash table has no entries\n"); - } + hPtr = Tcl_FirstHashEntry(threadStorageHashTablePtr, &search); + + if (hPtr != NULL) { + fprintf(outFile, "master thread storage hash table:\n"); + for (; hPtr != NULL; hPtr = Tcl_NextHashEntry(&search)) { + fprintf(outFile, + "master entry ptr %p, thread %p, thread table ptr %p\n", + hPtr, Tcl_GetHashKey(threadStorageHashTablePtr, hPtr), + Tcl_GetHashValue(hPtr)); + } + } else { + fprintf(outFile, + "master thread storage hash table has no entries\n"); + } } else { - fprintf(outFile, "master thread storage hash table not initialized\n"); + fprintf(outFile, + "master thread storage hash table not initialized\n"); } header = 0; /* we have not output the header yet. */ for (index = 0; index < STORAGE_CACHE_SLOTS; index++) { - if (threadStorageCache[index].id != STORAGE_INVALID_THREAD) { - if (!header) { - fprintf(outFile, "thread storage cache (%d total slots):\n", STORAGE_CACHE_SLOTS); - header = 1; - } - - fprintf(outFile, "slot %d, thread %p, thread table ptr %p\n", - index, threadStorageCache[index].id, - threadStorageCache[index].hashTablePtr); -#if 0 - /* - * Currently disabled due to Tcl_HashStats use of ckalloc and ckfree. - * Please note that this can produce a LOT of output. - */ - if (threadStorageCache[index].hashTablePtr != NULL) { - stats = Tcl_HashStats(threadStorageCache[index].hashTablePtr); - if (stats != NULL) { - fprintf(outFile, "%s\n", stats); - ckfree((void *)stats); - } else { - fprintf(outFile, "could not get table statistics for slot %d\n", index); - } - } + if (threadStorageCache[index].id != STORAGE_INVALID_THREAD) { + if (!header) { + fprintf(outFile, "thread storage cache (%d total slots):\n", + STORAGE_CACHE_SLOTS); + header = 1; + } + + fprintf(outFile, "slot %d, thread %p, thread table ptr %p\n", + index, threadStorageCache[index].id, + threadStorageCache[index].hashTablePtr); +#ifdef VERBOSE_THREAD_STORAGE_DEBUGGING + /* + * Currently not enabled by default due to Tcl_HashStats + * use of ckalloc and ckfree. Please note that this can + * produce a LOT of output. + */ + if (threadStorageCache[index].hashTablePtr != NULL) { + CONST char *stats = + Tcl_HashStats(threadStorageCache[index].hashTablePtr); + if (stats != NULL) { + fprintf(outFile, "%s\n", stats); + ckfree((void *)stats); + } else { + fprintf(outFile, + "could not get table statistics for slot %d\n", + index); + } + } #endif - } else { - /* fprintf(outFile, "cache slot %d not used\n", index); */ - } + } else { + /* fprintf(outFile, "cache slot %d not used\n", index); */ + } } if (!header) { - fprintf(outFile, "thread storage cache is empty (%d total slots)\n", STORAGE_CACHE_SLOTS); - header = 1; + fprintf(outFile, "thread storage cache is empty (%d total slots)\n", + STORAGE_CACHE_SLOTS); + header = 1; } - /* Show the next data key value. */ + /* + * Show the next data key value. + */ + fprintf(outFile, "next data key value is: %d\n", nextThreadStorageKey); } @@ -338,25 +356,25 @@ TclThreadStoragePrint(outFile, flags) * * TclThreadStorageGetHashTable -- * - * This procedure returns a hash table pointer to be used for thread - * storage for the specified thread. + * This procedure returns a hash table pointer to be used for thread + * storage for the specified thread. * - * Results: - * A hash table pointer for the specified thread, or NULL - * if the hash table has not been created yet. + * This assumes that thread storage lock is held. * - * This assumes that thread storage lock is held. + * Results: + * A hash table pointer for the specified thread, or NULL + * if the hash table has not been created yet. * * Side effects: - * May change an entry in the master thread storage cache to point - * to the specified thread and it's associated hash table. + * May change an entry in the master thread storage cache to point + * to the specified thread and it's associated hash table. * *---------------------------------------------------------------------- */ Tcl_HashTable * TclThreadStorageGetHashTable(id) - Tcl_ThreadId id; /* Id of the thread to get the hash table for */ + Tcl_ThreadId id; /* Id of thread to get hash table for */ { int index = (unsigned int)id % STORAGE_CACHE_SLOTS; Tcl_HashEntry *hPtr; @@ -367,54 +385,79 @@ TclThreadStorageGetHashTable(id) * comparing thread Id in case another thread is in the critical * region changing things out from under you. */ + Tcl_HashTable *hashTablePtr = threadStorageCache[index].hashTablePtr; if (threadStorageCache[index].id != id) { - TclThreadStorageLock(); - - /* make sure the master hash table is initialized. */ - TclThreadStorageInit(STORAGE_INVALID_THREAD, NULL); - - if (threadStorageHashTablePtr != NULL) { - /* it's not in the cache, so we look it up... */ - hPtr = Tcl_FindHashEntry(threadStorageHashTablePtr, (char *)id); - - if (hPtr != NULL) { - /* we found it, extract the hash table pointer. */ - hashTablePtr = Tcl_GetHashValue(hPtr); - } else { - /* the thread specific hash table is not found. */ - hashTablePtr = NULL; - } - - if (hashTablePtr == NULL) { - hashTablePtr = (Tcl_HashTable *)TclpSysAlloc(sizeof(Tcl_HashTable), 0); - - if (hashTablePtr != NULL) { - Tcl_InitCustomHashTable(hashTablePtr, TCL_CUSTOM_TYPE_KEYS, &tclThreadStorageHashKeyType); - - /* add new thread storage hash table to the master hash table */ - hPtr = Tcl_CreateHashEntry(threadStorageHashTablePtr, (char *)id, &new); - - if (hPtr != NULL) { - Tcl_SetHashValue(hPtr, hashTablePtr); - } else { - Tcl_Panic("Tcl_CreateHashEntry failed from TclThreadStorageInit!"); - } - } else { - Tcl_Panic("could not allocate thread specific hash table, TclpSysAlloc failed from TclThreadStorageGetHashTable!"); - } - } - - /* now, we put it in the cache since it is highly likely it will - be needed again shortly. */ - threadStorageCache[index].id = id; - threadStorageCache[index].hashTablePtr = hashTablePtr; - } else { - /* we cannot look it up, the master hash table has not been initialized. */ - hashTablePtr = NULL; - } - TclThreadStorageUnlock(); + TclThreadStorageLock(); + + /* + * Make sure the master hash table is initialized. + */ + + TclThreadStorageInit(STORAGE_INVALID_THREAD, NULL); + + if (threadStorageHashTablePtr != NULL) { + /* + * It's not in the cache, so we look it up... + */ + + hPtr = Tcl_FindHashEntry(threadStorageHashTablePtr, (char *)id); + + if (hPtr != NULL) { + /* + * We found it, extract the hash table pointer. + */ + hashTablePtr = Tcl_GetHashValue(hPtr); + } else { + /* + * The thread specific hash table is not found. + */ + hashTablePtr = NULL; + } + + if (hashTablePtr == NULL) { + hashTablePtr = (Tcl_HashTable *) + TclpSysAlloc(sizeof(Tcl_HashTable), 0); + + if (hashTablePtr == NULL) { + Tcl_Panic("could not allocate thread specific hash " + "table, TclpSysAlloc failed from " + "TclThreadStorageGetHashTable!"); + } + Tcl_InitCustomHashTable(hashTablePtr, TCL_CUSTOM_TYPE_KEYS, + &tclThreadStorageHashKeyType); + + /* + * Add new thread storage hash table to the master + * hash table. + */ + + hPtr = Tcl_CreateHashEntry(threadStorageHashTablePtr, + (char *)id, &new); + + if (hPtr == NULL) { + Tcl_Panic("Tcl_CreateHashEntry failed from " + "TclThreadStorageInit!"); + } + Tcl_SetHashValue(hPtr, hashTablePtr); + } + + /* + * Now, we put it in the cache since it is highly likely + * it will be needed again shortly. + */ + + threadStorageCache[index].id = id; + threadStorageCache[index].hashTablePtr = hashTablePtr; + } else { + /* + * We cannot look it up, the master hash table has not + * been initialized. + */ + hashTablePtr = NULL; + } + TclThreadStorageUnlock(); } return hashTablePtr; @@ -425,49 +468,59 @@ TclThreadStorageGetHashTable(id) * * TclThreadStorageInit -- * - * This procedure initializes a thread specific hash table for the - * current thread. It may also initialize the master hash table which - * stores all the thread specific hash tables. + * This procedure initializes a thread specific hash table for the + * current thread. It may also initialize the master hash table which + * stores all the thread specific hash tables. * - * This assumes that thread storage lock is held. + * This assumes that thread storage lock is held. * * Results: - * A hash table pointer for the specified thread, or NULL if we are - * be called to initialize the master hash table only. + * A hash table pointer for the specified thread, or NULL if we are + * be called to initialize the master hash table only. * * Side effects: - * The thread specific hash table may be initialized and added to the - * master hash table. + * The thread specific hash table may be initialized and added to the + * master hash table. * *---------------------------------------------------------------------- */ Tcl_HashTable * TclThreadStorageInit(id, reserved) - Tcl_ThreadId id; /* Id of the thread to get the hash table for */ - void *reserved; /* reserved for future use */ + Tcl_ThreadId id; /* Id of thread to get hash table for */ + void *reserved; /* reserved for future use */ { #if 0 /* #ifdef TCL_THREAD_STORAGE_DEBUG */ TclThreadStoragePrint(stderr, 0); #endif if (threadStorageHashTablePtr == NULL) { - /* looks like we haven't created the outer hash table yet - we can just do that now. */ - threadStorageHashTablePtr = (Tcl_HashTable *)TclpSysAlloc(sizeof(Tcl_HashTable), 0); - - if (threadStorageHashTablePtr != NULL) { - /* initialize the hash table */ - Tcl_InitCustomHashTable(threadStorageHashTablePtr, TCL_CUSTOM_TYPE_KEYS, &tclThreadStorageHashKeyType); - } else { - Tcl_Panic("could not allocate master thread storage hash table, TclpSysAlloc failed from TclThreadStorageInit!"); - } - - /* we also initialize the cache. */ - memset((ThreadStorage *)&threadStorageCache, 0, sizeof(ThreadStorage) * STORAGE_CACHE_SLOTS); - - /* now, we set the first value to be used for a thread data key. */ - nextThreadStorageKey = STORAGE_FIRST_KEY; + /* + * Looks like we haven't created the outer hash table yet we + * can just do that now. + */ + + threadStorageHashTablePtr = (Tcl_HashTable *) + TclpSysAlloc(sizeof(Tcl_HashTable), 0); + if (threadStorageHashTablePtr == NULL) { + Tcl_Panic("could not allocate master thread storage hash table, " + "TclpSysAlloc failed from TclThreadStorageInit!"); + } + Tcl_InitCustomHashTable(threadStorageHashTablePtr, + TCL_CUSTOM_TYPE_KEYS, &tclThreadStorageHashKeyType); + + /* + * We also initialize the cache. + */ + + memset((ThreadStorage *)&threadStorageCache, 0, + sizeof(ThreadStorage) * STORAGE_CACHE_SLOTS); + + /* + * Now, we set the first value to be used for a thread data key. + */ + + nextThreadStorageKey = STORAGE_FIRST_KEY; } return NULL; @@ -478,67 +531,67 @@ TclThreadStorageInit(id, reserved) * * TclThreadStorageDataKeyInit -- * - * This procedure initializes a thread specific data block key. - * Each thread has table of pointers to thread specific data. - * all threads agree on which table entry is used by each module. - * this is remembered in a "data key", that is just an index into - * this table. To allow self initialization, the interface - * passes a pointer to this key and the first thread to use - * the key fills in the pointer to the key. The key should be - * a process-wide static. + * This procedure initializes a thread specific data block key. + * Each thread has table of pointers to thread specific data. + * all threads agree on which table entry is used by each module. + * this is remembered in a "data key", that is just an index into + * this table. To allow self initialization, the interface + * passes a pointer to this key and the first thread to use + * the key fills in the pointer to the key. The key should be + * a process-wide static. * * Results: - * None. + * None. * * Side effects: - * Will allocate memory the first time this process calls for - * this key. In this case it modifies its argument - * to hold the pointer to information about the key. + * Will allocate memory the first time this process calls for + * this key. In this case it modifies its argument + * to hold the pointer to information about the key. * *---------------------------------------------------------------------- */ void TclThreadStorageDataKeyInit(keyPtr) - Tcl_ThreadDataKey *keyPtr; /* Identifier for the data chunk, - * really (int **) */ + Tcl_ThreadDataKey *keyPtr; /* Identifier for the data chunk, + * really (int **) */ { int *indexPtr; int newKey; if (*keyPtr == NULL) { - indexPtr = (int *)TclpSysAlloc(sizeof(int), 0); - - if (indexPtr != NULL) { - /* - * We must call this now to make sure - * that nextThreadStorageKey has a well defined - * value. - */ - TclThreadStorageLock(); - - /* make sure the master hash table is initialized. */ - TclThreadStorageInit(STORAGE_INVALID_THREAD, NULL); - - /* - * These data key values are sequentially - * assigned and we must use the storage - * lock to prevent serious problems here. - * Also note that the caller should - * NOT make any assumptions about the - * provided values. In particular, we may - * need to reserve some values in the - * future. - */ - newKey = nextThreadStorageKey++; - TclThreadStorageUnlock(); - - *indexPtr = newKey; - *keyPtr = (Tcl_ThreadDataKey)indexPtr; - TclRememberDataKey(keyPtr); - } else { - Tcl_Panic("TclpSysAlloc failed from TclThreadStorageDataKeyInit!"); /* this should also be a fatal error */ - } + indexPtr = (int *)TclpSysAlloc(sizeof(int), 0); + if (indexPtr == NULL) { + Tcl_Panic("TclpSysAlloc failed from TclThreadStorageDataKeyInit!"); + } + + /* + * We must call this now to make sure that + * nextThreadStorageKey has a well defined value. + */ + + TclThreadStorageLock(); + + /* + * Make sure the master hash table is initialized. + */ + + TclThreadStorageInit(STORAGE_INVALID_THREAD, NULL); + + /* + * These data key values are sequentially assigned and we must + * use the storage lock to prevent serious problems here. + * Also note that the caller should NOT make any assumptions + * about the provided values. In particular, we may need to + * reserve some values in the future. + */ + + newKey = nextThreadStorageKey++; + TclThreadStorageUnlock(); + + *indexPtr = newKey; + *keyPtr = (Tcl_ThreadDataKey)indexPtr; + TclRememberDataKey(keyPtr); } } @@ -547,46 +600,44 @@ TclThreadStorageDataKeyInit(keyPtr) * * TclThreadStorageDataKeyGet -- * - * This procedure returns a pointer to a block of thread local storage. + * This procedure returns a pointer to a block of thread local storage. * * Results: - * A thread-specific pointer to the data structure, or NULL - * if the memory has not been assigned to this key for this thread. + * A thread-specific pointer to the data structure, or NULL + * if the memory has not been assigned to this key for this thread. * * Side effects: - * None. + * None. * *---------------------------------------------------------------------- */ void * TclThreadStorageDataKeyGet(keyPtr) - Tcl_ThreadDataKey *keyPtr; /* Identifier for the data chunk, - * really (int **) */ + Tcl_ThreadDataKey *keyPtr; /* Identifier for the data chunk, + * really (int **) */ { int *indexPtr = *(int **)keyPtr; void *result; - Tcl_ThreadId id = Tcl_GetCurrentThread(); - Tcl_HashTable *hashTablePtr; - Tcl_HashEntry *hPtr; if (indexPtr == NULL) { - result = NULL; + return NULL; } else { - hashTablePtr = TclThreadStorageGetHashTable(id); - - if (hashTablePtr != NULL) { - hPtr = Tcl_FindHashEntry(hashTablePtr, (char *)*indexPtr); - - if (hPtr != NULL) { - result = (void *)Tcl_GetHashValue(hPtr); - } else { - result = NULL; - } - } else { - Tcl_Panic("TclThreadStorageGetHashTable failed from TclThreadStorageDataKeyGet!"); - result = NULL; - } + Tcl_HashTable *hashTablePtr = + TclThreadStorageGetHashTable(Tcl_GetCurrentThread();); + Tcl_HashEntry *hPtr; + + if (hashTablePtr != NULL) { + Tcl_Panic("TclThreadStorageGetHashTable failed from " + "TclThreadStorageDataKeyGet!"); + } + + hPtr = Tcl_FindHashEntry(hashTablePtr, (char *)*indexPtr); + + if (hPtr == NULL) { + return NULL; + } + return (void *)Tcl_GetHashValue(hPtr); } return result; } @@ -596,48 +647,49 @@ TclThreadStorageDataKeyGet(keyPtr) * * TclThreadStorageDataKeySet -- * - * This procedure sets the pointer to a block of thread local storage. + * This procedure sets the pointer to a block of thread local storage. * * Results: - * None. + * None. * * Side effects: - * Sets up the thread so future calls to TclThreadStorageDataKeyGet - * with this key will return the data pointer. + * Sets up the thread so future calls to TclThreadStorageDataKeyGet + * with this key will return the data pointer. * *---------------------------------------------------------------------- */ void TclThreadStorageDataKeySet(keyPtr, data) - Tcl_ThreadDataKey *keyPtr; /* Identifier for the data chunk, - * really (pthread_key_t **) */ - void *data; /* Thread local storage */ + Tcl_ThreadDataKey *keyPtr; /* Identifier for the data chunk, + * really (pthread_key_t **) */ + void *data; /* Thread local storage */ { int *indexPtr = *(int **)keyPtr; - Tcl_ThreadId id = Tcl_GetCurrentThread(); Tcl_HashTable *hashTablePtr; Tcl_HashEntry *hPtr; - int new; - - hashTablePtr = TclThreadStorageGetHashTable(id); - if (hashTablePtr != NULL) { - hPtr = Tcl_FindHashEntry(hashTablePtr, (char *)*indexPtr); + hashTablePtr = TclThreadStorageGetHashTable(Tcl_GetCurrentThread()); + if (hashTablePtr == NULL) { + Tcl_Panic("TclThreadStorageGetHashTable failed from " + "TclThreadStorageDataKeySet!"); + } - /* does the item need to be created? */ - if (hPtr == NULL) { - hPtr = Tcl_CreateHashEntry(hashTablePtr, (char *)*indexPtr, &new); - } + hPtr = Tcl_FindHashEntry(hashTablePtr, (char *)*indexPtr); - if (hPtr != NULL) { - Tcl_SetHashValue(hPtr, data); - } else { - Tcl_Panic("could not set hash entry value from TclThreadStorageDataKeySet"); - } - } else { - Tcl_Panic("TclThreadStorageGetHashTable failed from TclThreadStorageDataKeySet!"); + /* + * Does the item need to be created? + */ + if (hPtr == NULL) { + int new; + hPtr = Tcl_CreateHashEntry(hashTablePtr, (char *)*indexPtr, &new); + if (hPtr == NULL) { + Tcl_Panic("could not create hash entry value from " + "TclThreadStorageDataKeySet"); + } } + + Tcl_SetHashValue(hPtr, data); } /* @@ -645,56 +697,72 @@ TclThreadStorageDataKeySet(keyPtr, data) * * TclFinalizeThreadStorageThread -- * - * This procedure cleans up the thread storage hash table for the - * specified thread. + * This procedure cleans up the thread storage hash table for the + * specified thread. * * Results: - * None. + * None. * * Side effects: - * None. + * None. * *---------------------------------------------------------------------- */ + void TclFinalizeThreadStorageThread(id) - Tcl_ThreadId id; /* Id of the thread to finalize */ + Tcl_ThreadId id; /* Id of the thread to finalize */ { int index = (unsigned int)id % STORAGE_CACHE_SLOTS; - Tcl_HashTable *hashTablePtr; /* hash table for current thread */ - Tcl_HashEntry *hPtr; /* hash entry for current thread in master table */ + Tcl_HashTable *hashTablePtr; /* Hash table for current thread */ + Tcl_HashEntry *hPtr; /* Hash entry for current thread in master + * table */ TclThreadStorageLock(); + if (threadStorageHashTablePtr != NULL) { - hPtr = Tcl_FindHashEntry(threadStorageHashTablePtr, (char *)id); + hPtr = Tcl_FindHashEntry(threadStorageHashTablePtr, (char *)id); + + if (hPtr != NULL) { + /* + * We found it, extract the hash table pointer. + */ + + hashTablePtr = Tcl_GetHashValue(hPtr); - if (hPtr != NULL) { - /* we found it, extract the hash table pointer. */ - hashTablePtr = Tcl_GetHashValue(hPtr); + if (hashTablePtr != NULL) { + /* + * Delete thread specific hash table and free the + * struct. + */ - if (hashTablePtr != NULL) { - /* delete thread specific hash table and free the struct. */ - Tcl_DeleteHashTable(hashTablePtr); - TclpSysFree((char *)hashTablePtr); - } + Tcl_DeleteHashTable(hashTablePtr); + TclpSysFree((char *)hashTablePtr); + } - /* delete thread specific entry from master hash table. */ - Tcl_DeleteHashEntry(hPtr); - } + /* + * Delete thread specific entry from master hash table. + */ + + Tcl_DeleteHashEntry(hPtr); + } } /* * Make sure cache entry for this thread is NULL. */ + if (threadStorageCache[index].id == id) { - /* - * We do not step on another thread's cache entry. - * This is especially important if we are creating - * and exiting a lot of threads. - */ - threadStorageCache[index].id = STORAGE_INVALID_THREAD; - threadStorageCache[index].hashTablePtr = NULL; + /* + * We do not step on another thread's cache entry. This is + * especially important if we are creating and exiting a lot + * of threads. + */ + + threadStorageCache[index].id = STORAGE_INVALID_THREAD; + threadStorageCache[index].hashTablePtr = NULL; } + TclThreadStorageUnlock(); } @@ -703,15 +771,15 @@ TclFinalizeThreadStorageThread(id) * * TclFinalizeThreadStorage -- * - * This procedure cleans up the master thread storage hash table, - * all thread specific hash tables, and the thread storage cache. + * This procedure cleans up the master thread storage hash table, + * all thread specific hash tables, and the thread storage cache. * * Results: - * None. + * None. * * Side effects: - * The master thread storage hash table and thread storage cache are - * reset to their initial (empty) state. + * The master thread storage hash table and thread storage cache are + * reset to their initial (empty) state. * *---------------------------------------------------------------------- */ @@ -719,45 +787,63 @@ TclFinalizeThreadStorageThread(id) void TclFinalizeThreadStorage() { - Tcl_HashTable *hashTablePtr; /* hash table for current thread */ - Tcl_HashSearch search; /* we need to hit every thread with this search */ - Tcl_HashEntry *hPtr; /* hash entry for current thread in master table */ - TclThreadStorageLock(); + if (threadStorageHashTablePtr != NULL) { - /* - * We are going to delete the hash table for every thread now. This - * hash table should be empty at this point, except for one entry - * for the current thread. - */ - for (hPtr = Tcl_FirstHashEntry(threadStorageHashTablePtr, &search); - hPtr != NULL; hPtr = Tcl_NextHashEntry(&search)) { - - /* get the hash table corresponding to the thread in question. */ - hashTablePtr = Tcl_GetHashValue(hPtr); - - if (hashTablePtr != NULL) { - /* delete thread specific hash table and free the struct. */ - Tcl_DeleteHashTable(hashTablePtr); - TclpSysFree((char *)hashTablePtr); - } - - /* delete thread specific entry from master hash table. */ - Tcl_SetHashValue(hPtr, NULL); - } - - Tcl_DeleteHashTable(threadStorageHashTablePtr); - TclpSysFree((char *)threadStorageHashTablePtr); - - /* reset this so that next time around we know it's not valid. */ - threadStorageHashTablePtr = NULL; + Tcl_HashSearch search; /* We need to hit every thread with + * this search. */ + Tcl_HashEntry *hPtr; /* Hash entry for current thread in + * master table. */ + + /* + * We are going to delete the hash table for every thread now. + * This hash table should be empty at this point, except for + * one entry for the current thread. + */ + + for (hPtr = Tcl_FirstHashEntry(threadStorageHashTablePtr, &search); + hPtr != NULL; hPtr = Tcl_NextHashEntry(&search)) { + Tcl_HashTable *hashTablePtr = Tcl_GetHashValue(hPtr); + + if (hashTablePtr != NULL) { + /* + * Delete thread specific hash table for the thread in + * question and free the struct. + */ + + Tcl_DeleteHashTable(hashTablePtr); + TclpSysFree((char *)hashTablePtr); + } + + /* + * Delete thread specific entry from master hash table. + */ + + Tcl_SetHashValue(hPtr, NULL); + } + + Tcl_DeleteHashTable(threadStorageHashTablePtr); + TclpSysFree((char *)threadStorageHashTablePtr); + + /* + * Reset this so that next time around we know it's not valid. + */ + + threadStorageHashTablePtr = NULL; } - /* clear out the thread storage cache as well */ - memset((ThreadStorage *)&threadStorageCache, 0, sizeof(ThreadStorage) * STORAGE_CACHE_SLOTS); + /* + * Clear out the thread storage cache as well. + */ + + memset((ThreadStorage *)&threadStorageCache, 0, + sizeof(ThreadStorage) * STORAGE_CACHE_SLOTS); + + /* + * Reset this to zero, it will be set to STORAGE_FIRST_KEY if the + * thread storage subsystem gets reinitialized + */ - /* reset this to zero, it will be set to STORAGE_FIRST_KEY if the - thread storage subsystem gets reinitialized */ nextThreadStorageKey = STORAGE_INVALID_KEY; TclThreadStorageUnlock(); @@ -768,14 +854,14 @@ TclFinalizeThreadStorage() * * TclFinalizeThreadStorageData -- * - * This procedure cleans up the thread-local storage. This is - * called once for each thread. + * This procedure cleans up the thread-local storage. This is + * called once for each thread. * * Results: - * None. + * None. * * Side effects: - * Frees up the memory. + * Frees up the memory. * *---------------------------------------------------------------------- */ @@ -784,34 +870,33 @@ void TclFinalizeThreadStorageData(keyPtr) Tcl_ThreadDataKey *keyPtr; { - void *result; - int *indexPtr; - Tcl_ThreadId id = Tcl_GetCurrentThread(); - Tcl_HashTable *hashTablePtr; /* hash table for current thread */ - Tcl_HashEntry *hPtr; /* hash entry for data key in current thread */ - if (*keyPtr != NULL) { - indexPtr = *(int **)keyPtr; - - hashTablePtr = TclThreadStorageGetHashTable(id); - - if (hashTablePtr != NULL) { - hPtr = Tcl_FindHashEntry(hashTablePtr, (char *)*indexPtr); - - if (hPtr != NULL) { - result = Tcl_GetHashValue(hPtr); - - if (result != NULL) { - /* this must be ckfree because tclThread.c - allocates these using ckalloc. */ - ckfree((char *)result); - } - - Tcl_SetHashValue(hPtr, NULL); - } - } else { - Tcl_Panic("TclThreadStorageGetHashTable failed from TclFinalizeThreadStorageData!"); - } + Tcl_ThreadId id = Tcl_GetCurrentThread(); + Tcl_HashTable *hashTablePtr; /* Hash table for current thread */ + Tcl_HashEntry *hPtr; /* Hash entry for data key in current + * thread. */ + int *indexPtr = *(int **)keyPtr; + + hashTablePtr = TclThreadStorageGetHashTable(id); + if (hashTablePtr == NULL) { + Tcl_Panic("TclThreadStorageGetHashTable failed from " + "TclFinalizeThreadStorageData!"); + } + + hPtr = Tcl_FindHashEntry(hashTablePtr, (char *)*indexPtr); + if (hPtr != NULL) { + void *result = Tcl_GetHashValue(hPtr); + + if (result != NULL) { + /* + * This must be ckfree because tclThread.c allocates + * these using ckalloc. + */ + ckfree((char *)result); + } + + Tcl_SetHashValue(hPtr, NULL); + } } } @@ -820,17 +905,17 @@ TclFinalizeThreadStorageData(keyPtr) * * TclFinalizeThreadStorageDataKey -- * - * This procedure is invoked to clean up one key. This is a - * process-wide storage identifier. The thread finalization code - * cleans up the thread local storage itself. + * This procedure is invoked to clean up one key. This is a + * process-wide storage identifier. The thread finalization code + * cleans up the thread local storage itself. * - * This assumes the master lock is held. + * This assumes the master lock is held. * * Results: - * None. + * None. * * Side effects: - * The key is deallocated. + * The key is deallocated. * *---------------------------------------------------------------------- */ @@ -840,55 +925,66 @@ TclFinalizeThreadStorageDataKey(keyPtr) Tcl_ThreadDataKey *keyPtr; { int *indexPtr; - Tcl_HashTable *hashTablePtr; /* hash table for current thread */ - Tcl_HashSearch search; /* we need to hit every thread with this search */ - Tcl_HashEntry *hPtr; /* hash entry for current thread in master table */ - Tcl_HashEntry *hDataPtr; /* hash entry for data key in current thread */ + Tcl_HashTable *hashTablePtr;/* Hash table for current thread */ + Tcl_HashSearch search; /* Need to hit every thread with this search */ + Tcl_HashEntry *hPtr; /* Hash entry for current thread in master + * table. */ + Tcl_HashEntry *hDataPtr; /* Hash entry for data key in current thread */ if (*keyPtr != NULL) { - indexPtr = *(int **)keyPtr; - - TclThreadStorageLock(); - if (threadStorageHashTablePtr != NULL) { - /* - * We are going to delete the specified data key entry from every thread. - */ - for (hPtr = Tcl_FirstHashEntry(threadStorageHashTablePtr, &search); - hPtr != NULL; hPtr = Tcl_NextHashEntry(&search)) { - - /* get the hash table corresponding to the thread in question. */ - hashTablePtr = Tcl_GetHashValue(hPtr); - - if (hashTablePtr != NULL) { - /* now find the entry for the specified data key. */ - hDataPtr = Tcl_FindHashEntry(hashTablePtr, (char *)*indexPtr); - - if (hDataPtr != NULL) { - /* delete the data key for this thread. */ - Tcl_DeleteHashEntry(hDataPtr); - } - } - } - } - TclThreadStorageUnlock(); - - TclpSysFree((char *)indexPtr); - *keyPtr = NULL; + indexPtr = *(int **)keyPtr; + + TclThreadStorageLock(); + + if (threadStorageHashTablePtr != NULL) { + /* + * We are going to delete the specified data key entry + * from every thread. + */ + + for (hPtr = Tcl_FirstHashEntry(threadStorageHashTablePtr, &search); + hPtr != NULL; hPtr = Tcl_NextHashEntry(&search)) { + + /* + * Get the hash table corresponding to the thread in question. + */ + hashTablePtr = Tcl_GetHashValue(hPtr); + + if (hashTablePtr != NULL) { + /* + * Now find the entry for the specified data key. + */ + hDataPtr = Tcl_FindHashEntry(hashTablePtr, + (char *)*indexPtr); + + if (hDataPtr != NULL) { + /* + * Delete the data key for this thread. + */ + Tcl_DeleteHashEntry(hDataPtr); + } + } + } + } + + TclThreadStorageUnlock(); + + TclpSysFree((char *)indexPtr); + *keyPtr = NULL; } } -#else /* !defined(TCL_THREADS) || !defined(USE_THREAD_STORAGE) */ +#else /* !defined(TCL_THREADS) || !defined(USE_THREAD_STORAGE) */ -static void ThreadStoragePanic _ANSI_ARGS_(( - CONST char *message)); +static void ThreadStoragePanic _ANSI_ARGS_((CONST char *message)); /* *---------------------------------------------------------------------- * * ThreadStoragePanic -- * - * Panic if Tcl was compiled without TCL_THREADS or without - * USE_THREAD_STORAGE and a thread storage function has been + * Panic if Tcl was compiled without TCL_THREADS or without + * USE_THREAD_STORAGE and a thread storage function has been * called. * * Results: @@ -900,49 +996,54 @@ static void ThreadStoragePanic _ANSI_ARGS_(( *---------------------------------------------------------------------- */ static void ThreadStoragePanic(message) - CONST char *message; /* currently ignored */ + CONST char *message; /* currently ignored */ { #ifdef TCL_THREADS - #ifdef USE_THREAD_STORAGE - /* - * Do nothing, everything is OK. However, this should never happen - * because this function only gets called by the dummy thread storage - * functions (used when one or both of these DEFINES are not present). +# ifdef USE_THREAD_STORAGE + /* + * Do nothing, everything is OK. However, this should never happen + * because this function only gets called by the dummy thread + * storage functions (used when one or both of these DEFINES are + * not present). */ - #else +# else Tcl_Panic("Tcl was not compiled with thread storage enabled."); - #endif +# endif /* USE_THREAD_STORAGE */ #else Tcl_Panic("Tcl was not compiled with threads enabled."); -#endif +#endif /* TCL_THREADS */ } +/* + * Stub functions that just call ThreadStoragePanic. + */ + void TclThreadStorageLockInit() { ThreadStoragePanic(NULL); } - + void TclThreadStorageLock() { ThreadStoragePanic(NULL); } - + void TclThreadStorageUnlock() { ThreadStoragePanic(NULL); } - + void TclThreadStoragePrint(outFile, flags) - FILE *outFile; - int flags; + FILE *outFile; + int flags; { ThreadStoragePanic(NULL); } - + Tcl_HashTable * TclThreadStorageGetHashTable(id) Tcl_ThreadId id; @@ -950,59 +1051,59 @@ TclThreadStorageGetHashTable(id) ThreadStoragePanic(NULL); return NULL; } - + Tcl_HashTable * TclThreadStorageInit(id, reserved) - Tcl_ThreadId id; + Tcl_ThreadId id; void *reserved; { ThreadStoragePanic(NULL); return NULL; } - + void TclThreadStorageDataKeyInit(keyPtr) - Tcl_ThreadDataKey *keyPtr; + Tcl_ThreadDataKey *keyPtr; { ThreadStoragePanic(NULL); -} - +} + void * TclThreadStorageDataKeyGet(keyPtr) - Tcl_ThreadDataKey *keyPtr; + Tcl_ThreadDataKey *keyPtr; { ThreadStoragePanic(NULL); return NULL; -} - +} + void TclThreadStorageDataKeySet(keyPtr, data) - Tcl_ThreadDataKey *keyPtr; - void *data; + Tcl_ThreadDataKey *keyPtr; + void *data; { ThreadStoragePanic(NULL); } - + void TclFinalizeThreadStorageThread(id) - Tcl_ThreadId id; + Tcl_ThreadId id; { ThreadStoragePanic(NULL); } - + void TclFinalizeThreadStorage() { ThreadStoragePanic(NULL); } - + void TclFinalizeThreadStorageData(keyPtr) Tcl_ThreadDataKey *keyPtr; { ThreadStoragePanic(NULL); } - + void TclFinalizeThreadStorageDataKey(keyPtr) Tcl_ThreadDataKey *keyPtr; |