| 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 |