Source code of Windows XP (NT5)
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.
 
 
 
 
 
 

400 lines
11 KiB

//*****************************************************************************
// locks.h
//
// This class provides a number of locking primitives for multi-threaded
// programming. The main class of interest are:
// CCritLock Critical section based lock wrapper class.
// CExclLock A Spin lock class for classic test & set behavior.
// CSingleLock A spin lock with no nesting capabilities.
// CAutoLock A helper class to lock/unlock in ctor/dtor.
//
// CMReadSWrite A highly efficient lock for multiple readers and
// single writer behavior.
// CAutoReadLock A helper for read locking in ctor/dtor.
// CAutoWriteLock A helper for write locking in ctor/dtor.
//
// Copyright (c) 1996, Microsoft Corp. All rights reserved.
//*****************************************************************************
#ifndef __LOCKS_H__
#define __LOCKS_H__
//*****************************************************************************
// This lock implements a spin lock that does not support nesting. It is very
// lean because of this, but locks cannot be nested.
//*****************************************************************************
class CSingleLock
{
long volatile m_iLock; // Test and set spin value.
public:
inline CSingleLock() :
m_iLock(0)
{ }
inline ~CSingleLock()
{
m_iLock = 0;
}
//*****************************************************************************
// This version spins forever until it wins. Nested calls to Lock from the
// same thread are not supported.
//*****************************************************************************
inline void Lock()
{
// Spin until we win.
while (InterlockedExchange((long*)&m_iLock, 1L) == 1L)
;
}
//*****************************************************************************
// This version spins until it wins or times out. Nested calls to Lock from
// the same thread are supported.
//*****************************************************************************
HRESULT Lock( // S_OK, or E_FAIL.
DWORD dwTimeout) // Millisecond timeout value, 0 is forever.
{
DWORD dwTime = 0;
// Keep spinning until we get the lock.
while (InterlockedExchange((long*)&m_iLock, 1L) == 1L)
{
// Wait for 1/10 a second.
Sleep(100);
// See if we have gone over the timeout value.
if (dwTimeout)
{
if ((dwTime += 100) >= dwTimeout)
return (E_FAIL);
}
}
return (S_OK);
}
//*****************************************************************************
// Assigning to 0 is thread safe and yields much faster performance than
// an Interlocked* operation.
//*****************************************************************************
inline void Unlock()
{
m_iLock = 0;
}
};
//*****************************************************************************
// This lock class is based on NT's critical sections and has all of their
// semantics.
//*****************************************************************************
class CCritLock
{
private:
CRITICAL_SECTION m_sCrit; // The critical section to block on.
#ifdef _DEBUG
BOOL m_bInit; // Track init status.
int m_iLocks; // Count of locks.
#endif
public:
inline CCritLock()
{
#ifdef _DEBUG
m_bInit = TRUE;
m_iLocks = 0;
#endif
InitializeCriticalSection(&m_sCrit);
}
inline ~CCritLock()
{
_ASSERTE(m_bInit);
_ASSERTE(m_iLocks == 0);
DeleteCriticalSection(&m_sCrit);
}
inline void Lock()
{
_ASSERTE(m_bInit);
EnterCriticalSection(&m_sCrit);
_ASSERTE(++m_iLocks > 0);
}
inline void Unlock()
{
_ASSERTE(m_bInit);
_ASSERTE(--m_iLocks >= 0);
LeaveCriticalSection(&m_sCrit);
}
#ifdef _DEBUG
inline int GetLockCnt()
{ return (m_iLocks); }
inline BOOL IsLocked()
{ return (m_iLocks != 0); }
#endif
};
//*****************************************************************************
// Provides a mututal exclusion lock for a resource through a spin lock. This
// type of lock does not keep a queue, so thread starvation is theoretically
// possible. In addition, thread priority could cause a potential dead lock if
// a low priority thread got the lock but didn't get enough time to eventually
// free it.
// NOTE: There is a bug in the Pentium cache that InterlockedExchange will
// force a cache flush of the value. For this reason, doing an assignment
// to free the lock is much, much faster than using an Interlocked instruction.
//*****************************************************************************
class CExclLock
{
long volatile m_iLock; // Test and set spin value.
long m_iNest; // Nesting count.
DWORD m_iThreadId; // The thread that owns the lock.
public:
inline CExclLock() :
m_iLock(0),
m_iNest(0),
m_iThreadId(0)
{ }
inline ~CExclLock()
{
m_iNest = 0;
m_iThreadId = 0;
m_iLock = 0;
}
//*****************************************************************************
// This version spins forever until it wins. Nested calls to Lock from the
// same thread are supported.
//*****************************************************************************
inline void Lock()
{
DWORD iThread; // Local thread ID.
// Allow nested calls to lock in the same thread.
if ((iThread = GetCurrentThreadId()) == m_iThreadId && m_iLock)
{
++m_iNest;
return;
}
// Spin until we win.
while (InterlockedExchange((long*)&m_iLock, 1L) == 1L)
;
// Store our thread ID and nesting count now that we've won.
m_iThreadId = iThread;
m_iNest = 1;
}
//*****************************************************************************
// This version spins until it wins or times out. Nested calls to Lock from
// the same thread are supported.
//*****************************************************************************
HRESULT Lock( // S_OK, or E_FAIL.
DWORD dwTimeout) // Millisecond timeout value, 0 is forever.
{
DWORD dwTime = 0;
DWORD iThread; // Local thread ID.
// Allow nested calls to lock in the same thread.
if (m_iLock && (iThread = GetCurrentThreadId()) == m_iThreadId)
{
++m_iNest;
return (S_OK);
}
// Keep spinning until we get the lock.
while (InterlockedExchange((long*)&m_iLock, 1L) == 1L)
{
// Wait for 1/10 a second.
Sleep(100);
// See if we have gone over the timeout value.
if (dwTimeout)
{
if ((dwTime += 100) >= dwTimeout)
return (E_FAIL);
}
}
// Store our thread ID and nesting count now that we've won.
m_iThreadId = iThread;
m_iNest = 1;
return (S_OK);
}
//*****************************************************************************
// Assigning to 0 is thread safe and yields much faster performance than
// an Interlocked* operation.
//*****************************************************************************
inline void Unlock()
{
_ASSERTE(m_iThreadId == GetCurrentThreadId() && m_iNest > 0);
// Unlock outer nesting level.
if (--m_iNest == 0)
{
m_iThreadId = 0;
m_iLock = 0;
}
}
#ifdef _DEBUG
inline BOOL IsLocked()
{ return (m_iLock); }
#endif
};
//*****************************************************************************
// This helper class automatically locks the given lock object in the ctor and
// frees it in the dtor. This makes your code slightly cleaner by not
// requiring an unlock in all failure conditions.
//*****************************************************************************
class CAutoLock
{
CExclLock *m_psLock; // The lock object to free up.
CCritLock *m_psCrit; // Crit lock.
CSingleLock *m_psSingle; // Single non-nested lock.
int m_iNest; // Nesting count for the item.
public:
//*****************************************************************************
// Use this ctor with the assignment operators to do deffered locking.
//*****************************************************************************
CAutoLock() :
m_psLock(NULL),
m_psCrit(NULL),
m_psSingle(NULL),
m_iNest(0)
{
}
//*****************************************************************************
// This version handles a spin lock.
//*****************************************************************************
CAutoLock(CExclLock *psLock) :
m_psLock(psLock),
m_psCrit(NULL),
m_psSingle(NULL),
m_iNest(1)
{
_ASSERTE(psLock != NULL);
psLock->Lock();
}
//*****************************************************************************
// This version handles a critical section lock.
//*****************************************************************************
CAutoLock(CCritLock *psLock) :
m_psLock(NULL),
m_psCrit(psLock),
m_psSingle(NULL),
m_iNest(1)
{
_ASSERTE(psLock != NULL);
psLock->Lock();
}
//*****************************************************************************
// This version handles a critical section lock.
//*****************************************************************************
CAutoLock(CSingleLock *psLock) :
m_psLock(NULL),
m_psCrit(NULL),
m_psSingle(psLock),
m_iNest(1)
{
_ASSERTE(psLock != NULL);
psLock->Lock();
}
//*****************************************************************************
// Free the lock we actually have.
//*****************************************************************************
~CAutoLock()
{
// If we actually took a lock, unlock it.
if (m_iNest != 0)
{
if (m_psLock)
{
while (m_iNest--)
m_psLock->Unlock();
}
else if (m_psCrit)
{
while (m_iNest--)
m_psCrit->Unlock();
}
else if (m_psSingle)
{
while (m_iNest--)
m_psSingle->Unlock();
}
}
}
//*****************************************************************************
// Lock after ctor runs with NULL.
//*****************************************************************************
void Lock(
CSingleLock *psLock)
{
m_psSingle = psLock;
psLock->Lock();
m_iNest = 1;
}
//*****************************************************************************
// Assignment causes a lock to occur. dtor will free the lock. Nested
// assignments are allowed.
//*****************************************************************************
CAutoLock & operator=( // Reference to this class.
CExclLock *psLock) // The lock.
{
_ASSERTE(m_psCrit == NULL && m_psSingle == NULL);
++m_iNest;
m_psLock = psLock;
psLock->Lock();
return (*this);
}
//*****************************************************************************
// Assignment causes a lock to occur. dtor will free the lock. Nested
// assignments are allowed.
//*****************************************************************************
CAutoLock & operator=( // Reference to this class.
CCritLock *psLock) // The lock.
{
_ASSERTE(m_psSingle == NULL && m_psLock == NULL);
++m_iNest;
m_psCrit = psLock;
psLock->Lock();
return (*this);
}
//*****************************************************************************
// Assignment causes a lock to occur. dtor will free the lock. Nested
// assignments are allowed.
//*****************************************************************************
CAutoLock & operator=( // Reference to this class.
CSingleLock *psLock) // The lock.
{
_ASSERTE(m_psCrit == NULL && m_psLock == NULL);
++m_iNest;
m_psSingle = psLock;
psLock->Lock();
return (*this);
}
};
#endif // __LOCKS_H__