/* * tclThreadStorage.c -- * * This file implements platform independent thread storage operations. * * Copyright (c) 2003-2004 by Joe Mistachkin * * See the file "license.terms" for information on usage and redistribution of * this file, and for a DISCLAIMER OF ALL WARRANTIES. * * RCS: @(#) $Id: tclThreadStorage.c,v 1.6 2005/08/05 23:56:29 dkf Exp $ */ #include "tclInt.h" #if defined(TCL_THREADS) && defined(USE_THREAD_STORAGE) /* * This is the thread storage cache array and it's accompanying mutex. The * elements are pairs of thread Id and an associated hash table pointer; the * hash table being pointed to contains the thread storage for it's associated * thread. The purpose of this cache is to minimize the number of hash table * lookups in the master thread storage hash table. */ static Tcl_Mutex threadStorageLock; /* * This is the struct used for a thread storage cache slot. It contains the * owning thread Id and the associated hash table pointer. */ typedef struct ThreadStorage { Tcl_ThreadId id; /* the owning thread id */ Tcl_HashTable *hashTablePtr;/* the hash table for the thread */ } ThreadStorage; /* * These are the prototypes for the custom hash table allocation functions * used by the thread storage subsystem. */ static Tcl_HashEntry * AllocThreadStorageEntry(Tcl_HashTable *tablePtr, void *keyPtr); static void FreeThreadStorageEntry(Tcl_HashEntry *hPtr); static void ThreadStorageLockInit(void); static void ThreadStorageLock(void); static void ThreadStorageUnlock(void); static Tcl_HashTable * ThreadStorageGetHashTable(Tcl_ThreadId id); static Tcl_HashTable * ThreadStorageInit(Tcl_ThreadId id, void *reserved); static void FinalizeThreadStorageThreadMushroomMushroom( 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. */ 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 ThreadStorageInit. */ 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 Kevin Kenny's idea, this * prevents unnecessary lookups for threads that use a lot of thread storage. */ static volatile ThreadStorage threadStorageCache[STORAGE_CACHE_SLOTS]; /* *---------------------------------------------------------------------- * * ThreadStorageLockInit * * 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 void ThreadStorageLockInit() { 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; } } /* *---------------------------------------------------------------------- * * ThreadStorageLock * * 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. * *---------------------------------------------------------------------- */ static void ThreadStorageLock() { ThreadStorageLockInit(); Tcl_MutexLock(&threadStorageLock); } /* *---------------------------------------------------------------------- * * ThreadStorageUnlock * * This procedure is used to release a lock that serializes creation of * thread storage. * * Results: * None. * * Side effects: * Release the thread storage mutex. * *---------------------------------------------------------------------- */ static void ThreadStorageUnlock() { 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); } #ifdef TCL_THREAD_STORAGE_DEBUG /* *---------------------------------------------------------------------- * * ThreadStoragePrint -- * * 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. * *---------------------------------------------------------------------- */ static void ThreadStoragePrint(outFile, flags) FILE *outFile; /* The file to print the information to. */ int flags; /* Reserved for future use. */ { Tcl_HashEntry *hPtr; Tcl_HashSearch search; int header, 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