/* * tclThreadStorage.c -- * * This file implements platform independent thread storage operations. * * 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 $ */ #include "tclInt.h" #include #if defined(TCL_THREADS) static void *tclTsdKey = NULL; static Tcl_Mutex tclTsdMutex; static sig_atomic_t tclTsdCounter = 0; typedef struct TSDTable { sig_atomic_t allocated; void **table; } TSDTable; typedef union { void *ptr; volatile sig_atomic_t offset; } TSDUnion; static TSDTable *TSDTableCreate(void); static void TSDTableDelete(TSDTable *t); static void TSDTableGrow(TSDTable *t, sig_atomic_t atLeast); static TSDTable * TSDTableCreate(void) { TSDTable *t; sig_atomic_t i; t = TclpSysAlloc(sizeof *t, 0); if (NULL == t) { Tcl_Panic("unable to allocate TSDTable"); } t->allocated = 8; t->table = TclpSysAlloc(sizeof (*(t->table)) * t->allocated, 0); if (NULL == t->table) { Tcl_Panic("unable to allocate TSDTable"); } for (i = 0; i < t->allocated; ++i) { t->table[i] = NULL; } return t; } static void TSDTableDelete(TSDTable *t) { TclpSysFree(t->table); TclpSysFree(t); } /* *---------------------------------------------------------------------- * * TSDTableGrow -- * * This procedure makes the passed TSDTable grow to fit the atLeast value. * * Side effects: The table is enlarged. * *---------------------------------------------------------------------- */ static void TSDTableGrow(TSDTable *t, sig_atomic_t atLeast) { sig_atomic_t newAllocated = t->allocated * 2; void **newTablePtr; sig_atomic_t i; if (newAllocated <= atLeast) { newAllocated = atLeast + 10; } newTablePtr = TclpSysRealloc(t->table, sizeof (*newTablePtr) * newAllocated); if (NULL == newTablePtr) { Tcl_Panic("unable to reallocate TSDTable"); } for (i = t->allocated; i < newAllocated; ++i) { newTablePtr[i] = NULL; } t->allocated = newAllocated; t->table = newTablePtr; } /* *---------------------------------------------------------------------- * * TclThreadStorageKeyGet -- * * This procedure gets the value associated with the passed key. * * Results: A pointer value associated with the Tcl_ThreadDataKey or NULL. * *---------------------------------------------------------------------- */ void * TclThreadStorageKeyGet(Tcl_ThreadDataKey *dataKeyPtr) { TSDTable *t = TclpThreadGetMasterTSD(tclTsdKey); void *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]; } return resultPtr; } /* *---------------------------------------------------------------------- * * TclThreadStorageKeySet -- * * 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. * *---------------------------------------------------------------------- */ void TclThreadStorageKeySet(Tcl_ThreadDataKey *dataKeyPtr, void *value) { TSDTable *t = TclpThreadGetMasterTSD(tclTsdKey); TSDUnion *keyPtr = (TSDUnion *)dataKeyPtr; if (NULL == t) { t = TSDTableCreate(); TclpThreadSetMasterTSD(tclTsdKey, t); } Tcl_MutexLock(&tclTsdMutex); if (0 == keyPtr->offset) { /* * The Tcl_ThreadDataKey hasn't been used yet. */ ++tclTsdCounter; if (tclTsdCounter >= t->allocated) { TSDTableGrow(t, tclTsdCounter); } keyPtr->offset = tclTsdCounter; t->table[tclTsdCounter] = value; } else { if (keyPtr->offset >= t->allocated) { /* * This is the first time this Tcl_ThreadDataKey has been * used with the current thread. */ TSDTableGrow(t, keyPtr->offset); } t->table[keyPtr->offset] = value; } Tcl_MutexUnlock(&tclTsdMutex); } /* *---------------------------------------------------------------------- * * TclFinalizeThreadDataThread -- * * This procedure finalizes the data for a single thread. * * * Side effects: The TSDTable is deleted/freed. *---------------------------------------------------------------------- */ void TclFinalizeThreadDataThread(void) { TSDTable *t = TclpThreadGetMasterTSD(tclTsdKey); if (NULL == t) { return; } TSDTableDelete(t); TclpThreadSetMasterTSD(tclTsdKey, NULL); } /* *---------------------------------------------------------------------- * * TclInitializeThreadStorage -- * * This procedure initializes the TSD subsystem with per-platform * code. This should be called before any Tcl threads are created. * *---------------------------------------------------------------------- */ void TclInitThreadStorage(void) { tclTsdKey = 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(tclTsdKey); tclTsdKey = NULL; } #else /* !defined(TCL_THREADS) */ /* * Stub functions for non-threaded builds */ void TclInitThreadStorage(void) { } void TclFinalizeThreadDataThread(void) { } void TclFinalizeThreadStorage(void) { } #endif /* defined(TCL_THREADS) && defined(USE_THREAD_STORAGE) */ /* * Local Variables: * mode: c * c-basic-offset: 4 * fill-column: 78 * End: */