summaryrefslogtreecommitdiffstats
path: root/generic
diff options
context:
space:
mode:
Diffstat (limited to 'generic')
-rw-r--r--generic/tclInt.decls44
-rw-r--r--generic/tclIntDecls.h141
-rw-r--r--generic/tclStubInit.c15
-rw-r--r--generic/tclThread.c41
-rw-r--r--generic/tclThreadStorage.c1013
5 files changed, 1249 insertions, 5 deletions
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 ; i<keyRecord.num ; i++) {
keyPtr = (Tcl_ThreadDataKey *) keyRecord.list[i];
#ifdef TCL_THREADS
+#ifdef USE_THREAD_STORAGE
+ TclFinalizeThreadStorageData(keyPtr);
+#else
TclpFinalizeThreadData(keyPtr);
+#endif
#else
if (*keyPtr != NULL) {
ckfree((char *)*keyPtr);
@@ -449,7 +477,11 @@ TclFinalizeSynchronization()
TclpMasterLock();
for (i=0 ; i<keyRecord.num ; i++) {
keyPtr = (Tcl_ThreadDataKey *)keyRecord.list[i];
+#ifdef USE_THREAD_STORAGE
+ TclFinalizeThreadStorageDataKey(keyPtr);
+#else
TclpFinalizeThreadDataKey(keyPtr);
+#endif
}
if (keyRecord.list != NULL) {
ckfree((char *)keyRecord.list);
@@ -458,6 +490,11 @@ TclFinalizeSynchronization()
keyRecord.max = 0;
keyRecord.num = 0;
+#ifdef USE_THREAD_STORAGE
+ /* call platform specific thread storage master cleanup */
+ TclFinalizeThreadStorage();
+#endif
+
for (i=0 ; i<mutexRecord.num ; i++) {
mutexPtr = (Tcl_Mutex *)mutexRecord.list[i];
if (mutexPtr != NULL) {
diff --git a/generic/tclThreadStorage.c b/generic/tclThreadStorage.c
new file mode 100644
index 0000000..aa04cfc
--- /dev/null
+++ b/generic/tclThreadStorage.c
@@ -0,0 +1,1013 @@
+/*
+ * tclThreadStorage.c --
+ *
+ * This file implements platform independent thread storage operations.
+ *
+ * Copyright (c) 2003-2004 by Joe Mistachkin
+ *
+ * See the file "license.terms" for information on usage and redistribution
+ * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+ *
+ * RCS: @(#) $Id: tclThreadStorage.c,v 1.1 2004/06/24 01:29:02 mistachkin Exp $
+ */
+
+#include "tclInt.h"
+
+#if defined(TCL_THREADS) && defined(USE_THREAD_STORAGE)
+
+/*
+ * This is the thread storage cache array and it's accompanying mutex.
+ * The elements are pairs of thread Id and an associated hash table
+ * pointer; the hash table being pointed to contains the thread storage
+ * for it's associated thread. The purpose of this cache is to minimize
+ * the number of hash table lookups in the master thread storage hash
+ * table.
+ */
+static Tcl_Mutex threadStorageLock;
+
+/*
+ * This is the struct used for a thread storage cache slot. It contains
+ * the owning thread Id and the associated hash table pointer.
+ */
+typedef struct ThreadStorage {
+ Tcl_ThreadId id; /* the owning thread id */
+ Tcl_HashTable *hashTablePtr; /* the hash table for the thread */
+} ThreadStorage;
+
+/*
+ * These are the prototypes for the custom hash table allocation
+ * functions used by the thread storage subsystem.
+ */
+static Tcl_HashEntry * AllocThreadStorageEntry _ANSI_ARGS_((
+ Tcl_HashTable *tablePtr, void *keyPtr));
+static void FreeThreadStorageEntry _ANSI_ARGS_((
+ Tcl_HashEntry *hPtr));
+
+/*
+ * This is the hash key type for thread storage. We MUST use this in
+ * combination with the new hash key type flag TCL_HASH_KEY_SYSTEM_HASH
+ * because these hash tables MAY be used by the threaded memory allocator.
+ */
+Tcl_HashKeyType tclThreadStorageHashKeyType = {
+ TCL_HASH_KEY_TYPE_VERSION, /* version */
+ TCL_HASH_KEY_SYSTEM_HASH, /* flags */
+ NULL, /* hashKeyProc */
+ NULL, /* compareKeysProc */
+ AllocThreadStorageEntry, /* allocEntryProc */
+ FreeThreadStorageEntry /* freeEntryProc */
+};
+
+/*
+ * This is an invalid thread value.
+ */
+#define STORAGE_INVALID_THREAD (Tcl_ThreadId)0
+
+/*
+ * This is the value for an invalid thread storage key.
+ */
+#define STORAGE_INVALID_KEY 0
+
+/*
+ * This is the first valid key for use by external callers.
+ * All the values below this are RESERVED for future use.
+ */
+#define STORAGE_FIRST_KEY 101
+
+/*
+ * This is the default number of thread storage cache slots.
+ * This define may need to be fine tuned for maximum performance.
+ */
+#define STORAGE_CACHE_SLOTS 97
+
+/*
+ * This is the master thread storage hash table. It is keyed on
+ * thread Id and contains values that are hash tables for each thread.
+ * The thread specific hash tables contain the actual thread storage.
+ */
+static Tcl_HashTable *threadStorageHashTablePtr = NULL;
+
+/*
+ * This is the next thread data key value to use. We increment this
+ * everytime we "allocate" one. It is initially set to 1 in
+ * TclThreadStorageInit.
+ */
+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 kennykb's idea, this
+ * prevents unnecessary lookups for threads that use a lot of thread
+ * storage.
+ */
+static volatile ThreadStorage threadStorageCache[STORAGE_CACHE_SLOTS];
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TclThreadStorageLockInit
+ *
+ * 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.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+TclThreadStorageLockInit()
+{
+ if (!initThreadStorage) {
+ /*
+ * Mutexes in Tcl are self initializing, and we are taking
+ * advantage of that fact since this file cannot contain
+ * platform specific calls.
+ */
+ initThreadStorage = 1;
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TclThreadStorageLock
+ *
+ * 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.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+TclThreadStorageLock()
+{
+ TclThreadStorageLockInit();
+ Tcl_MutexLock(&threadStorageLock);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TclThreadStorageUnlock
+ *
+ * This procedure is used to release a lock that serializes creation
+ * of thread storage.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Release the thread storage mutex.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+TclThreadStorageUnlock()
+{
+ Tcl_MutexUnlock(&threadStorageLock);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * AllocThreadStorageEntry --
+ *
+ * Allocate space for a Tcl_HashEntry using TclpSysAlloc (not
+ * ckalloc). We do this because the threaded memory allocator MAY
+ * use the thread storage hash tables.
+ *
+ * Results:
+ * The return value is a pointer to the created entry.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static Tcl_HashEntry *
+AllocThreadStorageEntry(tablePtr, keyPtr)
+ Tcl_HashTable *tablePtr; /* Hash table. */
+ void *keyPtr; /* Key to store in the hash table entry. */
+{
+ Tcl_HashEntry *hPtr;
+
+ hPtr = (Tcl_HashEntry *)TclpSysAlloc(sizeof(Tcl_HashEntry), 0);
+ hPtr->key.oneWordValue = (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) */