/src/nspr/pr/src/threads/prtpd.c
Line | Count | Source (jump to first uncovered line) |
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 | 0 | #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 | 0 | void _PR_InitTPD(void) { |
61 | 0 | _pr_tpd_destructors = (PRThreadPrivateDTOR*)PR_CALLOC( |
62 | 0 | _PR_TPD_LIMIT * sizeof(PRThreadPrivateDTOR*)); |
63 | 0 | PR_ASSERT(NULL != _pr_tpd_destructors); |
64 | 0 | _pr_tpd_length = _PR_TPD_LIMIT; |
65 | 0 | } |
66 | | |
67 | | /* |
68 | | ** Clean up the thread private data manipulation |
69 | | */ |
70 | 0 | 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 | 0 | PR_NewThreadPrivateIndex(PRUintn* newIndex, PRThreadPrivateDTOR dtor) { |
94 | 0 | PRStatus rv; |
95 | 0 | PRInt32 index; |
96 | |
|
97 | 0 | if (!_pr_initialized) { |
98 | 0 | _PR_ImplicitInitialization(); |
99 | 0 | } |
100 | |
|
101 | 0 | PR_ASSERT(NULL != newIndex); |
102 | 0 | PR_ASSERT(NULL != _pr_tpd_destructors); |
103 | |
|
104 | 0 | index = PR_ATOMIC_INCREMENT(&_pr_tpd_highwater) - 1; /* allocate index */ |
105 | 0 | if (_PR_TPD_LIMIT <= index) { |
106 | 0 | PR_SetError(PR_TPD_RANGE_ERROR, 0); |
107 | 0 | rv = PR_FAILURE; /* that's just wrong */ |
108 | 0 | } else { |
109 | 0 | _pr_tpd_destructors[index] = dtor; /* record destructor @index */ |
110 | 0 | *newIndex = (PRUintn)index; /* copy into client's location */ |
111 | 0 | rv = PR_SUCCESS; /* that's okay */ |
112 | 0 | } |
113 | |
|
114 | 0 | return rv; |
115 | 0 | } |
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 | 0 | PR_IMPLEMENT(PRStatus) PR_SetThreadPrivate(PRUintn index, void* priv) { |
132 | 0 | PRThread* self = PR_GetCurrentThread(); |
133 | | |
134 | | /* |
135 | | ** To improve performance, we don't check if the index has been |
136 | | ** allocated. |
137 | | */ |
138 | 0 | if (index >= _PR_TPD_LIMIT) { |
139 | 0 | PR_SetError(PR_TPD_RANGE_ERROR, 0); |
140 | 0 | return PR_FAILURE; |
141 | 0 | } |
142 | | |
143 | 0 | PR_ASSERT(((NULL == self->privateData) && (0 == self->tpdLength)) || |
144 | 0 | ((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 | 0 | if ((NULL == self->privateData) || (self->tpdLength <= index)) { |
151 | 0 | void* extension = PR_CALLOC(_pr_tpd_length * sizeof(void*)); |
152 | 0 | if (NULL == extension) { |
153 | 0 | PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); |
154 | 0 | return PR_FAILURE; |
155 | 0 | } |
156 | 0 | if (self->privateData) { |
157 | 0 | (void)memcpy(extension, self->privateData, |
158 | 0 | self->tpdLength * sizeof(void*)); |
159 | 0 | PR_DELETE(self->privateData); |
160 | 0 | } |
161 | 0 | self->tpdLength = _pr_tpd_length; |
162 | 0 | self->privateData = (void**)extension; |
163 | 0 | } |
164 | | /* |
165 | | ** There wasn't much chance of having to call the destructor |
166 | | ** unless the slot already existed. |
167 | | */ |
168 | 0 | else if (self->privateData[index] && _pr_tpd_destructors[index]) { |
169 | 0 | void* data = self->privateData[index]; |
170 | 0 | self->privateData[index] = NULL; |
171 | 0 | (*_pr_tpd_destructors[index])(data); |
172 | 0 | } |
173 | | |
174 | 0 | PR_ASSERT(index < self->tpdLength); |
175 | 0 | self->privateData[index] = priv; |
176 | |
|
177 | 0 | return PR_SUCCESS; |
178 | 0 | } |
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 | 0 | PR_IMPLEMENT(void*) PR_GetThreadPrivate(PRUintn index) { |
190 | 0 | PRThread* self = PR_GetCurrentThread(); |
191 | 0 | void* tpd = ((NULL == self->privateData) || (index >= self->tpdLength)) |
192 | 0 | ? NULL |
193 | 0 | : self->privateData[index]; |
194 | |
|
195 | 0 | return tpd; |
196 | 0 | } |
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 | 0 | void _PR_DestroyThreadPrivate(PRThread* self) { |
204 | 0 | #define _PR_TPD_DESTRUCTOR_ITERATIONS 4 |
205 | |
|
206 | 0 | if (NULL != self->privateData) /* we have some */ |
207 | 0 | { |
208 | 0 | PRBool clean; |
209 | 0 | PRUint32 index; |
210 | 0 | PRInt32 passes = _PR_TPD_DESTRUCTOR_ITERATIONS; |
211 | 0 | PR_ASSERT(0 != self->tpdLength); |
212 | 0 | do { |
213 | 0 | clean = PR_TRUE; |
214 | 0 | for (index = 0; index < self->tpdLength; ++index) { |
215 | 0 | void* priv = self->privateData[index]; /* extract */ |
216 | 0 | if (NULL != priv) /* we have data at this index */ |
217 | 0 | { |
218 | 0 | if (NULL != _pr_tpd_destructors[index]) { |
219 | 0 | self->privateData[index] = NULL; /* precondition */ |
220 | 0 | (*_pr_tpd_destructors[index])(priv); /* destroy */ |
221 | 0 | clean = PR_FALSE; /* unknown side effects */ |
222 | 0 | } |
223 | 0 | } |
224 | 0 | } |
225 | 0 | } 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 | 0 | memset(self->privateData, 0, self->tpdLength * sizeof(void*)); |
232 | 0 | } |
233 | 0 | } /* _PR_DestroyThreadPrivate */ |