summaryrefslogtreecommitdiffstats
path: root/generic
diff options
context:
space:
mode:
authorKevin B Kenny <kennykb@acm.org>2005-08-11 22:06:46 (GMT)
committerKevin B Kenny <kennykb@acm.org>2005-08-11 22:06:46 (GMT)
commit9b74d96f71d7f7d8bc7bd3a5956a1d3132c2330a (patch)
tree0331916c4d6eb03e334e0686ab5ed947945519aa /generic
parent3d07a4f66acb32cc71599c3192ae22c380c6520f (diff)
downloadtcl-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.c28
-rw-r--r--generic/tclInt.h21
-rw-r--r--generic/tclThread.c134
-rw-r--r--generic/tclThreadStorage.c860
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) */