summaryrefslogtreecommitdiffstats
path: root/generic/tclThreadStorage.c
diff options
context:
space:
mode:
Diffstat (limited to 'generic/tclThreadStorage.c')
-rw-r--r--generic/tclThreadStorage.c309
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: