diff options
Diffstat (limited to 'generic/tclThreadStorage.c')
| -rw-r--r-- | generic/tclThreadStorage.c | 597 |
1 files changed, 0 insertions, 597 deletions
diff --git a/generic/tclThreadStorage.c b/generic/tclThreadStorage.c deleted file mode 100644 index f1df888..0000000 --- a/generic/tclThreadStorage.c +++ /dev/null @@ -1,597 +0,0 @@ -/* - * tclThreadStorage.c -- - * - * This file implements platform independent thread storage operations. - * - * 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. - */ - -#include "tclInt.h" - -#if defined(TCL_THREADS) - -/* - * 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. - */ - -typedef struct ThreadStorage { - 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(Tcl_HashTable *tablePtr, - void *keyPtr); -static void FreeThreadStorageEntry(Tcl_HashEntry *hPtr); -static Tcl_HashTable * ThreadStorageGetHashTable(Tcl_ThreadId id); - -/* - * This is the hash key type for thread storage. We MUST use this in - * 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. - */ - -static Tcl_HashKeyType tclThreadStorageHashKeyType = { - TCL_HASH_KEY_TYPE_VERSION, /* version */ - TCL_HASH_KEY_SYSTEM_HASH | TCL_HASH_KEY_RANDOMIZE_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 - -/* - * 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 1 - -/* - * 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. - */ - -static Tcl_HashTable threadStorageHashTable; - -/* - * This is the next thread data key value to use. We increment this everytime - * we "allocate" one. It is initially set to 1 in TclInitThreadStorage. - */ - -static int nextThreadStorageKey = STORAGE_INVALID_KEY; - -/* - * 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]; - -/* - *---------------------------------------------------------------------- - * - * 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. - * - * Results: - * The return value is a pointer to the created entry. - * - * Side effects: - * None. - * - *---------------------------------------------------------------------- - */ - -static Tcl_HashEntry * -AllocThreadStorageEntry( - Tcl_HashTable *tablePtr, /* Hash table. */ - void *keyPtr) /* Key to store in the hash table entry. */ -{ - Tcl_HashEntry *hPtr; - - hPtr = (Tcl_HashEntry *) TclpSysAlloc(sizeof(Tcl_HashEntry), 0); - hPtr->key.oneWordValue = keyPtr; - hPtr->clientData = NULL; - - return hPtr; -} - -/* - *---------------------------------------------------------------------- - * - * 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. - * - * Results: - * None. - * - * Side effects: - * None. - * - *---------------------------------------------------------------------- - */ - -static void -FreeThreadStorageEntry( - Tcl_HashEntry *hPtr) /* Hash entry to free. */ -{ - TclpSysFree((char *) hPtr); -} - -/* - *---------------------------------------------------------------------- - * - * ThreadStorageGetHashTable -- - * - * 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. - * - * Side effects: - * May change an entry in the master thread storage cache to point to the - * specified thread and it's associated hash table. - * - * Thread safety: - * This function assumes that integer operations are safe (atomic) - * on all (currently) supported Tcl platforms. Hence there are - * places where shared integer arithmetic is done w/o protective locks. - * - *---------------------------------------------------------------------- - */ - -static Tcl_HashTable * -ThreadStorageGetHashTable( - Tcl_ThreadId id) /* Id of thread to get hash table for */ -{ - int index = PTR2UINT(id) % STORAGE_CACHE_SLOTS; - Tcl_HashEntry *hPtr; - int isNew; - Tcl_HashTable *hashTablePtr; - - /* - * 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. - * - * Thread safety: threadStorageCache is accessed w/o locks in order to - * avoid serialization of all threads at this hot-spot. It is safe to - * do this here because (threadStorageCache[index].id != id) test below - * should be atomic on all (currently) supported platforms and there - * are no devastatig side effects of the test. - * - * Note Valgrind users: this place will show up as a race-condition in - * helgrind-tool output. To silence this warnings, define VALGRIND - * symbol at compilation time. - */ - -#if !defined(VALGRIND) - hashTablePtr = threadStorageCache[index].hashTablePtr; - if (threadStorageCache[index].id != id) { - Tcl_MutexLock(&threadStorageLock); -#else - Tcl_MutexLock(&threadStorageLock); - hashTablePtr = threadStorageCache[index].hashTablePtr; - if (threadStorageCache[index].id != id) { -#endif - - /* - * It's not in the cache, so we look it up... - */ - - hPtr = Tcl_FindHashEntry(&threadStorageHashTable, (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 ThreadStorageGetHashTable!"); - } - Tcl_InitCustomHashTable(hashTablePtr, TCL_CUSTOM_TYPE_KEYS, - &tclThreadStorageHashKeyType); - - /* - * Add new thread storage hash table to the master hash table. - */ - - hPtr = Tcl_CreateHashEntry(&threadStorageHashTable, (char *) id, - &isNew); - - if (hPtr == NULL) { - Tcl_Panic("Tcl_CreateHashEntry failed from " - "ThreadStorageGetHashTable!"); - } - 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; -#if !defined(VALGRIND) - Tcl_MutexUnlock(&threadStorageLock); - } -#else - } - Tcl_MutexUnlock(&threadStorageLock); -#endif - - return hashTablePtr; -} - -/* - *---------------------------------------------------------------------- - * - * TclInitThreadStorage -- - * - * Initializes the thread storage allocator. - * - * Results: - * None. - * - * Side effects: - * This procedure initializes the master hash table that maps thread ID - * onto the individual index tables that map thread data key to thread - * data. It also creates a cache that enables fast lookup of the thread - * data block array for a recently executing thread without using - * spinlocks. - * - * This procedure is called from an extremely early point in Tcl's - * initialization. In particular, it may not use ckalloc/ckfree because they - * may depend on thread-local storage (it uses TclpSysAlloc and TclpSysFree - * instead). It may not depend on synchronization primitives - but no threads - * other than the master thread have yet been launched. - * - *---------------------------------------------------------------------- - */ - -void -TclInitThreadStorage(void) -{ - Tcl_InitCustomHashTable(&threadStorageHashTable, TCL_CUSTOM_TYPE_KEYS, - &tclThreadStorageHashKeyType); - - /* - * We also initialize the cache. - */ - - memset((void*) &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; -} - -/* - *---------------------------------------------------------------------- - * - * TclpThreadDataKeyGet -- - * - * 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. - * - * Side effects: - * None. - * - *---------------------------------------------------------------------- - */ - -void * -TclpThreadDataKeyGet( - Tcl_ThreadDataKey *keyPtr) /* Identifier for the data chunk, really - * (int**) */ -{ - Tcl_HashTable *hashTablePtr = - ThreadStorageGetHashTable(Tcl_GetCurrentThread()); - Tcl_HashEntry *hPtr = Tcl_FindHashEntry(hashTablePtr, (char *) keyPtr); - - if (hPtr == NULL) { - return NULL; - } - return Tcl_GetHashValue(hPtr); -} - -/* - *---------------------------------------------------------------------- - * - * TclpThreadDataKeySet -- - * - * This procedure sets the pointer to a block of thread local storage. - * - * Results: - * None. - * - * Side effects: - * Sets up the thread so future calls to TclpThreadDataKeyGet with this - * key will return the data pointer. - * - *---------------------------------------------------------------------- - */ - -void -TclpThreadDataKeySet( - Tcl_ThreadDataKey *keyPtr, /* Identifier for the data chunk, really - * (pthread_key_t **) */ - void *data) /* Thread local storage */ -{ - Tcl_HashTable *hashTablePtr; - Tcl_HashEntry *hPtr; - int dummy; - - hashTablePtr = ThreadStorageGetHashTable(Tcl_GetCurrentThread()); - hPtr = Tcl_CreateHashEntry(hashTablePtr, (char *)keyPtr, &dummy); - - Tcl_SetHashValue(hPtr, data); -} - -/* - *---------------------------------------------------------------------- - * - * TclpFinalizeThreadDataThread -- - * - * This procedure cleans up the thread storage hash table for the - * current thread. - * - * Results: - * None. - * - * Side effects: - * Frees all associated thread storage, all hash table entries for - * the thread's thread storage, and the hash table itself. - * - *---------------------------------------------------------------------- - */ - -void -TclpFinalizeThreadDataThread(void) -{ - Tcl_ThreadId id = Tcl_GetCurrentThread(); - /* Id of the thread to finalize. */ - int index = PTR2UINT(id) % STORAGE_CACHE_SLOTS; - Tcl_HashEntry *hPtr; /* Hash entry for current thread in master - * table. */ - Tcl_HashTable* hashTablePtr;/* Pointer to the hash table holding TSD - * blocks for the current thread*/ - Tcl_HashSearch search; /* Search object to walk the TSD blocks in the - * designated thread */ - Tcl_HashEntry *hPtr2; /* Hash entry for a TSD block in the - * designated thread. */ - - Tcl_MutexLock(&threadStorageLock); - hPtr = Tcl_FindHashEntry(&threadStorageHashTable, (char*)id); - if (hPtr == NULL) { - hashTablePtr = NULL; - } else { - /* - * We found it, extract the hash table pointer. - */ - - hashTablePtr = Tcl_GetHashValue(hPtr); - 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; - } - } - Tcl_MutexUnlock(&threadStorageLock); - - /* - * The thread's hash table has been extracted and removed from the master - * hash table. Now clean up the thread. - */ - - if (hashTablePtr != NULL) { - /* - * Free all TSD - */ - - for (hPtr2 = Tcl_FirstHashEntry(hashTablePtr, &search); hPtr2 != NULL; - hPtr2 = Tcl_NextHashEntry(&search)) { - void *blockPtr = Tcl_GetHashValue(hPtr2); - - if (blockPtr != NULL) { - /* - * The block itself was allocated in Tcl_GetThreadData using - * ckalloc; use ckfree to dispose of it. - */ - - ckfree(blockPtr); - } - } - - /* - * Delete thread specific hash table and free the struct. - */ - - Tcl_DeleteHashTable(hashTablePtr); - TclpSysFree((char *) hashTablePtr); - } -} - -/* - *---------------------------------------------------------------------- - * - * TclFinalizeThreadStorage -- - * - * This procedure cleans up the master thread storage hash table, all - * thread specific hash tables, and the thread storage cache. - * - * Results: - * None. - * - * Side effects: - * The master thread storage hash table and thread storage cache are - * reset to their initial (empty) state. - * - *---------------------------------------------------------------------- - */ - -void -TclFinalizeThreadStorage(void) -{ - Tcl_HashSearch search; /* We need to hit every thread with this - * search. */ - Tcl_HashEntry *hPtr; /* Hash entry for current thread in master - * table. */ - Tcl_MutexLock(&threadStorageLock); - - /* - * 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(&threadStorageHashTable, &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(&threadStorageHashTable); - - /* - * Clear out the thread storage cache as well. - */ - - memset((void*) &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 - */ - - nextThreadStorageKey = STORAGE_INVALID_KEY; - - Tcl_MutexUnlock(&threadStorageLock); -} - -#else /* !defined(TCL_THREADS) */ - -/* - * Stub functions for non-threaded builds - */ - -void -TclInitThreadStorage(void) -{ -} - -void -TclpFinalizeThreadDataThread(void) -{ -} - -void -TclFinalizeThreadStorage(void) -{ -} - -#endif /* defined(TCL_THREADS) && defined(USE_THREAD_STORAGE) */ - -/* - * Local Variables: - * mode: c - * c-basic-offset: 4 - * fill-column: 78 - * End: - */ |
