diff options
Diffstat (limited to 'generic/tclThreadStorage.c')
| -rw-r--r-- | generic/tclThreadStorage.c | 1105 | 
1 files changed, 183 insertions, 922 deletions
diff --git a/generic/tclThreadStorage.c b/generic/tclThreadStorage.c index 7eb66be..f24e334 100644 --- a/generic/tclThreadStorage.c +++ b/generic/tclThreadStorage.c @@ -1,610 +1,183 @@  /*   * tclThreadStorage.c --   * - *	This file implements platform independent thread storage operations. + *	This file implements platform independent thread storage operations to + *	work around system limits on the number of thread-specific variables.   *   * Copyright (c) 2003-2004 by Joe Mistachkin + * 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: tclThreadStorage.c,v 1.4 2004/06/24 09:05:46 dkf Exp $ + * See the file "license.terms" for information on usage and redistribution of + * this file, and for a DISCLAIMER OF ALL WARRANTIES.   */  #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; +#ifdef TCL_THREADS +#include <signal.h>  /* - * Have we initialized the thread storage mutex yet? + * IMPLEMENTATION NOTES: + * + * 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 greater than 0; 0 is for the initialized Tcl_ThreadDataKey.   */ -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. + * The master collection of information about TSDs. This is shared across the + * whole process, and includes the mutex used to protect it.   */ -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. - * - *---------------------------------------------------------------------- - */ +static struct TSDMaster { +    void *key;			/* Key into the system TSD structure. The +				 * collection of Tcl TSD values for a +				 * particular thread will hang off the +				 * back-end of this. */ +    sig_atomic_t counter;	/* The number of different Tcl TSDs used +				 * across *all* threads. This is a strictly +				 * increasing value. */ +    Tcl_Mutex mutex;		/* Protection for the rest of this structure, +				 * which holds per-process data. */ +} tsdMaster = { NULL, 0, NULL }; -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. - * - *---------------------------------------------------------------------- + * The type of the data held per thread in a system TSD.   */ -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. - * - *---------------------------------------------------------------------- - */ +typedef struct TSDTable { +    ClientData *tablePtr;	/* The table of Tcl TSDs. */ +    sig_atomic_t allocated;	/* The size of the table in the current +				 * thread. */ +} TSDTable; -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. - * - *---------------------------------------------------------------------- + * The actual type of Tcl_ThreadDataKey.   */ -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; +typedef union TSDUnion { +    volatile sig_atomic_t offset; +				/* The type is really an offset into the +				 * thread-local table of TSDs, which is this +				 * field. */ +    void *ptr;			/* For alignment purposes only. Not actually +				 * accessed through this. */ +} TSDUnion; -    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. - * - *---------------------------------------------------------------------- + * Forward declarations of functions in this file.   */ -static void -FreeThreadStorageEntry(hPtr) -    Tcl_HashEntry *hPtr;	/* Hash entry to free. */ -{ -    TclpSysFree((char *)hPtr); -} +static TSDTable *	TSDTableCreate(void); +static void		TSDTableDelete(TSDTable *tsdTablePtr); +static void		TSDTableGrow(TSDTable *tsdTablePtr, +			    sig_atomic_t atLeast);  /* - *---------------------------------------------------------------------- - * - *  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. - * - *---------------------------------------------------------------------- + * Allocator and deallocator for a TSDTable structure.   */ -void -TclThreadStoragePrint(outFile, flags) -    FILE *outFile;		/* The file to print the information to. */ -    int flags;			/* Reserved for future use. */ +static TSDTable * +TSDTableCreate(void)  { -    Tcl_HashEntry *hPtr; -    Tcl_HashSearch search; -    int header, index; - -    if (threadStorageHashTablePtr != NULL) { -	hPtr = Tcl_FirstHashEntry(threadStorageHashTablePtr, &search); +    TSDTable *tsdTablePtr; +    sig_atomic_t i; -	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"); +    tsdTablePtr = TclpSysAlloc(sizeof(TSDTable), 0); +    if (tsdTablePtr == NULL) { +	Tcl_Panic("unable to allocate TSDTable");      } -    header = 0; /* we have not output the header yet. */ -    for (index = 0; index < STORAGE_CACHE_SLOTS; index++) { -	if (threadStorageCache[index].id != STORAGE_INVALID_THREAD) { -	    if (!header) { -		fprintf(outFile, "thread storage cache (%d total slots):\n", -			STORAGE_CACHE_SLOTS); -		header = 1; -	    } - -	    fprintf(outFile, "slot %d, thread %p, thread table ptr %p\n", -		    index, threadStorageCache[index].id, -		    threadStorageCache[index].hashTablePtr); -#ifdef VERBOSE_THREAD_STORAGE_DEBUGGING -	    /* -	     * Currently not enabled by default due to Tcl_HashStats -	     * use of ckalloc and ckfree.  Please note that this can -	     * produce a LOT of output. -	     */ -	    if (threadStorageCache[index].hashTablePtr != NULL) { -		CONST char *stats = -			Tcl_HashStats(threadStorageCache[index].hashTablePtr); -		if (stats != NULL) { -		    fprintf(outFile, "%s\n", stats); -		    ckfree((void *)stats); -		} else { -		    fprintf(outFile, -			    "could not get table statistics for slot %d\n", -			    index); -		} -	    } -#endif -	} else { -	    /* fprintf(outFile, "cache slot %d not used\n", index); */ -	} +    tsdTablePtr->allocated = 8; +    tsdTablePtr->tablePtr = +	    TclpSysAlloc(sizeof(void *) * tsdTablePtr->allocated, 0); +    if (tsdTablePtr->tablePtr == NULL) { +	Tcl_Panic("unable to allocate TSDTable");      } -    if (!header) { -	fprintf(outFile, "thread storage cache is empty (%d total slots)\n", -		STORAGE_CACHE_SLOTS); -	header = 1; +    for (i = 0; i < tsdTablePtr->allocated; ++i) { +	tsdTablePtr->tablePtr[i] = NULL;      } -    /* -     * Show the next data key value. -     */ - -    fprintf(outFile, "next data key value is: %d\n", nextThreadStorageKey); +    return tsdTablePtr;  } - -/* - *---------------------------------------------------------------------- - * - * TclThreadStorageGetHashTable -- - * - *	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. - * - * 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. - * - *---------------------------------------------------------------------- - */ -Tcl_HashTable * -TclThreadStorageGetHashTable(id) -    Tcl_ThreadId id;		/* Id of thread to get hash table for */ +static void +TSDTableDelete( +    TSDTable *tsdTablePtr)  { -    int index = (unsigned 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_Panic("could not allocate thread specific hash " -			    "table, TclpSysAlloc failed from " -			    "TclThreadStorageGetHashTable!"); -		} -		Tcl_InitCustomHashTable(hashTablePtr, TCL_CUSTOM_TYPE_KEYS, -			&tclThreadStorageHashKeyType); - -		/* -		 * Add new thread storage hash table to the master -		 * hash table. -		 */ - -		hPtr = Tcl_CreateHashEntry(threadStorageHashTablePtr, -			(char *)id, &new); - -		if (hPtr == NULL) { -		    Tcl_Panic("Tcl_CreateHashEntry failed from " -			    "TclThreadStorageInit!"); -		} -		Tcl_SetHashValue(hPtr, hashTablePtr); -	    } +    sig_atomic_t i; +    for (i=0 ; i<tsdTablePtr->allocated ; i++) { +	if (tsdTablePtr->tablePtr[i] != NULL) {  	    /* -	     * Now, we put it in the cache since it is highly likely -	     * it will be needed again shortly. +	     * These values were allocated in Tcl_GetThreadData in tclThread.c +	     * and must now be deallocated or they will leak.  	     */ -	    threadStorageCache[index].id = id; -	    threadStorageCache[index].hashTablePtr = hashTablePtr; -	} else { -	    /* -	     * We cannot look it up, the master hash table has not -	     * been initialized. -	     */ -	    hashTablePtr = NULL; +	    ckfree(tsdTablePtr->tablePtr[i]);  	} -	TclThreadStorageUnlock();      } -    return hashTablePtr; +    TclpSysFree(tsdTablePtr->tablePtr); +    TclpSysFree(tsdTablePtr);  }  /*   *----------------------------------------------------------------------   * - * 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. + * TSDTableGrow --   * - *	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 thread to get 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) { -	    Tcl_Panic("could not allocate master thread storage hash table, " -		    "TclpSysAlloc failed from TclThreadStorageInit!"); -	} -	Tcl_InitCustomHashTable(threadStorageHashTablePtr, -		TCL_CUSTOM_TYPE_KEYS, &tclThreadStorageHashKeyType); - -	/* -	 * We also initialize the cache. -	 */ - -	memset((ThreadStorage *)&threadStorageCache, 0, -		sizeof(ThreadStorage) * STORAGE_CACHE_SLOTS); - -	/* -	 * Now, we set the first value to be used for a thread data key. -	 */ - -	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. + *	This procedure makes the passed TSDTable grow to fit the atLeast + *	value.   *   * 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. + *	The table is enlarged.   *   *----------------------------------------------------------------------   */ -void -TclThreadStorageDataKeyInit(keyPtr) -    Tcl_ThreadDataKey *keyPtr;	/* Identifier for the data chunk, -				 * really (int **) */ +static void +TSDTableGrow( +    TSDTable *tsdTablePtr, +    sig_atomic_t atLeast)  { -    int *indexPtr; -    int newKey; - -    if (*keyPtr == NULL) { -	indexPtr = (int *)TclpSysAlloc(sizeof(int), 0); -	if (indexPtr == NULL) { -	    Tcl_Panic("TclpSysAlloc failed from TclThreadStorageDataKeyInit!"); -	} +    sig_atomic_t newAllocated = tsdTablePtr->allocated * 2; +    ClientData *newTablePtr; +    sig_atomic_t i; -	/* -	 * 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. -	 */ +    if (newAllocated <= atLeast) { +	newAllocated = atLeast + 10; +    } -	newKey = nextThreadStorageKey++; -	TclThreadStorageUnlock(); +    newTablePtr = TclpSysRealloc(tsdTablePtr->tablePtr, +	    sizeof(ClientData) * newAllocated); +    if (newTablePtr == NULL) { +	Tcl_Panic("unable to reallocate TSDTable"); +    } -	*indexPtr = newKey; -	*keyPtr = (Tcl_ThreadDataKey)indexPtr; -	TclRememberDataKey(keyPtr); +    for (i = tsdTablePtr->allocated; i < newAllocated; ++i) { +	newTablePtr[i] = NULL;      } + +    tsdTablePtr->allocated = newAllocated; +    tsdTablePtr->tablePtr = newTablePtr;  }  /*   *----------------------------------------------------------------------   * - * TclThreadStorageDataKeyGet -- + * TclThreadStorageKeyGet --   * - *	This procedure returns a pointer to a block of thread local storage. + *    This procedure gets the value associated with the passed key.   *   * Results: - *	A thread-specific pointer to the data structure, or NULL - *	if the memory has not been assigned to this key for this thread. + *	A pointer value associated with the Tcl_ThreadDataKey or NULL.   *   * Side effects:   *	None. @@ -613,500 +186,188 @@ TclThreadStorageDataKeyInit(keyPtr)   */  void * -TclThreadStorageDataKeyGet(keyPtr) -    Tcl_ThreadDataKey *keyPtr;	/* Identifier for the data chunk, -				 * really (int **) */ +TclThreadStorageKeyGet( +    Tcl_ThreadDataKey *dataKeyPtr)  { -    int *indexPtr = *(int **)keyPtr; - -    if (indexPtr == NULL) { -	return NULL; -    } else { -	Tcl_HashTable *hashTablePtr = -		TclThreadStorageGetHashTable(Tcl_GetCurrentThread()); -	Tcl_HashEntry *hPtr; - -	if (hashTablePtr == NULL) { -	    Tcl_Panic("TclThreadStorageGetHashTable failed from " -		    "TclThreadStorageDataKeyGet!"); -	} - -	hPtr = Tcl_FindHashEntry(hashTablePtr, (char *)*indexPtr); - -	if (hPtr == NULL) { -	    return NULL; -	} -	return (void *)Tcl_GetHashValue(hPtr); +    TSDTable *tsdTablePtr = TclpThreadGetMasterTSD(tsdMaster.key); +    ClientData resultPtr = NULL; +    TSDUnion *keyPtr = (TSDUnion *) dataKeyPtr; +    sig_atomic_t offset = keyPtr->offset; + +    if ((tsdTablePtr != NULL) && (offset > 0) +	    && (offset < tsdTablePtr->allocated)) { +	resultPtr = tsdTablePtr->tablePtr[offset];      } +    return resultPtr;  }  /*   *----------------------------------------------------------------------   * - * TclThreadStorageDataKeySet -- - * - *	This procedure sets the pointer to a block of thread local storage. + * TclThreadStorageKeySet --   * + *	This procedure set an association of value with the key passed. The + *	associated value may be retrieved with TclThreadDataKeyGet(). + *    * Results:   *	None.   *   * Side effects: - *	Sets up the thread so future calls to TclThreadStorageDataKeyGet - *	with this key will return the data pointer. + *	The thread-specific table may be created or reallocated.    *   *----------------------------------------------------------------------   */  void -TclThreadStorageDataKeySet(keyPtr, data) -    Tcl_ThreadDataKey *keyPtr;	/* Identifier for the data chunk, -				 * really (pthread_key_t **) */ -    void *data;			/* Thread local storage */ +TclThreadStorageKeySet( +    Tcl_ThreadDataKey *dataKeyPtr, +    void *value)  { -    int *indexPtr = *(int **)keyPtr; -    Tcl_HashTable *hashTablePtr; -    Tcl_HashEntry *hPtr; +    TSDTable *tsdTablePtr = TclpThreadGetMasterTSD(tsdMaster.key); +    TSDUnion *keyPtr = (TSDUnion *) dataKeyPtr; -    hashTablePtr = TclThreadStorageGetHashTable(Tcl_GetCurrentThread()); -    if (hashTablePtr == NULL) { -	Tcl_Panic("TclThreadStorageGetHashTable failed from " -		"TclThreadStorageDataKeySet!"); +    if (tsdTablePtr == NULL) { +	tsdTablePtr = TSDTableCreate(); +	TclpThreadSetMasterTSD(tsdMaster.key, tsdTablePtr);      } -    hPtr = Tcl_FindHashEntry(hashTablePtr, (char *)*indexPtr); -      /* -     * Does the item need to be created? +     * Get the lock while we check if this TSD is new or not. Note that this +     * is the only place where Tcl_ThreadDataKey values are set. We use a +     * double-checked lock to try to avoid having to grab this lock a lot, +     * since it is on quite a few critical paths and will only get set once in +     * each location.       */ -    if (hPtr == NULL) { -	int new; -	hPtr = Tcl_CreateHashEntry(hashTablePtr, (char *)*indexPtr, &new); -	if (hPtr == NULL) { -	    Tcl_Panic("could not create hash entry value from " -		    "TclThreadStorageDataKeySet"); -	} -    } - -    Tcl_SetHashValue(hPtr, data); -} - -/* - *---------------------------------------------------------------------- - * - * 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 = (unsigned int)id % STORAGE_CACHE_SLOTS; -    Tcl_HashTable *hashTablePtr; /* Hash table for current thread */ -    Tcl_HashEntry *hPtr;	/* Hash entry for current thread in master -				 * table */ - -    TclThreadStorageLock(); - -    if (threadStorageHashTablePtr != NULL) { -	hPtr = Tcl_FindHashEntry(threadStorageHashTablePtr, (char *)id); - -	if (hPtr != NULL) { +    if (keyPtr->offset == 0) { +	Tcl_MutexLock(&tsdMaster.mutex); +	if (keyPtr->offset == 0) {  	    /* -	     * We found it, extract the hash table pointer. +	     * The Tcl_ThreadDataKey hasn't been used yet. Make a new one.  	     */ -	    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); +	    keyPtr->offset = ++tsdMaster.counter;  	} +	Tcl_MutexUnlock(&tsdMaster.mutex);      }      /* -     * Make sure cache entry for this thread is NULL. +     * Check if this is the first time this Tcl_ThreadDataKey has been used +     * with the current thread. Note that we don't need to hold a lock when +     * doing this, as we are *definitely* the only point accessing this +     * tsdTablePtr right now; it's thread-local.       */ -    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; +    if (keyPtr->offset >= tsdTablePtr->allocated) { +	TSDTableGrow(tsdTablePtr, keyPtr->offset);      } -    TclThreadStorageUnlock(); +    /* +     * Set the value in the Tcl thread-local variable. +     */ + +    tsdTablePtr->tablePtr[keyPtr->offset] = value;  }  /*   *----------------------------------------------------------------------   * - * TclFinalizeThreadStorage -- + * TclFinalizeThreadDataThread --   * - *	This procedure cleans up the master thread storage hash table, - *	all thread specific hash tables, and the thread storage cache. + *	This procedure finalizes the data for a single thread.   *   * Results:   *	None.   *   * Side effects: - *	The master thread storage hash table and thread storage cache are - *	reset to their initial (empty) state. + *	The TSDTable is deleted/freed.   *   *----------------------------------------------------------------------   */ -void -TclFinalizeThreadStorage() +void  +TclFinalizeThreadDataThread(void)  { -    TclThreadStorageLock(); - -    if (threadStorageHashTablePtr != NULL) { -	Tcl_HashSearch search;		/* We need to hit every thread with -					 * this search. */ -	Tcl_HashEntry *hPtr;		/* Hash entry for current thread in -					 * master table. */ - -	/* -	 * We are going to delete the hash table for every thread now. -	 * This hash table should be empty at this point, except for -	 * one entry for the current thread. -	 */ - -	for (hPtr = Tcl_FirstHashEntry(threadStorageHashTablePtr, &search); -		hPtr != NULL; hPtr = Tcl_NextHashEntry(&search)) { -	    Tcl_HashTable *hashTablePtr = Tcl_GetHashValue(hPtr); - -	    if (hashTablePtr != NULL) { -		/* -		 * Delete thread specific hash table for the thread in -		 * question and free the struct. -		 */ - -		Tcl_DeleteHashTable(hashTablePtr); -		TclpSysFree((char *)hashTablePtr); -	    } - -	    /* -	     * 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. -	 */ +    TSDTable *tsdTablePtr = TclpThreadGetMasterTSD(tsdMaster.key); -	threadStorageHashTablePtr = NULL; +    if (tsdTablePtr != NULL) { +	TSDTableDelete(tsdTablePtr); +	TclpThreadSetMasterTSD(tsdMaster.key, 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 -- + * TclInitializeThreadStorage --   * - *	This procedure cleans up the thread-local storage.  This is - *	called once for each thread. + *	This procedure initializes the TSD subsystem with per-platform code. + *	This should be called before any Tcl threads are created.   *   * Results:   *	None.   *   * Side effects: - *	Frees up the memory. + *	Allocates a system TSD.   *   *----------------------------------------------------------------------   */  void -TclFinalizeThreadStorageData(keyPtr) -    Tcl_ThreadDataKey *keyPtr; +TclInitThreadStorage(void)  { -    if (*keyPtr != NULL) { -	Tcl_ThreadId id = Tcl_GetCurrentThread(); -	Tcl_HashTable *hashTablePtr;	/* Hash table for current thread */ -	Tcl_HashEntry *hPtr;		/* Hash entry for data key in current -					 * thread. */ -	int *indexPtr = *(int **)keyPtr; - -	hashTablePtr = TclThreadStorageGetHashTable(id); -	if (hashTablePtr == NULL) { -	    Tcl_Panic("TclThreadStorageGetHashTable failed from " -		    "TclFinalizeThreadStorageData!"); -	} - -	hPtr = Tcl_FindHashEntry(hashTablePtr, (char *)*indexPtr); -	if (hPtr != NULL) { -	    void *result = Tcl_GetHashValue(hPtr); - -	    if (result != NULL) { -		/* -		 * This must be ckfree because tclThread.c allocates -		 * these using ckalloc. -		 */ -		ckfree((char *)result); -	    } - -	    Tcl_SetHashValue(hPtr, NULL); -	} -    } +    tsdMaster.key = TclpThreadCreateKey();  }  /*   *----------------------------------------------------------------------   * - * 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. + * TclFinalizeThreadStorage --   * - *	This assumes the master lock is held. + *	This procedure cleans up the thread storage data key for all threads. + *	IMPORTANT: All Tcl threads must be finalized before calling this!   *   * Results:   *	None.   *   * Side effects: - *	The key is deallocated. + *	Releases the thread data key.   *   *----------------------------------------------------------------------   */  void -TclFinalizeThreadStorageDataKey(keyPtr) -    Tcl_ThreadDataKey *keyPtr; +TclFinalizeThreadStorage(void)  { -    int *indexPtr; -    Tcl_HashTable *hashTablePtr;/* Hash table for current thread */ -    Tcl_HashSearch search;	/* Need to hit every thread with this search */ -    Tcl_HashEntry *hPtr;	/* Hash entry for current thread in master -				 * table. */ -    Tcl_HashEntry *hDataPtr;	/* Hash entry for data key in current thread */ - -    if (*keyPtr != NULL) { -	indexPtr = *(int **)keyPtr; - -	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 /* USE_THREAD_STORAGE */ -#else -    Tcl_Panic("Tcl was not compiled with threads enabled."); -#endif /* TCL_THREADS */ +    TclpThreadDeleteKey(tsdMaster.key); +    tsdMaster.key = NULL;  } +#else /* !TCL_THREADS */  /* - * Stub functions that just call ThreadStoragePanic. + * Stub functions for non-threaded builds   */  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() +TclInitThreadStorage(void)  { -    ThreadStoragePanic(NULL);  }  void -TclFinalizeThreadStorageData(keyPtr) -    Tcl_ThreadDataKey *keyPtr; +TclFinalizeThreadDataThread(void)  { -    ThreadStoragePanic(NULL);  }  void -TclFinalizeThreadStorageDataKey(keyPtr) -    Tcl_ThreadDataKey *keyPtr; +TclFinalizeThreadStorage(void)  { -    ThreadStoragePanic(NULL);  } - -#endif /* defined(TCL_THREADS) && defined(USE_THREAD_STORAGE) */ +#endif /* TCL_THREADS */ + +/* + * Local Variables: + * mode: c + * c-basic-offset: 4 + * fill-column: 78 + * End: + */  | 
