/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* 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/. */ /* ** prcountr.c -- NSPR Instrumentation Counters ** ** Implement the interface defined in prcountr.h ** ** Design Notes: ** ** The Counter Facility (CF) has a single anchor: qNameList. ** The anchor is a PRCList. qNameList is a list of links in QName ** structures. From qNameList any QName structure and its ** associated RName structure can be located. ** ** For each QName, a list of RName structures is anchored at ** rnLink in the QName structure. ** ** The counter itself is embedded in the RName structure. ** ** For manipulating the counter database, single lock is used to ** protect the entire list: counterLock. ** ** A PRCounterHandle, defined in prcountr.h, is really a pointer ** to a RName structure. References by PRCounterHandle are ** dead-reconed to the RName structure. The PRCounterHandle is ** "overloaded" for traversing the QName structures; only the ** function PR_FindNextQnameHandle() uses this overloading. ** ** ** ToDo (lth): decide on how to lock or atomically update ** individual counters. Candidates are: the global lock; a lock ** per RName structure; Atomic operations (Note that there are ** not adaquate atomic operations (yet) to achieve this goal). At ** this writing (6/19/98) , the update of the counter variable in ** a QName structure is unprotected. ** */ #include "prcountr.h" #include "prclist.h" #include "prlock.h" #include "prlog.h" #include "prmem.h" #include /* ** */ typedef struct QName { PRCList link; PRCList rNameList; char name[PRCOUNTER_NAME_MAX+1]; } QName; /* ** */ typedef struct RName { PRCList link; QName *qName; PRLock *lock; volatile PRUint32 counter; char name[PRCOUNTER_NAME_MAX+1]; char desc[PRCOUNTER_DESC_MAX+1]; } RName; /* ** Define the Counter Facility database */ static PRLock *counterLock; static PRCList qNameList; static PRLogModuleInfo *lm; /* ** _PR_CounterInitialize() -- Initialize the Counter Facility ** */ static void _PR_CounterInitialize( void ) { /* ** This function should be called only once */ PR_ASSERT( counterLock == NULL ); counterLock = PR_NewLock(); PR_INIT_CLIST( &qNameList ); lm = PR_NewLogModule("counters"); PR_LOG( lm, PR_LOG_DEBUG, ("PR_Counter: Initialization complete")); return; } /* end _PR_CounterInitialize() */ /* ** PR_CreateCounter() -- Create a counter ** ** ValidateArguments ** Lock ** if (qName not already in database) ** NewQname ** if (rName already in database ) ** Assert ** else NewRname ** NewCounter ** link 'em up ** Unlock ** */ PR_IMPLEMENT(PRCounterHandle) PR_CreateCounter( const char *qName, const char *rName, const char *description ) { QName *qnp; RName *rnp; PRBool matchQname = PR_FALSE; /* Self initialize, if necessary */ if ( counterLock == NULL ) { _PR_CounterInitialize(); } /* Validate input arguments */ PR_ASSERT( strlen(qName) <= PRCOUNTER_NAME_MAX ); PR_ASSERT( strlen(rName) <= PRCOUNTER_NAME_MAX ); PR_ASSERT( strlen(description) <= PRCOUNTER_DESC_MAX ); /* Lock the Facility */ PR_Lock( counterLock ); /* Do we already have a matching QName? */ if (!PR_CLIST_IS_EMPTY( &qNameList )) { qnp = (QName *) PR_LIST_HEAD( &qNameList ); do { if ( strcmp(qnp->name, qName) == 0) { matchQname = PR_TRUE; break; } qnp = (QName *)PR_NEXT_LINK( &qnp->link ); } while( qnp != (QName *)&qNameList ); } /* ** If we did not find a matching QName, ** allocate one and initialize it. ** link it onto the qNameList. ** */ if ( matchQname != PR_TRUE ) { qnp = PR_NEWZAP( QName ); PR_ASSERT( qnp != NULL ); PR_INIT_CLIST( &qnp->link ); PR_INIT_CLIST( &qnp->rNameList ); strcpy( qnp->name, qName ); PR_APPEND_LINK( &qnp->link, &qNameList ); } /* Do we already have a matching RName? */ if (!PR_CLIST_IS_EMPTY( &qnp->rNameList )) { rnp = (RName *) PR_LIST_HEAD( &qnp->rNameList ); do { /* ** No duplicate RNames are allowed within a QName ** */ PR_ASSERT( strcmp(rnp->name, rName)); rnp = (RName *)PR_NEXT_LINK( &rnp->link ); } while( rnp != (RName *)&qnp->rNameList ); } /* Get a new RName structure; initialize its members */ rnp = PR_NEWZAP( RName ); PR_ASSERT( rnp != NULL ); PR_INIT_CLIST( &rnp->link ); strcpy( rnp->name, rName ); strcpy( rnp->desc, description ); rnp->lock = PR_NewLock(); if ( rnp->lock == NULL ) { PR_ASSERT(0); } PR_APPEND_LINK( &rnp->link, &qnp->rNameList ); /* add RName to QName's rnList */ rnp->qName = qnp; /* point the RName to the QName */ /* Unlock the Facility */ PR_Unlock( counterLock ); PR_LOG( lm, PR_LOG_DEBUG, ("PR_Counter: Create: QName: %s %p, RName: %s %p\n\t", qName, qnp, rName, rnp )); return((PRCounterHandle)rnp); } /* end PR_CreateCounter() */ /* ** */ PR_IMPLEMENT(void) PR_DestroyCounter( PRCounterHandle handle ) { RName *rnp = (RName *)handle; QName *qnp = rnp->qName; PR_LOG( lm, PR_LOG_DEBUG, ("PR_Counter: Deleting: QName: %s, RName: %s", qnp->name, rnp->name)); /* Lock the Facility */ PR_Lock( counterLock ); /* ** Remove RName from the list of RNames in QName ** and free RName */ PR_LOG( lm, PR_LOG_DEBUG, ("PR_Counter: Deleting RName: %s, %p", rnp->name, rnp)); PR_REMOVE_LINK( &rnp->link ); PR_Free( rnp->lock ); PR_DELETE( rnp ); /* ** If this is the last RName within QName ** remove QName from the qNameList and free it */ if ( PR_CLIST_IS_EMPTY( &qnp->rNameList ) ) { PR_LOG( lm, PR_LOG_DEBUG, ("PR_Counter: Deleting unused QName: %s, %p", qnp->name, qnp)); PR_REMOVE_LINK( &qnp->link ); PR_DELETE( qnp ); } /* Unlock the Facility */ PR_Unlock( counterLock ); return; } /* end PR_DestroyCounter() */ /* ** */ PR_IMPLEMENT(PRCounterHandle) PR_GetCounterHandleFromName( const char *qName, const char *rName ) { const char *qn, *rn, *desc; PRCounterHandle qh, rh = NULL; RName *rnp = NULL; PR_LOG( lm, PR_LOG_DEBUG, ("PR_Counter: GetCounterHandleFromName:\n\t" "QName: %s, RName: %s", qName, rName )); qh = PR_FindNextCounterQname( NULL ); while (qh != NULL) { rh = PR_FindNextCounterRname( NULL, qh ); while ( rh != NULL ) { PR_GetCounterNameFromHandle( rh, &qn, &rn, &desc ); if ( (strcmp( qName, qn ) == 0) && (strcmp( rName, rn ) == 0 )) { rnp = (RName *)rh; goto foundIt; } rh = PR_FindNextCounterRname( rh, qh ); } qh = PR_FindNextCounterQname( NULL ); } foundIt: PR_LOG( lm, PR_LOG_DEBUG, ("PR_Counter: GetConterHandleFromName: %p", rnp )); return(rh); } /* end PR_GetCounterHandleFromName() */ /* ** */ PR_IMPLEMENT(void) PR_GetCounterNameFromHandle( PRCounterHandle handle, const char **qName, const char **rName, const char **description ) { RName *rnp = (RName *)handle; QName *qnp = rnp->qName; *qName = qnp->name; *rName = rnp->name; *description = rnp->desc; PR_LOG( lm, PR_LOG_DEBUG, ("PR_Counter: GetConterNameFromHandle: " "QNp: %p, RNp: %p,\n\tQName: %s, RName: %s, Desc: %s", qnp, rnp, qnp->name, rnp->name, rnp->desc )); return; } /* end PR_GetCounterNameFromHandle() */ /* ** */ PR_IMPLEMENT(void) PR_IncrementCounter( PRCounterHandle handle ) { PR_Lock(((RName *)handle)->lock); ((RName *)handle)->counter++; PR_Unlock(((RName *)handle)->lock); PR_LOG( lm, PR_LOG_DEBUG, ("PR_Counter: Increment: %p, %ld", handle, ((RName *)handle)->counter )); return; } /* end PR_IncrementCounter() */ /* ** */ PR_IMPLEMENT(void) PR_DecrementCounter( PRCounterHandle handle ) { PR_Lock(((RName *)handle)->lock); ((RName *)handle)->counter--; PR_Unlock(((RName *)handle)->lock); PR_LOG( lm, PR_LOG_DEBUG, ("PR_Counter: Decrement: %p, %ld", handle, ((RName *)handle)->counter )); return; } /* end PR_DecrementCounter() */ /* ** */ PR_IMPLEMENT(void) PR_AddToCounter( PRCounterHandle handle, PRUint32 value ) { PR_Lock(((RName *)handle)->lock); ((RName *)handle)->counter += value; PR_Unlock(((RName *)handle)->lock); PR_LOG( lm, PR_LOG_DEBUG, ("PR_Counter: AddToCounter: %p, %ld", handle, ((RName *)handle)->counter )); return; } /* end PR_AddToCounter() */ /* ** */ PR_IMPLEMENT(void) PR_SubtractFromCounter( PRCounterHandle handle, PRUint32 value ) { PR_Lock(((RName *)handle)->lock); ((RName *)handle)->counter -= value; PR_Unlock(((RName *)handle)->lock); PR_LOG( lm, PR_LOG_DEBUG, ("PR_Counter: SubtractFromCounter: %p, %ld", handle, ((RName *)handle)->counter )); return; } /* end PR_SubtractFromCounter() */ /* ** */ PR_IMPLEMENT(PRUint32) PR_GetCounter( PRCounterHandle handle ) { PR_LOG( lm, PR_LOG_DEBUG, ("PR_Counter: GetCounter: %p, %ld", handle, ((RName *)handle)->counter )); return(((RName *)handle)->counter); } /* end PR_GetCounter() */ /* ** */ PR_IMPLEMENT(void) PR_SetCounter( PRCounterHandle handle, PRUint32 value ) { ((RName *)handle)->counter = value; PR_LOG( lm, PR_LOG_DEBUG, ("PR_Counter: SetCounter: %p, %ld", handle, ((RName *)handle)->counter )); return; } /* end PR_SetCounter() */ /* ** */ PR_IMPLEMENT(PRCounterHandle) PR_FindNextCounterQname( PRCounterHandle handle ) { QName *qnp = (QName *)handle; if ( PR_CLIST_IS_EMPTY( &qNameList )) { qnp = NULL; } else if ( qnp == NULL ) { qnp = (QName *)PR_LIST_HEAD( &qNameList ); } else if ( PR_NEXT_LINK( &qnp->link ) == &qNameList ) { qnp = NULL; } else { qnp = (QName *)PR_NEXT_LINK( &qnp->link ); } PR_LOG( lm, PR_LOG_DEBUG, ("PR_Counter: FindNextQname: Handle: %p, Returns: %p", handle, qnp )); return((PRCounterHandle)qnp); } /* end PR_FindNextCounterQname() */ /* ** */ PR_IMPLEMENT(PRCounterHandle) PR_FindNextCounterRname( PRCounterHandle rhandle, PRCounterHandle qhandle ) { RName *rnp = (RName *)rhandle; QName *qnp = (QName *)qhandle; if ( PR_CLIST_IS_EMPTY( &qnp->rNameList )) { rnp = NULL; } else if ( rnp == NULL ) { rnp = (RName *)PR_LIST_HEAD( &qnp->rNameList ); } else if ( PR_NEXT_LINK( &rnp->link ) == &qnp->rNameList ) { rnp = NULL; } else { rnp = (RName *)PR_NEXT_LINK( &rnp->link ); } PR_LOG( lm, PR_LOG_DEBUG, ("PR_Counter: FindNextRname: Rhandle: %p, QHandle: %p, Returns: %p", rhandle, qhandle, rnp )); return((PRCounterHandle)rnp); } /* end PR_FindNextCounterRname() */