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
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!!!");
|
|
}
|