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