Tcl Source Code

Artifact [077ca26d9a]
Login

Artifact 077ca26d9a30694786fdc65d57989fbed73e9503:

Attachment "976496.patch" to ticket [976496ffff] added by dgp 2004-06-24 03:35:43.
Index: generic/tclInt.decls
===================================================================
RCS file: /cvsroot/tcl/tcl/generic/tclInt.decls,v
retrieving revision 1.74
diff -u -r1.74 tclInt.decls
--- generic/tclInt.decls	5 Jun 2004 17:31:08 -0000	1.74
+++ generic/tclInt.decls	23 Jun 2004 20:23:06 -0000
@@ -745,6 +745,48 @@
      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
Index: generic/tclIntDecls.h
===================================================================
RCS file: /cvsroot/tcl/tcl/generic/tclIntDecls.h,v
retrieving revision 1.64
diff -u -r1.64 tclIntDecls.h
--- generic/tclIntDecls.h	7 Jun 2004 16:48:45 -0000	1.64
+++ generic/tclIntDecls.h	23 Jun 2004 20:23:07 -0000
@@ -969,6 +969,80 @@
 /* 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 @@
     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 @@
 #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) */
 
Index: generic/tclStubInit.c
===================================================================
RCS file: /cvsroot/tcl/tcl/generic/tclStubInit.c,v
retrieving revision 1.98
diff -u -r1.98 tclStubInit.c
--- generic/tclStubInit.c	7 Jun 2004 16:48:45 -0000	1.98
+++ generic/tclStubInit.c	23 Jun 2004 20:23:07 -0000
@@ -268,6 +268,19 @@
     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 = {
Index: generic/tclThread.c
===================================================================
RCS file: /cvsroot/tcl/tcl/generic/tclThread.c,v
retrieving revision 1.7
diff -u -r1.7 tclThread.c
--- generic/tclThread.c	23 Apr 2004 07:21:36 -0000	1.7
+++ generic/tclThread.c	23 Jun 2004 20:23:08 -0000
@@ -89,18 +89,30 @@
      */
 
     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 @@
 				 * 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 @@
 {
 #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 @@
     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 @@
     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 @@
     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) {
Index: generic/tclThreadStorage.c
===================================================================
RCS file: generic/tclThreadStorage.c
diff -N generic/tclThreadStorage.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ generic/tclThreadStorage.c	23 Jun 2004 20:23:08 -0000
@@ -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$
+ */
+
+#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) */
Index: unix/Makefile.in
===================================================================
RCS file: /cvsroot/tcl/tcl/unix/Makefile.in,v
retrieving revision 1.138
diff -u -r1.138 Makefile.in
--- unix/Makefile.in	11 Jun 2004 21:30:08 -0000	1.138
+++ unix/Makefile.in	23 Jun 2004 20:23:09 -0000
@@ -313,8 +313,8 @@
 	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_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 @@
 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
 
Index: unix/configure
===================================================================
RCS file: /cvsroot/tcl/tcl/unix/configure,v
retrieving revision 1.114
diff -u -r1.114 configure
--- unix/configure	23 Jun 2004 00:25:03 -0000	1.114
+++ unix/configure	23 Jun 2004 20:23:10 -0000
@@ -2854,6 +2854,12 @@
 #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
Index: unix/tcl.m4
===================================================================
RCS file: /cvsroot/tcl/tcl/unix/tcl.m4,v
retrieving revision 1.117
diff -u -r1.117 tcl.m4
--- unix/tcl.m4	23 Jun 2004 00:24:41 -0000	1.117
+++ unix/tcl.m4	23 Jun 2004 20:23:11 -0000
@@ -428,6 +428,9 @@
 	# 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)
Index: win/Makefile.in
===================================================================
RCS file: /cvsroot/tcl/tcl/win/Makefile.in,v
retrieving revision 1.77
diff -u -r1.77 Makefile.in
--- win/Makefile.in	14 Jun 2004 22:14:12 -0000	1.77
+++ win/Makefile.in	23 Jun 2004 20:23:11 -0000
@@ -265,6 +265,7 @@
 	tclThread.$(OBJEXT) \
 	tclThreadAlloc.$(OBJEXT) \
 	tclThreadJoin.$(OBJEXT) \
+	tclThreadStorage.$(OBJEXT) \
 	tclTimer.$(OBJEXT) \
 	tclTrace.$(OBJEXT) \
 	tclUtf.$(OBJEXT) \
Index: win/configure
===================================================================
RCS file: /cvsroot/tcl/tcl/win/configure,v
retrieving revision 1.71
diff -u -r1.71 configure
--- win/configure	11 Jun 2004 20:25:24 -0000	1.71
+++ win/configure	23 Jun 2004 20:23:12 -0000
@@ -2967,6 +2967,12 @@
 #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
Index: win/makefile.vc
===================================================================
RCS file: /cvsroot/tcl/tcl/win/makefile.vc,v
retrieving revision 1.128
diff -u -r1.128 makefile.vc
--- win/makefile.vc	23 Jun 2004 19:23:49 -0000	1.128
+++ win/makefile.vc	23 Jun 2004 20:23:12 -0000
@@ -80,6 +80,7 @@
 #			   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 @@
 	$(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 \
Index: win/rules.vc
===================================================================
RCS file: /cvsroot/tcl/tcl/win/rules.vc,v
retrieving revision 1.18
diff -u -r1.18 rules.vc
--- win/rules.vc	7 Feb 2004 21:47:19 -0000	1.18
+++ win/rules.vc	23 Jun 2004 20:23:12 -0000
@@ -179,6 +179,12 @@
 !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 @@
 !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
Index: win/tcl.m4
===================================================================
RCS file: /cvsroot/tcl/tcl/win/tcl.m4,v
retrieving revision 1.55
diff -u -r1.55 tcl.m4
--- win/tcl.m4	6 Oct 2003 19:08:45 -0000	1.55
+++ win/tcl.m4	23 Jun 2004 20:23:12 -0000
@@ -257,6 +257,9 @@
 	# 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)])