andre@0: /* This Source Code Form is subject to the terms of the Mozilla Public andre@0: * License, v. 2.0. If a copy of the MPL was not distributed with this andre@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ andre@0: andre@0: /* andre@0: * tracker.c andre@0: * andre@0: * This file contains the code used by the pointer-tracking calls used andre@0: * in the debug builds to catch bad pointers. The entire contents are andre@0: * only available in debug builds (both internal and external builds). andre@0: */ andre@0: andre@0: #ifndef BASE_H andre@0: #include "base.h" andre@0: #endif /* BASE_H */ andre@0: andre@0: #ifdef DEBUG andre@0: /* andre@0: * identity_hash andre@0: * andre@0: * This static callback is a PLHashFunction as defined in plhash.h andre@0: * It merely returns the value of the object pointer as its hash. andre@0: * There are no possible errors. andre@0: */ andre@0: andre@0: static PLHashNumber PR_CALLBACK andre@0: identity_hash andre@0: ( andre@0: const void *key andre@0: ) andre@0: { andre@0: return (PLHashNumber)key; andre@0: } andre@0: andre@0: /* andre@0: * trackerOnceFunc andre@0: * andre@0: * This function is called once, using the nssCallOnce function above. andre@0: * It creates a new pointer tracker object; initialising its hash andre@0: * table and protective lock. andre@0: */ andre@0: andre@0: static PRStatus andre@0: trackerOnceFunc andre@0: ( andre@0: void *arg andre@0: ) andre@0: { andre@0: nssPointerTracker *tracker = (nssPointerTracker *)arg; andre@0: andre@0: tracker->lock = PZ_NewLock(nssILockOther); andre@0: if( (PZLock *)NULL == tracker->lock ) { andre@0: return PR_FAILURE; andre@0: } andre@0: andre@0: tracker->table = PL_NewHashTable(0, andre@0: identity_hash, andre@0: PL_CompareValues, andre@0: PL_CompareValues, andre@0: (PLHashAllocOps *)NULL, andre@0: (void *)NULL); andre@0: if( (PLHashTable *)NULL == tracker->table ) { andre@0: PZ_DestroyLock(tracker->lock); andre@0: tracker->lock = (PZLock *)NULL; andre@0: return PR_FAILURE; andre@0: } andre@0: andre@0: return PR_SUCCESS; andre@0: } andre@0: andre@0: /* andre@0: * nssPointerTracker_initialize andre@0: * andre@0: * This method is only present in debug builds. andre@0: * andre@0: * This routine initializes an nssPointerTracker object. Note that andre@0: * the object must have been declared *static* to guarantee that it andre@0: * is in a zeroed state initially. This routine is idempotent, and andre@0: * may even be safely called by multiple threads simultaneously with andre@0: * the same argument. This routine returns a PRStatus value; if andre@0: * successful, it will return PR_SUCCESS. On failure it will set an andre@0: * error on the error stack and return PR_FAILURE. andre@0: * andre@0: * The error may be one of the following values: andre@0: * NSS_ERROR_NO_MEMORY andre@0: * andre@0: * Return value: andre@0: * PR_SUCCESS andre@0: * PR_FAILURE andre@0: */ andre@0: andre@0: NSS_IMPLEMENT PRStatus andre@0: nssPointerTracker_initialize andre@0: ( andre@0: nssPointerTracker *tracker andre@0: ) andre@0: { andre@0: PRStatus rv = PR_CallOnceWithArg(&tracker->once, trackerOnceFunc, tracker); andre@0: if( PR_SUCCESS != rv ) { andre@0: nss_SetError(NSS_ERROR_NO_MEMORY); andre@0: } andre@0: andre@0: return rv; andre@0: } andre@0: andre@0: #ifdef DONT_DESTROY_EMPTY_TABLES andre@0: /* See same #ifdef below */ andre@0: /* andre@0: * count_entries andre@0: * andre@0: * This static routine is a PLHashEnumerator, as defined in plhash.h. andre@0: * It merely causes the enumeration function to count the number of andre@0: * entries. andre@0: */ andre@0: andre@0: static PRIntn PR_CALLBACK andre@0: count_entries andre@0: ( andre@0: PLHashEntry *he, andre@0: PRIntn index, andre@0: void *arg andre@0: ) andre@0: { andre@0: return HT_ENUMERATE_NEXT; andre@0: } andre@0: #endif /* DONT_DESTROY_EMPTY_TABLES */ andre@0: andre@0: /* andre@0: * zero_once andre@0: * andre@0: * This is a guaranteed zeroed once block. It's used to help clear andre@0: * the tracker. andre@0: */ andre@0: andre@0: static const PRCallOnceType zero_once; andre@0: andre@0: /* andre@0: * nssPointerTracker_finalize andre@0: * andre@0: * This method is only present in debug builds. andre@0: * andre@0: * This routine returns the nssPointerTracker object to the pre- andre@0: * initialized state, releasing all resources used by the object. andre@0: * It will *NOT* destroy the objects being tracked by the pointer andre@0: * (should any remain), and therefore cannot be used to "sweep up" andre@0: * remaining objects. This routine returns a PRStatus value; if andre@0: * successful, it will return PR_SUCCES. On failure it will set an andre@0: * error on the error stack and return PR_FAILURE. If any objects andre@0: * remain in the tracker when it is finalized, that will be treated andre@0: * as an error. andre@0: * andre@0: * The error may be one of the following values: andre@0: * NSS_ERROR_INVALID_POINTER andre@0: * NSS_ERROR_TRACKER_NOT_INITIALIZED andre@0: * NSS_ERROR_TRACKER_NOT_EMPTY andre@0: * andre@0: * Return value: andre@0: * PR_SUCCESS andre@0: * PR_FAILURE andre@0: */ andre@0: andre@0: NSS_IMPLEMENT PRStatus andre@0: nssPointerTracker_finalize andre@0: ( andre@0: nssPointerTracker *tracker andre@0: ) andre@0: { andre@0: PZLock *lock; andre@0: andre@0: if( (nssPointerTracker *)NULL == tracker ) { andre@0: nss_SetError(NSS_ERROR_INVALID_POINTER); andre@0: return PR_FAILURE; andre@0: } andre@0: andre@0: if( (PZLock *)NULL == tracker->lock ) { andre@0: nss_SetError(NSS_ERROR_TRACKER_NOT_INITIALIZED); andre@0: return PR_FAILURE; andre@0: } andre@0: andre@0: lock = tracker->lock; andre@0: PZ_Lock(lock); andre@0: andre@0: if( (PLHashTable *)NULL == tracker->table ) { andre@0: PZ_Unlock(lock); andre@0: nss_SetError(NSS_ERROR_TRACKER_NOT_INITIALIZED); andre@0: return PR_FAILURE; andre@0: } andre@0: andre@0: #ifdef DONT_DESTROY_EMPTY_TABLES andre@0: /* andre@0: * I changed my mind; I think we don't want this after all. andre@0: * Comments? andre@0: */ andre@0: count = PL_HashTableEnumerateEntries(tracker->table, andre@0: count_entries, andre@0: (void *)NULL); andre@0: andre@0: if( 0 != count ) { andre@0: PZ_Unlock(lock); andre@0: nss_SetError(NSS_ERROR_TRACKER_NOT_EMPTY); andre@0: return PR_FAILURE; andre@0: } andre@0: #endif /* DONT_DESTROY_EMPTY_TABLES */ andre@0: andre@0: PL_HashTableDestroy(tracker->table); andre@0: /* memset(tracker, 0, sizeof(nssPointerTracker)); */ andre@0: tracker->once = zero_once; andre@0: tracker->lock = (PZLock *)NULL; andre@0: tracker->table = (PLHashTable *)NULL; andre@0: andre@0: PZ_Unlock(lock); andre@0: PZ_DestroyLock(lock); andre@0: andre@0: return PR_SUCCESS; andre@0: } andre@0: andre@0: /* andre@0: * nssPointerTracker_add andre@0: * andre@0: * This method is only present in debug builds. andre@0: * andre@0: * This routine adds the specified pointer to the nssPointerTracker andre@0: * object. It should be called in constructor objects to register andre@0: * new valid objects. The nssPointerTracker is threadsafe, but this andre@0: * call is not idempotent. This routine returns a PRStatus value; andre@0: * if successful it will return PR_SUCCESS. On failure it will set andre@0: * an error on the error stack and return PR_FAILURE. andre@0: * andre@0: * The error may be one of the following values: andre@0: * NSS_ERROR_INVALID_POINTER andre@0: * NSS_ERROR_NO_MEMORY andre@0: * NSS_ERROR_TRACKER_NOT_INITIALIZED andre@0: * NSS_ERROR_DUPLICATE_POINTER andre@0: * andre@0: * Return value: andre@0: * PR_SUCCESS andre@0: * PR_FAILURE andre@0: */ andre@0: andre@0: NSS_IMPLEMENT PRStatus andre@0: nssPointerTracker_add andre@0: ( andre@0: nssPointerTracker *tracker, andre@0: const void *pointer andre@0: ) andre@0: { andre@0: void *check; andre@0: PLHashEntry *entry; andre@0: andre@0: if( (nssPointerTracker *)NULL == tracker ) { andre@0: nss_SetError(NSS_ERROR_INVALID_POINTER); andre@0: return PR_FAILURE; andre@0: } andre@0: andre@0: if( (PZLock *)NULL == tracker->lock ) { andre@0: nss_SetError(NSS_ERROR_TRACKER_NOT_INITIALIZED); andre@0: return PR_FAILURE; andre@0: } andre@0: andre@0: PZ_Lock(tracker->lock); andre@0: andre@0: if( (PLHashTable *)NULL == tracker->table ) { andre@0: PZ_Unlock(tracker->lock); andre@0: nss_SetError(NSS_ERROR_TRACKER_NOT_INITIALIZED); andre@0: return PR_FAILURE; andre@0: } andre@0: andre@0: check = PL_HashTableLookup(tracker->table, pointer); andre@0: if( (void *)NULL != check ) { andre@0: PZ_Unlock(tracker->lock); andre@0: nss_SetError(NSS_ERROR_DUPLICATE_POINTER); andre@0: return PR_FAILURE; andre@0: } andre@0: andre@0: entry = PL_HashTableAdd(tracker->table, pointer, (void *)pointer); andre@0: andre@0: PZ_Unlock(tracker->lock); andre@0: andre@0: if( (PLHashEntry *)NULL == entry ) { andre@0: nss_SetError(NSS_ERROR_NO_MEMORY); andre@0: return PR_FAILURE; andre@0: } andre@0: andre@0: return PR_SUCCESS; andre@0: } andre@0: andre@0: /* andre@0: * nssPointerTracker_remove andre@0: * andre@0: * This method is only present in debug builds. andre@0: * andre@0: * This routine removes the specified pointer from the andre@0: * nssPointerTracker object. It does not call any destructor for the andre@0: * object; rather, this should be called from the object's destructor. andre@0: * The nssPointerTracker is threadsafe, but this call is not andre@0: * idempotent. This routine returns a PRStatus value; if successful andre@0: * it will return PR_SUCCESS. On failure it will set an error on the andre@0: * error stack and return PR_FAILURE. andre@0: * andre@0: * The error may be one of the following values: andre@0: * NSS_ERROR_INVALID_POINTER andre@0: * NSS_ERROR_TRACKER_NOT_INITIALIZED andre@0: * NSS_ERROR_POINTER_NOT_REGISTERED andre@0: * andre@0: * Return value: andre@0: * PR_SUCCESS andre@0: * PR_FAILURE andre@0: */ andre@0: andre@0: NSS_IMPLEMENT PRStatus andre@0: nssPointerTracker_remove andre@0: ( andre@0: nssPointerTracker *tracker, andre@0: const void *pointer andre@0: ) andre@0: { andre@0: PRBool registered; andre@0: andre@0: if( (nssPointerTracker *)NULL == tracker ) { andre@0: nss_SetError(NSS_ERROR_INVALID_POINTER); andre@0: return PR_FAILURE; andre@0: } andre@0: andre@0: if( (PZLock *)NULL == tracker->lock ) { andre@0: nss_SetError(NSS_ERROR_TRACKER_NOT_INITIALIZED); andre@0: return PR_FAILURE; andre@0: } andre@0: andre@0: PZ_Lock(tracker->lock); andre@0: andre@0: if( (PLHashTable *)NULL == tracker->table ) { andre@0: PZ_Unlock(tracker->lock); andre@0: nss_SetError(NSS_ERROR_TRACKER_NOT_INITIALIZED); andre@0: return PR_FAILURE; andre@0: } andre@0: andre@0: registered = PL_HashTableRemove(tracker->table, pointer); andre@0: PZ_Unlock(tracker->lock); andre@0: andre@0: if( !registered ) { andre@0: nss_SetError(NSS_ERROR_POINTER_NOT_REGISTERED); andre@0: return PR_FAILURE; andre@0: } andre@0: andre@0: return PR_SUCCESS; andre@0: } andre@0: andre@0: /* andre@0: * nssPointerTracker_verify andre@0: * andre@0: * This method is only present in debug builds. andre@0: * andre@0: * This routine verifies that the specified pointer has been registered andre@0: * with the nssPointerTracker object. The nssPointerTracker object is andre@0: * threadsafe, and this call may be safely called from multiple threads andre@0: * simultaneously with the same arguments. This routine returns a andre@0: * PRStatus value; if the pointer is registered this will return andre@0: * PR_SUCCESS. Otherwise it will set an error on the error stack and andre@0: * return PR_FAILURE. Although the error is suitable for leaving on andre@0: * the stack, callers may wish to augment the information available by andre@0: * placing a more type-specific error on the stack. andre@0: * andre@0: * The error may be one of the following values: andre@0: * NSS_ERROR_INVALID_POINTER andre@0: * NSS_ERROR_TRACKER_NOT_INITIALIZED andre@0: * NSS_ERROR_POINTER_NOT_REGISTERED andre@0: * andre@0: * Return value: andre@0: * PR_SUCCESS andre@0: * PR_FAILRUE andre@0: */ andre@0: andre@0: NSS_IMPLEMENT PRStatus andre@0: nssPointerTracker_verify andre@0: ( andre@0: nssPointerTracker *tracker, andre@0: const void *pointer andre@0: ) andre@0: { andre@0: void *check; andre@0: andre@0: if( (nssPointerTracker *)NULL == tracker ) { andre@0: nss_SetError(NSS_ERROR_INVALID_POINTER); andre@0: return PR_FAILURE; andre@0: } andre@0: andre@0: if( (PZLock *)NULL == tracker->lock ) { andre@0: nss_SetError(NSS_ERROR_TRACKER_NOT_INITIALIZED); andre@0: return PR_FAILURE; andre@0: } andre@0: andre@0: PZ_Lock(tracker->lock); andre@0: andre@0: if( (PLHashTable *)NULL == tracker->table ) { andre@0: PZ_Unlock(tracker->lock); andre@0: nss_SetError(NSS_ERROR_TRACKER_NOT_INITIALIZED); andre@0: return PR_FAILURE; andre@0: } andre@0: andre@0: check = PL_HashTableLookup(tracker->table, pointer); andre@0: PZ_Unlock(tracker->lock); andre@0: andre@0: if( (void *)NULL == check ) { andre@0: nss_SetError(NSS_ERROR_POINTER_NOT_REGISTERED); andre@0: return PR_FAILURE; andre@0: } andre@0: andre@0: return PR_SUCCESS; andre@0: } andre@0: andre@0: #endif /* DEBUG */