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