diff options
Diffstat (limited to 'generic/tclThreadStorage.c')
-rw-r--r-- | generic/tclThreadStorage.c | 309 |
1 files changed, 189 insertions, 120 deletions
diff --git a/generic/tclThreadStorage.c b/generic/tclThreadStorage.c index d0fdeec..71fa1e9 100644 --- a/generic/tclThreadStorage.c +++ b/generic/tclThreadStorage.c @@ -1,78 +1,117 @@ /* * 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 * - * 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.16 2008/05/09 04:58:54 georgeps Exp $ + * RCS: @(#) $Id: tclThreadStorage.c,v 1.17 2008/11/29 12:15:30 dkf Exp $ */ #include "tclInt.h" + +#ifdef TCL_THREADS #include <signal.h> -#if defined(TCL_THREADS) +/* + * 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 }; -static void *tclTsdKey = NULL; -static Tcl_Mutex tclTsdMutex; -static sig_atomic_t tclTsdCounter = 0; +/* + * The type of the data held per thread in a system TSD. + */ typedef struct TSDTable { - sig_atomic_t allocated; - void **table; + ClientData *tablePtr; /* The table of Tcl TSDs. */ + sig_atomic_t allocated; /* The size of the table in the current + * thread. */ } TSDTable; -typedef union { - void *ptr; +/* + * 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; -static TSDTable *TSDTableCreate(void); -static void TSDTableDelete(TSDTable *t); -static void TSDTableGrow(TSDTable *t, sig_atomic_t atLeast); +/* + * 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 *t; +TSDTableCreate(void) +{ + TSDTable *tsdTablePtr; sig_atomic_t i; - t = TclpSysAlloc(sizeof *t, 0); - if (NULL == t) { + tsdTablePtr = TclpSysAlloc(sizeof(TSDTable), 0); + if (tsdTablePtr == NULL) { Tcl_Panic("unable to allocate TSDTable"); } - t->allocated = 8; - t->table = TclpSysAlloc(sizeof (*(t->table)) * t->allocated, 0); - if (NULL == t->table) { + 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 < t->allocated; ++i) { - t->table[i] = NULL; + for (i = 0; i < tsdTablePtr->allocated; ++i) { + tsdTablePtr->tablePtr[i] = NULL; } - return t; + return tsdTablePtr; } - + static void -TSDTableDelete(TSDTable *t) { - TclpSysFree(t->table); - TclpSysFree(t); +TSDTableDelete( + TSDTable *tsdTablePtr) +{ + TclpSysFree(tsdTablePtr->tablePtr); + TclpSysFree(tsdTablePtr); } /* @@ -80,34 +119,43 @@ TSDTableDelete(TSDTable *t) { * * TSDTableGrow -- * - * This procedure makes the passed TSDTable grow to fit the atLeast value. + * This procedure makes the passed TSDTable grow to fit the atLeast + * value. * - * Side effects: The table is enlarged. + * Results: + * 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; +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(t->table, sizeof (*newTablePtr) * newAllocated); - - if (NULL == newTablePtr) { + newTablePtr = TclpSysRealloc(tsdTablePtr->tablePtr, + sizeof(ClientData) * newAllocated); + if (newTablePtr == NULL) { Tcl_Panic("unable to reallocate TSDTable"); } - - for (i = t->allocated; i < newAllocated; ++i) { + + for (i = tsdTablePtr->allocated; i < newAllocated; ++i) { newTablePtr[i] = NULL; } - - t->allocated = newAllocated; - t->table = newTablePtr; + + tsdTablePtr->allocated = newAllocated; + tsdTablePtr->tablePtr = newTablePtr; } /* @@ -117,25 +165,28 @@ TSDTableGrow(TSDTable *t, sig_atomic_t atLeast) { * * This procedure gets the value associated with the passed key. * - * Results: A pointer value associated with the Tcl_ThreadDataKey or NULL. + * Results: + * A pointer value associated with the Tcl_ThreadDataKey or NULL. + * + * Side effects: + * None. * *---------------------------------------------------------------------- */ + void * -TclThreadStorageKeyGet(Tcl_ThreadDataKey *dataKeyPtr) { - TSDTable *t = TclpThreadGetMasterTSD(tclTsdKey); - void *resultPtr = NULL; - TSDUnion *keyPtr = (TSDUnion *)dataKeyPtr; +TclThreadStorageKeyGet( + Tcl_ThreadDataKey *dataKeyPtr) +{ + TSDTable *tsdTablePtr = TclpThreadGetMasterTSD(tsdMaster.key); + ClientData resultPtr = NULL; + TSDUnion *keyPtr = (TSDUnion *) dataKeyPtr; sig_atomic_t offset = keyPtr->offset; - - if (t == NULL) { - return NULL; - } - if (offset && offset > 0 && offset < t->allocated) { - resultPtr = t->table[offset]; + if ((tsdTablePtr != NULL) && (offset > 0) + && (offset < tsdTablePtr->allocated)) { + resultPtr = tsdTablePtr->tablePtr[offset]; } - return resultPtr; } @@ -144,53 +195,62 @@ TclThreadStorageKeyGet(Tcl_ThreadDataKey *dataKeyPtr) { * * TclThreadStorageKeySet -- * - * This procedure set an association of value with the key passed. - * The associated value may be retrieved with TclThreadDataKeyGet(). + * 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. + * Results: + * None. + * + * Side effects: + * The thread-specific table may be created or reallocated. * *---------------------------------------------------------------------- */ void -TclThreadStorageKeySet(Tcl_ThreadDataKey *dataKeyPtr, void *value) { - TSDTable *t = TclpThreadGetMasterTSD(tclTsdKey); - TSDUnion *keyPtr = (TSDUnion *)dataKeyPtr; +TclThreadStorageKeySet( + Tcl_ThreadDataKey *dataKeyPtr, + void *value) +{ + TSDTable *tsdTablePtr = TclpThreadGetMasterTSD(tsdMaster.key); + TSDUnion *keyPtr = (TSDUnion *) dataKeyPtr; - if (NULL == t) { - t = TSDTableCreate(); - TclpThreadSetMasterTSD(tclTsdKey, t); + if (tsdTablePtr == NULL) { + tsdTablePtr = TSDTableCreate(); + TclpThreadSetMasterTSD(tsdMaster.key, tsdTablePtr); } - Tcl_MutexLock(&tclTsdMutex); + /* + * 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. + */ - if (0 == keyPtr->offset) { + Tcl_MutexLock(&tsdMaster.mutex); + if (keyPtr->offset == 0) { /* - * The Tcl_ThreadDataKey hasn't been used yet. + * The Tcl_ThreadDataKey hasn't been used yet. Make a new one. */ - ++tclTsdCounter; - - if (tclTsdCounter >= t->allocated) { - TSDTableGrow(t, tclTsdCounter); - } - - keyPtr->offset = tclTsdCounter; - t->table[tclTsdCounter] = value; - } else { + keyPtr->offset = ++tsdMaster.counter; + } + Tcl_MutexUnlock(&tsdMaster.mutex); - if (keyPtr->offset >= t->allocated) { - /* - * This is the first time this Tcl_ThreadDataKey has been - * used with the current thread. - */ - TSDTableGrow(t, keyPtr->offset); - } + /* + * 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. + */ - t->table[keyPtr->offset] = value; + if (keyPtr->offset >= tsdTablePtr->allocated) { + TSDTableGrow(tsdTablePtr, keyPtr->offset); } - Tcl_MutexUnlock(&tclTsdMutex); + /* + * Set the value in the Tcl thread-local variable. + */ + + tsdTablePtr->tablePtr[keyPtr->offset] = value; } /* @@ -198,23 +258,26 @@ TclThreadStorageKeySet(Tcl_ThreadDataKey *dataKeyPtr, void *value) { * * TclFinalizeThreadDataThread -- * - * This procedure finalizes the data for a single thread. - * + * This procedure finalizes the data for a single thread. + * + * Results: + * None. + * + * Side effects: + * The TSDTable is deleted/freed. * - * Side effects: The TSDTable is deleted/freed. *---------------------------------------------------------------------- */ + void -TclFinalizeThreadDataThread(void) { - TSDTable *t = TclpThreadGetMasterTSD(tclTsdKey); +TclFinalizeThreadDataThread(void) +{ + TSDTable *tsdTablePtr = TclpThreadGetMasterTSD(tsdMaster.key); - if (NULL == t) { - return; + if (tsdTablePtr != NULL) { + TSDTableDelete(tsdTablePtr); + TclpThreadSetMasterTSD(tsdMaster.key, NULL); } - - TSDTableDelete(t); - - TclpThreadSetMasterTSD(tclTsdKey, NULL); } /* @@ -222,14 +285,22 @@ TclFinalizeThreadDataThread(void) { * * TclInitializeThreadStorage -- * - * This procedure initializes the TSD subsystem with per-platform - * code. This should be called before any Tcl threads are created. + * 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) { - tclTsdKey = TclpThreadCreateKey(); +TclInitThreadStorage(void) +{ + tsdMaster.key = TclpThreadCreateKey(); } /* @@ -237,27 +308,26 @@ TclInitThreadStorage(void) { * * TclFinalizeThreadStorage -- * - * This procedure cleans up the thread storage data key for all - * threads. - * - * IMPORTANT: All Tcl threads must be finalized before calling this! + * 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: - * Releases the thread data key. + * Releases the thread data key. * *---------------------------------------------------------------------- */ + void -TclFinalizeThreadStorage(void) { - TclpThreadDeleteKey(tclTsdKey); - tclTsdKey = NULL; +TclFinalizeThreadStorage(void) +{ + TclpThreadDeleteKey(tsdMaster.key); + tsdMaster.key = NULL; } -#else /* !defined(TCL_THREADS) */ - +#else /* !TCL_THREADS */ /* * Stub functions for non-threaded builds */ @@ -276,8 +346,7 @@ void TclFinalizeThreadStorage(void) { } - -#endif /* defined(TCL_THREADS) && defined(USE_THREAD_STORAGE) */ +#endif /* TCL_THREADS */ /* * Local Variables: |