Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

628 lines
18 KiB

//-----------------------------------------------------------------------------
//
//
// File: rwinst.cpp
//
// Description: Implementation of CShareLockInst library functions
//
// Author: Mike Swafford (MikeSwa)
//
// History:
// 5/24/99 - MikeSwa Created
// 8/6/99 - MikeSwa created phatq version
//
// Copyright (C) 1999 Microsoft Corporation
//
//-----------------------------------------------------------------------------
#include <windows.h>
#include <rwinst.h>
#include <stdlib.h>
#include <dbgtrace.h>
//Static initialization
LIST_ENTRY CShareLockInst::s_liLocks;
volatile DWORD CShareLockInst::s_dwLock = 0;
DWORD CShareLockInst::s_cLockSpins = 0;
DWORD CShareLockInst::s_dwSignature = SHARE_LOCK_INST_SIG_FREE;
//---[ CThreadIdBlock::cIncThreadCount ]---------------------------------------
//
//
// Description:
// Increments the thread count for a given thread ID
// Parameters:
// dwThreadId Thread to increment the thread count for
// Returns:
// New count value
// History:
// 8/9/99 - MikeSwa Created
//
//-----------------------------------------------------------------------------
DWORD CThreadIdBlock::cIncThreadCount(DWORD dwThreadId)
{
_ASSERT(THREAD_ID_BLOCK_UNUSED != dwThreadId);
CThreadIdBlock *ptblkCurrent = this;
CThreadIdBlock *ptblkOld = NULL;
CThreadIdBlock *ptblkNew = NULL;
while (ptblkCurrent)
{
_ASSERT(THREAD_ID_BLOCK_SIG == ptblkCurrent->m_dwSignature);
if (dwThreadId == ptblkCurrent->m_dwThreadId)
return InterlockedIncrement((PLONG) &(ptblkCurrent->m_cThreadRecursionCount));
ptblkOld = ptblkCurrent;
ptblkCurrent = ptblkCurrent->m_ptblkNext;
}
_ASSERT(ptblkOld); //we should hit loop at least once
//See if the current block has a thread ID associated with it
if (THREAD_ID_BLOCK_UNUSED == ptblkOld->m_dwThreadId)
{
//This is actually the head block... use it to avoid an extra alloc
if (THREAD_ID_BLOCK_UNUSED == InterlockedCompareExchange(
(PLONG) &ptblkOld->m_dwThreadId,
dwThreadId, THREAD_ID_BLOCK_UNUSED))
{
_ASSERT(dwThreadId == ptblkOld->m_dwThreadId);
//Now this thread block is the current one
return InterlockedIncrement((PLONG) &ptblkOld->m_cThreadRecursionCount);
}
}
//We did not find it... we must create a new CThreadIdBlock
ptblkNew = new CThreadIdBlock();
//if we fail to alloc 32 bytes... I should see if we have spun out of
//control
_ASSERT(ptblkNew);
if (!ptblkNew)
return 1; //Fake success for our callers
ptblkNew->m_dwThreadId = dwThreadId;
ptblkNew->m_cThreadRecursionCount = 1;
ptblkCurrent = (CThreadIdBlock *) InterlockedCompareExchangePointer(
(PVOID *) &ptblkOld->m_ptblkNext,
(PVOID) ptblkNew,
NULL);
//If it is non-NULL, then our insert failed
if (ptblkCurrent)
{
_ASSERT(ptblkCurrent != ptblkNew);
//Whoops... another thread has added a block... time to try again
//This time, start search from the block the just appeared
delete ptblkNew;
return ptblkCurrent->cIncThreadCount(dwThreadId);
}
//We inserted the block... inital count was 1
return 1;
}
//---[ CThreadIdBlock::cDecThreadCount ]---------------------------------------
//
//
// Description:
// Decrements the thread count for a given thread ID
// Parameters:
// dwThreadId
// Returns:
// The resulting count
// History:
// 8/9/99 - MikeSwa Created
//
//-----------------------------------------------------------------------------
DWORD CThreadIdBlock::cDecThreadCount(DWORD dwThreadId)
{
_ASSERT(THREAD_ID_BLOCK_UNUSED != dwThreadId);
CThreadIdBlock *ptblkCurrent = this;
while (ptblkCurrent)
{
_ASSERT(THREAD_ID_BLOCK_SIG == ptblkCurrent->m_dwSignature);
if (dwThreadId == ptblkCurrent->m_dwThreadId)
return InterlockedDecrement((PLONG) &(ptblkCurrent->m_cThreadRecursionCount));
ptblkCurrent = ptblkCurrent->m_ptblkNext;
}
//We didn't find it... we would have asserted on insertion
//Don't assert twice
//$$TODO - Add global counts of these failures
return 0;
}
//---[ CThreadIdBlock::cMatchesId ]--------------------------------------------
//
//
// Description:
// Checks if this thread block (or one in this thread blocks chain)
// matches the given thread id. Returns the count for this thread
// Parameters:
// dwThreadId - Thread Id to search for
// Returns:
// Thread count if the thread ID is found
// 0 if not found (or count is 0)
// History:
// 8/9/99 - MikeSwa Created
//
//-----------------------------------------------------------------------------
DWORD CThreadIdBlock::cMatchesId(DWORD dwThreadId)
{
CThreadIdBlock *ptblk = this;
while (ptblk)
{
_ASSERT(THREAD_ID_BLOCK_SIG == ptblk->m_dwSignature);
if (ptblk->m_dwThreadId == dwThreadId)
return ptblk->m_cThreadRecursionCount;
ptblk = ptblk->m_ptblkNext;
}
return 0;
}
//---[ CShareLockInst::AcquireStaticSpinLock ]---------------------------------
//
//
// Description:
// Acquires static spin lock... from aqueue\cat\ldapstor
// Parameters:
// -
// Returns:
// -
// History:
// 5/21/99 - MikeSwa Adapted from JStamerJ's code
//
//-----------------------------------------------------------------------------
void CShareLockInst::AcquireStaticSpinLock()
{
do {
//
// Spin while the lock is unavailable
//
while (s_dwLock > 0)
{
Sleep(0);
InterlockedIncrement((PLONG) &s_cLockSpins);
}
//
// Lock just became available, try to grab it
//
} while ( InterlockedIncrement((PLONG) &s_dwLock) != 1 );
//We have the lock... make sure that s_liLocks has been initialized
if (s_dwSignature != SHARE_LOCK_INST_SIG)
{
InitializeListHead(&s_liLocks);
s_dwSignature = SHARE_LOCK_INST_SIG;
}
}
//---[ CShareLockInst::ReleaseStaticSpinLock ]---------------------------------
//
//
// Description:
// Releases previously acquired spinlock
// Parameters:
// -
// Returns:
// -
// History:
// 5/21/99 - MikeSwa Adapted from JStamerJ's code
//
//-----------------------------------------------------------------------------
void CShareLockInst::ReleaseStaticSpinLock()
{
_ASSERT(SHARE_LOCK_INST_SIG == s_dwSignature); //static init was done
_ASSERT(s_dwLock > 0);
InterlockedExchange((PLONG) &s_dwLock, 0 );
}
//---[ CShareLockInst::CShareLockInst ]----------------------------------------
//
//
// Description:
// Constructor for CShareLockInst
// Parameters:
// szDescription Constant string passed in to describe lock
// dwFlags Flags describing what to track
// cMaxTrackedSharedThreadIDs Maximum # of threads to track
// Returns:
// -
// History:
// 5/21/99 - MikeSwa Created
//
//-----------------------------------------------------------------------------
CShareLockInst::CShareLockInst(LPCSTR szDescription,
DWORD dwFlags, DWORD cMaxTrackedSharedThreadIDs)
{
DWORD cbArray = sizeof(DWORD) * cMaxTrackedSharedThreadIDs;
m_dwSignature = SHARE_LOCK_INST_SIG;
m_dwFlags = dwFlags;
m_liLocks.Flink = NULL;
m_liLocks.Blink = NULL;
m_cShareAttempts = 0;
m_cShareAttemptsBlocked = 0;
m_cExclusiveAttempts = 0;
m_cExclusiveAttemptsBlocked = 0;
m_szDescription = szDescription;
m_rgtblkSharedThreadIDs = NULL;
m_dwExclusiveThread = NULL;
m_cCurrentSharedThreads = 0;
m_cMaxConcurrentSharedThreads = 0;
m_cMaxTrackedSharedThreadIDs = cMaxTrackedSharedThreadIDs;
if (SHARE_LOCK_INST_TRACK_NOTHING & m_dwFlags)
m_dwFlags = 0;
//Allocate memory to store thread IDs
if (fTrackSharedThreads())
{
_ASSERT(cbArray);
m_rgtblkSharedThreadIDs = new CThreadIdBlock[m_cMaxTrackedSharedThreadIDs];
if (!m_rgtblkSharedThreadIDs)
m_cMaxTrackedSharedThreadIDs = 0;
}
//Insert in list if we are tracking
if (fTrackInGlobalList())
{
AcquireStaticSpinLock();
InsertHeadList(&s_liLocks, &m_liLocks);
ReleaseStaticSpinLock();
}
};
//---[ CShareLockinst::~CShareLockinst ]---------------------------------------
//
//
// Description:
// CShareLockInst desctructor. Will remove this lock from the
// Parameters:
// -
// Returns:
// -
// History:
// 5/21/99 - MikeSwa Created
//
//-----------------------------------------------------------------------------
CShareLockInst::~CShareLockInst()
{
m_dwSignature = SHARE_LOCK_INST_SIG_FREE;
if (m_rgtblkSharedThreadIDs)
{
delete [] m_rgtblkSharedThreadIDs;
m_rgtblkSharedThreadIDs = NULL;
}
if (fTrackInGlobalList())
{
AcquireStaticSpinLock();
RemoveEntryList(&m_liLocks);
ReleaseStaticSpinLock();
}
};
//---[ CShareLockInst::LogAcquireShareLock ]-----------------------------------
//
//
// Description:
// Does all the work of logging the appropriate information when a thread
// acquires the lock shared.
// - Updates max concurrent shared threads
// - Updates current shared threads
// - Updates lists of shared thread IDs
// - Asserts when shared deadlocks are detected
// Parameters:
// BOOL fTry - TRUE if this is for a try enter (deadlock cannot happen)
// Returns:
// -
// History:
// 5/21/99 - MikeSwa Created
//
//-----------------------------------------------------------------------------
void CShareLockInst::LogAcquireShareLock(BOOL fTry)
{
if (fTrackSharedThreads())
{
DWORD cCurrentSharedThreads = 0;
DWORD cMaxConcurrentSharedThreads = 0;
DWORD dwThreadID = GetCurrentThreadId();
DWORD dwThreadCount = 0;
DWORD dwThreadHash = 0;
_ASSERT(dwThreadID); //Our algorithm requires this to be non-zero
cCurrentSharedThreads = InterlockedIncrement((PLONG) &m_cCurrentSharedThreads);
//Update max concurrent threads if we have set a new record
cMaxConcurrentSharedThreads = m_cMaxConcurrentSharedThreads;
while (cCurrentSharedThreads > cMaxConcurrentSharedThreads)
{
InterlockedCompareExchange((PLONG) &m_cMaxConcurrentSharedThreads,
(LONG) cCurrentSharedThreads,
(LONG) cMaxConcurrentSharedThreads);
cMaxConcurrentSharedThreads = m_cMaxConcurrentSharedThreads;
}
//if we have a place to store our thread ID...save it
if (m_rgtblkSharedThreadIDs)
{
dwThreadHash = dwHashThreadId(dwThreadID,
m_cMaxTrackedSharedThreadIDs);
_ASSERT(dwThreadHash < m_cMaxTrackedSharedThreadIDs);
dwThreadCount = m_rgtblkSharedThreadIDs[dwThreadHash].cIncThreadCount(dwThreadID);
if (!fTry && (dwThreadCount > 1))
{
//This thread already holds this lock... this is a
//potential deadlock situation
if (fAssertSharedDeadlocks())
{
_ASSERT(0 && "Found potential share deadlock");
}
}
}
}
}
//---[ CShareLockInst::LogReleaseShareLock ]-----------------------------------
//
//
// Description:
// Called when a sharelock is released to cleanup the information stored
// in LogAcquireShareLock.
// Parameters:
// -
// Returns:
// -
// History:
// 5/21/99 - MikeSwa Created
//
//-----------------------------------------------------------------------------
void CShareLockInst::LogReleaseShareLock()
{
if (fTrackSharedThreads())
{
DWORD dwThreadID = GetCurrentThreadId();
DWORD dwThreadHash = 0;
_ASSERT(dwThreadID); //Our algorithm requires this to be non-zero
//Search through list of thread IDs for
if (m_rgtblkSharedThreadIDs)
{
dwThreadHash = dwHashThreadId(dwThreadID,
m_cMaxTrackedSharedThreadIDs);
_ASSERT(dwThreadHash < m_cMaxTrackedSharedThreadIDs);
m_rgtblkSharedThreadIDs[dwThreadHash].cDecThreadCount(dwThreadID);
}
}
}
//---[ CShareLockInst::ShareLock ]---------------------------------------------
//
//
// Description:
// Implements sharelock wrapper
// Parameters:
//
// Returns:
//
// History:
// 5/21/99 - MikeSwa Created
//
//-----------------------------------------------------------------------------
void CShareLockInst::PrvShareLock()
{
LogAcquireShareLock(FALSE);
//If we are tracking contention, then we will try to enter the sharelock
//and increment the contention count if that fails.
if (fTrackContention())
{
InterlockedIncrement((PLONG) &m_cShareAttempts);
if (!CShareLockInstBase::TryShareLock())
{
InterlockedIncrement((PLONG) &m_cShareAttemptsBlocked);
CShareLockInstBase::ShareLock();
}
}
else
{
CShareLockInstBase::ShareLock();
}
};
//---[ CShareLockInst::ShareUnlock ]-------------------------------------------
//
//
// Description:
// Wrapper for ShareUnlock
// Parameters:
//
// Returns:
//
// History:
// 5/21/99 - MikeSwa Created
//
//-----------------------------------------------------------------------------
void CShareLockInst::PrvShareUnlock()
{
LogReleaseShareLock();
CShareLockInstBase::ShareUnlock();
};
//---[ CShareLockInst::TryShareLock ]------------------------------------------
//
//
// Description:
// Implements TryShareLock wrapper.
// Parameters:
//
// Returns:
// TRUE if the lock was acquired.
// History:
// 5/21/99 - MikeSwa Created
//
//-----------------------------------------------------------------------------
BOOL CShareLockInst::PrvTryShareLock()
{
BOOL fLocked = FALSE;
fLocked = CShareLockInstBase::TryShareLock();
if (fLocked)
LogAcquireShareLock(TRUE);
return fLocked;
};
//---[ CShareLockInst::ExclusiveLock ]-----------------------------------------
//
//
// Description:
// Implements ExclusiveLock wrapper
// Parameters:
//
// Returns:
//
// History:
// 5/21/99 - MikeSwa Created
//
//-----------------------------------------------------------------------------
void CShareLockInst::PrvExclusiveLock()
{
//If we are tracking contention, then we will try to enter the lock
//and increment the contention count if that fails.
if (fTrackContention())
{
InterlockedIncrement((PLONG) &m_cExclusiveAttempts);
if (!CShareLockInstBase::TryExclusiveLock())
{
InterlockedIncrement((PLONG) &m_cExclusiveAttemptsBlocked);
CShareLockInstBase::ExclusiveLock();
}
}
else
{
CShareLockInstBase::ExclusiveLock();
}
if (fTrackExclusiveThreads())
{
//This should be the only thread accessing this now
_ASSERT(!m_dwExclusiveThread);
m_dwExclusiveThread = GetCurrentThreadId();
}
};
//---[ CShareLockInst::ExclusiveUnlock ]---------------------------------------
//
//
// Description:
// Wrapper for ExclusiveUnlock
// Parameters:
// -
// Returns:
// -
// History:
// 5/21/99 - MikeSwa Created
//
//-----------------------------------------------------------------------------
void CShareLockInst::PrvExclusiveUnlock()
{
if (fTrackExclusiveThreads())
{
_ASSERT(GetCurrentThreadId() == m_dwExclusiveThread);
m_dwExclusiveThread = 0;
}
CShareLockInstBase::ExclusiveUnlock();
};
//---[ CShareLockInst::TryExclusiveLock ]--------------------------------------
//
//
// Description:
// Implements TryExclusiveLock wrapper.
// Parameters:
//
// Returns:
// TRUE if the lock was acquired.
// History:
// 5/21/99 - MikeSwa Created
//
//-----------------------------------------------------------------------------
BOOL CShareLockInst::PrvTryExclusiveLock()
{
BOOL fLocked = FALSE;
fLocked = CShareLockInstBase::TryExclusiveLock();
if (fLocked && fTrackExclusiveThreads())
{
//This should be the only thread accessing this now
_ASSERT(!m_dwExclusiveThread);
m_dwExclusiveThread = GetCurrentThreadId();
}
return fLocked;
};
//---[ CShareLockInst::PrvAssertIsLocked ]-------------------------------------
//
//
// Description:
// Asserts if this threads ID is not recorded as one that acquired this
// lock.
// Parameters:
// -
// Returns:
// -
// History:
// 5/24/99 - MikeSwa Created
//
//-----------------------------------------------------------------------------
void CShareLockInst::PrvAssertIsLocked()
{
DWORD dwThreadID = GetCurrentThreadId();
DWORD dwThreadHash = 0;
BOOL fFoundThreadID = FALSE;
_ASSERT(dwThreadID); //Our algorithm requires this to be non-zero
//Bail out if we are not configured to track this things.
if (!fTrackSharedThreads() || !fTrackExclusiveThreads() || !m_rgtblkSharedThreadIDs)
return;
if (dwThreadID == m_dwExclusiveThread)
{
fFoundThreadID = TRUE;
}
else
{
dwThreadHash = dwHashThreadId(dwThreadID,
m_cMaxTrackedSharedThreadIDs);
_ASSERT(dwThreadHash < m_cMaxTrackedSharedThreadIDs);
fFoundThreadID = (0 < m_rgtblkSharedThreadIDs[dwThreadHash].cMatchesId(dwThreadID));
}
if (!fFoundThreadID)
_ASSERT(0 && "Lock is not held by this thread!!!");
}