diff options
Diffstat (limited to 'generic/tclThreadStorage.c')
-rw-r--r-- | generic/tclThreadStorage.c | 286 |
1 files changed, 149 insertions, 137 deletions
diff --git a/generic/tclThreadStorage.c b/generic/tclThreadStorage.c index 7eb66be..09aa125 100644 --- a/generic/tclThreadStorage.c +++ b/generic/tclThreadStorage.c @@ -5,10 +5,10 @@ * * Copyright (c) 2003-2004 by Joe Mistachkin * - * See the file "license.terms" for information on usage and redistribution - * of this file, and for a DISCLAIMER OF ALL WARRANTIES. + * 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.4 2004/06/24 09:05:46 dkf Exp $ + * RCS: @(#) $Id: tclThreadStorage.c,v 1.5 2005/07/19 22:45:35 dkf Exp $ */ #include "tclInt.h" @@ -16,29 +16,28 @@ #if defined(TCL_THREADS) && defined(USE_THREAD_STORAGE) /* - * This is the thread storage cache array and it's accompanying mutex. - * The elements are pairs of thread Id and an associated hash table - * pointer; the hash table being pointed to contains the thread storage - * for it's associated thread. The purpose of this cache is to minimize - * the number of hash table lookups in the master thread storage hash - * table. + * This is the thread storage cache array and it's accompanying mutex. The + * elements are pairs of thread Id and an associated hash table pointer; the + * hash table being pointed to contains the thread storage for it's associated + * thread. The purpose of this cache is to minimize 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. + * 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. + * These are the prototypes for the custom hash table allocation functions + * used by the thread storage subsystem. */ static Tcl_HashEntry * AllocThreadStorageEntry _ANSI_ARGS_(( @@ -51,6 +50,7 @@ static void FreeThreadStorageEntry _ANSI_ARGS_(( * combination with the new hash key type flag TCL_HASH_KEY_SYSTEM_HASH * 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 */ @@ -73,31 +73,30 @@ Tcl_HashKeyType tclThreadStorageHashKeyType = { #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. + * 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 /* - * This is the default number of thread storage cache slots. - * This define may need to be fine tuned for maximum performance. + * 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 /* - * 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. + * 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; /* - * This is the next thread data key value to use. We increment this - * everytime we "allocate" one. It is initially set to 1 in - * TclThreadStorageInit. + * This is the next thread data key value to use. We increment this everytime + * we "allocate" one. It is initially set to 1 in TclThreadStorageInit. */ static int nextThreadStorageKey = STORAGE_INVALID_KEY; @@ -109,9 +108,8 @@ static int nextThreadStorageKey = STORAGE_INVALID_KEY; static int initThreadStorage = 0; /* - * This is the master thread storage cache. Per kennykb's idea, this - * prevents unnecessary lookups for threads that use a lot of thread - * storage. + * This is the master thread storage cache. Per Kevin Kenny's idea, this + * prevents unnecessary lookups for threads that use a lot of thread storage. */ static volatile ThreadStorage threadStorageCache[STORAGE_CACHE_SLOTS]; @@ -121,15 +119,15 @@ 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. * * 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. * *---------------------------------------------------------------------- */ @@ -139,10 +137,11 @@ 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. + * Mutexes in Tcl are self initializing, and we are taking advantage + * of that fact since this file cannot contain platform specific + * calls. */ + initThreadStorage = 1; } } @@ -152,11 +151,11 @@ 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. @@ -179,8 +178,8 @@ 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. @@ -202,9 +201,9 @@ 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. @@ -233,9 +232,9 @@ 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. @@ -258,9 +257,9 @@ 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. * @@ -303,7 +302,7 @@ TclThreadStoragePrint(outFile, flags) } header = 0; /* we have not output the header yet. */ - for (index = 0; index < STORAGE_CACHE_SLOTS; index++) { + 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", @@ -314,12 +313,14 @@ TclThreadStoragePrint(outFile, flags) 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. + * 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); @@ -333,6 +334,7 @@ TclThreadStoragePrint(outFile, flags) } } #endif + } else { /* fprintf(outFile, "cache slot %d not used\n", index); */ } @@ -362,12 +364,12 @@ TclThreadStoragePrint(outFile, flags) * 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. + * 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. * *---------------------------------------------------------------------- */ @@ -381,9 +383,9 @@ TclThreadStorageGetHashTable(id) int new; /* - * It's important that we pick up the hash table pointer BEFORE - * comparing thread Id in case another thread is in the critical - * region changing things out from under you. + * It's important that we pick up the hash table pointer BEFORE comparing + * thread Id in case another thread is in the critical region changing + * things out from under you. */ Tcl_HashTable *hashTablePtr = threadStorageCache[index].hashTablePtr; @@ -408,11 +410,13 @@ TclThreadStorageGetHashTable(id) /* * We found it, extract the hash table pointer. */ + hashTablePtr = Tcl_GetHashValue(hPtr); } else { /* * The thread specific hash table is not found. */ + hashTablePtr = NULL; } @@ -429,8 +433,7 @@ TclThreadStorageGetHashTable(id) &tclThreadStorageHashKeyType); /* - * Add new thread storage hash table to the master - * hash table. + * Add new thread storage hash table to the master hash table. */ hPtr = Tcl_CreateHashEntry(threadStorageHashTablePtr, @@ -444,17 +447,18 @@ TclThreadStorageGetHashTable(id) } /* - * Now, we put it in the cache since it is highly likely - * it will be needed again shortly. + * 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. + * We cannot look it up, the master hash table has not been + * initialized. */ + hashTablePtr = NULL; } TclThreadStorageUnlock(); @@ -475,8 +479,8 @@ TclThreadStorageGetHashTable(id) * 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 @@ -496,8 +500,8 @@ TclThreadStorageInit(id, reserved) if (threadStorageHashTablePtr == NULL) { /* - * Looks like we haven't created the outer hash table yet we - * can just do that now. + * Looks like we haven't created the outer hash table yet we can just + * do that now. */ threadStorageHashTablePtr = (Tcl_HashTable *) @@ -531,30 +535,29 @@ 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. * * 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; @@ -566,8 +569,8 @@ TclThreadStorageDataKeyInit(keyPtr) } /* - * We must call this now to make sure that - * nextThreadStorageKey has a well defined value. + * We must call this now to make sure that nextThreadStorageKey has a + * well defined value. */ TclThreadStorageLock(); @@ -579,11 +582,11 @@ TclThreadStorageDataKeyInit(keyPtr) 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. + * 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++; @@ -603,8 +606,8 @@ TclThreadStorageDataKeyInit(keyPtr) * 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. @@ -614,8 +617,8 @@ TclThreadStorageDataKeyInit(keyPtr) 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; @@ -631,12 +634,12 @@ TclThreadStorageDataKeyGet(keyPtr) "TclThreadStorageDataKeyGet!"); } - hPtr = Tcl_FindHashEntry(hashTablePtr, (char *)*indexPtr); + hPtr = Tcl_FindHashEntry(hashTablePtr, (char *) *indexPtr); if (hPtr == NULL) { return NULL; } - return (void *)Tcl_GetHashValue(hPtr); + return (void *) Tcl_GetHashValue(hPtr); } } @@ -651,16 +654,16 @@ TclThreadStorageDataKeyGet(keyPtr) * 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 **) */ + Tcl_ThreadDataKey *keyPtr; /* Identifier for the data chunk, really + * (pthread_key_t **) */ void *data; /* Thread local storage */ { int *indexPtr = *(int **)keyPtr; @@ -678,8 +681,10 @@ TclThreadStorageDataKeySet(keyPtr, data) /* * 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 " @@ -709,12 +714,12 @@ TclThreadStorageDataKeySet(keyPtr, data) 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_HashTable *hashTablePtr;/* Hash table for current thread. */ Tcl_HashEntry *hPtr; /* Hash entry for current thread in master - * table */ + * table. */ TclThreadStorageLock(); @@ -730,8 +735,7 @@ TclFinalizeThreadStorageThread(id) if (hashTablePtr != NULL) { /* - * Delete thread specific hash table and free the - * struct. + * Delete thread specific hash table and free the struct. */ Tcl_DeleteHashTable(hashTablePtr); @@ -752,9 +756,8 @@ TclFinalizeThreadStorageThread(id) 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. + * 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; @@ -769,8 +772,8 @@ 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. @@ -794,9 +797,9 @@ TclFinalizeThreadStorage() * 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. + * 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); @@ -838,8 +841,8 @@ TclFinalizeThreadStorage() 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; @@ -852,8 +855,8 @@ 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. @@ -870,7 +873,7 @@ TclFinalizeThreadStorageData(keyPtr) { if (*keyPtr != NULL) { Tcl_ThreadId id = Tcl_GetCurrentThread(); - Tcl_HashTable *hashTablePtr; /* Hash table for current thread */ + Tcl_HashTable *hashTablePtr; /* Hash table for current thread. */ Tcl_HashEntry *hPtr; /* Hash entry for data key in current * thread. */ int *indexPtr = *(int **)keyPtr; @@ -887,9 +890,10 @@ TclFinalizeThreadStorageData(keyPtr) if (result != NULL) { /* - * This must be ckfree because tclThread.c allocates - * these using ckalloc. + * This must be ckfree because tclThread.c allocates these + * using ckalloc. */ + ckfree((char *)result); } @@ -903,9 +907,9 @@ 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. * @@ -923,7 +927,7 @@ TclFinalizeThreadStorageDataKey(keyPtr) Tcl_ThreadDataKey *keyPtr; { int *indexPtr; - Tcl_HashTable *hashTablePtr;/* Hash table for 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. */ @@ -936,22 +940,23 @@ TclFinalizeThreadStorageDataKey(keyPtr) if (threadStorageHashTablePtr != NULL) { /* - * We are going to delete the specified data key entry - * from every thread. + * 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); @@ -959,6 +964,7 @@ TclFinalizeThreadStorageDataKey(keyPtr) /* * Delete the data key for this thread. */ + Tcl_DeleteHashEntry(hDataPtr); } } @@ -981,15 +987,14 @@ 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 - * called. + * Panic if Tcl was compiled without TCL_THREADS or without + * USE_THREAD_STORAGE and a thread storage function has been called. * * Results: - * None. + * None. * * Side effects: - * None. + * None. * *---------------------------------------------------------------------- */ @@ -999,10 +1004,9 @@ static void ThreadStoragePanic(message) #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). + * 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 Tcl_Panic("Tcl was not compiled with thread storage enabled."); @@ -1110,3 +1114,11 @@ TclFinalizeThreadStorageDataKey(keyPtr) } #endif /* defined(TCL_THREADS) && defined(USE_THREAD_STORAGE) */ + +/* + * Local Variables: + * mode: c + * c-basic-offset: 4 + * fill-column: 78 + * End: + */ |