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