prtpd.c (7832B)
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* This Source Code Form is subject to the terms of the Mozilla Public 3 * License, v. 2.0. If a copy of the MPL was not distributed with this 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 5 6 /* 7 ** Thread Private Data 8 ** 9 ** There is an aribitrary limit on the number of keys that will be allocated 10 ** by the runtime. It's largish, so it is intended to be a sanity check, not 11 ** an impediment. 12 ** 13 ** There is a counter, initialized to zero and incremented every time a 14 ** client asks for a new key, that holds the high water mark for keys. All 15 ** threads logically have the same high water mark and are permitted to 16 ** ask for TPD up to that key value. 17 ** 18 ** The vector to hold the TPD are allocated when PR_SetThreadPrivate() is 19 ** called. The size of the vector will be some value greater than or equal 20 ** to the current high water mark. Each thread has its own TPD length and 21 ** vector. 22 ** 23 ** Threads that get private data for keys they have not set (or perhaps 24 ** don't even exist for that thread) get a NULL return. If the key is 25 ** beyond the high water mark, an error will be returned. 26 */ 27 28 /* 29 ** As of this time, BeOS has its own TPD implementation. Integrating 30 ** this standard one is a TODO for anyone with a bit of spare time on 31 ** their hand. For now, we just #ifdef out this whole file and use 32 ** the routines in pr/src/btthreads/ 33 */ 34 35 #include "primpl.h" 36 37 #include <string.h> 38 39 #if defined(WIN95) 40 /* 41 ** Some local variables report warnings on Win95 because the code paths 42 ** using them are conditioned on HAVE_CUSTOME_USER_THREADS. 43 ** The pragma suppresses the warning. 44 ** 45 */ 46 # pragma warning(disable : 4101) 47 #endif 48 49 #define _PR_TPD_LIMIT 128 /* arbitary limit on the TPD slots */ 50 static PRInt32 _pr_tpd_length = 0; /* current length of destructor vector */ 51 static PRInt32 _pr_tpd_highwater = 0; /* next TPD key to be assigned */ 52 static PRThreadPrivateDTOR* _pr_tpd_destructors = NULL; 53 /* the destructors are associated with 54 the keys, therefore asserting that 55 the TPD key depicts the data's 'type' */ 56 57 /* 58 ** Initialize the thread private data manipulation 59 */ 60 void _PR_InitTPD(void) { 61 _pr_tpd_destructors = (PRThreadPrivateDTOR*)PR_CALLOC( 62 _PR_TPD_LIMIT * sizeof(PRThreadPrivateDTOR*)); 63 PR_ASSERT(NULL != _pr_tpd_destructors); 64 _pr_tpd_length = _PR_TPD_LIMIT; 65 } 66 67 /* 68 ** Clean up the thread private data manipulation 69 */ 70 void _PR_CleanupTPD(void) {} /* _PR_CleanupTPD */ 71 72 /* 73 ** This routine returns a new index for per-thread-private data table. 74 ** The index is visible to all threads within a process. This index can 75 ** be used with the PR_SetThreadPrivate() and PR_GetThreadPrivate() routines 76 ** to save and retrieve data associated with the index for a thread. 77 ** 78 ** The index independently maintains specific values for each binding thread. 79 ** A thread can only get access to its own thread-specific-data. 80 ** 81 ** Upon a new index return the value associated with the index for all threads 82 ** is NULL, and upon thread creation the value associated with all indices for 83 ** that thread is NULL. 84 ** 85 ** "dtor" is the destructor function to invoke when the private 86 ** data is set or destroyed 87 ** 88 ** Returns PR_FAILURE if the total number of indices will exceed the maximun 89 ** allowed. 90 */ 91 92 PR_IMPLEMENT(PRStatus) 93 PR_NewThreadPrivateIndex(PRUintn* newIndex, PRThreadPrivateDTOR dtor) { 94 PRStatus rv; 95 PRInt32 index; 96 97 if (!_pr_initialized) { 98 _PR_ImplicitInitialization(); 99 } 100 101 PR_ASSERT(NULL != newIndex); 102 PR_ASSERT(NULL != _pr_tpd_destructors); 103 104 index = PR_ATOMIC_INCREMENT(&_pr_tpd_highwater) - 1; /* allocate index */ 105 if (_PR_TPD_LIMIT <= index) { 106 PR_SetError(PR_TPD_RANGE_ERROR, 0); 107 rv = PR_FAILURE; /* that's just wrong */ 108 } else { 109 _pr_tpd_destructors[index] = dtor; /* record destructor @index */ 110 *newIndex = (PRUintn)index; /* copy into client's location */ 111 rv = PR_SUCCESS; /* that's okay */ 112 } 113 114 return rv; 115 } 116 117 /* 118 ** Define some per-thread-private data. 119 ** "index" is an index into the per-thread private data table 120 ** "priv" is the per-thread-private data 121 ** 122 ** If the per-thread private data table has a previously registered 123 ** destructor function and a non-NULL per-thread-private data value, 124 ** the destructor function is invoked. 125 ** 126 ** This can return PR_FAILURE if index is invalid (ie., beyond the limit 127 ** on the TPD slots) or memory is insufficient to allocate an expanded 128 ** vector. 129 */ 130 131 PR_IMPLEMENT(PRStatus) PR_SetThreadPrivate(PRUintn index, void* priv) { 132 PRThread* self = PR_GetCurrentThread(); 133 134 /* 135 ** To improve performance, we don't check if the index has been 136 ** allocated. 137 */ 138 if (index >= _PR_TPD_LIMIT) { 139 PR_SetError(PR_TPD_RANGE_ERROR, 0); 140 return PR_FAILURE; 141 } 142 143 PR_ASSERT(((NULL == self->privateData) && (0 == self->tpdLength)) || 144 ((NULL != self->privateData) && (0 != self->tpdLength))); 145 146 /* 147 ** If this thread does not have a sufficient vector for the index 148 ** being set, go ahead and extend this vector now. 149 */ 150 if ((NULL == self->privateData) || (self->tpdLength <= index)) { 151 void* extension = PR_CALLOC(_pr_tpd_length * sizeof(void*)); 152 if (NULL == extension) { 153 PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); 154 return PR_FAILURE; 155 } 156 if (self->privateData) { 157 (void)memcpy(extension, self->privateData, 158 self->tpdLength * sizeof(void*)); 159 PR_DELETE(self->privateData); 160 } 161 self->tpdLength = _pr_tpd_length; 162 self->privateData = (void**)extension; 163 } 164 /* 165 ** There wasn't much chance of having to call the destructor 166 ** unless the slot already existed. 167 */ 168 else if (self->privateData[index] && _pr_tpd_destructors[index]) { 169 void* data = self->privateData[index]; 170 self->privateData[index] = NULL; 171 (*_pr_tpd_destructors[index])(data); 172 } 173 174 PR_ASSERT(index < self->tpdLength); 175 self->privateData[index] = priv; 176 177 return PR_SUCCESS; 178 } 179 180 /* 181 ** Recover the per-thread-private data for the current thread. "index" is 182 ** the index into the per-thread private data table. 183 ** 184 ** The returned value may be NULL which is indistinguishable from an error 185 ** condition. 186 ** 187 */ 188 189 PR_IMPLEMENT(void*) PR_GetThreadPrivate(PRUintn index) { 190 PRThread* self = PR_GetCurrentThread(); 191 void* tpd = ((NULL == self->privateData) || (index >= self->tpdLength)) 192 ? NULL 193 : self->privateData[index]; 194 195 return tpd; 196 } 197 198 /* 199 ** Destroy the thread's private data, if any exists. This is called at 200 ** thread termination time only. There should be no threading issues 201 ** since this is being called by the thread itself. 202 */ 203 void _PR_DestroyThreadPrivate(PRThread* self) { 204 #define _PR_TPD_DESTRUCTOR_ITERATIONS 4 205 206 if (NULL != self->privateData) /* we have some */ 207 { 208 PRBool clean; 209 PRUint32 index; 210 PRInt32 passes = _PR_TPD_DESTRUCTOR_ITERATIONS; 211 PR_ASSERT(0 != self->tpdLength); 212 do { 213 clean = PR_TRUE; 214 for (index = 0; index < self->tpdLength; ++index) { 215 void* priv = self->privateData[index]; /* extract */ 216 if (NULL != priv) /* we have data at this index */ 217 { 218 if (NULL != _pr_tpd_destructors[index]) { 219 self->privateData[index] = NULL; /* precondition */ 220 (*_pr_tpd_destructors[index])(priv); /* destroy */ 221 clean = PR_FALSE; /* unknown side effects */ 222 } 223 } 224 } 225 } while ((--passes > 0) && !clean); /* limit # of passes */ 226 /* 227 ** We give up after a fixed number of passes. Any non-NULL 228 ** thread-private data value with a registered destructor 229 ** function is not destroyed. 230 */ 231 memset(self->privateData, 0, self->tpdLength * sizeof(void*)); 232 } 233 } /* _PR_DestroyThreadPrivate */