/* -*- 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() */