Files
mars-flaim/ftk/src/ftklock.cpp
ahodgkinson c670abc2df Fixed keyword substitution tokens.
git-svn-id: https://svn.code.sf.net/p/flaim/code/trunk@1008 0109f412-320b-0410-ab79-c3e0c5ffbbe6
2007-01-23 09:36:47 +00:00

1089 lines
24 KiB
C++

//------------------------------------------------------------------------------
// 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);
}