//------------------------------------------------------------------------------ // Desc: Contains the methods for the lock manager and lock object classes // Tabs: 3 // // Copyright (c) 1998-2007 Novell, Inc. All Rights Reserved. // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public // License as published by the Free Software Foundation; version 2.1 // of the License. // // This library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Library Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public // License along with this library; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com. // // $Id$ //------------------------------------------------------------------------------ #include "ftksys.h" /************************************************************************** Desc: This structure is used to keep track of threads waiting for a lock **************************************************************************/ typedef struct F_LOCK_WAITER { F_SEM hWaitSem; FLMUINT uiThreadId; RCODE * pRc; FLMUINT uiWaitStartTime; FLMUINT uiWaitTime; FLMBOOL bExclReq; FLMINT iPriority; F_TMSTAMP StartTime; F_LOCK_STATS * pLockStats; F_LOCK_WAITER * pNextInList; F_LOCK_WAITER * pPrevInList; F_LOCK_WAITER * pNextByTime; F_LOCK_WAITER * pPrevByTime; } F_LOCK_WAITER; /**************************************************************************** Desc: ****************************************************************************/ class F_LockObject : public IF_LockObject { public: F_LockObject(); virtual ~F_LockObject(); FLMINT FLMAPI AddRef( void); FLMINT FLMAPI Release( void); RCODE setupLockObject( void); RCODE FLMAPI lock( F_SEM hWaitSem, FLMBOOL bExclLock, FLMUINT uiMaxWaitSecs, FLMINT iPriority, F_LOCK_STATS * pLockStats = NULL); RCODE FLMAPI unlock( F_LOCK_STATS * pLockStats = NULL); FLMUINT FLMAPI getLockCount( void) { return( m_uiLockCount); } FLMUINT FLMAPI getWaiterCount( void) { return( m_uiNumWaiters); } RCODE FLMAPI getLockInfo( FLMINT iPriority, eLockType * peCurrLockType, FLMUINT * puiThreadId, FLMUINT * puiLockHeldTime, FLMUINT * puiNumExclQueued, FLMUINT * puiNumSharedQueued, FLMUINT * puiPriorityCount); RCODE FLMAPI getLockInfo( IF_LockInfoClient * pLockInfo); RCODE FLMAPI getLockQueue( F_LOCK_USER ** ppLockUsers); FLMBOOL FLMAPI haveHigherPriorityWaiter( FLMINT iPriority); void FLMAPI timeoutLockWaiter( FLMUINT uiThreadId); void FLMAPI timeoutAllWaiters( void); private: void cleanupLockObject( void); static RCODE FLMAPI timeoutThread( IF_Thread * pThread); void insertWaiter( F_LOCK_WAITER * pLockWaiter); void removeWaiter( F_LOCK_WAITER * pLockWaiter); IF_Thread * m_pTimeoutThread; F_MUTEX m_hMutex; FLMUINT m_uiLockThreadId; FLMUINT m_uiLockTime; FLMUINT m_uiLockCount; F_LOCK_WAITER * m_pFirstInList; F_LOCK_WAITER * m_pLastInList; F_LOCK_WAITER * m_pFirstToTimeout; F_LOCK_WAITER * m_pLastToTimeout; FLMUINT m_uiNumWaiters; FLMUINT m_uiSharedLockCnt; FLMBOOL m_bExclLock; F_TMSTAMP m_StartTime; FLMBOOL m_bStartTimeSet; }; /**************************************************************************** Desc: ****************************************************************************/ RCODE FLMAPI FlmAllocLockObject( IF_LockObject ** ppLockObject) { RCODE rc = NE_FLM_OK; F_LockObject * pLockObject = NULL; if ((pLockObject = f_new F_LockObject) == NULL) { rc = RC_SET( NE_FLM_MEM); goto Exit; } if( RC_BAD( rc = pLockObject->setupLockObject())) { goto Exit; } *ppLockObject = pLockObject; pLockObject = NULL; Exit: if( pLockObject) { pLockObject->Release(); } return( rc); } /**************************************************************************** Desc: ****************************************************************************/ F_LockObject::F_LockObject() { m_pTimeoutThread = NULL; m_hMutex = F_MUTEX_NULL; m_uiLockThreadId = 0; m_uiLockTime = 0; m_uiLockCount = 0; m_pFirstInList = NULL; m_pLastInList = NULL; m_pFirstToTimeout = NULL; m_pLastToTimeout = NULL; m_uiNumWaiters = 0; m_uiSharedLockCnt = 0; m_bExclLock = FALSE; m_bStartTimeSet = FALSE; } /**************************************************************************** Desc: ****************************************************************************/ F_LockObject::~F_LockObject() { timeoutAllWaiters(); cleanupLockObject(); } /**************************************************************************** Desc: ****************************************************************************/ void F_LockObject::cleanupLockObject( void) { if( m_pTimeoutThread) { f_threadDestroy( &m_pTimeoutThread); } if( m_hMutex != F_MUTEX_NULL) { f_mutexDestroy( &m_hMutex); } } /**************************************************************************** Desc: ****************************************************************************/ FLMINT FLMAPI F_LockObject::AddRef( void) { return( f_atomicInc( &m_refCnt)); } /**************************************************************************** Desc: ****************************************************************************/ FLMINT FLMAPI F_LockObject::Release( void) { FLMINT iRefCnt = f_atomicDec( &m_refCnt); if( !iRefCnt) { delete this; } return( iRefCnt); } /**************************************************************************** Desc: ****************************************************************************/ RCODE F_LockObject::setupLockObject( void) { RCODE rc = NE_FLM_OK; if( RC_BAD( rc = f_mutexCreate( &m_hMutex))) { goto Exit; } if( RC_BAD( rc = f_threadCreate( &m_pTimeoutThread, F_LockObject::timeoutThread, "FTK lock timeout thread", 0, 0, (void *)this))) { goto Exit; } Exit: if( RC_BAD( rc)) { cleanupLockObject(); } return( rc); } /**************************************************************************** Desc: ****************************************************************************/ RCODE FLMAPI F_LockObject::timeoutThread( IF_Thread * pThread) { RCODE rc = NE_FLM_OK; F_LockObject * pThis = (F_LockObject *)pThread->getParm1(); FLMUINT uiLoop; FLMUINT uiCurrTime; F_LOCK_WAITER * pLockWaiter; for( ;;) { if( pThis->m_pFirstInList && pThis->m_pFirstInList->uiWaitTime) { f_mutexLock( pThis->m_hMutex); uiCurrTime = FLM_GET_TIMER(); while( pThis->m_pFirstToTimeout && pThis->m_pFirstToTimeout->uiWaitTime && FLM_ELAPSED_TIME( uiCurrTime, pThis->m_pFirstToTimeout->uiWaitStartTime) >= pThis->m_pFirstToTimeout->uiWaitTime) { f_assert( !pThis->m_pFirstToTimeout->pPrevByTime); // Lock waiter has timed out. pLockWaiter = pThis->m_pFirstToTimeout; // Remove the waiter from the list pThis->removeWaiter( pLockWaiter); // Tell the waiter that the lock request timed out. *(pLockWaiter->pRc) = RC_SET( NE_FLM_LOCK_REQ_TIMEOUT); f_semSignal( pLockWaiter->hWaitSem); } f_mutexUnlock( pThis->m_hMutex); } for( uiLoop = 0; uiLoop < 20; uiLoop++) { if( pThread->getShutdownFlag()) { goto Exit; } f_sleep( 50); } } Exit: return( rc); } /**************************************************************************** Desc: ****************************************************************************/ void FLMAPI F_LockObject::timeoutLockWaiter( FLMUINT uiThreadId) { FLMUINT uiCurrTime; F_LOCK_WAITER * pLockWaiter; F_LOCK_WAITER * pNextWaiter; f_mutexLock( m_hMutex); uiCurrTime = FLM_GET_TIMER(); for( pLockWaiter = m_pFirstToTimeout; pLockWaiter; pLockWaiter = pNextWaiter) { pNextWaiter = pLockWaiter->pNextByTime; if( pLockWaiter->uiThreadId == uiThreadId) { // Remove the lock waiter from the list removeWaiter( pLockWaiter); // Tell the waiter that the lock request timed out. *(pLockWaiter->pRc) = RC_SET( NE_FLM_LOCK_REQ_TIMEOUT); f_semSignal( pLockWaiter->hWaitSem); break; } } f_mutexUnlock( m_hMutex); } /**************************************************************************** Desc: Inserts a waiter into the global list of waiters, sorted by its end wait time. ****************************************************************************/ void FLMAPI F_LockObject::timeoutAllWaiters( void) { F_LOCK_WAITER * pLockWaiter; if( m_hMutex != F_MUTEX_NULL) { f_mutexLock( m_hMutex); } while( m_pFirstInList) { pLockWaiter = m_pFirstInList; removeWaiter( pLockWaiter); *(pLockWaiter->pRc) = RC_SET( NE_FLM_LOCK_REQ_TIMEOUT); f_semSignal( pLockWaiter->hWaitSem); } if( m_hMutex != F_MUTEX_NULL) { f_mutexUnlock( m_hMutex); } } /**************************************************************************** Desc: Inserts a waiter into the global list of waiters, sorted by its end wait time. ****************************************************************************/ void F_LockObject::insertWaiter( F_LOCK_WAITER * pLockWaiter) { F_LOCK_WAITER * pPrevLockWaiter; // Link into list of waiters on this object. if( (pLockWaiter->pPrevInList = m_pLastInList) != NULL) { pLockWaiter->pPrevInList->pNextInList = pLockWaiter; } else { m_pFirstInList = pLockWaiter; } m_pLastInList = pLockWaiter; // Determine where in the list this lock waiter should go. if ((pPrevLockWaiter = m_pFirstToTimeout) != NULL) { FLMUINT uiCurrTime = FLM_GET_TIMER(); FLMUINT uiElapTime; FLMUINT uiTimeLeft; while( pPrevLockWaiter) { // Waiters with zero wait time go to end of list. // They never time out. if( !pPrevLockWaiter->uiWaitTime) { // Should go BEFORE the first zero waiter. pPrevLockWaiter = pPrevLockWaiter->pPrevByTime; break; } else if( !pLockWaiter->uiWaitTime) { if( !pPrevLockWaiter->pNextByTime) { break; } pPrevLockWaiter = pPrevLockWaiter->pNextByTime; } else { // Determine how much time is left on the previous // lock waiter's timer. If it is less than the // new lock waiter's wait time, the new lock waiter // should be inserted AFTER it. Otherwise, the // new lock waiter should be inserted BEFORE it. uiElapTime = FLM_ELAPSED_TIME( uiCurrTime, pPrevLockWaiter->uiWaitStartTime); if( uiElapTime >= pPrevLockWaiter->uiWaitTime) { uiTimeLeft = 0; } else { uiTimeLeft = pPrevLockWaiter->uiWaitTime - uiElapTime; } // New lock waiter will time out before previous lock // waiter - insert it BEFORE the previous lock waiter. if( pLockWaiter->uiWaitTime < uiTimeLeft) { pPrevLockWaiter = pPrevLockWaiter->pPrevByTime; break; } else { if( !pPrevLockWaiter->pNextByTime) { break; } pPrevLockWaiter = pPrevLockWaiter->pNextByTime; } } } } // Insert into list AFTER pPrevLockWaiter. if( (pLockWaiter->pPrevByTime = pPrevLockWaiter) != NULL) { if ((pLockWaiter->pNextByTime = pPrevLockWaiter->pNextByTime) != NULL) { pLockWaiter->pNextByTime->pPrevByTime = pLockWaiter; } pPrevLockWaiter->pNextByTime = pLockWaiter; } else { if( (pLockWaiter->pNextByTime = m_pFirstToTimeout) != NULL) { m_pFirstToTimeout->pPrevByTime = pLockWaiter; } m_pFirstToTimeout = pLockWaiter; } m_uiNumWaiters++; } /**************************************************************************** Desc: ****************************************************************************/ void F_LockObject::removeWaiter( F_LOCK_WAITER * pLockWaiter) { if (pLockWaiter->pNextByTime) { pLockWaiter->pNextByTime->pPrevByTime = pLockWaiter->pPrevByTime; } if (pLockWaiter->pPrevByTime) { pLockWaiter->pPrevByTime->pNextByTime = pLockWaiter->pNextByTime; } else { m_pFirstToTimeout = pLockWaiter->pNextByTime; } if (pLockWaiter->pNextInList) { pLockWaiter->pNextInList->pPrevInList = pLockWaiter->pPrevInList; } else { m_pLastInList = pLockWaiter->pPrevInList; } if (pLockWaiter->pPrevInList) { pLockWaiter->pPrevInList->pNextInList = pLockWaiter->pNextInList; } else { m_pFirstInList = pLockWaiter->pNextInList; } f_assert( m_uiNumWaiters > 0); m_uiNumWaiters--; } /**************************************************************************** Desc: Lock this object. If object is locked, wait the specified number of seconds. ****************************************************************************/ RCODE FLMAPI F_LockObject::lock( F_SEM hWaitSem, FLMBOOL bExclReq, FLMUINT uiMaxWaitSecs, FLMINT iPriority, F_LOCK_STATS * pLockStats) { RCODE rc = NE_FLM_OK; RCODE TempRc; F_LOCK_WAITER LockWait; FLMBOOL bMutexLocked = FALSE; f_assert( hWaitSem != F_SEM_NULL); f_mutexLock( m_hMutex); bMutexLocked = TRUE; if (m_pFirstInList || m_bExclLock || (bExclReq && m_uiSharedLockCnt)) { // Object is locked by another thread, wait to get lock. if( !uiMaxWaitSecs) { rc = RC_SET( NE_FLM_LOCK_REQ_TIMEOUT); goto Exit; } // Set up to wait for the lock. f_memset( &LockWait, 0, sizeof( LockWait)); LockWait.hWaitSem = hWaitSem; LockWait.uiThreadId = f_threadId(); LockWait.pRc = &rc; rc = RC_SET( NE_FLM_FAILURE); LockWait.bExclReq = bExclReq; LockWait.iPriority = iPriority; LockWait.uiWaitStartTime = (FLMUINT)FLM_GET_TIMER(); if( bExclReq && pLockStats) { f_timeGetTimeStamp( &LockWait.StartTime); LockWait.pLockStats = pLockStats; } if( uiMaxWaitSecs >= 0xFF) { LockWait.uiWaitTime = 0; } else { LockWait.uiWaitTime = FLM_SECS_TO_TIMER_UNITS( uiMaxWaitSecs); } // Link to list of global waiters - ordered by end time. insertWaiter( &LockWait); f_mutexUnlock( m_hMutex); bMutexLocked = FALSE; // Now just wait to be signaled. if( RC_BAD( TempRc = f_semWait( hWaitSem, F_WAITFOREVER))) { RC_UNEXPECTED_ASSERT( TempRc); rc = TempRc; } else { // Process that signaled us better set the rc to something // besides NE_FLM_FAILURE. if (rc == NE_FLM_FAILURE) { RC_UNEXPECTED_ASSERT( rc); } } } else { // Object is NOT locked in a conflicting mode. Grant the // lock immediately. m_uiLockThreadId = f_threadId(); m_bExclLock = bExclReq; if (!bExclReq) { m_uiSharedLockCnt++; } else { m_uiLockTime = FLM_GET_TIMER(); f_assert( m_uiSharedLockCnt == 0); // Take care of statistics gathering. if (pLockStats) { // If m_bStartTimeSet is TRUE, we started the // clock the last time nobody had the exclusive // lock, so we need to sum up idle time now. if (m_bStartTimeSet) { f_addElapsedTime( &m_StartTime, &pLockStats->NoLocks.ui64ElapMilli); pLockStats->NoLocks.ui64Count++; } // Restart the clock for this locker. f_timeGetTimeStamp( &m_StartTime); m_bStartTimeSet = TRUE; } else { m_bStartTimeSet = FALSE; } } } Exit: if (RC_OK( rc)) { m_uiLockCount++; } if (bMutexLocked) { f_mutexUnlock( m_hMutex); } return( rc); } /**************************************************************************** Desc: Unlock this object. If there is a pending lock request, give the lock to the next waiter. ****************************************************************************/ RCODE FLMAPI F_LockObject::unlock( F_LOCK_STATS * pLockStats) { RCODE rc = NE_FLM_OK; F_SEM hWaitSem; F_LOCK_WAITER * pLockWaiter; f_mutexLock( m_hMutex); if( m_bExclLock) { f_assert( !m_uiSharedLockCnt); m_bExclLock = FALSE; // Record how long the lock was held, if we were tracking it. if( pLockStats && m_bStartTimeSet) { f_addElapsedTime( &m_StartTime, &pLockStats->HeldLock.ui64ElapMilli); pLockStats->HeldLock.ui64Count++; } m_bStartTimeSet = FALSE; } else { f_assert( m_uiSharedLockCnt > 0); m_uiSharedLockCnt--; } m_uiLockThreadId = 0; // See if we need to signal the next set of waiters if( m_pFirstInList && !m_uiSharedLockCnt) { m_bExclLock = m_pFirstInList->bExclReq; while( m_pFirstInList) { if (!m_bExclLock) { m_uiSharedLockCnt++; } pLockWaiter = m_pFirstInList; hWaitSem = pLockWaiter->hWaitSem; // Unlink the waiter from the list of waiters on this lock object. // // IMPORTANT NOTE: Do NOT signal the semaphore until AFTER // doing this unlinking. This is because F_LOCK_WAITER // structures exist only on the stack of the thread // being signaled. If we tried to assign m_pFirstInList after // signaling the semaphore, the F_LOCK_WAITER structure could // disappear and m_pFirstInList would get garbage. removeWaiter( pLockWaiter); // Update statistics for the waiter. if (pLockWaiter->pLockStats) { f_addElapsedTime( &pLockWaiter->StartTime, &pLockWaiter->pLockStats->WaitingForLock.ui64ElapMilli); pLockWaiter->pLockStats->WaitingForLock.ui64Count++; } // Grant the lock to this waiter and signal the thread // to wake it up. m_uiLockThreadId = pLockWaiter->uiThreadId; if (m_bExclLock) { m_uiLockTime = FLM_GET_TIMER(); // Restart the stats timer if (pLockStats) { m_bStartTimeSet = TRUE; f_timeGetTimeStamp( &m_StartTime); } } *(pLockWaiter->pRc) = NE_FLM_OK; f_semSignal( hWaitSem); // If the next waiter is not a shared lock request or // the lock that was granted was exclusive, we stop // here. if (m_bExclLock || (m_pFirstInList && m_pFirstInList->bExclReq)) { break; } } } // Start timer, if not already running. If the timer is not set at // this point, it will be because nobody has been granted the exclusive // lock. If someone was granted the exclusive lock, the timer would // have been started above. We start it here so we can track idle // time. if (pLockStats && !m_bStartTimeSet) { f_assert( !m_bExclLock); m_bStartTimeSet = TRUE; f_timeGetTimeStamp( &m_StartTime); } f_mutexUnlock( m_hMutex); return( rc); } /**************************************************************************** Desc: Returns information about the pending lock requests. ****************************************************************************/ RCODE FLMAPI F_LockObject::getLockInfo( FLMINT iPriority, eLockType * peCurrLockType, FLMUINT * puiThreadId, FLMUINT * puiLockHeldTime, FLMUINT * puiNumExclQueued, FLMUINT * puiNumSharedQueued, FLMUINT * puiPriorityCount) { F_LOCK_WAITER * pLockWaiter; if( puiNumExclQueued) { *puiNumExclQueued = 0; } if( puiNumSharedQueued) { *puiNumSharedQueued = 0; } if( puiPriorityCount) { *puiPriorityCount = 0; } if( puiThreadId) { *puiThreadId = 0; } if( puiLockHeldTime) { *puiLockHeldTime = 0; } f_mutexLock( m_hMutex); // Get the type of lock, if any. if (m_bExclLock) { if( peCurrLockType) { *peCurrLockType = FLM_LOCK_EXCLUSIVE; } if( puiThreadId) { *puiThreadId = m_uiLockThreadId; } if( puiLockHeldTime) { *puiLockHeldTime = FLM_TIMER_UNITS_TO_MILLI( FLM_ELAPSED_TIME( FLM_GET_TIMER(), m_uiLockTime)); } } else if (m_uiSharedLockCnt) { if( peCurrLockType) { *peCurrLockType = FLM_LOCK_SHARED; } } else { if( peCurrLockType) { *peCurrLockType = FLM_LOCK_NONE; } } // Get information on pending lock requests. if( puiNumExclQueued || puiNumSharedQueued || puiPriorityCount) { pLockWaiter = m_pFirstInList; for( ; pLockWaiter; pLockWaiter = pLockWaiter->pNextInList) { // Count the number of exclusive and shared waiters. if (pLockWaiter->bExclReq) { if( puiNumExclQueued) { (*puiNumExclQueued)++; } } else { if( puiNumSharedQueued) { (*puiNumSharedQueued)++; } } // Count the number of waiters at or above input priority. if (pLockWaiter->iPriority >= iPriority) { if( puiPriorityCount) { (*puiPriorityCount)++; } } } } f_mutexUnlock( m_hMutex); return( NE_FLM_OK); } /**************************************************************************** Desc: ****************************************************************************/ RCODE FLMAPI F_LockObject::getLockInfo( IF_LockInfoClient * pLockInfo) { RCODE rc = NE_FLM_OK; F_LOCK_WAITER * pLockWaiter; FLMUINT uiCnt; FLMUINT uiElapTime; FLMUINT uiCurrTime; FLMUINT uiMilli; f_mutexLock( m_hMutex); uiCurrTime = FLM_GET_TIMER(); if( !m_uiNumWaiters && !m_uiLockThreadId) { pLockInfo->setLockCount( 0); goto Exit; } uiCnt = m_uiNumWaiters + 1; if( pLockInfo->setLockCount( uiCnt) == FALSE) { goto Exit; } // Output the lock holder first. uiElapTime = FLM_ELAPSED_TIME( uiCurrTime, m_uiLockTime); uiMilli = FLM_TIMER_UNITS_TO_MILLI( uiElapTime); if( pLockInfo->addLockInfo( 0, m_uiLockThreadId, uiMilli) == FALSE) { goto Exit; } uiCnt--; // Output the lock waiters. pLockWaiter = m_pFirstInList; while( pLockWaiter && uiCnt) { uiElapTime = FLM_ELAPSED_TIME( uiCurrTime, pLockWaiter->uiWaitStartTime); uiMilli = FLM_TIMER_UNITS_TO_MILLI( uiElapTime); if( pLockInfo->addLockInfo( (m_uiNumWaiters - uiCnt) + 1, pLockWaiter->uiThreadId, uiMilli) == FALSE) { goto Exit; } pLockWaiter = pLockWaiter->pNextInList; uiCnt--; } f_assert( !pLockWaiter && !uiCnt); Exit: f_mutexUnlock( m_hMutex); return( rc); } /**************************************************************************** Desc: Return a list that includes the current lock holder as well as the lock waiters. ****************************************************************************/ RCODE FLMAPI F_LockObject::getLockQueue( F_LOCK_USER ** ppLockUsers) { RCODE rc = NE_FLM_OK; F_LOCK_USER * pLockUser; F_LOCK_WAITER * pLockWaiter; FLMUINT uiCnt; FLMUINT uiElapTime; FLMUINT uiCurrTime; f_mutexLock( m_hMutex); uiCurrTime = (FLMUINT)FLM_GET_TIMER(); if( !m_uiNumWaiters && !m_uiLockThreadId) { *ppLockUsers = NULL; goto Exit; } uiCnt = m_uiNumWaiters + 1; if( RC_BAD( rc = f_alloc( sizeof( F_LOCK_USER) * (uiCnt + 1), &pLockUser))) { goto Exit; } *ppLockUsers = pLockUser; // Output the lock holder first. pLockUser->uiThreadId = m_uiLockThreadId; uiElapTime = FLM_ELAPSED_TIME( uiCurrTime, m_uiLockTime); pLockUser->uiTime = FLM_TIMER_UNITS_TO_MILLI( uiElapTime); pLockUser++; uiCnt--; // Output the lock waiters. pLockWaiter = m_pFirstInList; while( pLockWaiter && uiCnt) { pLockUser->uiThreadId = pLockWaiter->uiThreadId; uiElapTime = FLM_ELAPSED_TIME( uiCurrTime, pLockWaiter->uiWaitStartTime); pLockUser->uiTime = FLM_TIMER_UNITS_TO_MILLI( uiElapTime); pLockWaiter = pLockWaiter->pNextInList; pLockUser++; uiCnt--; } flmAssert( pLockWaiter == NULL && uiCnt == 0); // Zero out the last one. f_memset( pLockUser, 0, sizeof( F_LOCK_USER)); Exit: f_mutexUnlock( m_hMutex); return( rc); } /**************************************************************************** Desc: Returns TRUE if there are lock waiters with a priority > iPriority ****************************************************************************/ FLMBOOL FLMAPI F_LockObject::haveHigherPriorityWaiter( FLMINT iPriority) { F_LOCK_WAITER * pLockWaiter; FLMBOOL bWaiters = FALSE; f_mutexLock( m_hMutex); pLockWaiter = m_pFirstInList; for( ; pLockWaiter; pLockWaiter = pLockWaiter->pNextInList) { // If we find a waiter with a priority > the specified // priority, we're done. if( pLockWaiter->iPriority > iPriority) { bWaiters = TRUE; break; } } f_mutexUnlock( m_hMutex); return( bWaiters); }