| OLD | NEW |
| (Empty) |
| 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 #ifndef XP_BEOS | |
| 36 | |
| 37 #include "primpl.h" | |
| 38 | |
| 39 #include <string.h> | |
| 40 | |
| 41 #if defined(WIN95) | |
| 42 /* | |
| 43 ** Some local variables report warnings on Win95 because the code paths | |
| 44 ** using them are conditioned on HAVE_CUSTOME_USER_THREADS. | |
| 45 ** The pragma suppresses the warning. | |
| 46 ** | |
| 47 */ | |
| 48 #pragma warning(disable : 4101) | |
| 49 #endif | |
| 50 | |
| 51 #define _PR_TPD_LIMIT 128 /* arbitary limit on the TPD slots */ | |
| 52 static PRInt32 _pr_tpd_length = 0; /* current length of destructor vector *
/ | |
| 53 static PRInt32 _pr_tpd_highwater = 0; /* next TPD key to be assigned */ | |
| 54 static PRThreadPrivateDTOR *_pr_tpd_destructors = NULL; | |
| 55 /* the destructors are associated with | |
| 56 the keys, therefore asserting that | |
| 57 the TPD key depicts the data's 'type
' */ | |
| 58 | |
| 59 /* | |
| 60 ** Initialize the thread private data manipulation | |
| 61 */ | |
| 62 void _PR_InitTPD(void) | |
| 63 { | |
| 64 _pr_tpd_destructors = (PRThreadPrivateDTOR*) | |
| 65 PR_CALLOC(_PR_TPD_LIMIT * sizeof(PRThreadPrivateDTOR*)); | |
| 66 PR_ASSERT(NULL != _pr_tpd_destructors); | |
| 67 _pr_tpd_length = _PR_TPD_LIMIT; | |
| 68 } | |
| 69 | |
| 70 /* | |
| 71 ** Clean up the thread private data manipulation | |
| 72 */ | |
| 73 void _PR_CleanupTPD(void) | |
| 74 { | |
| 75 } /* _PR_CleanupTPD */ | |
| 76 | |
| 77 /* | |
| 78 ** This routine returns a new index for per-thread-private data table. | |
| 79 ** The index is visible to all threads within a process. This index can | |
| 80 ** be used with the PR_SetThreadPrivate() and PR_GetThreadPrivate() routines | |
| 81 ** to save and retrieve data associated with the index for a thread. | |
| 82 ** | |
| 83 ** The index independently maintains specific values for each binding thread. | |
| 84 ** A thread can only get access to its own thread-specific-data. | |
| 85 ** | |
| 86 ** Upon a new index return the value associated with the index for all threads | |
| 87 ** is NULL, and upon thread creation the value associated with all indices for | |
| 88 ** that thread is NULL. | |
| 89 ** | |
| 90 ** "dtor" is the destructor function to invoke when the private | |
| 91 ** data is set or destroyed | |
| 92 ** | |
| 93 ** Returns PR_FAILURE if the total number of indices will exceed the maximun | |
| 94 ** allowed. | |
| 95 */ | |
| 96 | |
| 97 PR_IMPLEMENT(PRStatus) PR_NewThreadPrivateIndex( | |
| 98 PRUintn *newIndex, PRThreadPrivateDTOR dtor) | |
| 99 { | |
| 100 PRStatus rv; | |
| 101 PRInt32 index; | |
| 102 | |
| 103 if (!_pr_initialized) _PR_ImplicitInitialization(); | |
| 104 | |
| 105 PR_ASSERT(NULL != newIndex); | |
| 106 PR_ASSERT(NULL != _pr_tpd_destructors); | |
| 107 | |
| 108 index = PR_ATOMIC_INCREMENT(&_pr_tpd_highwater) - 1; /* allocate index */ | |
| 109 if (_PR_TPD_LIMIT <= index) | |
| 110 { | |
| 111 PR_SetError(PR_TPD_RANGE_ERROR, 0); | |
| 112 rv = PR_FAILURE; /* that's just wrong */ | |
| 113 } | |
| 114 else | |
| 115 { | |
| 116 _pr_tpd_destructors[index] = dtor; /* record destructor @index */ | |
| 117 *newIndex = (PRUintn)index; /* copy into client's location */ | |
| 118 rv = PR_SUCCESS; /* that's okay */ | |
| 119 } | |
| 120 | |
| 121 return rv; | |
| 122 } | |
| 123 | |
| 124 /* | |
| 125 ** Define some per-thread-private data. | |
| 126 ** "index" is an index into the per-thread private data table | |
| 127 ** "priv" is the per-thread-private data | |
| 128 ** | |
| 129 ** If the per-thread private data table has a previously registered | |
| 130 ** destructor function and a non-NULL per-thread-private data value, | |
| 131 ** the destructor function is invoked. | |
| 132 ** | |
| 133 ** This can return PR_FAILURE if index is invalid (ie., beyond the limit | |
| 134 ** on the TPD slots) or memory is insufficient to allocate an expanded | |
| 135 ** vector. | |
| 136 */ | |
| 137 | |
| 138 PR_IMPLEMENT(PRStatus) PR_SetThreadPrivate(PRUintn index, void *priv) | |
| 139 { | |
| 140 PRThread *self = PR_GetCurrentThread(); | |
| 141 | |
| 142 /* | |
| 143 ** To improve performance, we don't check if the index has been | |
| 144 ** allocated. | |
| 145 */ | |
| 146 if (index >= _PR_TPD_LIMIT) | |
| 147 { | |
| 148 PR_SetError(PR_TPD_RANGE_ERROR, 0); | |
| 149 return PR_FAILURE; | |
| 150 } | |
| 151 | |
| 152 PR_ASSERT(((NULL == self->privateData) && (0 == self->tpdLength)) | |
| 153 || ((NULL != self->privateData) && (0 != self->tpdLength))); | |
| 154 | |
| 155 /* | |
| 156 ** If this thread does not have a sufficient vector for the index | |
| 157 ** being set, go ahead and extend this vector now. | |
| 158 */ | |
| 159 if ((NULL == self->privateData) || (self->tpdLength <= index)) | |
| 160 { | |
| 161 void *extension = PR_CALLOC(_pr_tpd_length * sizeof(void*)); | |
| 162 if (NULL == extension) | |
| 163 { | |
| 164 PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); | |
| 165 return PR_FAILURE; | |
| 166 } | |
| 167 if (self->privateData) { | |
| 168 (void)memcpy( | |
| 169 extension, self->privateData, | |
| 170 self->tpdLength * sizeof(void*)); | |
| 171 PR_DELETE(self->privateData); | |
| 172 } | |
| 173 self->tpdLength = _pr_tpd_length; | |
| 174 self->privateData = (void**)extension; | |
| 175 } | |
| 176 /* | |
| 177 ** There wasn't much chance of having to call the destructor | |
| 178 ** unless the slot already existed. | |
| 179 */ | |
| 180 else if (self->privateData[index] && _pr_tpd_destructors[index]) | |
| 181 { | |
| 182 void *data = self->privateData[index]; | |
| 183 self->privateData[index] = NULL; | |
| 184 (*_pr_tpd_destructors[index])(data); | |
| 185 } | |
| 186 | |
| 187 PR_ASSERT(index < self->tpdLength); | |
| 188 self->privateData[index] = priv; | |
| 189 | |
| 190 return PR_SUCCESS; | |
| 191 } | |
| 192 | |
| 193 /* | |
| 194 ** Recover the per-thread-private data for the current thread. "index" is | |
| 195 ** the index into the per-thread private data table. | |
| 196 ** | |
| 197 ** The returned value may be NULL which is indistinguishable from an error | |
| 198 ** condition. | |
| 199 ** | |
| 200 */ | |
| 201 | |
| 202 PR_IMPLEMENT(void*) PR_GetThreadPrivate(PRUintn index) | |
| 203 { | |
| 204 PRThread *self = PR_GetCurrentThread(); | |
| 205 void *tpd = ((NULL == self->privateData) || (index >= self->tpdLength)) ? | |
| 206 NULL : self->privateData[index]; | |
| 207 | |
| 208 return tpd; | |
| 209 } | |
| 210 | |
| 211 /* | |
| 212 ** Destroy the thread's private data, if any exists. This is called at | |
| 213 ** thread termination time only. There should be no threading issues | |
| 214 ** since this is being called by the thread itself. | |
| 215 */ | |
| 216 void _PR_DestroyThreadPrivate(PRThread* self) | |
| 217 { | |
| 218 #define _PR_TPD_DESTRUCTOR_ITERATIONS 4 | |
| 219 | |
| 220 if (NULL != self->privateData) /* we have some */ | |
| 221 { | |
| 222 PRBool clean; | |
| 223 PRUint32 index; | |
| 224 PRInt32 passes = _PR_TPD_DESTRUCTOR_ITERATIONS; | |
| 225 PR_ASSERT(0 != self->tpdLength); | |
| 226 do | |
| 227 { | |
| 228 clean = PR_TRUE; | |
| 229 for (index = 0; index < self->tpdLength; ++index) | |
| 230 { | |
| 231 void *priv = self->privateData[index]; /* extract */ | |
| 232 if (NULL != priv) /* we have data at this index */ | |
| 233 { | |
| 234 if (NULL != _pr_tpd_destructors[index]) | |
| 235 { | |
| 236 self->privateData[index] = NULL; /* precondition */ | |
| 237 (*_pr_tpd_destructors[index])(priv); /* destroy */ | |
| 238 clean = PR_FALSE; /* unknown side effects */ | |
| 239 } | |
| 240 } | |
| 241 } | |
| 242 } while ((--passes > 0) && !clean); /* limit # of passes */ | |
| 243 /* | |
| 244 ** We give up after a fixed number of passes. Any non-NULL | |
| 245 ** thread-private data value with a registered destructor | |
| 246 ** function is not destroyed. | |
| 247 */ | |
| 248 memset(self->privateData, 0, self->tpdLength * sizeof(void*)); | |
| 249 } | |
| 250 } /* _PR_DestroyThreadPrivate */ | |
| 251 | |
| 252 #endif /* !XP_BEOS */ | |
| OLD | NEW |