diff options
Diffstat (limited to 'tcl8.6/generic/tclThreadStorage.c')
-rw-r--r-- | tcl8.6/generic/tclThreadStorage.c | 373 |
1 files changed, 373 insertions, 0 deletions
diff --git a/tcl8.6/generic/tclThreadStorage.c b/tcl8.6/generic/tclThreadStorage.c new file mode 100644 index 0000000..755a461 --- /dev/null +++ b/tcl8.6/generic/tclThreadStorage.c @@ -0,0 +1,373 @@ +/* + * tclThreadStorage.c -- + * + * 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. + */ + +#include "tclInt.h" + +#ifdef TCL_THREADS +#include <signal.h> + +/* + * 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. + */ + +/* + * The master collection of information about TSDs. This is shared across the + * whole process, and includes the mutex used to protect it. + */ + +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 }; + +/* + * The type of the data held per thread in a system TSD. + */ + +typedef struct TSDTable { + ClientData *tablePtr; /* The table of Tcl TSDs. */ + sig_atomic_t allocated; /* The size of the table in the current + * thread. */ +} TSDTable; + +/* + * The actual type of Tcl_ThreadDataKey. + */ + +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; + +/* + * Forward declarations of functions in this file. + */ + +static TSDTable * TSDTableCreate(void); +static void TSDTableDelete(TSDTable *tsdTablePtr); +static void TSDTableGrow(TSDTable *tsdTablePtr, + sig_atomic_t atLeast); + +/* + * Allocator and deallocator for a TSDTable structure. + */ + +static TSDTable * +TSDTableCreate(void) +{ + TSDTable *tsdTablePtr; + sig_atomic_t i; + + tsdTablePtr = TclpSysAlloc(sizeof(TSDTable), 0); + if (tsdTablePtr == NULL) { + Tcl_Panic("unable to allocate TSDTable"); + } + + tsdTablePtr->allocated = 8; + tsdTablePtr->tablePtr = + TclpSysAlloc(sizeof(void *) * tsdTablePtr->allocated, 0); + if (tsdTablePtr->tablePtr == NULL) { + Tcl_Panic("unable to allocate TSDTable"); + } + + for (i = 0; i < tsdTablePtr->allocated; ++i) { + tsdTablePtr->tablePtr[i] = NULL; + } + + return tsdTablePtr; +} + +static void +TSDTableDelete( + TSDTable *tsdTablePtr) +{ + sig_atomic_t i; + + for (i=0 ; i<tsdTablePtr->allocated ; i++) { + if (tsdTablePtr->tablePtr[i] != NULL) { + /* + * These values were allocated in Tcl_GetThreadData in tclThread.c + * and must now be deallocated or they will leak. + */ + + ckfree(tsdTablePtr->tablePtr[i]); + } + } + + TclpSysFree(tsdTablePtr->tablePtr); + TclpSysFree(tsdTablePtr); +} + +/* + *---------------------------------------------------------------------- + * + * TSDTableGrow -- + * + * This procedure makes the passed TSDTable grow to fit the atLeast + * value. + * + * Results: + * None. + * + * Side effects: + * The table is enlarged. + * + *---------------------------------------------------------------------- + */ + +static void +TSDTableGrow( + TSDTable *tsdTablePtr, + sig_atomic_t atLeast) +{ + sig_atomic_t newAllocated = tsdTablePtr->allocated * 2; + ClientData *newTablePtr; + sig_atomic_t i; + + if (newAllocated <= atLeast) { + newAllocated = atLeast + 10; + } + + newTablePtr = TclpSysRealloc(tsdTablePtr->tablePtr, + sizeof(ClientData) * newAllocated); + if (newTablePtr == NULL) { + Tcl_Panic("unable to reallocate TSDTable"); + } + + for (i = tsdTablePtr->allocated; i < newAllocated; ++i) { + newTablePtr[i] = NULL; + } + + tsdTablePtr->allocated = newAllocated; + tsdTablePtr->tablePtr = newTablePtr; +} + +/* + *---------------------------------------------------------------------- + * + * TclThreadStorageKeyGet -- + * + * This procedure gets the value associated with the passed key. + * + * Results: + * A pointer value associated with the Tcl_ThreadDataKey or NULL. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +void * +TclThreadStorageKeyGet( + Tcl_ThreadDataKey *dataKeyPtr) +{ + 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; +} + +/* + *---------------------------------------------------------------------- + * + * TclThreadStorageKeySet -- + * + * This procedure set an association of value with the key passed. The + * associated value may be retrieved with TclThreadDataKeyGet(). + * + * Results: + * None. + * + * Side effects: + * The thread-specific table may be created or reallocated. + * + *---------------------------------------------------------------------- + */ + +void +TclThreadStorageKeySet( + Tcl_ThreadDataKey *dataKeyPtr, + void *value) +{ + TSDTable *tsdTablePtr = TclpThreadGetMasterTSD(tsdMaster.key); + TSDUnion *keyPtr = (TSDUnion *) dataKeyPtr; + + if (tsdTablePtr == NULL) { + tsdTablePtr = TSDTableCreate(); + TclpThreadSetMasterTSD(tsdMaster.key, tsdTablePtr); + } + + /* + * 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 (keyPtr->offset == 0) { + Tcl_MutexLock(&tsdMaster.mutex); + if (keyPtr->offset == 0) { + /* + * The Tcl_ThreadDataKey hasn't been used yet. Make a new one. + */ + + keyPtr->offset = ++tsdMaster.counter; + } + Tcl_MutexUnlock(&tsdMaster.mutex); + } + + /* + * 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 (keyPtr->offset >= tsdTablePtr->allocated) { + TSDTableGrow(tsdTablePtr, keyPtr->offset); + } + + /* + * Set the value in the Tcl thread-local variable. + */ + + tsdTablePtr->tablePtr[keyPtr->offset] = value; +} + +/* + *---------------------------------------------------------------------- + * + * TclFinalizeThreadDataThread -- + * + * This procedure finalizes the data for a single thread. + * + * Results: + * None. + * + * Side effects: + * The TSDTable is deleted/freed. + * + *---------------------------------------------------------------------- + */ + +void +TclFinalizeThreadDataThread(void) +{ + TSDTable *tsdTablePtr = TclpThreadGetMasterTSD(tsdMaster.key); + + if (tsdTablePtr != NULL) { + TSDTableDelete(tsdTablePtr); + TclpThreadSetMasterTSD(tsdMaster.key, NULL); + } +} + +/* + *---------------------------------------------------------------------- + * + * TclInitializeThreadStorage -- + * + * This procedure initializes the TSD subsystem with per-platform code. + * This should be called before any Tcl threads are created. + * + * Results: + * None. + * + * Side effects: + * Allocates a system TSD. + * + *---------------------------------------------------------------------- + */ + +void +TclInitThreadStorage(void) +{ + tsdMaster.key = TclpThreadCreateKey(); +} + +/* + *---------------------------------------------------------------------- + * + * TclFinalizeThreadStorage -- + * + * 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: + * Releases the thread data key. + * + *---------------------------------------------------------------------- + */ + +void +TclFinalizeThreadStorage(void) +{ + TclpThreadDeleteKey(tsdMaster.key); + tsdMaster.key = NULL; +} + +#else /* !TCL_THREADS */ +/* + * Stub functions for non-threaded builds + */ + +void +TclInitThreadStorage(void) +{ +} + +void +TclFinalizeThreadDataThread(void) +{ +} + +void +TclFinalizeThreadStorage(void) +{ +} +#endif /* TCL_THREADS */ + +/* + * Local Variables: + * mode: c + * c-basic-offset: 4 + * fill-column: 78 + * End: + */ |