Line data Source code
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 3 : void _PR_InitTPD(void)
63 : {
64 3 : _pr_tpd_destructors = (PRThreadPrivateDTOR*)
65 3 : PR_CALLOC(_PR_TPD_LIMIT * sizeof(PRThreadPrivateDTOR*));
66 3 : PR_ASSERT(NULL != _pr_tpd_destructors);
67 3 : _pr_tpd_length = _PR_TPD_LIMIT;
68 3 : }
69 :
70 : /*
71 : ** Clean up the thread private data manipulation
72 : */
73 0 : void _PR_CleanupTPD(void)
74 : {
75 0 : } /* _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 22 : if (!_pr_initialized) _PR_ImplicitInitialization();
104 :
105 22 : PR_ASSERT(NULL != newIndex);
106 22 : PR_ASSERT(NULL != _pr_tpd_destructors);
107 :
108 22 : index = PR_ATOMIC_INCREMENT(&_pr_tpd_highwater) - 1; /* allocate index */
109 22 : if (_PR_TPD_LIMIT <= index)
110 : {
111 0 : PR_SetError(PR_TPD_RANGE_ERROR, 0);
112 0 : rv = PR_FAILURE; /* that's just wrong */
113 : }
114 : else
115 : {
116 22 : _pr_tpd_destructors[index] = dtor; /* record destructor @index */
117 22 : *newIndex = (PRUintn)index; /* copy into client's location */
118 22 : rv = PR_SUCCESS; /* that's okay */
119 : }
120 :
121 22 : 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 187382 : PRThread *self = PR_GetCurrentThread();
141 :
142 : /*
143 : ** To improve performance, we don't check if the index has been
144 : ** allocated.
145 : */
146 187346 : if (index >= _PR_TPD_LIMIT)
147 : {
148 0 : PR_SetError(PR_TPD_RANGE_ERROR, 0);
149 0 : return PR_FAILURE;
150 : }
151 :
152 187346 : 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 187368 : if ((NULL == self->privateData) || (self->tpdLength <= index))
160 91 : {
161 90 : void *extension = PR_CALLOC(_pr_tpd_length * sizeof(void*));
162 91 : if (NULL == extension)
163 : {
164 0 : PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
165 0 : return PR_FAILURE;
166 : }
167 91 : if (self->privateData) {
168 0 : (void)memcpy(
169 0 : extension, self->privateData,
170 0 : self->tpdLength * sizeof(void*));
171 0 : PR_DELETE(self->privateData);
172 : }
173 91 : self->tpdLength = _pr_tpd_length;
174 91 : 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 187278 : else if (self->privateData[index] && _pr_tpd_destructors[index])
181 : {
182 1 : void *data = self->privateData[index];
183 1 : self->privateData[index] = NULL;
184 1 : (*_pr_tpd_destructors[index])(data);
185 : }
186 :
187 187374 : PR_ASSERT(index < self->tpdLength);
188 187374 : self->privateData[index] = priv;
189 :
190 187374 : 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 3796594 : PRThread *self = PR_GetCurrentThread();
205 11389077 : void *tpd = ((NULL == self->privateData) || (index >= self->tpdLength)) ?
206 7592655 : NULL : self->privateData[index];
207 :
208 3796454 : 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 2 : void _PR_DestroyThreadPrivate(PRThread* self)
217 : {
218 : #define _PR_TPD_DESTRUCTOR_ITERATIONS 4
219 :
220 2 : if (NULL != self->privateData) /* we have some */
221 : {
222 : PRBool clean;
223 : PRUint32 index;
224 2 : PRInt32 passes = _PR_TPD_DESTRUCTOR_ITERATIONS;
225 2 : PR_ASSERT(0 != self->tpdLength);
226 : do
227 : {
228 2 : clean = PR_TRUE;
229 258 : for (index = 0; index < self->tpdLength; ++index)
230 : {
231 256 : void *priv = self->privateData[index]; /* extract */
232 256 : if (NULL != priv) /* we have data at this index */
233 : {
234 0 : if (NULL != _pr_tpd_destructors[index])
235 : {
236 0 : self->privateData[index] = NULL; /* precondition */
237 0 : (*_pr_tpd_destructors[index])(priv); /* destroy */
238 0 : clean = PR_FALSE; /* unknown side effects */
239 : }
240 : }
241 : }
242 2 : } 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 2 : memset(self->privateData, 0, self->tpdLength * sizeof(void*));
249 : }
250 2 : } /* _PR_DestroyThreadPrivate */
251 :
252 : #endif /* !XP_BEOS */
|