summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ChangeLog18
-rw-r--r--generic/tcl.h5
-rw-r--r--generic/tclInt.h13
-rw-r--r--generic/tclThread.c33
-rw-r--r--generic/tclThreadStorage.c585
-rw-r--r--unix/tclUnixThrd.c45
-rw-r--r--win/tclWinThrd.c45
7 files changed, 284 insertions, 460 deletions
diff --git a/ChangeLog b/ChangeLog
index 75991e3..df677d6 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,21 @@
+2008-05-09 George Peter Staplin <georgeps@xmission.com>
+
+ * generic/tcl.h: Make Tcl_ThreadDataKey a void *.
+ * generic/tclInt.h: Change around some function names and
+ add some new per-platform declarations for thread-specific data
+ functions.
+ * generic/tclThread.c: Make use of of the new function names
+ that no longer have a Tclp prefix.
+ * generic/tclThreadStorage.c: Replace the core thread-specific data
+ (TSD) mechanism with an array offset solution that eliminates the
+ hash tables, and only uses one slot of native TSD.
+ Many thanks to Kevin B. Kenny for his help with this.
+
+ * unix/tclUnixThrd.c: Add platform-specific TSD functions for use
+ by tclThreadStorage.c.
+ * win/tclWinThrd.c: Add platform-specific TSD functions for use
+ by tclThreadStorage.c.
+
2008-05-09 Kevin B. Kenny <kennykb@acm.org>
* tests/dict.test (dict-19.2): Corrected a bug where
diff --git a/generic/tcl.h b/generic/tcl.h
index 10ef922..a1a61d6 100644
--- a/generic/tcl.h
+++ b/generic/tcl.h
@@ -13,7 +13,7 @@
* See the file "license.terms" for information on usage and redistribution of
* this file, and for a DISCLAIMER OF ALL WARRANTIES.
*
- * RCS: @(#) $Id: tcl.h,v 1.256 2008/04/24 21:11:57 nijtmans Exp $
+ * RCS: @(#) $Id: tcl.h,v 1.257 2008/05/09 04:58:53 georgeps Exp $
*/
#ifndef _TCL
@@ -477,12 +477,13 @@ typedef struct Tcl_LoadHandle_ *Tcl_LoadHandle;
typedef struct Tcl_Mutex_ *Tcl_Mutex;
typedef struct Tcl_Pid_ *Tcl_Pid;
typedef struct Tcl_RegExp_ *Tcl_RegExp;
-typedef struct Tcl_ThreadDataKey_ *Tcl_ThreadDataKey;
typedef struct Tcl_ThreadId_ *Tcl_ThreadId;
typedef struct Tcl_TimerToken_ *Tcl_TimerToken;
typedef struct Tcl_Trace_ *Tcl_Trace;
typedef struct Tcl_Var_ *Tcl_Var;
+typedef void *Tcl_ThreadDataKey;
+
/*
* Definition of the interface to functions implementing threads. A function
* following this definition is given to each call of 'Tcl_CreateThread' and
diff --git a/generic/tclInt.h b/generic/tclInt.h
index 93adf2a..eab070c 100644
--- a/generic/tclInt.h
+++ b/generic/tclInt.h
@@ -13,7 +13,7 @@
* See the file "license.terms" for information on usage and redistribution of
* this file, and for a DISCLAIMER OF ALL WARRANTIES.
*
- * RCS: @(#) $Id: tclInt.h,v 1.367 2008/05/02 20:08:52 patthoyts Exp $
+ * RCS: @(#) $Id: tclInt.h,v 1.368 2008/05/09 04:58:54 georgeps Exp $
*/
#ifndef _TCLINT
@@ -2630,8 +2630,8 @@ MODULE_SCOPE void TclpReleaseFile(TclFile file);
MODULE_SCOPE void TclpSetInterfaces(void);
MODULE_SCOPE void TclpSetVariables(Tcl_Interp *interp);
MODULE_SCOPE void TclpUnloadFile(Tcl_LoadHandle loadHandle);
-MODULE_SCOPE void * TclpThreadDataKeyGet(Tcl_ThreadDataKey *keyPtr);
-MODULE_SCOPE void TclpThreadDataKeySet(Tcl_ThreadDataKey *keyPtr,
+MODULE_SCOPE void * TclThreadStorageKeyGet(Tcl_ThreadDataKey *keyPtr);
+MODULE_SCOPE void TclThreadStorageKeySet(Tcl_ThreadDataKey *keyPtr,
void *data);
MODULE_SCOPE void TclpThreadExit(int status);
MODULE_SCOPE size_t TclpThreadGetStackSize(void);
@@ -2676,7 +2676,7 @@ MODULE_SCOPE int TclpLoadMemory(Tcl_Interp *interp, void *buffer,
Tcl_FSUnloadFileProc **unloadProcPtr);
#endif
MODULE_SCOPE void TclInitThreadStorage(void);
-MODULE_SCOPE void TclpFinalizeThreadDataThread(void);
+MODULE_SCOPE void TclFinalizeThreadDataThread(void);
MODULE_SCOPE void TclFinalizeThreadStorage(void);
#ifdef TCL_WIDE_CLICKS
MODULE_SCOPE Tcl_WideInt TclpGetWideClicks(void);
@@ -2684,6 +2684,11 @@ MODULE_SCOPE double TclpWideClicksToNanoseconds(Tcl_WideInt clicks);
#endif
MODULE_SCOPE Tcl_Obj * TclDisassembleByteCodeObj(Tcl_Obj *objPtr);
+MODULE_SCOPE void * TclpThreadCreateKey(void);
+MODULE_SCOPE void TclpThreadDeleteKey(void *keyPtr);
+MODULE_SCOPE void TclpThreadSetMasterTSD(void *tsdKeyPtr, void *ptr);
+MODULE_SCOPE void * TclpThreadGetMasterTSD(void *tsdKeyPtr);
+
/*
*----------------------------------------------------------------
* Command procedures in the generic core:
diff --git a/generic/tclThread.c b/generic/tclThread.c
index 1ab4bc9..1a544e3 100644
--- a/generic/tclThread.c
+++ b/generic/tclThread.c
@@ -5,11 +5,12 @@
* the real work is done in the platform dependent files.
*
* Copyright (c) 1998 by Sun Microsystems, Inc.
+ * Copyright (c) 2008 by George Peter Staplin
*
* 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.19 2007/12/13 15:23:20 dgp Exp $
+ * RCS: @(#) $Id: tclThread.c,v 1.20 2008/05/09 04:58:54 georgeps Exp $
*/
#include "tclInt.h"
@@ -84,21 +85,22 @@ Tcl_GetThreadData(
/*
* Initialize the key for this thread.
*/
- result = TclpThreadDataKeyGet(keyPtr);
+ result = TclThreadStorageKeyGet(keyPtr);
if (result == NULL) {
- result = ckalloc((size_t) size);
- memset(result, 0, (size_t) size);
- TclpThreadDataKeySet(keyPtr, result);
+ result = ckalloc((size_t)size);
+ memset(result, 0, (size_t)size);
+ TclThreadStorageKeySet(keyPtr, result);
}
#else /* TCL_THREADS */
if (*keyPtr == NULL) {
- result = ckalloc((size_t) size);
- memset(result, 0, (size_t) size);
- *keyPtr = (Tcl_ThreadDataKey)result;
+ result = ckalloc((size_t)size);
+ memset(result, 0, (size_t)size);
+ *keyPtr = result;
RememberSyncObject((char *) keyPtr, &keyRecord);
+ } else {
+ result = *keyPtr;
}
- result = * (void **) keyPtr;
#endif /* TCL_THREADS */
return result;
}
@@ -122,14 +124,13 @@ Tcl_GetThreadData(
void *
TclThreadDataKeyGet(
- Tcl_ThreadDataKey *keyPtr) /* Identifier for the data chunk, really
- * (pthread_key_t **) */
+ Tcl_ThreadDataKey *keyPtr) /* Identifier for the data chunk. */
+
{
#ifdef TCL_THREADS
- return TclpThreadDataKeyGet(keyPtr);
+ return TclThreadStorageKeyGet(keyPtr);
#else /* TCL_THREADS */
- char *result = *(char **) keyPtr;
- return result;
+ return *keyPtr;
#endif /* TCL_THREADS */
}
@@ -355,7 +356,7 @@ Tcl_ConditionFinalize(
void
TclFinalizeThreadData(void)
{
- TclpFinalizeThreadDataThread();
+ TclFinalizeThreadDataThread();
}
/*
@@ -396,7 +397,7 @@ TclFinalizeSynchronization(void)
if (keyRecord.list != NULL) {
for (i=0 ; i<keyRecord.num ; i++) {
keyPtr = (Tcl_ThreadDataKey *) keyRecord.list[i];
- blockPtr = (void *) *keyPtr;
+ blockPtr = *keyPtr;
ckfree(blockPtr);
}
ckfree((char *) keyRecord.list);
diff --git a/generic/tclThreadStorage.c b/generic/tclThreadStorage.c
index 55587b3..d0fdeec 100644
--- a/generic/tclThreadStorage.c
+++ b/generic/tclThreadStorage.c
@@ -4,470 +4,232 @@
* This file implements platform independent thread storage operations.
*
* Copyright (c) 2003-2004 by Joe Mistachkin
+ * Copyright (c) 2008 by George Peter Staplin
+ *
+ * The primary idea is that we create one platform-specific TSD slot, and
+ * use it for storing a table pointer.
+ *
+ * Each Tcl_ThreadDataKey has an offset into the table of TSD values.
+ *
+ * We don't use more than 1 platform-specific TSD slot, because there is
+ * a hard limit on the number of TSD slots.
+ *
+ * Valid key offsets are > 0. 0 is for the initialized Tcl_ThreadDataKey.
*
* 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.15 2007/12/13 15:23:20 dgp Exp $
+ * RCS: @(#) $Id: tclThreadStorage.c,v 1.16 2008/05/09 04:58:54 georgeps Exp $
*/
#include "tclInt.h"
+#include <signal.h>
#if defined(TCL_THREADS)
-/*
- * 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(Tcl_HashTable *tablePtr,
- void *keyPtr);
-static void FreeThreadStorageEntry(Tcl_HashEntry *hPtr);
-static Tcl_HashTable * ThreadStorageGetHashTable(Tcl_ThreadId id);
-
-/*
- * 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.
- */
-
-static Tcl_HashKeyType tclThreadStorageHashKeyType = {
- TCL_HASH_KEY_TYPE_VERSION, /* version */
- TCL_HASH_KEY_SYSTEM_HASH | TCL_HASH_KEY_RANDOMIZE_HASH,
- /* flags */
- NULL, /* hashKeyProc */
- NULL, /* compareKeysProc */
- AllocThreadStorageEntry, /* allocEntryProc */
- FreeThreadStorageEntry /* freeEntryProc */
-};
-
-/*
- * This is an invalid thread value.
- */
-
-#define STORAGE_INVALID_THREAD (Tcl_ThreadId)0
+static void *tclTsdKey = NULL;
+static Tcl_Mutex tclTsdMutex;
+static sig_atomic_t tclTsdCounter = 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.
- */
+typedef struct TSDTable {
+ sig_atomic_t allocated;
+ void **table;
+} TSDTable;
-#define STORAGE_FIRST_KEY 1
+typedef union {
+ void *ptr;
+ volatile sig_atomic_t offset;
+} TSDUnion;
-/*
- * 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 TSDTable *TSDTableCreate(void);
+static void TSDTableDelete(TSDTable *t);
+static void TSDTableGrow(TSDTable *t, sig_atomic_t atLeast);
-static Tcl_HashTable threadStorageHashTable;
-
-/*
- * This is the next thread data key value to use. We increment this everytime
- * we "allocate" one. It is initially set to 1 in TclInitThreadStorage.
- */
+
+static TSDTable *
+TSDTableCreate(void) {
+ TSDTable *t;
+ sig_atomic_t i;
+
+ t = TclpSysAlloc(sizeof *t, 0);
+ if (NULL == t) {
+ Tcl_Panic("unable to allocate TSDTable");
+ }
-static int nextThreadStorageKey = STORAGE_INVALID_KEY;
+ t->allocated = 8;
+ t->table = TclpSysAlloc(sizeof (*(t->table)) * t->allocated, 0);
+ if (NULL == t->table) {
+ Tcl_Panic("unable to allocate TSDTable");
+ }
-/*
- * This is the master thread storage cache. Per Kevin Kenny's idea, this
- * prevents unnecessary lookups for threads that use a lot of thread storage.
- */
+ for (i = 0; i < t->allocated; ++i) {
+ t->table[i] = NULL;
+ }
-static volatile ThreadStorage threadStorageCache[STORAGE_CACHE_SLOTS];
+ return t;
+}
+
+static void
+TSDTableDelete(TSDTable *t) {
+ TclpSysFree(t->table);
+ TclpSysFree(t);
+}
/*
*----------------------------------------------------------------------
*
- * 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.
+ * TSDTableGrow --
*
- * Results:
- * The return value is a pointer to the created entry.
+ * This procedure makes the passed TSDTable grow to fit the atLeast value.
*
- * Side effects:
- * None.
+ * Side effects: The table is enlarged.
*
*----------------------------------------------------------------------
*/
+static void
+TSDTableGrow(TSDTable *t, sig_atomic_t atLeast) {
+ sig_atomic_t newAllocated = t->allocated * 2;
+ void **newTablePtr;
+ sig_atomic_t i;
-static Tcl_HashEntry *
-AllocThreadStorageEntry(
- Tcl_HashTable *tablePtr, /* Hash table. */
- void *keyPtr) /* Key to store in the hash table entry. */
-{
- Tcl_HashEntry *hPtr;
+ if (newAllocated <= atLeast) {
+ newAllocated = atLeast + 10;
+ }
+
+ newTablePtr = TclpSysRealloc(t->table, sizeof (*newTablePtr) * newAllocated);
- hPtr = (Tcl_HashEntry *) TclpSysAlloc(sizeof(Tcl_HashEntry), 0);
- hPtr->key.oneWordValue = keyPtr;
- hPtr->clientData = NULL;
+ if (NULL == newTablePtr) {
+ Tcl_Panic("unable to reallocate TSDTable");
+ }
+
+ for (i = t->allocated; i < newAllocated; ++i) {
+ newTablePtr[i] = NULL;
+ }
- return hPtr;
+ t->allocated = newAllocated;
+ t->table = newTablePtr;
}
/*
*----------------------------------------------------------------------
*
- * FreeThreadStorageEntry --
+ * TclThreadStorageKeyGet --
*
- * 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.
+ * This procedure gets the value associated with the passed key.
*
- * Results:
- * None.
- *
- * Side effects:
- * None.
+ * Results: A pointer value associated with the Tcl_ThreadDataKey or NULL.
*
*----------------------------------------------------------------------
*/
+void *
+TclThreadStorageKeyGet(Tcl_ThreadDataKey *dataKeyPtr) {
+ TSDTable *t = TclpThreadGetMasterTSD(tclTsdKey);
+ void *resultPtr = NULL;
+ TSDUnion *keyPtr = (TSDUnion *)dataKeyPtr;
+ sig_atomic_t offset = keyPtr->offset;
+
+ if (t == NULL) {
+ return NULL;
+ }
-static void
-FreeThreadStorageEntry(
- Tcl_HashEntry *hPtr) /* Hash entry to free. */
-{
- TclpSysFree((char *) hPtr);
+ if (offset && offset > 0 && offset < t->allocated) {
+ resultPtr = t->table[offset];
+ }
+
+ return resultPtr;
}
/*
*----------------------------------------------------------------------
*
- * ThreadStorageGetHashTable --
- *
- * This procedure returns a hash table pointer to be used for thread
- * storage for the specified thread. This assumes that thread storage
- * lock is held.
+ * TclThreadStorageKeySet --
*
- * Results:
- * A hash table pointer for the specified thread, or NULL if the hash
- * table has not been created yet.
- *
- * Side effects:
- * May change an entry in the master thread storage cache to point to the
- * specified thread and it's associated hash table.
+ * This procedure set an association of value with the key passed.
+ * The associated value may be retrieved with TclThreadDataKeyGet().
+ *
+ * Side effects: The thread-specific table may be created or reallocated.
*
*----------------------------------------------------------------------
*/
-static Tcl_HashTable *
-ThreadStorageGetHashTable(
- Tcl_ThreadId id) /* Id of thread to get hash table for */
-{
- int index = PTR2UINT(id) % STORAGE_CACHE_SLOTS;
- Tcl_HashEntry *hPtr;
- int isNew;
-
- /*
- * 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.
- */
+void
+TclThreadStorageKeySet(Tcl_ThreadDataKey *dataKeyPtr, void *value) {
+ TSDTable *t = TclpThreadGetMasterTSD(tclTsdKey);
+ TSDUnion *keyPtr = (TSDUnion *)dataKeyPtr;
- Tcl_HashTable *hashTablePtr = threadStorageCache[index].hashTablePtr;
+ if (NULL == t) {
+ t = TSDTableCreate();
+ TclpThreadSetMasterTSD(tclTsdKey, t);
+ }
- if (threadStorageCache[index].id != id) {
- Tcl_MutexLock(&threadStorageLock);
+ Tcl_MutexLock(&tclTsdMutex);
- /*
- * It's not in the cache, so we look it up...
+ if (0 == keyPtr->offset) {
+ /*
+ * The Tcl_ThreadDataKey hasn't been used yet.
*/
+ ++tclTsdCounter;
- hPtr = Tcl_FindHashEntry(&threadStorageHashTable, (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 (tclTsdCounter >= t->allocated) {
+ TSDTableGrow(t, tclTsdCounter);
}
- if (hashTablePtr == NULL) {
- hashTablePtr = (Tcl_HashTable *)
- TclpSysAlloc(sizeof(Tcl_HashTable), 0);
+ keyPtr->offset = tclTsdCounter;
- if (hashTablePtr == NULL) {
- Tcl_Panic("could not allocate thread specific hash table, "
- "TclpSysAlloc failed from ThreadStorageGetHashTable!");
- }
- Tcl_InitCustomHashTable(hashTablePtr, TCL_CUSTOM_TYPE_KEYS,
- &tclThreadStorageHashKeyType);
+ t->table[tclTsdCounter] = value;
+ } else {
+ if (keyPtr->offset >= t->allocated) {
/*
- * Add new thread storage hash table to the master hash table.
+ * This is the first time this Tcl_ThreadDataKey has been
+ * used with the current thread.
*/
-
- hPtr = Tcl_CreateHashEntry(&threadStorageHashTable, (char *) id,
- &isNew);
-
- if (hPtr == NULL) {
- Tcl_Panic("Tcl_CreateHashEntry failed from "
- "ThreadStorageGetHashTable!");
- }
- Tcl_SetHashValue(hPtr, hashTablePtr);
+ TSDTableGrow(t, keyPtr->offset);
}
- /*
- * 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;
-
- Tcl_MutexUnlock(&threadStorageLock);
+ t->table[keyPtr->offset] = value;
}
- return hashTablePtr;
+ Tcl_MutexUnlock(&tclTsdMutex);
}
/*
*----------------------------------------------------------------------
*
- * TclInitThreadStorage --
+ * TclFinalizeThreadDataThread --
*
- * Initializes the thread storage allocator.
- *
- * Results:
- * None.
- *
- * Side effects:
- * This procedure initializes the master hash table that maps thread ID
- * onto the individual index tables that map thread data key to thread
- * data. It also creates a cache that enables fast lookup of the thread
- * data block array for a recently executing thread without using
- * spinlocks.
- *
- * This procedure is called from an extremely early point in Tcl's
- * initialization. In particular, it may not use ckalloc/ckfree because they
- * may depend on thread-local storage (it uses TclpSysAlloc and TclpSysFree
- * instead). It may not depend on synchronization primitives - but no threads
- * other than the master thread have yet been launched.
- *
- *----------------------------------------------------------------------
- */
-
-void
-TclInitThreadStorage(void)
-{
- Tcl_InitCustomHashTable(&threadStorageHashTable, TCL_CUSTOM_TYPE_KEYS,
- &tclThreadStorageHashKeyType);
-
- /*
- * We also initialize the cache.
- */
-
- memset((void*) &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;
-}
-
-/*
- *----------------------------------------------------------------------
- *
- * TclpThreadDataKeyGet --
- *
- * 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.
+ * This procedure finalizes the data for a single thread.
+ *
*
+ * Side effects: The TSDTable is deleted/freed.
*----------------------------------------------------------------------
*/
+void
+TclFinalizeThreadDataThread(void) {
+ TSDTable *t = TclpThreadGetMasterTSD(tclTsdKey);
-void *
-TclpThreadDataKeyGet(
- Tcl_ThreadDataKey *keyPtr) /* Identifier for the data chunk, really
- * (int**) */
-{
- Tcl_HashTable *hashTablePtr =
- ThreadStorageGetHashTable(Tcl_GetCurrentThread());
- Tcl_HashEntry *hPtr = Tcl_FindHashEntry(hashTablePtr, (char *) keyPtr);
-
- if (hPtr == NULL) {
- return NULL;
+ if (NULL == t) {
+ return;
}
- return Tcl_GetHashValue(hPtr);
-}
-
-/*
- *----------------------------------------------------------------------
- *
- * TclpThreadDataKeySet --
- *
- * This procedure sets the pointer to a block of thread local storage.
- *
- * Results:
- * None.
- *
- * Side effects:
- * Sets up the thread so future calls to TclpThreadDataKeyGet with this
- * key will return the data pointer.
- *
- *----------------------------------------------------------------------
- */
-void
-TclpThreadDataKeySet(
- Tcl_ThreadDataKey *keyPtr, /* Identifier for the data chunk, really
- * (pthread_key_t **) */
- void *data) /* Thread local storage */
-{
- Tcl_HashTable *hashTablePtr;
- Tcl_HashEntry *hPtr;
- int dummy;
-
- hashTablePtr = ThreadStorageGetHashTable(Tcl_GetCurrentThread());
- hPtr = Tcl_CreateHashEntry(hashTablePtr, (char *)keyPtr, &dummy);
-
- Tcl_SetHashValue(hPtr, data);
+ TSDTableDelete(t);
+
+ TclpThreadSetMasterTSD(tclTsdKey, NULL);
}
/*
*----------------------------------------------------------------------
*
- * TclpFinalizeThreadDataThread --
+ * TclInitializeThreadStorage --
*
- * This procedure cleans up the thread storage hash table for the
- * current thread.
- *
- * Results:
- * None.
- *
- * Side effects:
- * Frees all associated thread storage, all hash table entries for
- * the thread's thread storage, and the hash table itself.
+ * This procedure initializes the TSD subsystem with per-platform
+ * code. This should be called before any Tcl threads are created.
*
*----------------------------------------------------------------------
*/
-
void
-TclpFinalizeThreadDataThread(void)
-{
- Tcl_ThreadId id = Tcl_GetCurrentThread();
- /* Id of the thread to finalize. */
- int index = PTR2UINT(id) % STORAGE_CACHE_SLOTS;
- Tcl_HashEntry *hPtr; /* Hash entry for current thread in master
- * table. */
- Tcl_HashTable* hashTablePtr;/* Pointer to the hash table holding TSD
- * blocks for the current thread*/
- Tcl_HashSearch search; /* Search object to walk the TSD blocks in the
- * designated thread */
- Tcl_HashEntry *hPtr2; /* Hash entry for a TSD block in the
- * designated thread. */
-
- Tcl_MutexLock(&threadStorageLock);
- hPtr = Tcl_FindHashEntry(&threadStorageHashTable, (char*)id);
- if (hPtr == NULL) {
- hashTablePtr = NULL;
- } else {
- /*
- * We found it, extract the hash table pointer.
- */
-
- hashTablePtr = Tcl_GetHashValue(hPtr);
- Tcl_DeleteHashEntry(hPtr);
-
- /*
- * Make sure cache entry for this thread is NULL.
- */
-
- if (threadStorageCache[index].id == id) {
- /*
- * 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;
- }
- }
- Tcl_MutexUnlock(&threadStorageLock);
-
- /*
- * The thread's hash table has been extracted and removed from the master
- * hash table. Now clean up the thread.
- */
-
- if (hashTablePtr != NULL) {
- /*
- * Free all TSD
- */
-
- for (hPtr2 = Tcl_FirstHashEntry(hashTablePtr, &search); hPtr2 != NULL;
- hPtr2 = Tcl_NextHashEntry(&search)) {
- void *blockPtr = Tcl_GetHashValue(hPtr2);
-
- if (blockPtr != NULL) {
- /*
- * The block itself was allocated in Tcl_GetThreadData using
- * ckalloc; use ckfree to dispose of it.
- */
-
- ckfree(blockPtr);
- }
- }
-
- /*
- * Delete thread specific hash table and free the struct.
- */
-
- Tcl_DeleteHashTable(hashTablePtr);
- TclpSysFree((char *) hashTablePtr);
- }
+TclInitThreadStorage(void) {
+ tclTsdKey = TclpThreadCreateKey();
}
/*
@@ -475,72 +237,23 @@ TclpFinalizeThreadDataThread(void)
*
* TclFinalizeThreadStorage --
*
- * This procedure cleans up the master thread storage hash table, all
- * thread specific hash tables, and the thread storage cache.
+ * This procedure cleans up the thread storage data key for all
+ * threads.
+ *
+ * IMPORTANT: All Tcl threads must be finalized before calling this!
*
* Results:
- * None.
+ * None.
*
* Side effects:
- * The master thread storage hash table and thread storage cache are
- * reset to their initial (empty) state.
+ * Releases the thread data key.
*
*----------------------------------------------------------------------
*/
-
void
-TclFinalizeThreadStorage(void)
-{
- Tcl_HashSearch search; /* We need to hit every thread with this
- * search. */
- Tcl_HashEntry *hPtr; /* Hash entry for current thread in master
- * table. */
- Tcl_MutexLock(&threadStorageLock);
-
- /*
- * We are going to delete the hash table for every thread now. This hash
- * table should be empty at this point, except for one entry for the
- * current thread.
- */
-
- for (hPtr = Tcl_FirstHashEntry(&threadStorageHashTable, &search);
- hPtr != NULL; hPtr = Tcl_NextHashEntry(&search)) {
- Tcl_HashTable *hashTablePtr = Tcl_GetHashValue(hPtr);
-
- if (hashTablePtr != NULL) {
- /*
- * Delete thread specific hash table for the thread in question
- * and free the struct.
- */
-
- Tcl_DeleteHashTable(hashTablePtr);
- TclpSysFree((char *)hashTablePtr);
- }
-
- /*
- * Delete thread specific entry from master hash table.
- */
-
- Tcl_SetHashValue(hPtr, NULL);
- }
-
- Tcl_DeleteHashTable(&threadStorageHashTable);
-
- /*
- * Clear out the thread storage cache as well.
- */
-
- memset((void*) &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;
-
- Tcl_MutexUnlock(&threadStorageLock);
+TclFinalizeThreadStorage(void) {
+ TclpThreadDeleteKey(tclTsdKey);
+ tclTsdKey = NULL;
}
#else /* !defined(TCL_THREADS) */
@@ -555,7 +268,7 @@ TclInitThreadStorage(void)
}
void
-TclpFinalizeThreadDataThread(void)
+TclFinalizeThreadDataThread(void)
{
}
diff --git a/unix/tclUnixThrd.c b/unix/tclUnixThrd.c
index 0cb4b5d..63e8733 100644
--- a/unix/tclUnixThrd.c
+++ b/unix/tclUnixThrd.c
@@ -5,11 +5,12 @@
*
* Copyright (c) 1991-1994 The Regents of the University of California.
* Copyright (c) 1994-1997 Sun Microsystems, Inc.
+ * Copyright (c) 2008 by George Peter Staplin
*
* See the file "license.terms" for information on usage and redistribution of
* this file, and for a DISCLAIMER OF ALL WARRANTIES.
*
- * RCS: @(#) $Id: tclUnixThrd.c,v 1.57 2008/01/11 11:53:02 msofer Exp $
+ * RCS: @(#) $Id: tclUnixThrd.c,v 1.58 2008/05/09 04:58:54 georgeps Exp $
*/
#include "tclInt.h"
@@ -851,6 +852,48 @@ TclpSetAllocCache(
pthread_setspecific(key, arg);
}
#endif /* USE_THREAD_ALLOC */
+
+
+
+void *TclpThreadCreateKey(void) {
+ pthread_key_t *key;
+
+ key = TclpSysAlloc(sizeof *key, 0);
+ if (NULL == key) {
+ Tcl_Panic("unable to allocate thread key!");
+ }
+
+ if (pthread_key_create(key, NULL)) {
+ Tcl_Panic("unable to create pthread key!");
+ }
+
+ return key;
+}
+
+void TclpThreadDeleteKey(void *keyPtr) {
+ pthread_key_t *key = keyPtr;
+
+ if (pthread_key_delete(*key)) {
+ Tcl_Panic("unable to delete key!");
+ }
+
+ TclpSysFree(keyPtr);
+}
+
+void TclpThreadSetMasterTSD(void *tsdKeyPtr, void *ptr) {
+ pthread_key_t *key = tsdKeyPtr;
+
+ if (pthread_setspecific(*key, ptr)) {
+ Tcl_Panic("unable to set master TSD value");
+ }
+}
+
+void *TclpThreadGetMasterTSD(void *tsdKeyPtr) {
+ pthread_key_t *key = tsdKeyPtr;
+
+ return pthread_getspecific(*key);
+}
+
#endif /* TCL_THREADS */
/*
diff --git a/win/tclWinThrd.c b/win/tclWinThrd.c
index 11dcd1a..f6732b8 100644
--- a/win/tclWinThrd.c
+++ b/win/tclWinThrd.c
@@ -5,11 +5,12 @@
*
* Copyright (c) 1998 by Sun Microsystems, Inc.
* Copyright (c) 1999 by Scriptics Corporation
+ * Copyright (c) 2008 by George Peter Staplin
*
* See the file "license.terms" for information on usage and redistribution of
* this file, and for a DISCLAIMER OF ALL WARRANTIES.
*
- * RCS: @(#) $Id: tclWinThrd.c,v 1.44 2008/04/27 22:21:37 dkf Exp $
+ * RCS: @(#) $Id: tclWinThrd.c,v 1.45 2008/05/09 04:58:55 georgeps Exp $
*/
#include "tclWinInt.h"
@@ -854,6 +855,9 @@ TclpFinalizeCondition(
}
}
+
+
+
/*
* Additions by AOL for specialized thread memory allocator.
*/
@@ -954,6 +958,45 @@ TclpFreeAllocCache(
}
#endif /* USE_THREAD_ALLOC */
+
+
+void *TclpThreadCreateKey (void) {
+ DWORD *key;
+
+ key = TclpSysAlloc(sizeof *key, 0);
+ if (key == NULL) {
+ Tcl_Panic("unable to allocate thread key!");
+ }
+
+ *key = TlsAlloc();
+
+ return key;
+}
+
+void TclpThreadDeleteKey(void *keyPtr) {
+ DWORD *key = keyPtr;
+
+ if (!TlsFree(*key)) {
+ Tcl_Panic("unable to delete key");
+ }
+
+ TclpSysFree(keyPtr);
+}
+
+void TclpThreadSetMasterTSD(void *tsdKeyPtr, void *ptr) {
+ DWORD *key = tsdKeyPtr;
+
+ if (!TlsSetValue(*key, ptr)) {
+ Tcl_Panic("unable to set master TSD value");
+ }
+}
+
+void *TclpThreadGetMasterTSD(void *tsdKeyPtr) {
+ DWORD *key = tsdKeyPtr;
+
+ return TlsGetValue(*key);
+}
+
#endif /* TCL_THREADS */
/*