OpenFPGA/libs/EXTERNAL/tcl8.6.12/generic/tclThreadStorage.c

374 lines
8.6 KiB
C
Raw Normal View History

2022-06-07 11:15:20 -05:00
/*
* 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 global collection of information about TSDs. This is shared across the
* whole process, and includes the mutex used to protect it.
*/
static struct {
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. */
} tsdGlobal = { NULL, 0, NULL };
/*
* The type of the data held per thread in a system TSD.
*/
typedef struct {
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 {
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 = TclpThreadGetGlobalTSD(tsdGlobal.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 = TclpThreadGetGlobalTSD(tsdGlobal.key);
TSDUnion *keyPtr = (TSDUnion *) dataKeyPtr;
if (tsdTablePtr == NULL) {
tsdTablePtr = TSDTableCreate();
TclpThreadSetGlobalTSD(tsdGlobal.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(&tsdGlobal.mutex);
if (keyPtr->offset == 0) {
/*
* The Tcl_ThreadDataKey hasn't been used yet. Make a new one.
*/
keyPtr->offset = ++tsdGlobal.counter;
}
Tcl_MutexUnlock(&tsdGlobal.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 = TclpThreadGetGlobalTSD(tsdGlobal.key);
if (tsdTablePtr != NULL) {
TSDTableDelete(tsdTablePtr);
TclpThreadSetGlobalTSD(tsdGlobal.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)
{
tsdGlobal.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(tsdGlobal.key);
tsdGlobal.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:
*/