From 29cd385014b7d98f9e1209da72adade7679e3cf2 Mon Sep 17 00:00:00 2001 From: Joe Mistachkin Date: Thu, 24 Jun 2004 01:29:02 +0000 Subject: Version 5 of [Patch 976496] --- ChangeLog | 20 + generic/tclInt.decls | 44 +- generic/tclIntDecls.h | 141 +++++- generic/tclStubInit.c | 15 +- generic/tclThread.c | 41 +- generic/tclThreadStorage.c | 1013 ++++++++++++++++++++++++++++++++++++++++++++ unix/Makefile.in | 10 +- unix/configure | 6 + unix/tcl.m4 | 3 + win/Makefile.in | 3 +- win/configure | 6 + win/makefile.vc | 4 +- win/rules.vc | 11 +- win/tcl.m4 | 3 + 14 files changed, 1309 insertions(+), 11 deletions(-) create mode 100644 generic/tclThreadStorage.c diff --git a/ChangeLog b/ChangeLog index dbf4ca6..b71113a 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,23 @@ +2004-06-23 Joe Mistachkin + + * generic/tclThread.c: Implements platform independent thread storage + * generic/tclThreadStorage.c: mechanism and fixes associated bugs on + platforms where there is limited thread local storage space + (Win98/WinNT4). [Patch 976496] + + * generic/tclInt.decls: + * generic/tclIntDecls.h: Added thread storage functions to the + * generic/tclStubInit.c: internal stubs table. + + * unix/Makefile.in: + * unix/configure: + * unix/tcl.m4: + * win/makefile.vc: + * win/rules.vc: + * win/Makefile.in: Modified the unix, VC++, and Cygwin build systems + * win/configure: to include the new "tclThreadStorage.c" and the new + * win/tcl.m4: USE_THREAD_STORAGE define. + 2004-06-23 Pat Thoyts * tests/io.test: Added -force to 18.1 and 18.2. This was failing diff --git a/generic/tclInt.decls b/generic/tclInt.decls index d935a47..0f82b17 100644 --- a/generic/tclInt.decls +++ b/generic/tclInt.decls @@ -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.decls,v 1.74 2004/06/05 17:31:08 kennykb Exp $ +# RCS: @(#) $Id: tclInt.decls,v 1.75 2004/06/24 01:29:02 mistachkin Exp $ library tcl @@ -745,6 +745,48 @@ declare 183 generic { struct tm *TclpGmtime(CONST time_t *clock) } +# For the new "Thread Storage" subsystem. + +declare 184 generic { + void TclThreadStorageLockInit(void) +} +declare 185 generic { + void TclThreadStorageLock(void) +} +declare 186 generic { + void TclThreadStorageUnlock(void) +} +declare 187 generic { + void TclThreadStoragePrint(FILE *outFile, int flags) +} +declare 188 generic { + Tcl_HashTable *TclThreadStorageGetHashTable(Tcl_ThreadId id) +} +declare 189 generic { + Tcl_HashTable *TclThreadStorageInit(Tcl_ThreadId id, void *reserved) +} +declare 190 generic { + void TclThreadStorageDataKeyInit(Tcl_ThreadDataKey *keyPtr) +} +declare 191 generic { + void *TclThreadStorageDataKeyGet(Tcl_ThreadDataKey *keyPtr) +} +declare 192 generic { + void TclThreadStorageDataKeySet(Tcl_ThreadDataKey *keyPtr, void *data) +} +declare 193 generic { + void TclFinalizeThreadStorageThread(Tcl_ThreadId id) +} +declare 194 generic { + void TclFinalizeThreadStorage(void) +} +declare 195 generic { + void TclFinalizeThreadStorageData(Tcl_ThreadDataKey *keyPtr) +} +declare 196 generic { + void TclFinalizeThreadStorageDataKey(Tcl_ThreadDataKey *keyPtr) +} + ############################################################################## # Define the platform specific internal Tcl interface. These functions are diff --git a/generic/tclIntDecls.h b/generic/tclIntDecls.h index aa6e007..216992b 100644 --- a/generic/tclIntDecls.h +++ b/generic/tclIntDecls.h @@ -11,7 +11,7 @@ * See the file "license.terms" for information on usage and redistribution * of this file, and for a DISCLAIMER OF ALL WARRANTIES. * - * RCS: @(#) $Id: tclIntDecls.h,v 1.64 2004/06/07 16:48:45 dgp Exp $ + * RCS: @(#) $Id: tclIntDecls.h,v 1.65 2004/06/24 01:29:03 mistachkin Exp $ */ #ifndef _TCLINTDECLS @@ -969,6 +969,80 @@ EXTERN struct tm * TclpLocaltime _ANSI_ARGS_((CONST time_t * clock)); /* 183 */ EXTERN struct tm * TclpGmtime _ANSI_ARGS_((CONST time_t * clock)); #endif +#ifndef TclThreadStorageLockInit_TCL_DECLARED +#define TclThreadStorageLockInit_TCL_DECLARED +/* 184 */ +EXTERN void TclThreadStorageLockInit _ANSI_ARGS_((void)); +#endif +#ifndef TclThreadStorageLock_TCL_DECLARED +#define TclThreadStorageLock_TCL_DECLARED +/* 185 */ +EXTERN void TclThreadStorageLock _ANSI_ARGS_((void)); +#endif +#ifndef TclThreadStorageUnlock_TCL_DECLARED +#define TclThreadStorageUnlock_TCL_DECLARED +/* 186 */ +EXTERN void TclThreadStorageUnlock _ANSI_ARGS_((void)); +#endif +#ifndef TclThreadStoragePrint_TCL_DECLARED +#define TclThreadStoragePrint_TCL_DECLARED +/* 187 */ +EXTERN void TclThreadStoragePrint _ANSI_ARGS_((FILE * outFile, + int flags)); +#endif +#ifndef TclThreadStorageGetHashTable_TCL_DECLARED +#define TclThreadStorageGetHashTable_TCL_DECLARED +/* 188 */ +EXTERN Tcl_HashTable * TclThreadStorageGetHashTable _ANSI_ARGS_(( + Tcl_ThreadId id)); +#endif +#ifndef TclThreadStorageInit_TCL_DECLARED +#define TclThreadStorageInit_TCL_DECLARED +/* 189 */ +EXTERN Tcl_HashTable * TclThreadStorageInit _ANSI_ARGS_((Tcl_ThreadId id, + void * reserved)); +#endif +#ifndef TclThreadStorageDataKeyInit_TCL_DECLARED +#define TclThreadStorageDataKeyInit_TCL_DECLARED +/* 190 */ +EXTERN void TclThreadStorageDataKeyInit _ANSI_ARGS_(( + Tcl_ThreadDataKey * keyPtr)); +#endif +#ifndef TclThreadStorageDataKeyGet_TCL_DECLARED +#define TclThreadStorageDataKeyGet_TCL_DECLARED +/* 191 */ +EXTERN void * TclThreadStorageDataKeyGet _ANSI_ARGS_(( + Tcl_ThreadDataKey * keyPtr)); +#endif +#ifndef TclThreadStorageDataKeySet_TCL_DECLARED +#define TclThreadStorageDataKeySet_TCL_DECLARED +/* 192 */ +EXTERN void TclThreadStorageDataKeySet _ANSI_ARGS_(( + Tcl_ThreadDataKey * keyPtr, void * data)); +#endif +#ifndef TclFinalizeThreadStorageThread_TCL_DECLARED +#define TclFinalizeThreadStorageThread_TCL_DECLARED +/* 193 */ +EXTERN void TclFinalizeThreadStorageThread _ANSI_ARGS_(( + Tcl_ThreadId id)); +#endif +#ifndef TclFinalizeThreadStorage_TCL_DECLARED +#define TclFinalizeThreadStorage_TCL_DECLARED +/* 194 */ +EXTERN void TclFinalizeThreadStorage _ANSI_ARGS_((void)); +#endif +#ifndef TclFinalizeThreadStorageData_TCL_DECLARED +#define TclFinalizeThreadStorageData_TCL_DECLARED +/* 195 */ +EXTERN void TclFinalizeThreadStorageData _ANSI_ARGS_(( + Tcl_ThreadDataKey * keyPtr)); +#endif +#ifndef TclFinalizeThreadStorageDataKey_TCL_DECLARED +#define TclFinalizeThreadStorageDataKey_TCL_DECLARED +/* 196 */ +EXTERN void TclFinalizeThreadStorageDataKey _ANSI_ARGS_(( + Tcl_ThreadDataKey * keyPtr)); +#endif typedef struct TclIntStubs { int magic; @@ -1173,6 +1247,19 @@ typedef struct TclIntStubs { Tcl_Obj * (*tclDbNewListObjDirect) _ANSI_ARGS_((int objc, Tcl_Obj ** objv, CONST char * file, int line)); /* 181 */ struct tm * (*tclpLocaltime) _ANSI_ARGS_((CONST time_t * clock)); /* 182 */ struct tm * (*tclpGmtime) _ANSI_ARGS_((CONST time_t * clock)); /* 183 */ + void (*tclThreadStorageLockInit) _ANSI_ARGS_((void)); /* 184 */ + void (*tclThreadStorageLock) _ANSI_ARGS_((void)); /* 185 */ + void (*tclThreadStorageUnlock) _ANSI_ARGS_((void)); /* 186 */ + void (*tclThreadStoragePrint) _ANSI_ARGS_((FILE * outFile, int flags)); /* 187 */ + Tcl_HashTable * (*tclThreadStorageGetHashTable) _ANSI_ARGS_((Tcl_ThreadId id)); /* 188 */ + Tcl_HashTable * (*tclThreadStorageInit) _ANSI_ARGS_((Tcl_ThreadId id, void * reserved)); /* 189 */ + void (*tclThreadStorageDataKeyInit) _ANSI_ARGS_((Tcl_ThreadDataKey * keyPtr)); /* 190 */ + void * (*tclThreadStorageDataKeyGet) _ANSI_ARGS_((Tcl_ThreadDataKey * keyPtr)); /* 191 */ + void (*tclThreadStorageDataKeySet) _ANSI_ARGS_((Tcl_ThreadDataKey * keyPtr, void * data)); /* 192 */ + void (*tclFinalizeThreadStorageThread) _ANSI_ARGS_((Tcl_ThreadId id)); /* 193 */ + void (*tclFinalizeThreadStorage) _ANSI_ARGS_((void)); /* 194 */ + void (*tclFinalizeThreadStorageData) _ANSI_ARGS_((Tcl_ThreadDataKey * keyPtr)); /* 195 */ + void (*tclFinalizeThreadStorageDataKey) _ANSI_ARGS_((Tcl_ThreadDataKey * keyPtr)); /* 196 */ } TclIntStubs; #ifdef __cplusplus @@ -1817,6 +1904,58 @@ extern TclIntStubs *tclIntStubsPtr; #define TclpGmtime \ (tclIntStubsPtr->tclpGmtime) /* 183 */ #endif +#ifndef TclThreadStorageLockInit +#define TclThreadStorageLockInit \ + (tclIntStubsPtr->tclThreadStorageLockInit) /* 184 */ +#endif +#ifndef TclThreadStorageLock +#define TclThreadStorageLock \ + (tclIntStubsPtr->tclThreadStorageLock) /* 185 */ +#endif +#ifndef TclThreadStorageUnlock +#define TclThreadStorageUnlock \ + (tclIntStubsPtr->tclThreadStorageUnlock) /* 186 */ +#endif +#ifndef TclThreadStoragePrint +#define TclThreadStoragePrint \ + (tclIntStubsPtr->tclThreadStoragePrint) /* 187 */ +#endif +#ifndef TclThreadStorageGetHashTable +#define TclThreadStorageGetHashTable \ + (tclIntStubsPtr->tclThreadStorageGetHashTable) /* 188 */ +#endif +#ifndef TclThreadStorageInit +#define TclThreadStorageInit \ + (tclIntStubsPtr->tclThreadStorageInit) /* 189 */ +#endif +#ifndef TclThreadStorageDataKeyInit +#define TclThreadStorageDataKeyInit \ + (tclIntStubsPtr->tclThreadStorageDataKeyInit) /* 190 */ +#endif +#ifndef TclThreadStorageDataKeyGet +#define TclThreadStorageDataKeyGet \ + (tclIntStubsPtr->tclThreadStorageDataKeyGet) /* 191 */ +#endif +#ifndef TclThreadStorageDataKeySet +#define TclThreadStorageDataKeySet \ + (tclIntStubsPtr->tclThreadStorageDataKeySet) /* 192 */ +#endif +#ifndef TclFinalizeThreadStorageThread +#define TclFinalizeThreadStorageThread \ + (tclIntStubsPtr->tclFinalizeThreadStorageThread) /* 193 */ +#endif +#ifndef TclFinalizeThreadStorage +#define TclFinalizeThreadStorage \ + (tclIntStubsPtr->tclFinalizeThreadStorage) /* 194 */ +#endif +#ifndef TclFinalizeThreadStorageData +#define TclFinalizeThreadStorageData \ + (tclIntStubsPtr->tclFinalizeThreadStorageData) /* 195 */ +#endif +#ifndef TclFinalizeThreadStorageDataKey +#define TclFinalizeThreadStorageDataKey \ + (tclIntStubsPtr->tclFinalizeThreadStorageDataKey) /* 196 */ +#endif #endif /* defined(USE_TCL_STUBS) && !defined(USE_TCL_STUB_PROCS) */ diff --git a/generic/tclStubInit.c b/generic/tclStubInit.c index a4c32df..259e4ad 100644 --- a/generic/tclStubInit.c +++ b/generic/tclStubInit.c @@ -8,7 +8,7 @@ * See the file "license.terms" for information on usage and redistribution * of this file, and for a DISCLAIMER OF ALL WARRANTIES. * - * RCS: @(#) $Id: tclStubInit.c,v 1.98 2004/06/07 16:48:45 dgp Exp $ + * RCS: @(#) $Id: tclStubInit.c,v 1.99 2004/06/24 01:29:03 mistachkin Exp $ */ #include "tclInt.h" @@ -268,6 +268,19 @@ TclIntStubs tclIntStubs = { TclDbNewListObjDirect, /* 181 */ TclpLocaltime, /* 182 */ TclpGmtime, /* 183 */ + TclThreadStorageLockInit, /* 184 */ + TclThreadStorageLock, /* 185 */ + TclThreadStorageUnlock, /* 186 */ + TclThreadStoragePrint, /* 187 */ + TclThreadStorageGetHashTable, /* 188 */ + TclThreadStorageInit, /* 189 */ + TclThreadStorageDataKeyInit, /* 190 */ + TclThreadStorageDataKeyGet, /* 191 */ + TclThreadStorageDataKeySet, /* 192 */ + TclFinalizeThreadStorageThread, /* 193 */ + TclFinalizeThreadStorage, /* 194 */ + TclFinalizeThreadStorageData, /* 195 */ + TclFinalizeThreadStorageDataKey, /* 196 */ }; TclIntPlatStubs tclIntPlatStubs = { diff --git a/generic/tclThread.c b/generic/tclThread.c index 4a2d4b8..7cc8b68 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.7 2004/04/23 07:21:36 davygrvy Exp $ + * RCS: @(#) $Id: tclThread.c,v 1.8 2004/06/24 01:29:02 mistachkin Exp $ */ #include "tclInt.h" @@ -89,18 +89,30 @@ Tcl_GetThreadData(keyPtr, size) */ if (*keyPtr == NULL) { +#ifdef USE_THREAD_STORAGE + TclThreadStorageDataKeyInit(keyPtr); +#else TclpThreadDataKeyInit(keyPtr); +#endif } /* * Initialize the key for this thread. */ - +#ifdef USE_THREAD_STORAGE + result = TclThreadStorageDataKeyGet(keyPtr); +#else result = TclpThreadDataKeyGet(keyPtr); +#endif + 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 } #else if (*keyPtr == NULL) { @@ -137,7 +149,11 @@ TclThreadDataKeyGet(keyPtr) * really (pthread_key_t **) */ { #ifdef TCL_THREADS +#ifdef USE_THREAD_STORAGE + return (VOID *)TclThreadStorageDataKeyGet(keyPtr); +#else return (VOID *)TclpThreadDataKeyGet(keyPtr); +#endif #else char *result = *(char **)keyPtr; return (VOID *)result; @@ -169,9 +185,17 @@ TclThreadDataKeySet(keyPtr, data) { #ifdef TCL_THREADS if (*keyPtr == NULL) { +#ifdef USE_THREAD_STORAGE + TclThreadStorageDataKeyInit(keyPtr); +#else TclpThreadDataKeyInit(keyPtr); +#endif } +#ifdef USE_THREAD_STORAGE + TclThreadStorageDataKeySet(keyPtr, data); +#else TclpThreadDataKeySet(keyPtr, data); +#endif #else *keyPtr = (Tcl_ThreadDataKey)data; #endif /* TCL_THREADS */ @@ -409,7 +433,11 @@ TclFinalizeThreadData() for (i=0 ; ikey.oneWordValue = (char *)keyPtr; + + 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(hPtr) + Tcl_HashEntry *hPtr; /* Hash entry to free. */ +{ + TclpSysFree((char *)hPtr); +} + +/* + *---------------------------------------------------------------------- + * + * TclThreadStoragePrint -- + * + * This procedure prints out the contents of the master thread + * storage hash table, the thread storage cache, and the next key + * value to the specified file. + * + * This assumes that thread storage lock is held. + * + * Results: + * None. + * + * Side effects: + * The thread storage lock is acquired and released. + * + *---------------------------------------------------------------------- + */ + +void +TclThreadStoragePrint(outFile, flags) + FILE *outFile; /* The file to print the information to. */ + int flags; /* Reserved for future use. */ +{ + Tcl_HashEntry *hPtr; + Tcl_HashSearch search; +#if 0 + /* Please see comment regarding Tcl_HashStats below. */ + CONST char *stats; +#endif + int header; + int 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); +#if 0 + /* + * Currently disabled due to Tcl_HashStats use of ckalloc and ckfree. + * Please note that this can produce a LOT of output. + */ + if (threadStorageCache[index].hashTablePtr != NULL) { + stats = Tcl_HashStats(threadStorageCache[index].hashTablePtr); + if (stats != NULL) { + fprintf(outFile, "%s\n", stats); + ckfree((void *)stats); + } else { + fprintf(outFile, "could not get table statistics for slot %d\n", index); + } + } +#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); +} + +/* + *---------------------------------------------------------------------- + * + * TclThreadStorageGetHashTable -- + * + * This procedure returns a hash table pointer to be used for thread + * storage for the specified thread. + * + * Results: + * A hash table pointer for the specified thread, or NULL + * if the hash table has not been created yet. + * + * This assumes that thread storage lock is held. + * + * Side effects: + * May change an entry in the master thread storage cache to point + * to the specified thread and it's associated hash table. + * + *---------------------------------------------------------------------- + */ + +Tcl_HashTable * +TclThreadStorageGetHashTable(id) + Tcl_ThreadId id; /* Id of the thread to get the hash table for */ +{ + int index = (int)id % STORAGE_CACHE_SLOTS; + Tcl_HashEntry *hPtr; + int new; + + /* + * It's important that we pick up the hash table pointer BEFORE + * comparing thread Id in case another thread is in the critical + * region changing things out from under you. + */ + Tcl_HashTable *hashTablePtr = threadStorageCache[index].hashTablePtr; + + if (threadStorageCache[index].id != id) { + TclThreadStorageLock(); + + /* make sure the master hash table is initialized. */ + TclThreadStorageInit(STORAGE_INVALID_THREAD, NULL); + + if (threadStorageHashTablePtr != NULL) { + /* it's not in the cache, so we look it up... */ + hPtr = Tcl_FindHashEntry(threadStorageHashTablePtr, (char *)id); + + if (hPtr != NULL) { + /* we found it, extract the hash table pointer. */ + hashTablePtr = Tcl_GetHashValue(hPtr); + } else { + /* the thread specific hash table is not found. */ + hashTablePtr = NULL; + } + + if (hashTablePtr == NULL) { + hashTablePtr = (Tcl_HashTable *)TclpSysAlloc(sizeof(Tcl_HashTable), 0); + + if (hashTablePtr != NULL) { + Tcl_InitCustomHashTable(hashTablePtr, TCL_CUSTOM_TYPE_KEYS, &tclThreadStorageHashKeyType); + + /* add new thread storage hash table to the master hash table */ + hPtr = Tcl_CreateHashEntry(threadStorageHashTablePtr, (char *)id, &new); + + if (hPtr != NULL) { + Tcl_SetHashValue(hPtr, hashTablePtr); + } else { + Tcl_Panic("Tcl_CreateHashEntry failed from TclThreadStorageInit!"); + } + } else { + Tcl_Panic("could not allocate thread specific hash table, TclpSysAlloc failed from TclThreadStorageGetHashTable!"); + } + } + + /* now, we put it in the cache since it is highly likely it will + be needed again shortly. */ + threadStorageCache[index].id = id; + threadStorageCache[index].hashTablePtr = hashTablePtr; + } else { + /* we cannot look it up, the master hash table has not been initialized. */ + hashTablePtr = NULL; + } + TclThreadStorageUnlock(); + } + + return hashTablePtr; +} + +/* + *---------------------------------------------------------------------- + * + * TclThreadStorageInit -- + * + * This procedure initializes a thread specific hash table for the + * current thread. It may also initialize the master hash table which + * stores all the thread specific hash tables. + * + * This 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. + * + *---------------------------------------------------------------------- + */ + +Tcl_HashTable * +TclThreadStorageInit(id, reserved) + Tcl_ThreadId id; /* Id of the thread to get the hash table for */ + void *reserved; /* reserved for future use */ +{ +#if 0 /* #ifdef TCL_THREAD_STORAGE_DEBUG */ + TclThreadStoragePrint(stderr, 0); +#endif + + if (threadStorageHashTablePtr == NULL) { + /* looks like we haven't created the outer hash table yet + we can just do that now. */ + threadStorageHashTablePtr = (Tcl_HashTable *)TclpSysAlloc(sizeof(Tcl_HashTable), 0); + + if (threadStorageHashTablePtr != NULL) { + /* initialize the hash table */ + Tcl_InitCustomHashTable(threadStorageHashTablePtr, TCL_CUSTOM_TYPE_KEYS, &tclThreadStorageHashKeyType); + } else { + Tcl_Panic("could not allocate master thread storage hash table, TclpSysAlloc failed from TclThreadStorageInit!"); + } + + /* we also initialize the cache. */ + memset((ThreadStorage *)&threadStorageCache, 0, sizeof(ThreadStorage) * STORAGE_CACHE_SLOTS); + + /* now, we set the first value to be used for a thread data key. */ + nextThreadStorageKey = STORAGE_FIRST_KEY; + } + + return NULL; +} + +/* + *---------------------------------------------------------------------- + * + * TclThreadStorageDataKeyInit -- + * + * This procedure initializes a thread specific data block key. + * Each thread has table of pointers to thread specific data. + * all threads agree on which table entry is used by each module. + * this is remembered in a "data key", that is just an index into + * this table. To allow self initialization, the interface + * passes a pointer to this key and the first thread to use + * the key fills in the pointer to the key. The key should be + * a process-wide static. + * + * 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. + * + *---------------------------------------------------------------------- + */ + +void +TclThreadStorageDataKeyInit(keyPtr) + Tcl_ThreadDataKey *keyPtr; /* Identifier for the data chunk, + * really (int **) */ +{ + int *indexPtr; + int newKey; + + if (*keyPtr == NULL) { + indexPtr = (int *)TclpSysAlloc(sizeof(int), 0); + + if (indexPtr != NULL) { + /* + * We must call this now to make sure + * that nextThreadStorageKey has a well defined + * value. + */ + TclThreadStorageLock(); + + /* make sure the master hash table is initialized. */ + TclThreadStorageInit(STORAGE_INVALID_THREAD, NULL); + + /* + * These data key values are sequentially + * assigned and we must use the storage + * lock to prevent serious problems here. + * Also note that the caller should + * NOT make any assumptions about the + * provided values. In particular, we may + * need to reserve some values in the + * future. + */ + newKey = nextThreadStorageKey++; + TclThreadStorageUnlock(); + + *indexPtr = newKey; + *keyPtr = (Tcl_ThreadDataKey)indexPtr; + TclRememberDataKey(keyPtr); + } else { + Tcl_Panic("TclpSysAlloc failed from TclThreadStorageDataKeyInit!"); /* this should also be a fatal error */ + } + } +} + +/* + *---------------------------------------------------------------------- + * + * TclThreadStorageDataKeyGet -- + * + * 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 * +TclThreadStorageDataKeyGet(keyPtr) + Tcl_ThreadDataKey *keyPtr; /* Identifier for the data chunk, + * really (int **) */ +{ + int *indexPtr = *(int **)keyPtr; + void *result; + Tcl_ThreadId id = Tcl_GetCurrentThread(); + Tcl_HashTable *hashTablePtr; + Tcl_HashEntry *hPtr; + + if (indexPtr == NULL) { + result = NULL; + } else { + hashTablePtr = TclThreadStorageGetHashTable(id); + + if (hashTablePtr != NULL) { + hPtr = Tcl_FindHashEntry(hashTablePtr, (char *)*indexPtr); + + if (hPtr != NULL) { + result = (void *)Tcl_GetHashValue(hPtr); + } else { + result = NULL; + } + } else { + Tcl_Panic("TclThreadStorageGetHashTable failed from TclThreadStorageDataKeyGet!"); + result = NULL; + } + } + return result; +} + +/* + *---------------------------------------------------------------------- + * + * TclThreadStorageDataKeySet -- + * + * This procedure sets the pointer to a block of thread local storage. + * + * Results: + * None. + * + * Side effects: + * Sets up the thread so future calls to TclThreadStorageDataKeyGet + * with this key will return the data pointer. + * + *---------------------------------------------------------------------- + */ + +void +TclThreadStorageDataKeySet(keyPtr, data) + Tcl_ThreadDataKey *keyPtr; /* Identifier for the data chunk, + * really (pthread_key_t **) */ + void *data; /* Thread local storage */ +{ + int *indexPtr = *(int **)keyPtr; + Tcl_ThreadId id = Tcl_GetCurrentThread(); + Tcl_HashTable *hashTablePtr; + Tcl_HashEntry *hPtr; + int new; + + hashTablePtr = TclThreadStorageGetHashTable(id); + + if (hashTablePtr != NULL) { + hPtr = Tcl_FindHashEntry(hashTablePtr, (char *)*indexPtr); + + /* does the item need to be created? */ + if (hPtr == NULL) { + hPtr = Tcl_CreateHashEntry(hashTablePtr, (char *)*indexPtr, &new); + } + + if (hPtr != NULL) { + Tcl_SetHashValue(hPtr, data); + } else { + Tcl_Panic("could not set hash entry value from TclThreadStorageDataKeySet"); + } + } else { + Tcl_Panic("TclThreadStorageGetHashTable failed from TclThreadStorageDataKeySet!"); + } +} + +/* + *---------------------------------------------------------------------- + * + * TclFinalizeThreadStorageThread -- + * + * This procedure cleans up the thread storage hash table for the + * specified thread. + * + * Results: + * None. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ +void +TclFinalizeThreadStorageThread(id) + Tcl_ThreadId id; /* Id of the thread to finalize */ +{ + int index = (int)id % STORAGE_CACHE_SLOTS; + Tcl_HashTable *hashTablePtr; /* hash table for current thread */ + Tcl_HashEntry *hPtr; /* hash entry for current thread in master table */ + + TclThreadStorageLock(); + if (threadStorageHashTablePtr != NULL) { + hPtr = Tcl_FindHashEntry(threadStorageHashTablePtr, (char *)id); + + 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); + } + + /* delete thread specific entry from master hash table. */ + Tcl_DeleteHashEntry(hPtr); + } + } + + /* + * Make sure cache entry for this thread is NULL. + */ + if (threadStorageCache[index].id == id) { + /* + * We do not step on another thread's cache entry. + * This is especially important if we are creating + * and exiting a lot of threads. + */ + threadStorageCache[index].id = STORAGE_INVALID_THREAD; + threadStorageCache[index].hashTablePtr = NULL; + } + TclThreadStorageUnlock(); +} + +/* + *---------------------------------------------------------------------- + * + * 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() +{ + Tcl_HashTable *hashTablePtr; /* hash table for current thread */ + Tcl_HashSearch search; /* we need to hit every thread with this search */ + Tcl_HashEntry *hPtr; /* hash entry for current thread in master table */ + + TclThreadStorageLock(); + if (threadStorageHashTablePtr != NULL) { + /* + * We are going to delete the hash table for every thread now. This + * hash table should be empty at this point, except for one entry + * for the current thread. + */ + for (hPtr = Tcl_FirstHashEntry(threadStorageHashTablePtr, &search); + hPtr != NULL; hPtr = Tcl_NextHashEntry(&search)) { + + /* get the hash table corresponding to the thread in question. */ + hashTablePtr = Tcl_GetHashValue(hPtr); + + if (hashTablePtr != NULL) { + /* delete thread specific hash table and free the struct. */ + Tcl_DeleteHashTable(hashTablePtr); + TclpSysFree((char *)hashTablePtr); + } + + /* delete thread specific entry from master hash table. */ + Tcl_SetHashValue(hPtr, NULL); + } + + Tcl_DeleteHashTable(threadStorageHashTablePtr); + TclpSysFree((char *)threadStorageHashTablePtr); + + /* reset this so that next time around we know it's not valid. */ + threadStorageHashTablePtr = NULL; + } + + /* clear out the thread storage cache as well */ + memset((ThreadStorage *)&threadStorageCache, 0, sizeof(ThreadStorage) * STORAGE_CACHE_SLOTS); + + /* reset this to zero, it will be set to STORAGE_FIRST_KEY if the + thread storage subsystem gets reinitialized */ + nextThreadStorageKey = STORAGE_INVALID_KEY; + + TclThreadStorageUnlock(); +} + +/* + *---------------------------------------------------------------------- + * + * 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; +{ + void *result; + int *indexPtr; + Tcl_ThreadId id = Tcl_GetCurrentThread(); + Tcl_HashTable *hashTablePtr; /* hash table for current thread */ + Tcl_HashEntry *hPtr; /* hash entry for data key in current thread */ + + if (*keyPtr != NULL) { + indexPtr = *(int **)keyPtr; + + hashTablePtr = TclThreadStorageGetHashTable(id); + + if (hashTablePtr != NULL) { + hPtr = Tcl_FindHashEntry(hashTablePtr, (char *)*indexPtr); + + if (hPtr != NULL) { + result = Tcl_GetHashValue(hPtr); + + if (result != NULL) { + /* this must be ckfree because tclThread.c + allocates these using ckalloc. */ + ckfree((char *)result); + } + + Tcl_SetHashValue(hPtr, NULL); + } + } else { + Tcl_Panic("TclThreadStorageGetHashTable failed from TclFinalizeThreadStorageData!"); + } + } +} + +/* + *---------------------------------------------------------------------- + * + * 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; /* we need to hit every thread with this search */ + Tcl_HashEntry *hPtr; /* hash entry for current thread in master table */ + Tcl_HashEntry *hDataPtr; /* hash entry for data key in current thread */ + + if (*keyPtr != NULL) { + indexPtr = *(int **)keyPtr; + + TclThreadStorageLock(); + if (threadStorageHashTablePtr != NULL) { + /* + * We are going to delete the specified data key entry from every thread. + */ + for (hPtr = Tcl_FirstHashEntry(threadStorageHashTablePtr, &search); + hPtr != NULL; hPtr = Tcl_NextHashEntry(&search)) { + + /* get the hash table corresponding to the thread in question. */ + hashTablePtr = Tcl_GetHashValue(hPtr); + + if (hashTablePtr != NULL) { + /* now find the entry for the specified data key. */ + hDataPtr = Tcl_FindHashEntry(hashTablePtr, (char *)*indexPtr); + + if (hDataPtr != NULL) { + /* delete the data key for this thread. */ + Tcl_DeleteHashEntry(hDataPtr); + } + } + } + } + TclThreadStorageUnlock(); + + TclpSysFree((char *)indexPtr); + *keyPtr = NULL; + } +} + +#else /* !defined(TCL_THREADS) || !defined(USE_THREAD_STORAGE) */ + +static void ThreadStoragePanic _ANSI_ARGS_(( + CONST char *message)); + +/* + *---------------------------------------------------------------------- + * + * ThreadStoragePanic -- + * + * Panic if Tcl was compiled without TCL_THREADS or without + * USE_THREAD_STORAGE and a thread storage function has been + * called. + * + * Results: + * None. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ +static void ThreadStoragePanic(message) + 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 +#else + Tcl_Panic("Tcl was not compiled with threads enabled."); +#endif +} + +void +TclThreadStorageLockInit() +{ + ThreadStoragePanic(NULL); +} + +void +TclThreadStorageLock() +{ + ThreadStoragePanic(NULL); +} + +void +TclThreadStorageUnlock() +{ + ThreadStoragePanic(NULL); +} + +void +TclThreadStoragePrint(outFile, flags) + FILE *outFile; + int flags; +{ + ThreadStoragePanic(NULL); +} + +Tcl_HashTable * +TclThreadStorageGetHashTable(id) + Tcl_ThreadId id; +{ + ThreadStoragePanic(NULL); + return NULL; +} + +Tcl_HashTable * +TclThreadStorageInit(id, reserved) + Tcl_ThreadId id; + void *reserved; +{ + ThreadStoragePanic(NULL); + return NULL; +} + +void +TclThreadStorageDataKeyInit(keyPtr) + Tcl_ThreadDataKey *keyPtr; +{ + ThreadStoragePanic(NULL); +} + +void * +TclThreadStorageDataKeyGet(keyPtr) + Tcl_ThreadDataKey *keyPtr; +{ + ThreadStoragePanic(NULL); + return NULL; +} + +void +TclThreadStorageDataKeySet(keyPtr, data) + Tcl_ThreadDataKey *keyPtr; + void *data; +{ + ThreadStoragePanic(NULL); +} + +void +TclFinalizeThreadStorageThread(id) + Tcl_ThreadId id; +{ + ThreadStoragePanic(NULL); +} + +void +TclFinalizeThreadStorage() +{ + ThreadStoragePanic(NULL); +} + +void +TclFinalizeThreadStorageData(keyPtr) + Tcl_ThreadDataKey *keyPtr; +{ + ThreadStoragePanic(NULL); +} + +void +TclFinalizeThreadStorageDataKey(keyPtr) + Tcl_ThreadDataKey *keyPtr; +{ + ThreadStoragePanic(NULL); +} + +#endif /* defined(TCL_THREADS) && defined(USE_THREAD_STORAGE) */ diff --git a/unix/Makefile.in b/unix/Makefile.in index f1d6f57..ee369c3 100644 --- a/unix/Makefile.in +++ b/unix/Makefile.in @@ -5,7 +5,7 @@ # "autoconf" program (constructs like "@foo@" will get replaced in the # actual Makefile. # -# RCS: @(#) $Id: Makefile.in,v 1.138 2004/06/11 21:30:08 dgp Exp $ +# RCS: @(#) $Id: Makefile.in,v 1.139 2004/06/24 01:29:03 mistachkin Exp $ VERSION = @TCL_VERSION@ MAJOR_VERSION = @TCL_MAJOR_VERSION@ @@ -313,8 +313,8 @@ GENERIC_OBJS = regcomp.o regexec.o regfree.o regerror.o tclAlloc.o \ tclObj.o tclPanic.o tclParse.o tclParseExpr.o tclPathObj.o tclPipe.o \ tclPkg.o tclPkgConfig.o tclPosixStr.o tclPreserve.o tclProc.o tclRegexp.o \ tclResolve.o tclResult.o tclScan.o tclStringObj.o tclThread.o \ - tclThreadAlloc.o tclThreadJoin.o tclStubInit.o tclStubLib.o \ - tclTimer.o tclTrace.o tclUtf.o tclUtil.o tclVar.o + tclThreadAlloc.o tclThreadJoin.o tclThreadStorage.o tclStubInit.o \ + tclStubLib.o tclTimer.o tclTrace.o tclUtf.o tclUtil.o tclVar.o STUB_LIB_OBJS = tclStubLib.o ${COMPAT_OBJS} @@ -402,6 +402,7 @@ GENERIC_SRCS = \ $(GENERIC_DIR)/tclThread.c \ $(GENERIC_DIR)/tclThreadAlloc.c \ $(GENERIC_DIR)/tclThreadJoin.c \ + $(GENERIC_DIR)/tclThreadStorage.c \ $(GENERIC_DIR)/tclTimer.c \ $(GENERIC_DIR)/tclTrace.c \ $(GENERIC_DIR)/tclUtil.c \ @@ -1050,6 +1051,9 @@ tclThreadAlloc.o: $(GENERIC_DIR)/tclThreadAlloc.c tclThreadJoin.o: $(GENERIC_DIR)/tclThreadJoin.c $(CC) -c $(CC_SWITCHES) $(GENERIC_DIR)/tclThreadJoin.c +tclThreadStorage.o: $(GENERIC_DIR)/tclThreadStorage.c + $(CC) -c $(CC_SWITCHES) $(GENERIC_DIR)/tclThreadStorage.c + tclThreadTest.o: $(GENERIC_DIR)/tclThreadTest.c $(CC) -c $(CC_SWITCHES) $(GENERIC_DIR)/tclThreadTest.c diff --git a/unix/configure b/unix/configure index dc672d8..0155bcb 100755 --- a/unix/configure +++ b/unix/configure @@ -2854,6 +2854,12 @@ _ACEOF #define USE_THREAD_ALLOC 1 _ACEOF + # USE_THREAD_STORAGE tells us to use the new generic thread + # storage subsystem. + cat >>confdefs.h <<\_ACEOF +#define USE_THREAD_STORAGE 1 +_ACEOF + cat >>confdefs.h <<\_ACEOF #define _REENTRANT 1 _ACEOF diff --git a/unix/tcl.m4 b/unix/tcl.m4 index 37bc2d8..a81ad6a 100644 --- a/unix/tcl.m4 +++ b/unix/tcl.m4 @@ -428,6 +428,9 @@ AC_DEFUN(SC_ENABLE_THREADS, [ # USE_THREAD_ALLOC tells us to try the special thread-based # allocator that significantly reduces lock contention AC_DEFINE(USE_THREAD_ALLOC) + # USE_THREAD_STORAGE tells us to use the new generic thread + # storage subsystem. + AC_DEFINE(USE_THREAD_STORAGE) AC_DEFINE(_REENTRANT) AC_DEFINE(_THREAD_SAFE) AC_CHECK_LIB(pthread,pthread_mutex_init,tcl_ok=yes,tcl_ok=no) diff --git a/win/Makefile.in b/win/Makefile.in index c1bafe6..8b787f7 100644 --- a/win/Makefile.in +++ b/win/Makefile.in @@ -5,7 +5,7 @@ # "autoconf" program (constructs like "@foo@" will get replaced in the # actual Makefile. # -# RCS: @(#) $Id: Makefile.in,v 1.77 2004/06/14 22:14:12 kennykb Exp $ +# RCS: @(#) $Id: Makefile.in,v 1.78 2004/06/24 01:29:08 mistachkin Exp $ VERSION = @TCL_VERSION@ @@ -265,6 +265,7 @@ GENERIC_OBJS = \ tclThread.$(OBJEXT) \ tclThreadAlloc.$(OBJEXT) \ tclThreadJoin.$(OBJEXT) \ + tclThreadStorage.$(OBJEXT) \ tclTimer.$(OBJEXT) \ tclTrace.$(OBJEXT) \ tclUtf.$(OBJEXT) \ diff --git a/win/configure b/win/configure index b675b38..3537756 100755 --- a/win/configure +++ b/win/configure @@ -2967,6 +2967,12 @@ _ACEOF #define USE_THREAD_ALLOC 1 _ACEOF + # USE_THREAD_STORAGE tells us to use the new generic thread + # storage subsystem. + cat >>confdefs.h <<\_ACEOF +#define USE_THREAD_STORAGE 1 +_ACEOF + else TCL_THREADS=0 echo "$as_me:$LINENO: result: no (default)" >&5 diff --git a/win/makefile.vc b/win/makefile.vc index 611702d..ec11e56 100644 --- a/win/makefile.vc +++ b/win/makefile.vc @@ -12,7 +12,7 @@ # Copyright (c) 2001-2004 David Gravereaux. # #------------------------------------------------------------------------------ -# RCS: @(#) $Id: makefile.vc,v 1.128 2004/06/23 19:23:49 patthoyts Exp $ +# RCS: @(#) $Id: makefile.vc,v 1.129 2004/06/24 01:29:07 mistachkin Exp $ #------------------------------------------------------------------------------ !if !defined(MSDEVDIR) && !defined(MSVCDIR) @@ -80,6 +80,7 @@ the environment. Jump to this line to read the new instructions. # inside it. # threads = Turns on full multithreading support. # thrdalloc = Use the thread allocator (shared global free pool). +# thrdstorage = Use the generic thread storage support. # symbols = Adds symbols for step debugging. # profile = Adds profiling hooks. Map file is assumed. # loimpact = Adds a flag for how NT treats the heap to keep memory @@ -308,6 +309,7 @@ TCLOBJS = \ $(TMP_DIR)\tclThread.obj \ $(TMP_DIR)\tclThreadAlloc.obj \ $(TMP_DIR)\tclThreadJoin.obj \ + $(TMP_DIR)\tclThreadStorage.obj \ $(TMP_DIR)\tclTimer.obj \ $(TMP_DIR)\tclTrace.obj \ $(TMP_DIR)\tclUtf.obj \ diff --git a/win/rules.vc b/win/rules.vc index 0a102c9..411a192 100644 --- a/win/rules.vc +++ b/win/rules.vc @@ -10,7 +10,7 @@ # Copyright (c) 2001-2003 David Gravereaux. # #------------------------------------------------------------------------------ -# RCS: @(#) $Id: rules.vc,v 1.18 2004/02/07 21:47:19 davygrvy Exp $ +# RCS: @(#) $Id: rules.vc,v 1.19 2004/06/24 01:29:07 mistachkin Exp $ #------------------------------------------------------------------------------ !ifndef _RULES_VC @@ -179,6 +179,12 @@ USE_THREAD_ALLOC = 1 !else USE_THREAD_ALLOC = 0 !endif +!if [nmakehlp -f $(OPTS) "thrdstorage"] +!message *** Doing thrdstorage +USE_THREAD_STORAGE = 1 +!else +USE_THREAD_STORAGE = 0 +!endif !if [nmakehlp -f $(OPTS) "unchecked"] !message *** Doing unchecked UNCHECKED = 1 @@ -323,6 +329,9 @@ OPTDEFINES = $(OPTDEFINES) -DTCL_THREADS=1 !if $(USE_THREAD_ALLOC) OPTDEFINES = $(OPTDEFINES) -DUSE_THREAD_ALLOC=1 !endif +!if $(USE_THREAD_STORAGE) +OPTDEFINES = $(OPTDEFINES) -DUSE_THREAD_STORAGE=1 +!endif !endif !if $(STATIC_BUILD) OPTDEFINES = $(OPTDEFINES) -DSTATIC_BUILD diff --git a/win/tcl.m4 b/win/tcl.m4 index f263325..4a73af7 100644 --- a/win/tcl.m4 +++ b/win/tcl.m4 @@ -257,6 +257,9 @@ AC_DEFUN(SC_ENABLE_THREADS, [ # USE_THREAD_ALLOC tells us to try the special thread-based # allocator that significantly reduces lock contention AC_DEFINE(USE_THREAD_ALLOC) + # USE_THREAD_STORAGE tells us to use the new generic thread + # storage subsystem. + AC_DEFINE(USE_THREAD_STORAGE) else TCL_THREADS=0 AC_MSG_RESULT([no (default)]) -- cgit v0.12