diff options
author | Kevin B Kenny <kennykb@acm.org> | 2005-08-11 22:06:46 (GMT) |
---|---|---|
committer | Kevin B Kenny <kennykb@acm.org> | 2005-08-11 22:06:46 (GMT) |
commit | 9b74d96f71d7f7d8bc7bd3a5956a1d3132c2330a (patch) | |
tree | 0331916c4d6eb03e334e0686ab5ed947945519aa /generic | |
parent | 3d07a4f66acb32cc71599c3192ae22c380c6520f (diff) | |
download | tcl-9b74d96f71d7f7d8bc7bd3a5956a1d3132c2330a.zip tcl-9b74d96f71d7f7d8bc7bd3a5956a1d3132c2330a.tar.gz tcl-9b74d96f71d7f7d8bc7bd3a5956a1d3132c2330a.tar.bz2 |
radical refactoring of thread storage to untangle dependencies
Diffstat (limited to 'generic')
-rw-r--r-- | generic/tclEvent.c | 28 | ||||
-rw-r--r-- | generic/tclInt.h | 21 | ||||
-rw-r--r-- | generic/tclThread.c | 134 | ||||
-rw-r--r-- | generic/tclThreadStorage.c | 860 |
4 files changed, 226 insertions, 817 deletions
diff --git a/generic/tclEvent.c b/generic/tclEvent.c index 713ef30..270e11b 100644 --- a/generic/tclEvent.c +++ b/generic/tclEvent.c @@ -12,7 +12,7 @@ * See the file "license.terms" for information on usage and redistribution of * this file, and for a DISCLAIMER OF ALL WARRANTIES. * - * RCS: @(#) $Id: tclEvent.c,v 1.62 2005/08/10 16:28:02 kennykb Exp $ + * RCS: @(#) $Id: tclEvent.c,v 1.63 2005/08/11 22:06:47 kennykb Exp $ */ #include "tclInt.h" @@ -782,6 +782,8 @@ TclInitSubsystems() * implementation of self-initializing locks. */ + TclInitThreadStorage(); /* Creates master hash table for + * thread local storage */ #if USE_TCLALLOC TclInitAlloc(); /* Process wide mutex init */ #endif @@ -952,6 +954,23 @@ Tcl_Finalize() #if defined(TCL_THREADS) && defined(USE_THREAD_ALLOC) TclFinalizeThreadAlloc(); #endif + /* + * We defer unloading of packages until very late to avoid memory + * access issues. Both exit callbacks and synchronization variables + * may be stored in packages. + * + * Note that TclFinalizeLoad unloads packages in the reverse of the + * order they were loaded in (i.e. last to be loaded is the first to + * be unloaded). This can be important for correct unloading when + * dependencies exist. + * + * Once load has been finalized, we will have deleted any temporary + * copies of shared libraries and can therefore reset the filesystem + * to its original state. + */ + + TclFinalizeLoad(); + TclResetFilesystem(); /* * We defer unloading of packages until very late to avoid memory @@ -1002,6 +1021,13 @@ void Tcl_FinalizeThread() { ExitHandler *exitPtr; + + /* + * We use TclThreadDataKeyGet here, rather than Tcl_GetThreadData, + * because we don't want to initialize the data block if it hasn't + * been initialized already. + */ + ThreadSpecificData *tsdPtr = (ThreadSpecificData *)TclThreadDataKeyGet(&dataKey); diff --git a/generic/tclInt.h b/generic/tclInt.h index 82bf45e..8ba85b6 100644 --- a/generic/tclInt.h +++ b/generic/tclInt.h @@ -12,7 +12,7 @@ * See the file "license.terms" for information on usage and redistribution * of this file, and for a DISCLAIMER OF ALL WARRANTIES. * - * RCS: @(#) $Id: tclInt.h,v 1.244 2005/08/05 23:56:29 dkf Exp $ + * RCS: @(#) $Id: tclInt.h,v 1.245 2005/08/11 22:06:47 kennykb Exp $ */ #ifndef _TCLINT @@ -2061,15 +2061,11 @@ MODULE_SCOPE void TclpFinalizeCondition _ANSI_ARGS_(( Tcl_Condition *condPtr)); MODULE_SCOPE void TclpFinalizeMutex _ANSI_ARGS_((Tcl_Mutex *mutexPtr)); MODULE_SCOPE void TclpFinalizePipes _ANSI_ARGS_((void)); -MODULE_SCOPE void TclpFinalizeThreadData _ANSI_ARGS_(( - Tcl_ThreadDataKey *keyPtr)); MODULE_SCOPE int TclpThreadCreate _ANSI_ARGS_(( Tcl_ThreadId *idPtr, Tcl_ThreadCreateProc proc, ClientData clientData, int stackSize, int flags)); -MODULE_SCOPE void TclpFinalizeThreadDataKey _ANSI_ARGS_(( - Tcl_ThreadDataKey *keyPtr)); MODULE_SCOPE int TclpFindVariable _ANSI_ARGS_((CONST char *name, int *lengthPtr)); MODULE_SCOPE void TclpInitLibraryPath _ANSI_ARGS_((char **valuePtr, @@ -2121,16 +2117,12 @@ MODULE_SCOPE void TclpUnloadFile _ANSI_ARGS_(( Tcl_LoadHandle loadHandle)); MODULE_SCOPE VOID * TclpThreadDataKeyGet _ANSI_ARGS_(( Tcl_ThreadDataKey *keyPtr)); -MODULE_SCOPE void TclpThreadDataKeyInit _ANSI_ARGS_(( - Tcl_ThreadDataKey *keyPtr)); MODULE_SCOPE void TclpThreadDataKeySet _ANSI_ARGS_(( Tcl_ThreadDataKey *keyPtr, VOID *data)); MODULE_SCOPE void TclpThreadExit _ANSI_ARGS_((int status)); MODULE_SCOPE int TclpThreadGetStackSize _ANSI_ARGS_((void)); MODULE_SCOPE void TclRememberCondition _ANSI_ARGS_(( Tcl_Condition *mutex)); -MODULE_SCOPE void TclRememberDataKey _ANSI_ARGS_(( - Tcl_ThreadDataKey *mutex)); MODULE_SCOPE VOID TclRememberJoinableThread _ANSI_ARGS_(( Tcl_ThreadId id)); MODULE_SCOPE void TclRememberMutex _ANSI_ARGS_((Tcl_Mutex *mutex)); @@ -2171,16 +2163,9 @@ MODULE_SCOPE int TclpLoadMemory _ANSI_ARGS_((Tcl_Interp *interp, Tcl_LoadHandle *loadHandle, Tcl_FSUnloadFileProc **unloadProcPtr)); #endif -MODULE_SCOPE void TclThreadStorageDataKeyInit( - Tcl_ThreadDataKey *keyPtr); -MODULE_SCOPE void * TclThreadStorageDataKeyGet(Tcl_ThreadDataKey *keyPtr); -MODULE_SCOPE void TclThreadStorageDataKeySet(Tcl_ThreadDataKey *keyPtr, - void *data); +MODULE_SCOPE void TclInitThreadStorage(void); +MODULE_SCOPE void TclpFinalizeThreadDataThread(void); MODULE_SCOPE void TclFinalizeThreadStorage(void); -MODULE_SCOPE void TclFinalizeThreadStorageData( - Tcl_ThreadDataKey *keyPtr); -MODULE_SCOPE void TclFinalizeThreadStorageDataKey( - Tcl_ThreadDataKey *keyPtr); /* *---------------------------------------------------------------- diff --git a/generic/tclThread.c b/generic/tclThread.c index 766e984..17dd0ad 100644 --- a/generic/tclThread.c +++ b/generic/tclThread.c @@ -9,7 +9,7 @@ * See the file "license.terms" for information on usage and redistribution of * this file, and for a DISCLAIMER OF ALL WARRANTIES. * - * RCS: @(#) $Id: tclThread.c,v 1.12 2005/07/21 14:38:51 dkf Exp $ + * RCS: @(#) $Id: tclThread.c,v 1.13 2005/08/11 22:06:47 kennykb Exp $ */ #include "tclInt.h" @@ -83,43 +83,22 @@ Tcl_GetThreadData(keyPtr, size) { VOID *result; #ifdef TCL_THREADS - - /* - * See if this is the first thread to init this key. - */ - - if (*keyPtr == NULL) { -#ifdef USE_THREAD_STORAGE - TclThreadStorageDataKeyInit(keyPtr); -#else - TclpThreadDataKeyInit(keyPtr); -#endif /* USE_THREAD_STORAGE */ - } - /* * Initialize the key for this thread. */ -#ifdef USE_THREAD_STORAGE - result = TclThreadStorageDataKeyGet(keyPtr); -#else result = TclpThreadDataKeyGet(keyPtr); -#endif /* USE_THREAD_STORAGE */ if (result == NULL) { result = (VOID *)ckalloc((size_t)size); memset(result, 0, (size_t)size); -#ifdef USE_THREAD_STORAGE - TclThreadStorageDataKeySet(keyPtr, result); -#else TclpThreadDataKeySet(keyPtr, result); -#endif /* USE_THREAD_STORAGE */ } #else /* TCL_THREADS */ if (*keyPtr == NULL) { result = (VOID *)ckalloc((size_t)size); memset((char *)result, 0, (size_t)size); *keyPtr = (Tcl_ThreadDataKey)result; - TclRememberDataKey(keyPtr); + RememberSyncObject((char *)keyPtr, &keyRecord); } result = *(VOID **)keyPtr; #endif /* TCL_THREADS */ @@ -149,11 +128,7 @@ TclThreadDataKeyGet(keyPtr) * (pthread_key_t **) */ { #ifdef TCL_THREADS -#ifdef USE_THREAD_STORAGE - return (VOID *)TclThreadStorageDataKeyGet(keyPtr); -#else return (VOID *)TclpThreadDataKeyGet(keyPtr); -#endif /* USE_THREAD_STORAGE */ #else /* TCL_THREADS */ char *result = *(char **)keyPtr; return (VOID *)result; @@ -164,49 +139,6 @@ TclThreadDataKeyGet(keyPtr) /* *---------------------------------------------------------------------- * - * TclThreadDataKeySet -- - * - * This function sets a thread local storage pointer. - * - * Results: - * None. - * - * Side effects: - * The assigned value will be returned by TclpThreadDataKeyGet. - * - *---------------------------------------------------------------------- - */ - -void -TclThreadDataKeySet(keyPtr, data) - Tcl_ThreadDataKey *keyPtr; /* Identifier for the data chunk, really - * (pthread_key_t **) */ - VOID *data; /* Thread local storage */ -{ -#ifdef TCL_THREADS - - if (*keyPtr == NULL) { -#ifdef USE_THREAD_STORAGE - TclThreadStorageDataKeyInit(keyPtr); -#else - TclpThreadDataKeyInit(keyPtr); -#endif /* USE_THREAD_STORAGE */ - } - -#ifdef USE_THREAD_STORAGE - TclThreadStorageDataKeySet(keyPtr, data); -#else - TclpThreadDataKeySet(keyPtr, data); -#endif /* USE_THREAD_STORAGE */ - -#else /* TCL_THREADS */ - *keyPtr = (Tcl_ThreadDataKey) data; -#endif /* TCL_THREADS */ -} - -/* - *---------------------------------------------------------------------- - * * RememberSyncObject * * Keep a list of (mutexes/condition variable/data key) used during @@ -337,29 +269,6 @@ Tcl_MutexFinalize(mutexPtr) /* *---------------------------------------------------------------------- * - * TclRememberDataKey - * - * Keep a list of thread data keys used during finalization. - * - * Results: - * None. - * - * Side effects: - * Add to the key list. - * - *---------------------------------------------------------------------- - */ - -void -TclRememberDataKey(keyPtr) - Tcl_ThreadDataKey *keyPtr; -{ - RememberSyncObject((char *)keyPtr, &keyRecord); -} - -/* - *---------------------------------------------------------------------- - * * TclRememberCondition * * Keep a list of condition variables used during finalization. @@ -427,26 +336,7 @@ Tcl_ConditionFinalize(condPtr) void TclFinalizeThreadData() { - int i; - - TclpMasterLock(); - for (i=0 ; i<keyRecord.num ; i++) { - Tcl_ThreadDataKey *keyPtr = (Tcl_ThreadDataKey *) keyRecord.list[i]; - -#ifdef TCL_THREADS -#ifdef USE_THREAD_STORAGE - TclFinalizeThreadStorageData(keyPtr); -#else - TclpFinalizeThreadData(keyPtr); -#endif /* USE_THREAD_STORAGE */ -#else /* TCL_THREADS */ - if (*keyPtr != NULL) { - ckfree((char *)*keyPtr); - *keyPtr = NULL; - } -#endif /* TCL_THREADS */ - } - TclpMasterUnlock(); + TclpFinalizeThreadDataThread(); } /* @@ -470,19 +360,23 @@ void TclFinalizeSynchronization() { #ifdef TCL_THREADS + void* blockPtr; Tcl_ThreadDataKey *keyPtr; Tcl_Mutex *mutexPtr; Tcl_Condition *condPtr; int i; TclpMasterLock(); + + /* + * If we're running unthreaded, the TSD blocks are simply stored + * inside their thread data keys. Free them here. + */ + for (i=0 ; i<keyRecord.num ; i++) { keyPtr = (Tcl_ThreadDataKey *)keyRecord.list[i]; -#ifdef USE_THREAD_STORAGE - TclFinalizeThreadStorageDataKey(keyPtr); -#else - TclpFinalizeThreadDataKey(keyPtr); -#endif /* USE_THREAD_STORAGE */ + blockPtr = (void*) *keyPtr; + ckfree(blockPtr); } if (keyRecord.list != NULL) { ckfree((char *)keyRecord.list); @@ -492,12 +386,10 @@ TclFinalizeSynchronization() keyRecord.num = 0; /* - * Call platform specific thread storage master cleanup. + * Call thread storage master cleanup. */ -#ifdef USE_THREAD_STORAGE TclFinalizeThreadStorage(); -#endif /* USE_THREAD_STORAGE */ for (i=0 ; i<mutexRecord.num ; i++) { mutexPtr = (Tcl_Mutex *)mutexRecord.list[i]; diff --git a/generic/tclThreadStorage.c b/generic/tclThreadStorage.c index cd49457..10093e4 100644 --- a/generic/tclThreadStorage.c +++ b/generic/tclThreadStorage.c @@ -8,12 +8,12 @@ * 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.6 2005/08/05 23:56:29 dkf Exp $ + * RCS: @(#) $Id: tclThreadStorage.c,v 1.7 2005/08/11 22:06:47 kennykb Exp $ */ #include "tclInt.h" -#if defined(TCL_THREADS) && defined(USE_THREAD_STORAGE) +#if defined(TCL_THREADS) /* * This is the thread storage cache array and it's accompanying mutex. The @@ -43,13 +43,7 @@ typedef struct ThreadStorage { static Tcl_HashEntry * AllocThreadStorageEntry(Tcl_HashTable *tablePtr, void *keyPtr); static void FreeThreadStorageEntry(Tcl_HashEntry *hPtr); -static void ThreadStorageLockInit(void); -static void ThreadStorageLock(void); -static void ThreadStorageUnlock(void); static Tcl_HashTable * ThreadStorageGetHashTable(Tcl_ThreadId id); -static Tcl_HashTable * ThreadStorageInit(Tcl_ThreadId id, void *reserved); -static void FinalizeThreadStorageThreadMushroomMushroom( - Tcl_ThreadId id); /* * This is the hash key type for thread storage. We MUST use this in @@ -59,7 +53,8 @@ static void FinalizeThreadStorageThreadMushroomMushroom( Tcl_HashKeyType tclThreadStorageHashKeyType = { TCL_HASH_KEY_TYPE_VERSION, /* version */ - TCL_HASH_KEY_SYSTEM_HASH, /* flags */ + TCL_HASH_KEY_SYSTEM_HASH | TCL_HASH_KEY_RANDOMIZE_HASH, + /* flags */ NULL, /* hashKeyProc */ NULL, /* compareKeysProc */ AllocThreadStorageEntry, /* allocEntryProc */ @@ -83,7 +78,7 @@ Tcl_HashKeyType tclThreadStorageHashKeyType = { * below this are RESERVED for future use. */ -#define STORAGE_FIRST_KEY 101 +#define STORAGE_FIRST_KEY 1 /* * This is the default number of thread storage cache slots. This define may @@ -98,22 +93,16 @@ Tcl_HashKeyType tclThreadStorageHashKeyType = { * hash tables contain the actual thread storage. */ -static Tcl_HashTable *threadStorageHashTablePtr = NULL; +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 ThreadStorageInit. + * we "allocate" one. It is initially set to 1 in TclInitThreadStorage. */ static int nextThreadStorageKey = STORAGE_INVALID_KEY; /* - * Have we initialized the thread storage mutex yet? - */ - -static int initThreadStorage = 0; - -/* * 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. */ @@ -123,88 +112,6 @@ static volatile ThreadStorage threadStorageCache[STORAGE_CACHE_SLOTS]; /* *---------------------------------------------------------------------- * - * ThreadStorageLockInit - * - * 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. - * - *---------------------------------------------------------------------- - */ - -static void -ThreadStorageLockInit() -{ - 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; - } -} - -/* - *---------------------------------------------------------------------- - * - * ThreadStorageLock - * - * 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. - * - * Results: - * None. - * - * Side effects: - * Acquire the thread storage mutex. - * - *---------------------------------------------------------------------- - */ - -static void -ThreadStorageLock() -{ - ThreadStorageLockInit(); - Tcl_MutexLock(&threadStorageLock); -} - -/* - *---------------------------------------------------------------------- - * - * ThreadStorageUnlock - * - * This procedure is used to release a lock that serializes creation of - * thread storage. - * - * Results: - * None. - * - * Side effects: - * Release the thread storage mutex. - * - *---------------------------------------------------------------------- - */ - -static void -ThreadStorageUnlock() -{ - Tcl_MutexUnlock(&threadStorageLock); -} - -/* - *---------------------------------------------------------------------- - * * AllocThreadStorageEntry -- * * Allocate space for a Tcl_HashEntry using TclpSysAlloc (not ckalloc). @@ -258,109 +165,6 @@ FreeThreadStorageEntry(hPtr) TclpSysFree((char *)hPtr); } -#ifdef TCL_THREAD_STORAGE_DEBUG -/* - *---------------------------------------------------------------------- - * - * ThreadStoragePrint -- - * - * 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. - * - * Results: - * None. - * - * Side effects: - * The thread storage lock is acquired and released. - * - *---------------------------------------------------------------------- - */ - -static void -ThreadStoragePrint(outFile, flags) - FILE *outFile; /* The file to print the information to. */ - int flags; /* Reserved for future use. */ -{ - Tcl_HashEntry *hPtr; - Tcl_HashSearch search; - 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"); - } - } else { - 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); - -#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); */ - } - } - - if (!header) { - fprintf(outFile, "thread storage cache is empty (%d total slots)\n", - STORAGE_CACHE_SLOTS); - header = 1; - } - - /* - * Show the next data key value. - */ - - fprintf(outFile, "next data key value is: %d\n", nextThreadStorageKey); -} -#endif /* TCL_THREAD_STORAGE_DEBUG */ - /* *---------------------------------------------------------------------- * @@ -399,217 +203,120 @@ ThreadStorageGetHashTable(id) Tcl_HashTable *hashTablePtr = threadStorageCache[index].hashTablePtr; if (threadStorageCache[index].id != id) { - ThreadStorageLock(); + Tcl_MutexLock(&threadStorageLock); /* - * Make sure the master hash table is initialized. + * It's not in the cache, so we look it up... */ - - ThreadStorageInit(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 " - "ThreadStorageGetHashTable!"); - } - 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 " - "ThreadStorageInit!"); - } - Tcl_SetHashValue(hPtr, hashTablePtr); - } - + + hPtr = Tcl_FindHashEntry(&threadStorageHashTable, (char *)id); + + if (hPtr != NULL) { /* - * Now, we put it in the cache since it is highly likely it will - * be needed again shortly. + * We found it, extract the hash table pointer. */ - - threadStorageCache[index].id = id; - threadStorageCache[index].hashTablePtr = hashTablePtr; + + hashTablePtr = Tcl_GetHashValue(hPtr); } else { /* - * We cannot look it up, the master hash table has not been - * initialized. + * The thread specific hash table is not found. */ - + hashTablePtr = NULL; } - ThreadStorageUnlock(); - } - - return hashTablePtr; -} - -/* - *---------------------------------------------------------------------- - * - * ThreadStorageInit -- - * - * 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. - * - * Results: - * 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. - * - *---------------------------------------------------------------------- - */ - -static Tcl_HashTable * -ThreadStorageInit(id, reserved) - Tcl_ThreadId id; /* Id of thread to get hash table for */ - void *reserved; /* reserved for future use */ -{ -#ifdef TCL_THREAD_STORAGE_DEBUG - ThreadStoragePrint(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 *) + if (hashTablePtr == NULL) { + hashTablePtr = (Tcl_HashTable *) TclpSysAlloc(sizeof(Tcl_HashTable), 0); - if (threadStorageHashTablePtr == NULL) { - Tcl_Panic("could not allocate master thread storage hash table, " - "TclpSysAlloc failed from ThreadStorageInit!"); + + 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, &new); + + if (hPtr == NULL) { + Tcl_Panic("Tcl_CreateHashEntry failed from " + "ThreadStorageGetHashTable!"); + } + Tcl_SetHashValue(hPtr, hashTablePtr); } - 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. + * 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; - nextThreadStorageKey = STORAGE_FIRST_KEY; + Tcl_MutexUnlock(&threadStorageLock); } - return NULL; + return hashTablePtr; } /* *---------------------------------------------------------------------- * - * TclThreadStorageDataKeyInit -- + * TclInitThreadStorage -- * - * 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. + * Initializes the thread storage allocator. * * 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. + * 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 -TclThreadStorageDataKeyInit(keyPtr) - Tcl_ThreadDataKey *keyPtr; /* Identifier for the data chunk, really - * (int**) */ +TclInitThreadStorage() { - int *indexPtr; - int newKey; - - if (*keyPtr == NULL) { - 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. - */ - - ThreadStorageLock(); - - /* - * Make sure the master hash table is initialized. - */ - - ThreadStorageInit(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++; - ThreadStorageUnlock(); - - *indexPtr = newKey; - *keyPtr = (Tcl_ThreadDataKey)indexPtr; - TclRememberDataKey(keyPtr); - } + Tcl_InitCustomHashTable(&threadStorageHashTable, + 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; } /* *---------------------------------------------------------------------- * - * TclThreadStorageDataKeyGet -- + * TclpThreadDataKeyGet -- * * This procedure returns a pointer to a block of thread local storage. * @@ -624,37 +331,24 @@ TclThreadStorageDataKeyInit(keyPtr) */ void * -TclThreadStorageDataKeyGet(keyPtr) +TclpThreadDataKeyGet(keyPtr) Tcl_ThreadDataKey *keyPtr; /* Identifier for the data chunk, really * (int**) */ { - int *indexPtr = *(int **)keyPtr; - - if (indexPtr == NULL) { + Tcl_HashTable *hashTablePtr = + ThreadStorageGetHashTable(Tcl_GetCurrentThread()); + Tcl_HashEntry *hPtr = + Tcl_FindHashEntry(hashTablePtr, (char *) keyPtr); + if (hPtr == NULL) { return NULL; - } else { - Tcl_HashTable *hashTablePtr = - ThreadStorageGetHashTable(Tcl_GetCurrentThread()); - Tcl_HashEntry *hPtr; - - if (hashTablePtr == NULL) { - Tcl_Panic("ThreadStorageGetHashTable failed from " - "ThreadStorageDataKeyGet!"); - } - - hPtr = Tcl_FindHashEntry(hashTablePtr, (char *) *indexPtr); - - if (hPtr == NULL) { - return NULL; - } - return (void *) Tcl_GetHashValue(hPtr); } + return (void *) Tcl_GetHashValue(hPtr); } /* *---------------------------------------------------------------------- * - * TclThreadStorageDataKeySet -- + * TclpThreadDataKeySet -- * * This procedure sets the pointer to a block of thread local storage. * @@ -662,29 +356,23 @@ TclThreadStorageDataKeyGet(keyPtr) * None. * * Side effects: - * Sets up the thread so future calls to TclThreadStorageDataKeyGet with + * Sets up the thread so future calls to TclpThreadDataKeyGet with * this key will return the data pointer. * *---------------------------------------------------------------------- */ void -TclThreadStorageDataKeySet(keyPtr, data) +TclpThreadDataKeySet(keyPtr, data) Tcl_ThreadDataKey *keyPtr; /* Identifier for the data chunk, really * (pthread_key_t **) */ void *data; /* Thread local storage */ { - int *indexPtr = *(int **)keyPtr; Tcl_HashTable *hashTablePtr; Tcl_HashEntry *hPtr; hashTablePtr = ThreadStorageGetHashTable(Tcl_GetCurrentThread()); - if (hashTablePtr == NULL) { - Tcl_Panic("ThreadStorageGetHashTable failed from " - "TclThreadStorageDataKeySet!"); - } - - hPtr = Tcl_FindHashEntry(hashTablePtr, (char *)*indexPtr); + hPtr = Tcl_FindHashEntry(hashTablePtr, (char *)keyPtr); /* * Does the item need to be created? @@ -692,12 +380,7 @@ TclThreadStorageDataKeySet(keyPtr, data) 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"); - } + hPtr = Tcl_CreateHashEntry(hashTablePtr, (char *)keyPtr, &new); } Tcl_SetHashValue(hPtr, data); @@ -706,73 +389,94 @@ TclThreadStorageDataKeySet(keyPtr, data) /* *---------------------------------------------------------------------- * - * FinalizeThreadStorageThreadMushroomMushroom -- + * TclpFinalizeThreadDataThread -- * * This procedure cleans up the thread storage hash table for the - * specified thread. + * current thread. * * Results: * None. * * Side effects: - * None. + * Frees all associated thread storage, all hash table entries for + * the thread's thread storage, and the hash table itself. * *---------------------------------------------------------------------- */ -static void -FinalizeThreadStorageThreadMushroomMushroom(id) - Tcl_ThreadId id; /* Id of the thread to finalize. */ +void +TclpFinalizeThreadDataThread() { + Tcl_ThreadId id = Tcl_GetCurrentThread(); + /* 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; + /* 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. */ - ThreadStorageLock(); - - if (threadStorageHashTablePtr != NULL) { - hPtr = Tcl_FindHashEntry(threadStorageHashTablePtr, (char *)id); - - 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. - */ - - Tcl_DeleteHashTable(hashTablePtr); - TclpSysFree((char *)hashTablePtr); - } + 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) { /* - * Delete thread specific entry from master hash table. + * We do not step on another thread's cache entry. This is + * especially important if we are creating and exiting a lot + * of threads. */ - - Tcl_DeleteHashEntry(hPtr); + threadStorageCache[index].id = STORAGE_INVALID_THREAD; + threadStorageCache[index].hashTablePtr = NULL; } } + Tcl_MutexUnlock(&threadStorageLock); - /* - * Make sure cache entry for this thread is NULL. + /* + * The thread's hash table has been extracted and removed from the master + * hash table. Now clean up the thread. */ - if (threadStorageCache[index].id == id) { + 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); + } + } + /* - * We do not step on another thread's cache entry. This is especially - * important if we are creating and exiting a lot of threads. + * Delete thread specific hash table and free the struct. */ - - threadStorageCache[index].id = STORAGE_INVALID_THREAD; - threadStorageCache[index].hashTablePtr = NULL; + + Tcl_DeleteHashTable(hashTablePtr); + TclpSysFree((char *)hashTablePtr); } - - ThreadStorageUnlock(); } /* @@ -796,51 +500,41 @@ FinalizeThreadStorageThreadMushroomMushroom(id) void TclFinalizeThreadStorage() { - ThreadStorageLock(); - - if (threadStorageHashTablePtr != NULL) { - Tcl_HashSearch search; /* We need to hit every thread with + Tcl_HashSearch search; /* We need to hit every thread with * this search. */ - Tcl_HashEntry *hPtr; /* Hash entry for current thread in + 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(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); - } - + /* + * 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 entry from master hash table. + * Delete thread specific hash table for the thread in + * question and free the struct. */ - - Tcl_SetHashValue(hPtr, NULL); + + Tcl_DeleteHashTable(hashTablePtr); + TclpSysFree((char *)hashTablePtr); } - - Tcl_DeleteHashTable(threadStorageHashTablePtr); - TclpSysFree((char *)threadStorageHashTablePtr); - + /* - * Reset this so that next time around we know it's not valid. + * Delete thread specific entry from master hash table. */ - - threadStorageHashTablePtr = NULL; + + Tcl_SetHashValue(hPtr, NULL); } - + + Tcl_DeleteHashTable(&threadStorageHashTable); + /* * Clear out the thread storage cache as well. */ @@ -855,216 +549,28 @@ TclFinalizeThreadStorage() nextThreadStorageKey = STORAGE_INVALID_KEY; - ThreadStorageUnlock(); -} - -/* - *---------------------------------------------------------------------- - * - * TclFinalizeThreadStorageData -- - * - * This procedure cleans up the thread-local storage. This is called - * once for each thread. - * - * Results: - * None. - * - * Side effects: - * Frees up the memory. - * - *---------------------------------------------------------------------- - */ - -void -TclFinalizeThreadStorageData(keyPtr) - Tcl_ThreadDataKey *keyPtr; -{ - if (*keyPtr != NULL) { - 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 = ThreadStorageGetHashTable(id); - if (hashTablePtr == NULL) { - Tcl_Panic("ThreadStorageGetHashTable 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); - } - } -} - -/* - *---------------------------------------------------------------------- - * - * 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 assumes the master lock is held. - * - * Results: - * None. - * - * Side effects: - * The key is deallocated. - * - *---------------------------------------------------------------------- - */ - -void -TclFinalizeThreadStorageDataKey(keyPtr) - Tcl_ThreadDataKey *keyPtr; -{ - int *indexPtr; - 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; - - ThreadStorageLock(); - - 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); - } - } - } - } - - ThreadStorageUnlock(); - - TclpSysFree((char *)indexPtr); - *keyPtr = NULL; - } + Tcl_MutexUnlock(&threadStorageLock); } -#else /* !defined(TCL_THREADS) || !defined(USE_THREAD_STORAGE) */ - -static void ThreadStoragePanic(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. - * - * Results: - * None. - * - * Side effects: - * None. - * - *---------------------------------------------------------------------- - */ +#else /* !defined(TCL_THREADS) */ -static void -ThreadStoragePanic( - 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). - */ -# else - Tcl_Panic("Tcl was not compiled with thread storage enabled."); -# endif /* USE_THREAD_STORAGE */ -#else - Tcl_Panic("Tcl was not compiled with threads enabled."); -#endif /* TCL_THREADS */ -} - /* - * Stub functions that just call ThreadStoragePanic. + * Stub functions for non-threaded builds */ void -TclThreadStorageDataKeyInit(Tcl_ThreadDataKey *keyPtr) -{ - ThreadStoragePanic(NULL); -} - -void * -TclThreadStorageDataKeyGet(Tcl_ThreadDataKey *keyPtr) +TclInitThreadStorage() { - ThreadStoragePanic(NULL); - return NULL; } void -TclThreadStorageDataKeySet(Tcl_ThreadDataKey *keyPtr, void *data) +TclpFinalizeThreadDataThread() { - ThreadStoragePanic(NULL); } void TclFinalizeThreadStorage() { - ThreadStoragePanic(NULL); -} - -void -TclFinalizeThreadStorageData(Tcl_ThreadDataKey *keyPtr) -{ - ThreadStoragePanic(NULL); -} - -void -TclFinalizeThreadStorageDataKey(Tcl_ThreadDataKey *keyPtr) -{ - ThreadStoragePanic(NULL); } #endif /* defined(TCL_THREADS) && defined(USE_THREAD_STORAGE) */ |