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