OLD | NEW |
| (Empty) |
1 /* This Source Code Form is subject to the terms of the Mozilla Public | |
2 * License, v. 2.0. If a copy of the MPL was not distributed with this | |
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ | |
4 | |
5 /* | |
6 * tracker.c | |
7 * | |
8 * This file contains the code used by the pointer-tracking calls used | |
9 * in the debug builds to catch bad pointers. The entire contents are | |
10 * only available in debug builds (both internal and external builds). | |
11 */ | |
12 | |
13 #ifndef BASE_H | |
14 #include "base.h" | |
15 #endif /* BASE_H */ | |
16 | |
17 #ifdef DEBUG | |
18 /* | |
19 * identity_hash | |
20 * | |
21 * This static callback is a PLHashFunction as defined in plhash.h | |
22 * It merely returns the value of the object pointer as its hash. | |
23 * There are no possible errors. | |
24 */ | |
25 | |
26 static PLHashNumber PR_CALLBACK | |
27 identity_hash(const void *key) | |
28 { | |
29 return (PLHashNumber)((char *)key - (char *)NULL); | |
30 } | |
31 | |
32 /* | |
33 * trackerOnceFunc | |
34 * | |
35 * This function is called once, using the nssCallOnce function above. | |
36 * It creates a new pointer tracker object; initialising its hash | |
37 * table and protective lock. | |
38 */ | |
39 | |
40 static PRStatus | |
41 trackerOnceFunc(void *arg) | |
42 { | |
43 nssPointerTracker *tracker = (nssPointerTracker *)arg; | |
44 | |
45 tracker->lock = PZ_NewLock(nssILockOther); | |
46 if ((PZLock *)NULL == tracker->lock) { | |
47 return PR_FAILURE; | |
48 } | |
49 | |
50 tracker->table = | |
51 PL_NewHashTable(0, identity_hash, PL_CompareValues, PL_CompareValues, | |
52 (PLHashAllocOps *)NULL, (void *)NULL); | |
53 if ((PLHashTable *)NULL == tracker->table) { | |
54 PZ_DestroyLock(tracker->lock); | |
55 tracker->lock = (PZLock *)NULL; | |
56 return PR_FAILURE; | |
57 } | |
58 | |
59 return PR_SUCCESS; | |
60 } | |
61 | |
62 /* | |
63 * nssPointerTracker_initialize | |
64 * | |
65 * This method is only present in debug builds. | |
66 * | |
67 * This routine initializes an nssPointerTracker object. Note that | |
68 * the object must have been declared *static* to guarantee that it | |
69 * is in a zeroed state initially. This routine is idempotent, and | |
70 * may even be safely called by multiple threads simultaneously with | |
71 * the same argument. This routine returns a PRStatus value; if | |
72 * successful, it will return PR_SUCCESS. On failure it will set an | |
73 * error on the error stack and return PR_FAILURE. | |
74 * | |
75 * The error may be one of the following values: | |
76 * NSS_ERROR_NO_MEMORY | |
77 * | |
78 * Return value: | |
79 * PR_SUCCESS | |
80 * PR_FAILURE | |
81 */ | |
82 | |
83 NSS_IMPLEMENT PRStatus | |
84 nssPointerTracker_initialize(nssPointerTracker *tracker) | |
85 { | |
86 PRStatus rv = PR_CallOnceWithArg(&tracker->once, trackerOnceFunc, tracker); | |
87 if (PR_SUCCESS != rv) { | |
88 nss_SetError(NSS_ERROR_NO_MEMORY); | |
89 } | |
90 | |
91 return rv; | |
92 } | |
93 | |
94 #ifdef DONT_DESTROY_EMPTY_TABLES | |
95 /* See same #ifdef below */ | |
96 /* | |
97 * count_entries | |
98 * | |
99 * This static routine is a PLHashEnumerator, as defined in plhash.h. | |
100 * It merely causes the enumeration function to count the number of | |
101 * entries. | |
102 */ | |
103 | |
104 static PRIntn PR_CALLBACK | |
105 count_entries(PLHashEntry *he, PRIntn index, void *arg) | |
106 { | |
107 return HT_ENUMERATE_NEXT; | |
108 } | |
109 #endif /* DONT_DESTROY_EMPTY_TABLES */ | |
110 | |
111 /* | |
112 * zero_once | |
113 * | |
114 * This is a guaranteed zeroed once block. It's used to help clear | |
115 * the tracker. | |
116 */ | |
117 | |
118 static const PRCallOnceType zero_once; | |
119 | |
120 /* | |
121 * nssPointerTracker_finalize | |
122 * | |
123 * This method is only present in debug builds. | |
124 * | |
125 * This routine returns the nssPointerTracker object to the pre- | |
126 * initialized state, releasing all resources used by the object. | |
127 * It will *NOT* destroy the objects being tracked by the pointer | |
128 * (should any remain), and therefore cannot be used to "sweep up" | |
129 * remaining objects. This routine returns a PRStatus value; if | |
130 * successful, it will return PR_SUCCES. On failure it will set an | |
131 * error on the error stack and return PR_FAILURE. If any objects | |
132 * remain in the tracker when it is finalized, that will be treated | |
133 * as an error. | |
134 * | |
135 * The error may be one of the following values: | |
136 * NSS_ERROR_INVALID_POINTER | |
137 * NSS_ERROR_TRACKER_NOT_INITIALIZED | |
138 * NSS_ERROR_TRACKER_NOT_EMPTY | |
139 * | |
140 * Return value: | |
141 * PR_SUCCESS | |
142 * PR_FAILURE | |
143 */ | |
144 | |
145 NSS_IMPLEMENT PRStatus | |
146 nssPointerTracker_finalize(nssPointerTracker *tracker) | |
147 { | |
148 PZLock *lock; | |
149 | |
150 if ((nssPointerTracker *)NULL == tracker) { | |
151 nss_SetError(NSS_ERROR_INVALID_POINTER); | |
152 return PR_FAILURE; | |
153 } | |
154 | |
155 if ((PZLock *)NULL == tracker->lock) { | |
156 nss_SetError(NSS_ERROR_TRACKER_NOT_INITIALIZED); | |
157 return PR_FAILURE; | |
158 } | |
159 | |
160 lock = tracker->lock; | |
161 PZ_Lock(lock); | |
162 | |
163 if ((PLHashTable *)NULL == tracker->table) { | |
164 PZ_Unlock(lock); | |
165 nss_SetError(NSS_ERROR_TRACKER_NOT_INITIALIZED); | |
166 return PR_FAILURE; | |
167 } | |
168 | |
169 #ifdef DONT_DESTROY_EMPTY_TABLES | |
170 /* | |
171 * I changed my mind; I think we don't want this after all. | |
172 * Comments? | |
173 */ | |
174 count = PL_HashTableEnumerateEntries(tracker->table, count_entries, | |
175 (void *)NULL); | |
176 | |
177 if (0 != count) { | |
178 PZ_Unlock(lock); | |
179 nss_SetError(NSS_ERROR_TRACKER_NOT_EMPTY); | |
180 return PR_FAILURE; | |
181 } | |
182 #endif /* DONT_DESTROY_EMPTY_TABLES */ | |
183 | |
184 PL_HashTableDestroy(tracker->table); | |
185 /* memset(tracker, 0, sizeof(nssPointerTracker)); */ | |
186 tracker->once = zero_once; | |
187 tracker->lock = (PZLock *)NULL; | |
188 tracker->table = (PLHashTable *)NULL; | |
189 | |
190 PZ_Unlock(lock); | |
191 PZ_DestroyLock(lock); | |
192 | |
193 return PR_SUCCESS; | |
194 } | |
195 | |
196 /* | |
197 * nssPointerTracker_add | |
198 * | |
199 * This method is only present in debug builds. | |
200 * | |
201 * This routine adds the specified pointer to the nssPointerTracker | |
202 * object. It should be called in constructor objects to register | |
203 * new valid objects. The nssPointerTracker is threadsafe, but this | |
204 * call is not idempotent. This routine returns a PRStatus value; | |
205 * if successful it will return PR_SUCCESS. On failure it will set | |
206 * an error on the error stack and return PR_FAILURE. | |
207 * | |
208 * The error may be one of the following values: | |
209 * NSS_ERROR_INVALID_POINTER | |
210 * NSS_ERROR_NO_MEMORY | |
211 * NSS_ERROR_TRACKER_NOT_INITIALIZED | |
212 * NSS_ERROR_DUPLICATE_POINTER | |
213 * | |
214 * Return value: | |
215 * PR_SUCCESS | |
216 * PR_FAILURE | |
217 */ | |
218 | |
219 NSS_IMPLEMENT PRStatus | |
220 nssPointerTracker_add(nssPointerTracker *tracker, const void *pointer) | |
221 { | |
222 void *check; | |
223 PLHashEntry *entry; | |
224 | |
225 if ((nssPointerTracker *)NULL == tracker) { | |
226 nss_SetError(NSS_ERROR_INVALID_POINTER); | |
227 return PR_FAILURE; | |
228 } | |
229 | |
230 if ((PZLock *)NULL == tracker->lock) { | |
231 nss_SetError(NSS_ERROR_TRACKER_NOT_INITIALIZED); | |
232 return PR_FAILURE; | |
233 } | |
234 | |
235 PZ_Lock(tracker->lock); | |
236 | |
237 if ((PLHashTable *)NULL == tracker->table) { | |
238 PZ_Unlock(tracker->lock); | |
239 nss_SetError(NSS_ERROR_TRACKER_NOT_INITIALIZED); | |
240 return PR_FAILURE; | |
241 } | |
242 | |
243 check = PL_HashTableLookup(tracker->table, pointer); | |
244 if ((void *)NULL != check) { | |
245 PZ_Unlock(tracker->lock); | |
246 nss_SetError(NSS_ERROR_DUPLICATE_POINTER); | |
247 return PR_FAILURE; | |
248 } | |
249 | |
250 entry = PL_HashTableAdd(tracker->table, pointer, (void *)pointer); | |
251 | |
252 PZ_Unlock(tracker->lock); | |
253 | |
254 if ((PLHashEntry *)NULL == entry) { | |
255 nss_SetError(NSS_ERROR_NO_MEMORY); | |
256 return PR_FAILURE; | |
257 } | |
258 | |
259 return PR_SUCCESS; | |
260 } | |
261 | |
262 /* | |
263 * nssPointerTracker_remove | |
264 * | |
265 * This method is only present in debug builds. | |
266 * | |
267 * This routine removes the specified pointer from the | |
268 * nssPointerTracker object. It does not call any destructor for the | |
269 * object; rather, this should be called from the object's destructor. | |
270 * The nssPointerTracker is threadsafe, but this call is not | |
271 * idempotent. This routine returns a PRStatus value; if successful | |
272 * it will return PR_SUCCESS. On failure it will set an error on the | |
273 * error stack and return PR_FAILURE. | |
274 * | |
275 * The error may be one of the following values: | |
276 * NSS_ERROR_INVALID_POINTER | |
277 * NSS_ERROR_TRACKER_NOT_INITIALIZED | |
278 * NSS_ERROR_POINTER_NOT_REGISTERED | |
279 * | |
280 * Return value: | |
281 * PR_SUCCESS | |
282 * PR_FAILURE | |
283 */ | |
284 | |
285 NSS_IMPLEMENT PRStatus | |
286 nssPointerTracker_remove(nssPointerTracker *tracker, const void *pointer) | |
287 { | |
288 PRBool registered; | |
289 | |
290 if ((nssPointerTracker *)NULL == tracker) { | |
291 nss_SetError(NSS_ERROR_INVALID_POINTER); | |
292 return PR_FAILURE; | |
293 } | |
294 | |
295 if ((PZLock *)NULL == tracker->lock) { | |
296 nss_SetError(NSS_ERROR_TRACKER_NOT_INITIALIZED); | |
297 return PR_FAILURE; | |
298 } | |
299 | |
300 PZ_Lock(tracker->lock); | |
301 | |
302 if ((PLHashTable *)NULL == tracker->table) { | |
303 PZ_Unlock(tracker->lock); | |
304 nss_SetError(NSS_ERROR_TRACKER_NOT_INITIALIZED); | |
305 return PR_FAILURE; | |
306 } | |
307 | |
308 registered = PL_HashTableRemove(tracker->table, pointer); | |
309 PZ_Unlock(tracker->lock); | |
310 | |
311 if (!registered) { | |
312 nss_SetError(NSS_ERROR_POINTER_NOT_REGISTERED); | |
313 return PR_FAILURE; | |
314 } | |
315 | |
316 return PR_SUCCESS; | |
317 } | |
318 | |
319 /* | |
320 * nssPointerTracker_verify | |
321 * | |
322 * This method is only present in debug builds. | |
323 * | |
324 * This routine verifies that the specified pointer has been registered | |
325 * with the nssPointerTracker object. The nssPointerTracker object is | |
326 * threadsafe, and this call may be safely called from multiple threads | |
327 * simultaneously with the same arguments. This routine returns a | |
328 * PRStatus value; if the pointer is registered this will return | |
329 * PR_SUCCESS. Otherwise it will set an error on the error stack and | |
330 * return PR_FAILURE. Although the error is suitable for leaving on | |
331 * the stack, callers may wish to augment the information available by | |
332 * placing a more type-specific error on the stack. | |
333 * | |
334 * The error may be one of the following values: | |
335 * NSS_ERROR_INVALID_POINTER | |
336 * NSS_ERROR_TRACKER_NOT_INITIALIZED | |
337 * NSS_ERROR_POINTER_NOT_REGISTERED | |
338 * | |
339 * Return value: | |
340 * PR_SUCCESS | |
341 * PR_FAILRUE | |
342 */ | |
343 | |
344 NSS_IMPLEMENT PRStatus | |
345 nssPointerTracker_verify(nssPointerTracker *tracker, const void *pointer) | |
346 { | |
347 void *check; | |
348 | |
349 if ((nssPointerTracker *)NULL == tracker) { | |
350 nss_SetError(NSS_ERROR_INVALID_POINTER); | |
351 return PR_FAILURE; | |
352 } | |
353 | |
354 if ((PZLock *)NULL == tracker->lock) { | |
355 nss_SetError(NSS_ERROR_TRACKER_NOT_INITIALIZED); | |
356 return PR_FAILURE; | |
357 } | |
358 | |
359 PZ_Lock(tracker->lock); | |
360 | |
361 if ((PLHashTable *)NULL == tracker->table) { | |
362 PZ_Unlock(tracker->lock); | |
363 nss_SetError(NSS_ERROR_TRACKER_NOT_INITIALIZED); | |
364 return PR_FAILURE; | |
365 } | |
366 | |
367 check = PL_HashTableLookup(tracker->table, pointer); | |
368 PZ_Unlock(tracker->lock); | |
369 | |
370 if ((void *)NULL == check) { | |
371 nss_SetError(NSS_ERROR_POINTER_NOT_REGISTERED); | |
372 return PR_FAILURE; | |
373 } | |
374 | |
375 return PR_SUCCESS; | |
376 } | |
377 | |
378 #endif /* DEBUG */ | |
OLD | NEW |