mirror of
https://github.com/rn10950/RetroZilla.git
synced 2024-11-14 03:30:17 +01:00
44b7f056d9
bug1001332, 56b691c003ad, bug1086145, bug1054069, bug1155922, bug991783, bug1125025, bug1162521, bug1162644, bug1132941, bug1164364, bug1166205, bug1166163, bug1166515, bug1138554, bug1167046, bug1167043, bug1169451, bug1172128, bug1170322, bug102794, bug1128184, bug557830, bug1174648, bug1180244, bug1177784, bug1173413, bug1169174, bug1084669, bug951455, bug1183395, bug1177430, bug1183827, bug1160139, bug1154106, bug1142209, bug1185033, bug1193467, bug1182667(with sha512 changes backed out, which breaks VC6 compilation), bug1158489, bug337796
412 lines
10 KiB
C
412 lines
10 KiB
C
/* 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 */
|