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