1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* ***** BEGIN LICENSE BLOCK *****
3 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 : *
5 : * The contents of this file are subject to the Mozilla Public License Version
6 : * 1.1 (the "License"); you may not use this file except in compliance with
7 : * the License. You may obtain a copy of the License at
8 : * http://www.mozilla.org/MPL/
9 : *
10 : * Software distributed under the License is distributed on an "AS IS" basis,
11 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 : * for the specific language governing rights and limitations under the
13 : * License.
14 : *
15 : * The Original Code is the Netscape Portable Runtime (NSPR).
16 : *
17 : * The Initial Developer of the Original Code is
18 : * Netscape Communications Corporation.
19 : * Portions created by the Initial Developer are Copyright (C) 1998-2000
20 : * the Initial Developer. All Rights Reserved.
21 : *
22 : * Contributor(s):
23 : *
24 : * Alternatively, the contents of this file may be used under the terms of
25 : * either the GNU General Public License Version 2 or later (the "GPL"), or
26 : * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
27 : * in which case the provisions of the GPL or the LGPL are applicable instead
28 : * of those above. If you wish to allow use of your version of this file only
29 : * under the terms of either the GPL or the LGPL, and not to allow others to
30 : * use your version of this file under the terms of the MPL, indicate your
31 : * decision by deleting the provisions above and replace them with the notice
32 : * and other provisions required by the GPL or the LGPL. If you do not delete
33 : * the provisions above, a recipient may use your version of this file under
34 : * the terms of any one of the MPL, the GPL or the LGPL.
35 : *
36 : * ***** END LICENSE BLOCK ***** */
37 :
38 : /*
39 : ** Thread Private Data
40 : **
41 : ** There is an aribitrary limit on the number of keys that will be allocated
42 : ** by the runtime. It's largish, so it is intended to be a sanity check, not
43 : ** an impediment.
44 : **
45 : ** There is a counter, initialized to zero and incremented every time a
46 : ** client asks for a new key, that holds the high water mark for keys. All
47 : ** threads logically have the same high water mark and are permitted to
48 : ** ask for TPD up to that key value.
49 : **
50 : ** The vector to hold the TPD are allocated when PR_SetThreadPrivate() is
51 : ** called. The size of the vector will be some value greater than or equal
52 : ** to the current high water mark. Each thread has its own TPD length and
53 : ** vector.
54 : **
55 : ** Threads that get private data for keys they have not set (or perhaps
56 : ** don't even exist for that thread) get a NULL return. If the key is
57 : ** beyond the high water mark, an error will be returned.
58 : */
59 :
60 : /*
61 : ** As of this time, BeOS has its own TPD implementation. Integrating
62 : ** this standard one is a TODO for anyone with a bit of spare time on
63 : ** their hand. For now, we just #ifdef out this whole file and use
64 : ** the routines in pr/src/btthreads/
65 : */
66 :
67 : #ifndef XP_BEOS
68 :
69 : #include "primpl.h"
70 :
71 : #include <string.h>
72 :
73 : #if defined(WIN95)
74 : /*
75 : ** Some local variables report warnings on Win95 because the code paths
76 : ** using them are conditioned on HAVE_CUSTOME_USER_THREADS.
77 : ** The pragma suppresses the warning.
78 : **
79 : */
80 : #pragma warning(disable : 4101)
81 : #endif
82 :
83 : #define _PR_TPD_LIMIT 128 /* arbitary limit on the TPD slots */
84 : static PRInt32 _pr_tpd_length = 0; /* current length of destructor vector */
85 : static PRInt32 _pr_tpd_highwater = 0; /* next TPD key to be assigned */
86 : static PRThreadPrivateDTOR *_pr_tpd_destructors = NULL;
87 : /* the destructors are associated with
88 : the keys, therefore asserting that
89 : the TPD key depicts the data's 'type' */
90 :
91 : /*
92 : ** Initialize the thread private data manipulation
93 : */
94 20034 : void _PR_InitTPD(void)
95 : {
96 20034 : _pr_tpd_destructors = (PRThreadPrivateDTOR*)
97 20034 : PR_CALLOC(_PR_TPD_LIMIT * sizeof(PRThreadPrivateDTOR*));
98 20034 : PR_ASSERT(NULL != _pr_tpd_destructors);
99 20034 : _pr_tpd_length = _PR_TPD_LIMIT;
100 20034 : }
101 :
102 : /*
103 : ** Clean up the thread private data manipulation
104 : */
105 0 : void _PR_CleanupTPD(void)
106 : {
107 0 : } /* _PR_CleanupTPD */
108 :
109 : /*
110 : ** This routine returns a new index for per-thread-private data table.
111 : ** The index is visible to all threads within a process. This index can
112 : ** be used with the PR_SetThreadPrivate() and PR_GetThreadPrivate() routines
113 : ** to save and retrieve data associated with the index for a thread.
114 : **
115 : ** The index independently maintains specific values for each binding thread.
116 : ** A thread can only get access to its own thread-specific-data.
117 : **
118 : ** Upon a new index return the value associated with the index for all threads
119 : ** is NULL, and upon thread creation the value associated with all indices for
120 : ** that thread is NULL.
121 : **
122 : ** "dtor" is the destructor function to invoke when the private
123 : ** data is set or destroyed
124 : **
125 : ** Returns PR_FAILURE if the total number of indices will exceed the maximun
126 : ** allowed.
127 : */
128 :
129 47844 : PR_IMPLEMENT(PRStatus) PR_NewThreadPrivateIndex(
130 : PRUintn *newIndex, PRThreadPrivateDTOR dtor)
131 : {
132 : PRStatus rv;
133 : PRInt32 index;
134 :
135 47844 : if (!_pr_initialized) _PR_ImplicitInitialization();
136 :
137 47844 : PR_ASSERT(NULL != newIndex);
138 47844 : PR_ASSERT(NULL != _pr_tpd_destructors);
139 :
140 47844 : index = PR_ATOMIC_INCREMENT(&_pr_tpd_highwater) - 1; /* allocate index */
141 47844 : if (_PR_TPD_LIMIT <= index)
142 : {
143 0 : PR_SetError(PR_TPD_RANGE_ERROR, 0);
144 0 : rv = PR_FAILURE; /* that's just wrong */
145 : }
146 : else
147 : {
148 47844 : _pr_tpd_destructors[index] = dtor; /* record destructor @index */
149 47844 : *newIndex = (PRUintn)index; /* copy into client's location */
150 47844 : rv = PR_SUCCESS; /* that's okay */
151 : }
152 :
153 47844 : return rv;
154 : }
155 :
156 : /*
157 : ** Define some per-thread-private data.
158 : ** "index" is an index into the per-thread private data table
159 : ** "priv" is the per-thread-private data
160 : **
161 : ** If the per-thread private data table has a previously registered
162 : ** destructor function and a non-NULL per-thread-private data value,
163 : ** the destructor function is invoked.
164 : **
165 : ** This can return PR_FAILURE if index is invalid (ie., beyond the current
166 : ** high water mark) or memory is insufficient to allocate an exanded vector.
167 : */
168 :
169 53460716 : PR_IMPLEMENT(PRStatus) PR_SetThreadPrivate(PRUintn index, void *priv)
170 : {
171 53460716 : PRThread *self = PR_GetCurrentThread();
172 :
173 : /*
174 : ** The index being set might not have a sufficient vector in this
175 : ** thread. But if the index has been allocated, it's okay to go
176 : ** ahead and extend this one now.
177 : */
178 53460040 : if ((index >= _PR_TPD_LIMIT) || (index >= _pr_tpd_highwater))
179 : {
180 0 : PR_SetError(PR_TPD_RANGE_ERROR, 0);
181 0 : return PR_FAILURE;
182 : }
183 :
184 53460096 : PR_ASSERT(((NULL == self->privateData) && (0 == self->tpdLength))
185 : || ((NULL != self->privateData) && (0 != self->tpdLength)));
186 :
187 53460392 : if ((NULL == self->privateData) || (self->tpdLength <= index))
188 29153 : {
189 29134 : void *extension = PR_CALLOC(_pr_tpd_length * sizeof(void*));
190 29153 : if (NULL == extension)
191 : {
192 0 : PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
193 0 : return PR_FAILURE;
194 : }
195 29153 : if (self->privateData) {
196 0 : (void)memcpy(
197 0 : extension, self->privateData,
198 0 : self->tpdLength * sizeof(void*));
199 0 : PR_DELETE(self->privateData);
200 : }
201 29153 : self->tpdLength = _pr_tpd_length;
202 29153 : self->privateData = (void**)extension;
203 : }
204 : /*
205 : ** There wasn't much chance of having to call the destructor
206 : ** unless the slot already existed.
207 : */
208 53431258 : else if (self->privateData[index] && _pr_tpd_destructors[index])
209 : {
210 11116 : void *data = self->privateData[index];
211 11116 : self->privateData[index] = NULL;
212 11116 : (*_pr_tpd_destructors[index])(data);
213 : }
214 :
215 53460305 : PR_ASSERT(index < self->tpdLength);
216 53460305 : self->privateData[index] = priv;
217 :
218 53460305 : return PR_SUCCESS;
219 : }
220 :
221 : /*
222 : ** Recover the per-thread-private data for the current thread. "index" is
223 : ** the index into the per-thread private data table.
224 : **
225 : ** The returned value may be NULL which is indistinguishable from an error
226 : ** condition.
227 : **
228 : */
229 :
230 340780859 : PR_IMPLEMENT(void*) PR_GetThreadPrivate(PRUintn index)
231 : {
232 340780859 : PRThread *self = PR_GetCurrentThread();
233 1022234242 : void *tpd = ((NULL == self->privateData) || (index >= self->tpdLength)) ?
234 681484224 : NULL : self->privateData[index];
235 :
236 340750018 : return tpd;
237 : }
238 :
239 : /*
240 : ** Destroy the thread's private data, if any exists. This is called at
241 : ** thread termination time only. There should be no threading issues
242 : ** since this is being called by the thread itself.
243 : */
244 30602 : void _PR_DestroyThreadPrivate(PRThread* self)
245 : {
246 : #define _PR_TPD_DESTRUCTOR_ITERATIONS 4
247 :
248 30602 : if (NULL != self->privateData) /* we have some */
249 : {
250 : PRBool clean;
251 : PRUint32 index;
252 9282 : PRInt32 passes = _PR_TPD_DESTRUCTOR_ITERATIONS;
253 9282 : PR_ASSERT(0 != self->tpdLength);
254 : do
255 : {
256 9301 : clean = PR_TRUE;
257 1199829 : for (index = 0; index < self->tpdLength; ++index)
258 : {
259 1190528 : void *priv = self->privateData[index]; /* extract */
260 1190528 : if (NULL != priv) /* we have data at this index */
261 : {
262 27 : if (NULL != _pr_tpd_destructors[index])
263 : {
264 27 : self->privateData[index] = NULL; /* precondition */
265 27 : (*_pr_tpd_destructors[index])(priv); /* destroy */
266 27 : clean = PR_FALSE; /* unknown side effects */
267 : }
268 : }
269 : }
270 9301 : } while ((--passes > 0) && !clean); /* limit # of passes */
271 : /*
272 : ** We give up after a fixed number of passes. Any non-NULL
273 : ** thread-private data value with a registered destructor
274 : ** function is not destroyed.
275 : */
276 9282 : memset(self->privateData, 0, self->tpdLength * sizeof(void*));
277 : }
278 30602 : } /* _PR_DestroyThreadPrivate */
279 :
280 : #endif /* !XP_BEOS */
|