|
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 current |
|
134 ** high water mark) or memory is insufficient to allocate an exanded vector. |
|
135 */ |
|
136 |
|
137 PR_IMPLEMENT(PRStatus) PR_SetThreadPrivate(PRUintn index, void *priv) |
|
138 { |
|
139 PRThread *self = PR_GetCurrentThread(); |
|
140 |
|
141 /* |
|
142 ** The index being set might not have a sufficient vector in this |
|
143 ** thread. But if the index has been allocated, it's okay to go |
|
144 ** ahead and extend this one now. |
|
145 */ |
|
146 if ((index >= _PR_TPD_LIMIT) || (index >= _pr_tpd_highwater)) |
|
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 if ((NULL == self->privateData) || (self->tpdLength <= index)) |
|
156 { |
|
157 void *extension = PR_CALLOC(_pr_tpd_length * sizeof(void*)); |
|
158 if (NULL == extension) |
|
159 { |
|
160 PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); |
|
161 return PR_FAILURE; |
|
162 } |
|
163 if (self->privateData) { |
|
164 (void)memcpy( |
|
165 extension, self->privateData, |
|
166 self->tpdLength * sizeof(void*)); |
|
167 PR_DELETE(self->privateData); |
|
168 } |
|
169 self->tpdLength = _pr_tpd_length; |
|
170 self->privateData = (void**)extension; |
|
171 } |
|
172 /* |
|
173 ** There wasn't much chance of having to call the destructor |
|
174 ** unless the slot already existed. |
|
175 */ |
|
176 else if (self->privateData[index] && _pr_tpd_destructors[index]) |
|
177 { |
|
178 void *data = self->privateData[index]; |
|
179 self->privateData[index] = NULL; |
|
180 (*_pr_tpd_destructors[index])(data); |
|
181 } |
|
182 |
|
183 PR_ASSERT(index < self->tpdLength); |
|
184 self->privateData[index] = priv; |
|
185 |
|
186 return PR_SUCCESS; |
|
187 } |
|
188 |
|
189 /* |
|
190 ** Recover the per-thread-private data for the current thread. "index" is |
|
191 ** the index into the per-thread private data table. |
|
192 ** |
|
193 ** The returned value may be NULL which is indistinguishable from an error |
|
194 ** condition. |
|
195 ** |
|
196 */ |
|
197 |
|
198 PR_IMPLEMENT(void*) PR_GetThreadPrivate(PRUintn index) |
|
199 { |
|
200 PRThread *self = PR_GetCurrentThread(); |
|
201 void *tpd = ((NULL == self->privateData) || (index >= self->tpdLength)) ? |
|
202 NULL : self->privateData[index]; |
|
203 |
|
204 return tpd; |
|
205 } |
|
206 |
|
207 /* |
|
208 ** Destroy the thread's private data, if any exists. This is called at |
|
209 ** thread termination time only. There should be no threading issues |
|
210 ** since this is being called by the thread itself. |
|
211 */ |
|
212 void _PR_DestroyThreadPrivate(PRThread* self) |
|
213 { |
|
214 #define _PR_TPD_DESTRUCTOR_ITERATIONS 4 |
|
215 |
|
216 if (NULL != self->privateData) /* we have some */ |
|
217 { |
|
218 PRBool clean; |
|
219 PRUint32 index; |
|
220 PRInt32 passes = _PR_TPD_DESTRUCTOR_ITERATIONS; |
|
221 PR_ASSERT(0 != self->tpdLength); |
|
222 do |
|
223 { |
|
224 clean = PR_TRUE; |
|
225 for (index = 0; index < self->tpdLength; ++index) |
|
226 { |
|
227 void *priv = self->privateData[index]; /* extract */ |
|
228 if (NULL != priv) /* we have data at this index */ |
|
229 { |
|
230 if (NULL != _pr_tpd_destructors[index]) |
|
231 { |
|
232 self->privateData[index] = NULL; /* precondition */ |
|
233 (*_pr_tpd_destructors[index])(priv); /* destroy */ |
|
234 clean = PR_FALSE; /* unknown side effects */ |
|
235 } |
|
236 } |
|
237 } |
|
238 } while ((--passes > 0) && !clean); /* limit # of passes */ |
|
239 /* |
|
240 ** We give up after a fixed number of passes. Any non-NULL |
|
241 ** thread-private data value with a registered destructor |
|
242 ** function is not destroyed. |
|
243 */ |
|
244 memset(self->privateData, 0, self->tpdLength * sizeof(void*)); |
|
245 } |
|
246 } /* _PR_DestroyThreadPrivate */ |
|
247 |
|
248 #endif /* !XP_BEOS */ |