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