tclThreadStorage.cGo to the documentation of this file.00001 /* 00002 * tclThreadStorage.c -- 00003 * 00004 * This file implements platform independent thread storage operations. 00005 * 00006 * Copyright (c) 2003-2004 by Joe Mistachkin 00007 * 00008 * See the file "license.terms" for information on usage and redistribution of 00009 * this file, and for a DISCLAIMER OF ALL WARRANTIES. 00010 * 00011 * RCS: @(#) $Id: tclThreadStorage.c,v 1.15 2007/12/13 15:23:20 dgp Exp $ 00012 */ 00013 00014 #include "tclInt.h" 00015 00016 #if defined(TCL_THREADS) 00017 00018 /* 00019 * This is the thread storage cache array and it's accompanying mutex. The 00020 * elements are pairs of thread Id and an associated hash table pointer; the 00021 * hash table being pointed to contains the thread storage for it's associated 00022 * thread. The purpose of this cache is to minimize the number of hash table 00023 * lookups in the master thread storage hash table. 00024 */ 00025 00026 static Tcl_Mutex threadStorageLock; 00027 00028 /* 00029 * This is the struct used for a thread storage cache slot. It contains the 00030 * owning thread Id and the associated hash table pointer. 00031 */ 00032 00033 typedef struct ThreadStorage { 00034 Tcl_ThreadId id; /* the owning thread id */ 00035 Tcl_HashTable *hashTablePtr;/* the hash table for the thread */ 00036 } ThreadStorage; 00037 00038 /* 00039 * These are the prototypes for the custom hash table allocation functions 00040 * used by the thread storage subsystem. 00041 */ 00042 00043 static Tcl_HashEntry * AllocThreadStorageEntry(Tcl_HashTable *tablePtr, 00044 void *keyPtr); 00045 static void FreeThreadStorageEntry(Tcl_HashEntry *hPtr); 00046 static Tcl_HashTable * ThreadStorageGetHashTable(Tcl_ThreadId id); 00047 00048 /* 00049 * This is the hash key type for thread storage. We MUST use this in 00050 * combination with the new hash key type flag TCL_HASH_KEY_SYSTEM_HASH 00051 * because these hash tables MAY be used by the threaded memory allocator. 00052 */ 00053 00054 static Tcl_HashKeyType tclThreadStorageHashKeyType = { 00055 TCL_HASH_KEY_TYPE_VERSION, /* version */ 00056 TCL_HASH_KEY_SYSTEM_HASH | TCL_HASH_KEY_RANDOMIZE_HASH, 00057 /* flags */ 00058 NULL, /* hashKeyProc */ 00059 NULL, /* compareKeysProc */ 00060 AllocThreadStorageEntry, /* allocEntryProc */ 00061 FreeThreadStorageEntry /* freeEntryProc */ 00062 }; 00063 00064 /* 00065 * This is an invalid thread value. 00066 */ 00067 00068 #define STORAGE_INVALID_THREAD (Tcl_ThreadId)0 00069 00070 /* 00071 * This is the value for an invalid thread storage key. 00072 */ 00073 00074 #define STORAGE_INVALID_KEY 0 00075 00076 /* 00077 * This is the first valid key for use by external callers. All the values 00078 * below this are RESERVED for future use. 00079 */ 00080 00081 #define STORAGE_FIRST_KEY 1 00082 00083 /* 00084 * This is the default number of thread storage cache slots. This define may 00085 * need to be fine tuned for maximum performance. 00086 */ 00087 00088 #define STORAGE_CACHE_SLOTS 97 00089 00090 /* 00091 * This is the master thread storage hash table. It is keyed on thread Id and 00092 * contains values that are hash tables for each thread. The thread specific 00093 * hash tables contain the actual thread storage. 00094 */ 00095 00096 static Tcl_HashTable threadStorageHashTable; 00097 00098 /* 00099 * This is the next thread data key value to use. We increment this everytime 00100 * we "allocate" one. It is initially set to 1 in TclInitThreadStorage. 00101 */ 00102 00103 static int nextThreadStorageKey = STORAGE_INVALID_KEY; 00104 00105 /* 00106 * This is the master thread storage cache. Per Kevin Kenny's idea, this 00107 * prevents unnecessary lookups for threads that use a lot of thread storage. 00108 */ 00109 00110 static volatile ThreadStorage threadStorageCache[STORAGE_CACHE_SLOTS]; 00111 00112 /* 00113 *---------------------------------------------------------------------- 00114 * 00115 * AllocThreadStorageEntry -- 00116 * 00117 * Allocate space for a Tcl_HashEntry using TclpSysAlloc (not ckalloc). 00118 * We do this because the threaded memory allocator MAY use the thread 00119 * storage hash tables. 00120 * 00121 * Results: 00122 * The return value is a pointer to the created entry. 00123 * 00124 * Side effects: 00125 * None. 00126 * 00127 *---------------------------------------------------------------------- 00128 */ 00129 00130 static Tcl_HashEntry * 00131 AllocThreadStorageEntry( 00132 Tcl_HashTable *tablePtr, /* Hash table. */ 00133 void *keyPtr) /* Key to store in the hash table entry. */ 00134 { 00135 Tcl_HashEntry *hPtr; 00136 00137 hPtr = (Tcl_HashEntry *) TclpSysAlloc(sizeof(Tcl_HashEntry), 0); 00138 hPtr->key.oneWordValue = keyPtr; 00139 hPtr->clientData = NULL; 00140 00141 return hPtr; 00142 } 00143 00144 /* 00145 *---------------------------------------------------------------------- 00146 * 00147 * FreeThreadStorageEntry -- 00148 * 00149 * Frees space for a Tcl_HashEntry using TclpSysFree (not ckfree). We do 00150 * this because the threaded memory allocator MAY use the thread storage 00151 * hash tables. 00152 * 00153 * Results: 00154 * None. 00155 * 00156 * Side effects: 00157 * None. 00158 * 00159 *---------------------------------------------------------------------- 00160 */ 00161 00162 static void 00163 FreeThreadStorageEntry( 00164 Tcl_HashEntry *hPtr) /* Hash entry to free. */ 00165 { 00166 TclpSysFree((char *) hPtr); 00167 } 00168 00169 /* 00170 *---------------------------------------------------------------------- 00171 * 00172 * ThreadStorageGetHashTable -- 00173 * 00174 * This procedure returns a hash table pointer to be used for thread 00175 * storage for the specified thread. This assumes that thread storage 00176 * lock is held. 00177 * 00178 * Results: 00179 * A hash table pointer for the specified thread, or NULL if the hash 00180 * table has not been created yet. 00181 * 00182 * Side effects: 00183 * May change an entry in the master thread storage cache to point to the 00184 * specified thread and it's associated hash table. 00185 * 00186 *---------------------------------------------------------------------- 00187 */ 00188 00189 static Tcl_HashTable * 00190 ThreadStorageGetHashTable( 00191 Tcl_ThreadId id) /* Id of thread to get hash table for */ 00192 { 00193 int index = PTR2UINT(id) % STORAGE_CACHE_SLOTS; 00194 Tcl_HashEntry *hPtr; 00195 int isNew; 00196 00197 /* 00198 * It's important that we pick up the hash table pointer BEFORE comparing 00199 * thread Id in case another thread is in the critical region changing 00200 * things out from under you. 00201 */ 00202 00203 Tcl_HashTable *hashTablePtr = threadStorageCache[index].hashTablePtr; 00204 00205 if (threadStorageCache[index].id != id) { 00206 Tcl_MutexLock(&threadStorageLock); 00207 00208 /* 00209 * It's not in the cache, so we look it up... 00210 */ 00211 00212 hPtr = Tcl_FindHashEntry(&threadStorageHashTable, (char *) id); 00213 00214 if (hPtr != NULL) { 00215 /* 00216 * We found it, extract the hash table pointer. 00217 */ 00218 00219 hashTablePtr = Tcl_GetHashValue(hPtr); 00220 } else { 00221 /* 00222 * The thread specific hash table is not found. 00223 */ 00224 00225 hashTablePtr = NULL; 00226 } 00227 00228 if (hashTablePtr == NULL) { 00229 hashTablePtr = (Tcl_HashTable *) 00230 TclpSysAlloc(sizeof(Tcl_HashTable), 0); 00231 00232 if (hashTablePtr == NULL) { 00233 Tcl_Panic("could not allocate thread specific hash table, " 00234 "TclpSysAlloc failed from ThreadStorageGetHashTable!"); 00235 } 00236 Tcl_InitCustomHashTable(hashTablePtr, TCL_CUSTOM_TYPE_KEYS, 00237 &tclThreadStorageHashKeyType); 00238 00239 /* 00240 * Add new thread storage hash table to the master hash table. 00241 */ 00242 00243 hPtr = Tcl_CreateHashEntry(&threadStorageHashTable, (char *) id, 00244 &isNew); 00245 00246 if (hPtr == NULL) { 00247 Tcl_Panic("Tcl_CreateHashEntry failed from " 00248 "ThreadStorageGetHashTable!"); 00249 } 00250 Tcl_SetHashValue(hPtr, hashTablePtr); 00251 } 00252 00253 /* 00254 * Now, we put it in the cache since it is highly likely it will be 00255 * needed again shortly. 00256 */ 00257 00258 threadStorageCache[index].id = id; 00259 threadStorageCache[index].hashTablePtr = hashTablePtr; 00260 00261 Tcl_MutexUnlock(&threadStorageLock); 00262 } 00263 00264 return hashTablePtr; 00265 } 00266 00267 /* 00268 *---------------------------------------------------------------------- 00269 * 00270 * TclInitThreadStorage -- 00271 * 00272 * Initializes the thread storage allocator. 00273 * 00274 * Results: 00275 * None. 00276 * 00277 * Side effects: 00278 * This procedure initializes the master hash table that maps thread ID 00279 * onto the individual index tables that map thread data key to thread 00280 * data. It also creates a cache that enables fast lookup of the thread 00281 * data block array for a recently executing thread without using 00282 * spinlocks. 00283 * 00284 * This procedure is called from an extremely early point in Tcl's 00285 * initialization. In particular, it may not use ckalloc/ckfree because they 00286 * may depend on thread-local storage (it uses TclpSysAlloc and TclpSysFree 00287 * instead). It may not depend on synchronization primitives - but no threads 00288 * other than the master thread have yet been launched. 00289 * 00290 *---------------------------------------------------------------------- 00291 */ 00292 00293 void 00294 TclInitThreadStorage(void) 00295 { 00296 Tcl_InitCustomHashTable(&threadStorageHashTable, TCL_CUSTOM_TYPE_KEYS, 00297 &tclThreadStorageHashKeyType); 00298 00299 /* 00300 * We also initialize the cache. 00301 */ 00302 00303 memset((void*) &threadStorageCache, 0, 00304 sizeof(ThreadStorage) * STORAGE_CACHE_SLOTS); 00305 00306 /* 00307 * Now, we set the first value to be used for a thread data key. 00308 */ 00309 00310 nextThreadStorageKey = STORAGE_FIRST_KEY; 00311 } 00312 00313 /* 00314 *---------------------------------------------------------------------- 00315 * 00316 * TclpThreadDataKeyGet -- 00317 * 00318 * This procedure returns a pointer to a block of thread local storage. 00319 * 00320 * Results: 00321 * A thread-specific pointer to the data structure, or NULL if the memory 00322 * has not been assigned to this key for this thread. 00323 * 00324 * Side effects: 00325 * None. 00326 * 00327 *---------------------------------------------------------------------- 00328 */ 00329 00330 void * 00331 TclpThreadDataKeyGet( 00332 Tcl_ThreadDataKey *keyPtr) /* Identifier for the data chunk, really 00333 * (int**) */ 00334 { 00335 Tcl_HashTable *hashTablePtr = 00336 ThreadStorageGetHashTable(Tcl_GetCurrentThread()); 00337 Tcl_HashEntry *hPtr = Tcl_FindHashEntry(hashTablePtr, (char *) keyPtr); 00338 00339 if (hPtr == NULL) { 00340 return NULL; 00341 } 00342 return Tcl_GetHashValue(hPtr); 00343 } 00344 00345 /* 00346 *---------------------------------------------------------------------- 00347 * 00348 * TclpThreadDataKeySet -- 00349 * 00350 * This procedure sets the pointer to a block of thread local storage. 00351 * 00352 * Results: 00353 * None. 00354 * 00355 * Side effects: 00356 * Sets up the thread so future calls to TclpThreadDataKeyGet with this 00357 * key will return the data pointer. 00358 * 00359 *---------------------------------------------------------------------- 00360 */ 00361 00362 void 00363 TclpThreadDataKeySet( 00364 Tcl_ThreadDataKey *keyPtr, /* Identifier for the data chunk, really 00365 * (pthread_key_t **) */ 00366 void *data) /* Thread local storage */ 00367 { 00368 Tcl_HashTable *hashTablePtr; 00369 Tcl_HashEntry *hPtr; 00370 int dummy; 00371 00372 hashTablePtr = ThreadStorageGetHashTable(Tcl_GetCurrentThread()); 00373 hPtr = Tcl_CreateHashEntry(hashTablePtr, (char *)keyPtr, &dummy); 00374 00375 Tcl_SetHashValue(hPtr, data); 00376 } 00377 00378 /* 00379 *---------------------------------------------------------------------- 00380 * 00381 * TclpFinalizeThreadDataThread -- 00382 * 00383 * This procedure cleans up the thread storage hash table for the 00384 * current thread. 00385 * 00386 * Results: 00387 * None. 00388 * 00389 * Side effects: 00390 * Frees all associated thread storage, all hash table entries for 00391 * the thread's thread storage, and the hash table itself. 00392 * 00393 *---------------------------------------------------------------------- 00394 */ 00395 00396 void 00397 TclpFinalizeThreadDataThread(void) 00398 { 00399 Tcl_ThreadId id = Tcl_GetCurrentThread(); 00400 /* Id of the thread to finalize. */ 00401 int index = PTR2UINT(id) % STORAGE_CACHE_SLOTS; 00402 Tcl_HashEntry *hPtr; /* Hash entry for current thread in master 00403 * table. */ 00404 Tcl_HashTable* hashTablePtr;/* Pointer to the hash table holding TSD 00405 * blocks for the current thread*/ 00406 Tcl_HashSearch search; /* Search object to walk the TSD blocks in the 00407 * designated thread */ 00408 Tcl_HashEntry *hPtr2; /* Hash entry for a TSD block in the 00409 * designated thread. */ 00410 00411 Tcl_MutexLock(&threadStorageLock); 00412 hPtr = Tcl_FindHashEntry(&threadStorageHashTable, (char*)id); 00413 if (hPtr == NULL) { 00414 hashTablePtr = NULL; 00415 } else { 00416 /* 00417 * We found it, extract the hash table pointer. 00418 */ 00419 00420 hashTablePtr = Tcl_GetHashValue(hPtr); 00421 Tcl_DeleteHashEntry(hPtr); 00422 00423 /* 00424 * Make sure cache entry for this thread is NULL. 00425 */ 00426 00427 if (threadStorageCache[index].id == id) { 00428 /* 00429 * We do not step on another thread's cache entry. This is 00430 * especially important if we are creating and exiting a lot of 00431 * threads. 00432 */ 00433 00434 threadStorageCache[index].id = STORAGE_INVALID_THREAD; 00435 threadStorageCache[index].hashTablePtr = NULL; 00436 } 00437 } 00438 Tcl_MutexUnlock(&threadStorageLock); 00439 00440 /* 00441 * The thread's hash table has been extracted and removed from the master 00442 * hash table. Now clean up the thread. 00443 */ 00444 00445 if (hashTablePtr != NULL) { 00446 /* 00447 * Free all TSD 00448 */ 00449 00450 for (hPtr2 = Tcl_FirstHashEntry(hashTablePtr, &search); hPtr2 != NULL; 00451 hPtr2 = Tcl_NextHashEntry(&search)) { 00452 void *blockPtr = Tcl_GetHashValue(hPtr2); 00453 00454 if (blockPtr != NULL) { 00455 /* 00456 * The block itself was allocated in Tcl_GetThreadData using 00457 * ckalloc; use ckfree to dispose of it. 00458 */ 00459 00460 ckfree(blockPtr); 00461 } 00462 } 00463 00464 /* 00465 * Delete thread specific hash table and free the struct. 00466 */ 00467 00468 Tcl_DeleteHashTable(hashTablePtr); 00469 TclpSysFree((char *) hashTablePtr); 00470 } 00471 } 00472 00473 /* 00474 *---------------------------------------------------------------------- 00475 * 00476 * TclFinalizeThreadStorage -- 00477 * 00478 * This procedure cleans up the master thread storage hash table, all 00479 * thread specific hash tables, and the thread storage cache. 00480 * 00481 * Results: 00482 * None. 00483 * 00484 * Side effects: 00485 * The master thread storage hash table and thread storage cache are 00486 * reset to their initial (empty) state. 00487 * 00488 *---------------------------------------------------------------------- 00489 */ 00490 00491 void 00492 TclFinalizeThreadStorage(void) 00493 { 00494 Tcl_HashSearch search; /* We need to hit every thread with this 00495 * search. */ 00496 Tcl_HashEntry *hPtr; /* Hash entry for current thread in master 00497 * table. */ 00498 Tcl_MutexLock(&threadStorageLock); 00499 00500 /* 00501 * We are going to delete the hash table for every thread now. This hash 00502 * table should be empty at this point, except for one entry for the 00503 * current thread. 00504 */ 00505 00506 for (hPtr = Tcl_FirstHashEntry(&threadStorageHashTable, &search); 00507 hPtr != NULL; hPtr = Tcl_NextHashEntry(&search)) { 00508 Tcl_HashTable *hashTablePtr = Tcl_GetHashValue(hPtr); 00509 00510 if (hashTablePtr != NULL) { 00511 /* 00512 * Delete thread specific hash table for the thread in question 00513 * and free the struct. 00514 */ 00515 00516 Tcl_DeleteHashTable(hashTablePtr); 00517 TclpSysFree((char *)hashTablePtr); 00518 } 00519 00520 /* 00521 * Delete thread specific entry from master hash table. 00522 */ 00523 00524 Tcl_SetHashValue(hPtr, NULL); 00525 } 00526 00527 Tcl_DeleteHashTable(&threadStorageHashTable); 00528 00529 /* 00530 * Clear out the thread storage cache as well. 00531 */ 00532 00533 memset((void*) &threadStorageCache, 0, 00534 sizeof(ThreadStorage) * STORAGE_CACHE_SLOTS); 00535 00536 /* 00537 * Reset this to zero, it will be set to STORAGE_FIRST_KEY if the thread 00538 * storage subsystem gets reinitialized 00539 */ 00540 00541 nextThreadStorageKey = STORAGE_INVALID_KEY; 00542 00543 Tcl_MutexUnlock(&threadStorageLock); 00544 } 00545 00546 #else /* !defined(TCL_THREADS) */ 00547 00548 /* 00549 * Stub functions for non-threaded builds 00550 */ 00551 00552 void 00553 TclInitThreadStorage(void) 00554 { 00555 } 00556 00557 void 00558 TclpFinalizeThreadDataThread(void) 00559 { 00560 } 00561 00562 void 00563 TclFinalizeThreadStorage(void) 00564 { 00565 } 00566 00567 #endif /* defined(TCL_THREADS) && defined(USE_THREAD_STORAGE) */ 00568 00569 /* 00570 * Local Variables: 00571 * mode: c 00572 * c-basic-offset: 4 00573 * fill-column: 78 00574 * End: 00575 */
Generated on Wed Mar 12 12:18:22 2008 by 1.5.1 |