Back to index

lightning-sunbird  0.9+nobinonly
tracker.c
Go to the documentation of this file.
00001 /* ***** BEGIN LICENSE BLOCK *****
00002  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00003  *
00004  * The contents of this file are subject to the Mozilla Public License Version
00005  * 1.1 (the "License"); you may not use this file except in compliance with
00006  * the License. You may obtain a copy of the License at
00007  * http://www.mozilla.org/MPL/
00008  *
00009  * Software distributed under the License is distributed on an "AS IS" basis,
00010  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00011  * for the specific language governing rights and limitations under the
00012  * License.
00013  *
00014  * The Original Code is the Netscape security libraries.
00015  *
00016  * The Initial Developer of the Original Code is
00017  * Netscape Communications Corporation.
00018  * Portions created by the Initial Developer are Copyright (C) 1994-2000
00019  * the Initial Developer. All Rights Reserved.
00020  *
00021  * Contributor(s):
00022  *
00023  * Alternatively, the contents of this file may be used under the terms of
00024  * either the GNU General Public License Version 2 or later (the "GPL"), or
00025  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00026  * in which case the provisions of the GPL or the LGPL are applicable instead
00027  * of those above. If you wish to allow use of your version of this file only
00028  * under the terms of either the GPL or the LGPL, and not to allow others to
00029  * use your version of this file under the terms of the MPL, indicate your
00030  * decision by deleting the provisions above and replace them with the notice
00031  * and other provisions required by the GPL or the LGPL. If you do not delete
00032  * the provisions above, a recipient may use your version of this file under
00033  * the terms of any one of the MPL, the GPL or the LGPL.
00034  *
00035  * ***** END LICENSE BLOCK ***** */
00036 
00037 #ifdef DEBUG
00038 static const char CVS_ID[] = "@(#) $RCSfile: tracker.c,v $ $Revision: 1.6 $ $Date: 2005/01/20 02:25:45 $";
00039 #endif /* DEBUG */
00040 
00041 /*
00042  * tracker.c
00043  * 
00044  * This file contains the code used by the pointer-tracking calls used
00045  * in the debug builds to catch bad pointers.  The entire contents are
00046  * only available in debug builds (both internal and external builds).
00047  */
00048 
00049 #ifndef BASE_H
00050 #include "base.h"
00051 #endif /* BASE_H */
00052 
00053 #ifdef DEBUG
00054 
00055 /*
00056  * call_once
00057  *
00058  * Unfortunately, NSPR's PR_CallOnce function doesn't accept a closure
00059  * variable.  So I have a static version here which does.  This code 
00060  * is based on NSPR's, and uses the NSPR function to initialize the
00061  * required lock.
00062  */
00063 
00064 /*
00065  * The is the "once block" that's passed to the "real" PR_CallOnce
00066  * function, to call the local initializer myOnceFunction once.
00067  */
00068 static PRCallOnceType myCallOnce;
00069 
00070 /*
00071  * This structure is used by the call_once function to make sure that
00072  * any "other" threads calling the call_once don't return too quickly,
00073  * before the initializer has finished.
00074  */
00075 static struct {
00076   PZLock *ml;
00077   PZCondVar *cv;
00078 } mod_init;
00079 
00080 /*
00081  * This is the initializer for the above mod_init structure.
00082  */
00083 static PRStatus
00084 myOnceFunction
00085 (
00086   void
00087 )
00088 {
00089   mod_init.ml = PZ_NewLock(nssILockOther);
00090   if( (PZLock *)NULL == mod_init.ml ) {
00091     return PR_FAILURE;
00092   }
00093 
00094   mod_init.cv = PZ_NewCondVar(mod_init.ml);
00095   if( (PZCondVar *)NULL == mod_init.cv ) {
00096     PZ_DestroyLock(mod_init.ml);
00097     mod_init.ml = (PZLock *)NULL;
00098     return PR_FAILURE;
00099   }
00100 
00101   return PR_SUCCESS;
00102 }
00103 
00104 /*
00105  * The nss call_once callback takes a closure argument.
00106  */
00107 typedef PRStatus (PR_CALLBACK *nssCallOnceFN)(void *arg);
00108 
00109 /*
00110  * NSS's call_once function.
00111  */
00112 static PRStatus
00113 call_once
00114 (
00115   PRCallOnceType *once,
00116   nssCallOnceFN func,
00117   void *arg
00118 )
00119 {
00120   PRStatus rv;
00121 
00122   if( !myCallOnce.initialized ) {
00123     rv = PR_CallOnce(&myCallOnce, myOnceFunction);
00124     if( PR_SUCCESS != rv ) {
00125       return rv;
00126     }
00127   }
00128 
00129   if( !once->initialized ) {
00130     if( 0 == PR_AtomicSet(&once->inProgress, 1) ) {
00131       once->status = (*func)(arg);
00132       PZ_Lock(mod_init.ml);
00133       once->initialized = 1;
00134       PZ_NotifyAllCondVar(mod_init.cv);
00135       PZ_Unlock(mod_init.ml);
00136     } else {
00137       PZ_Lock(mod_init.ml);
00138       while( !once->initialized ) {
00139         PZ_WaitCondVar(mod_init.cv, PR_INTERVAL_NO_TIMEOUT);
00140       }
00141       PZ_Unlock(mod_init.ml);
00142     }
00143   }
00144 
00145   return once->status;
00146 }
00147 
00148 /*
00149  * Now we actually get to my own "call once" payload function.
00150  * But wait, to create the hash, I need a hash function!
00151  */
00152 
00153 /*
00154  * identity_hash
00155  *
00156  * This static callback is a PLHashFunction as defined in plhash.h
00157  * It merely returns the value of the object pointer as its hash.
00158  * There are no possible errors.
00159  */
00160 
00161 static PLHashNumber PR_CALLBACK
00162 identity_hash
00163 (
00164   const void *key
00165 )
00166 {
00167   return (PLHashNumber)key;
00168 }
00169 
00170 /*
00171  * trackerOnceFunc
00172  *
00173  * This function is called once, using the nssCallOnce function above.
00174  * It creates a new pointer tracker object; initialising its hash
00175  * table and protective lock.
00176  */
00177 
00178 static PRStatus
00179 trackerOnceFunc
00180 (
00181   void *arg
00182 )
00183 {
00184   nssPointerTracker *tracker = (nssPointerTracker *)arg;
00185 
00186   tracker->lock = PZ_NewLock(nssILockOther);
00187   if( (PZLock *)NULL == tracker->lock ) {
00188     return PR_FAILURE;
00189   }
00190 
00191   tracker->table = PL_NewHashTable(0, 
00192                                    identity_hash, 
00193                                    PL_CompareValues,
00194                                    PL_CompareValues,
00195                                    (PLHashAllocOps *)NULL, 
00196                                    (void *)NULL);
00197   if( (PLHashTable *)NULL == tracker->table ) {
00198     PZ_DestroyLock(tracker->lock);
00199     tracker->lock = (PZLock *)NULL;
00200     return PR_FAILURE;
00201   }
00202 
00203   return PR_SUCCESS;
00204 }
00205 
00206 /*
00207  * nssPointerTracker_initialize
00208  *
00209  * This method is only present in debug builds.
00210  * 
00211  * This routine initializes an nssPointerTracker object.  Note that
00212  * the object must have been declared *static* to guarantee that it
00213  * is in a zeroed state initially.  This routine is idempotent, and
00214  * may even be safely called by multiple threads simultaneously with 
00215  * the same argument.  This routine returns a PRStatus value; if 
00216  * successful, it will return PR_SUCCESS.  On failure it will set an 
00217  * error on the error stack and return PR_FAILURE.
00218  *
00219  * The error may be one of the following values:
00220  *  NSS_ERROR_NO_MEMORY
00221  *
00222  * Return value:
00223  *  PR_SUCCESS
00224  *  PR_FAILURE
00225  */
00226 
00227 NSS_IMPLEMENT PRStatus
00228 nssPointerTracker_initialize
00229 (
00230   nssPointerTracker *tracker
00231 )
00232 {
00233   PRStatus rv = call_once(&tracker->once, trackerOnceFunc, tracker);
00234   if( PR_SUCCESS != rv ) {
00235     nss_SetError(NSS_ERROR_NO_MEMORY);
00236   }
00237 
00238   return rv;
00239 }
00240 
00241 #ifdef DONT_DESTROY_EMPTY_TABLES
00242 /* See same #ifdef below */
00243 /*
00244  * count_entries
00245  *
00246  * This static routine is a PLHashEnumerator, as defined in plhash.h.
00247  * It merely causes the enumeration function to count the number of
00248  * entries.
00249  */
00250 
00251 static PRIntn PR_CALLBACK
00252 count_entries
00253 (
00254   PLHashEntry *he,
00255   PRIntn index,
00256   void *arg
00257 )
00258 {
00259   return HT_ENUMERATE_NEXT;
00260 }
00261 #endif /* DONT_DESTROY_EMPTY_TABLES */
00262 
00263 /*
00264  * zero_once
00265  *
00266  * This is a guaranteed zeroed once block.  It's used to help clear
00267  * the tracker.
00268  */
00269 
00270 static const PRCallOnceType zero_once;
00271 
00272 /*
00273  * nssPointerTracker_finalize
00274  *
00275  * This method is only present in debug builds.
00276  * 
00277  * This routine returns the nssPointerTracker object to the pre-
00278  * initialized state, releasing all resources used by the object.
00279  * It will *NOT* destroy the objects being tracked by the pointer
00280  * (should any remain), and therefore cannot be used to "sweep up"
00281  * remaining objects.  This routine returns a PRStatus value; if
00282  * successful, it will return PR_SUCCES.  On failure it will set an
00283  * error on the error stack and return PR_FAILURE.  If any objects
00284  * remain in the tracker when it is finalized, that will be treated
00285  * as an error.
00286  *
00287  * The error may be one of the following values:
00288  *  NSS_ERROR_INVALID_POINTER
00289  *  NSS_ERROR_TRACKER_NOT_INITIALIZED
00290  *  NSS_ERROR_TRACKER_NOT_EMPTY
00291  *
00292  * Return value:
00293  *  PR_SUCCESS
00294  *  PR_FAILURE
00295  */
00296 
00297 NSS_IMPLEMENT PRStatus
00298 nssPointerTracker_finalize
00299 (
00300   nssPointerTracker *tracker
00301 )
00302 {
00303   PZLock *lock;
00304 
00305   if( (nssPointerTracker *)NULL == tracker ) {
00306     nss_SetError(NSS_ERROR_INVALID_POINTER);
00307     return PR_FAILURE;
00308   }
00309 
00310   if( (PZLock *)NULL == tracker->lock ) {
00311     nss_SetError(NSS_ERROR_TRACKER_NOT_INITIALIZED);
00312     return PR_FAILURE;
00313   }
00314 
00315   lock = tracker->lock;
00316   PZ_Lock(lock);
00317 
00318   if( (PLHashTable *)NULL == tracker->table ) {
00319     PZ_Unlock(lock);
00320     nss_SetError(NSS_ERROR_TRACKER_NOT_INITIALIZED);
00321     return PR_FAILURE;
00322   }
00323 
00324 #ifdef DONT_DESTROY_EMPTY_TABLES
00325   /*
00326    * I changed my mind; I think we don't want this after all.
00327    * Comments?
00328    */
00329   count = PL_HashTableEnumerateEntries(tracker->table, 
00330                                        count_entries,
00331                                        (void *)NULL);
00332 
00333   if( 0 != count ) {
00334     PZ_Unlock(lock);
00335     nss_SetError(NSS_ERROR_TRACKER_NOT_EMPTY);
00336     return PR_FAILURE;
00337   }
00338 #endif /* DONT_DESTROY_EMPTY_TABLES */
00339 
00340   PL_HashTableDestroy(tracker->table);
00341   /* memset(tracker, 0, sizeof(nssPointerTracker)); */
00342   tracker->once = zero_once;
00343   tracker->lock = (PZLock *)NULL;
00344   tracker->table = (PLHashTable *)NULL;
00345 
00346   PZ_Unlock(lock);
00347   PZ_DestroyLock(lock);
00348 
00349   return PR_SUCCESS;
00350 }
00351 
00352 /*
00353  * nssPointerTracker_add
00354  *
00355  * This method is only present in debug builds.
00356  *
00357  * This routine adds the specified pointer to the nssPointerTracker
00358  * object.  It should be called in constructor objects to register
00359  * new valid objects.  The nssPointerTracker is threadsafe, but this
00360  * call is not idempotent.  This routine returns a PRStatus value;
00361  * if successful it will return PR_SUCCESS.  On failure it will set
00362  * an error on the error stack and return PR_FAILURE.
00363  *
00364  * The error may be one of the following values:
00365  *  NSS_ERROR_INVALID_POINTER
00366  *  NSS_ERROR_NO_MEMORY
00367  *  NSS_ERROR_TRACKER_NOT_INITIALIZED
00368  *  NSS_ERROR_DUPLICATE_POINTER
00369  *
00370  * Return value:
00371  *  PR_SUCCESS
00372  *  PR_FAILURE
00373  */
00374 
00375 NSS_IMPLEMENT PRStatus
00376 nssPointerTracker_add
00377 (
00378   nssPointerTracker *tracker,
00379   const void *pointer
00380 )
00381 {
00382   void *check;
00383   PLHashEntry *entry;
00384 
00385   if( (nssPointerTracker *)NULL == tracker ) {
00386     nss_SetError(NSS_ERROR_INVALID_POINTER);
00387     return PR_FAILURE;
00388   }
00389 
00390   if( (PZLock *)NULL == tracker->lock ) {
00391     nss_SetError(NSS_ERROR_TRACKER_NOT_INITIALIZED);
00392     return PR_FAILURE;
00393   }
00394 
00395   PZ_Lock(tracker->lock);
00396 
00397   if( (PLHashTable *)NULL == tracker->table ) {
00398     PZ_Unlock(tracker->lock);
00399     nss_SetError(NSS_ERROR_TRACKER_NOT_INITIALIZED);
00400     return PR_FAILURE;
00401   }
00402 
00403   check = PL_HashTableLookup(tracker->table, pointer);
00404   if( (void *)NULL != check ) {
00405     PZ_Unlock(tracker->lock);
00406     nss_SetError(NSS_ERROR_DUPLICATE_POINTER);
00407     return PR_FAILURE;
00408   }
00409 
00410   entry = PL_HashTableAdd(tracker->table, pointer, (void *)pointer);
00411 
00412   PZ_Unlock(tracker->lock);
00413 
00414   if( (PLHashEntry *)NULL == entry ) {
00415     nss_SetError(NSS_ERROR_NO_MEMORY);
00416     return PR_FAILURE;
00417   }
00418 
00419   return PR_SUCCESS;
00420 }
00421   
00422 /*
00423  * nssPointerTracker_remove
00424  *
00425  * This method is only present in debug builds.
00426  *
00427  * This routine removes the specified pointer from the 
00428  * nssPointerTracker object.  It does not call any destructor for the
00429  * object; rather, this should be called from the object's destructor.
00430  * The nssPointerTracker is threadsafe, but this call is not 
00431  * idempotent.  This routine returns a PRStatus value; if successful 
00432  * it will return PR_SUCCESS.  On failure it will set an error on the 
00433  * error stack and return PR_FAILURE.
00434  *
00435  * The error may be one of the following values:
00436  *  NSS_ERROR_INVALID_POINTER
00437  *  NSS_ERROR_TRACKER_NOT_INITIALIZED
00438  *  NSS_ERROR_POINTER_NOT_REGISTERED
00439  *
00440  * Return value:
00441  *  PR_SUCCESS
00442  *  PR_FAILURE
00443  */
00444 
00445 NSS_IMPLEMENT PRStatus
00446 nssPointerTracker_remove
00447 (
00448   nssPointerTracker *tracker,
00449   const void *pointer
00450 )
00451 {
00452   PRBool registered;
00453 
00454   if( (nssPointerTracker *)NULL == tracker ) {
00455     nss_SetError(NSS_ERROR_INVALID_POINTER);
00456     return PR_FAILURE;
00457   }
00458 
00459   if( (PZLock *)NULL == tracker->lock ) {
00460     nss_SetError(NSS_ERROR_TRACKER_NOT_INITIALIZED);
00461     return PR_FAILURE;
00462   }
00463 
00464   PZ_Lock(tracker->lock);
00465 
00466   if( (PLHashTable *)NULL == tracker->table ) {
00467     PZ_Unlock(tracker->lock);
00468     nss_SetError(NSS_ERROR_TRACKER_NOT_INITIALIZED);
00469     return PR_FAILURE;
00470   }
00471 
00472   registered = PL_HashTableRemove(tracker->table, pointer);
00473   PZ_Unlock(tracker->lock);
00474 
00475   if( !registered ) {
00476     nss_SetError(NSS_ERROR_POINTER_NOT_REGISTERED);
00477     return PR_FAILURE;
00478   }
00479 
00480   return PR_SUCCESS;
00481 }
00482 
00483 /*
00484  * nssPointerTracker_verify
00485  *
00486  * This method is only present in debug builds.
00487  *
00488  * This routine verifies that the specified pointer has been registered
00489  * with the nssPointerTracker object.  The nssPointerTracker object is
00490  * threadsafe, and this call may be safely called from multiple threads
00491  * simultaneously with the same arguments.  This routine returns a
00492  * PRStatus value; if the pointer is registered this will return 
00493  * PR_SUCCESS.  Otherwise it will set an error on the error stack and 
00494  * return PR_FAILURE.  Although the error is suitable for leaving on 
00495  * the stack, callers may wish to augment the information available by 
00496  * placing a more type-specific error on the stack.
00497  *
00498  * The error may be one of the following values:
00499  *  NSS_ERROR_INVALID_POINTER
00500  *  NSS_ERROR_TRACKER_NOT_INITIALIZED
00501  *  NSS_ERROR_POINTER_NOT_REGISTERED
00502  *
00503  * Return value:
00504  *  PR_SUCCESS
00505  *  PR_FAILRUE
00506  */
00507 
00508 NSS_IMPLEMENT PRStatus
00509 nssPointerTracker_verify
00510 (
00511   nssPointerTracker *tracker,
00512   const void *pointer
00513 )
00514 {
00515   void *check;
00516 
00517   if( (nssPointerTracker *)NULL == tracker ) {
00518     nss_SetError(NSS_ERROR_INVALID_POINTER);
00519     return PR_FAILURE;
00520   }
00521 
00522   if( (PZLock *)NULL == tracker->lock ) {
00523     nss_SetError(NSS_ERROR_TRACKER_NOT_INITIALIZED);
00524     return PR_FAILURE;
00525   }
00526 
00527   PZ_Lock(tracker->lock);
00528 
00529   if( (PLHashTable *)NULL == tracker->table ) {
00530     PZ_Unlock(tracker->lock);
00531     nss_SetError(NSS_ERROR_TRACKER_NOT_INITIALIZED);
00532     return PR_FAILURE;
00533   }
00534 
00535   check = PL_HashTableLookup(tracker->table, pointer);
00536   PZ_Unlock(tracker->lock);
00537 
00538   if( (void *)NULL == check ) {
00539     nss_SetError(NSS_ERROR_POINTER_NOT_REGISTERED);
00540     return PR_FAILURE;
00541   }
00542 
00543   return PR_SUCCESS;
00544 }
00545 
00546 #endif /* DEBUG */