1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373
|
/*
* tclThreadStorage.c --
*
* This file implements platform independent thread storage operations to
* work around system limits on the number of thread-specific variables.
*
* Copyright © 2003-2004 Joe Mistachkin
* Copyright © 2008 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"
#if 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 {
void **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 = (TSDTable *)TclpSysAlloc(sizeof(TSDTable));
if (tsdTablePtr == NULL) {
Tcl_Panic("unable to allocate TSDTable");
}
tsdTablePtr->allocated = 8;
tsdTablePtr->tablePtr =
(void **)TclpSysAlloc(sizeof(void *) * tsdTablePtr->allocated);
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.
*/
Tcl_Free(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;
void **newTablePtr;
sig_atomic_t i;
if (newAllocated <= atLeast) {
newAllocated = atLeast + 10;
}
newTablePtr = (void **)TclpSysRealloc(tsdTablePtr->tablePtr,
sizeof(void *) * 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 = (TSDTable *)TclpThreadGetGlobalTSD(tsdGlobal.key);
void *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 = (TSDTable *)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 = (TSDTable *)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:
*/
|