|
|
//+-------------------------------------------------------------------
//
// File: RWLock.hxx
//
// Contents: Reader writer lock implementation that supports the
// following features
// 1. Cheap enough to be used in large numbers
// such as per object synchronization.
// 2. Supports timeout. This is a valuable feature
// to detect deadlocks
// 3. Supports caching of events. The allows
// the events to be moved from least contentious
// regions to the most contentious regions.
// In other words, the number of events needed by
// Reader-Writer lockls is bounded by the number
// of threads in the process.
// 4. Supports nested locks by readers and writers
// 5. Supports spin counts for avoiding context switches
// on multi processor machines.
// 6. Supports functionality for upgrading to a writer
// lock with a return argument that indicates
// intermediate writes. Downgrading from a writer
// lock restores the state of the lock.
// 7. Supports functionality to Release Lock for calling
// app code. RestoreLock restores the lock state and
// indicates intermediate writes.
// 8. Recovers from most common failures such as creation of
// events. In other words, the lock mainitains consistent
// internal state and remains usable
//
// Classes: CRWLock,
// CStaticRWLock
//
// History: 19-Aug-98 Gopalk Created
//
//--------------------------------------------------------------------
#ifndef _RWLOCK_HXX_
#define _RWLOCK_HXX_
#if LOCK_PERF==1
#define RWLOCK_STATISTICS 1
#endif
extern DWORD gdwDefaultTimeout; extern DWORD gdwDefaultSpinCount; extern DWORD gdwNumberOfProcessors;
typedef struct { DWORD dwFlags; DWORD dwWriterSeqNum; WORD *pwReaderLevel; WORD wReaderLevel; WORD wWriterLevel; } LockCookie; typedef DWORD WriterCookie;
//+-------------------------------------------------------------------
//
// Class: CRWLock
//
// Synopsis: Abstract base class the implements the locking
// functionality. It supports nested read locks and
// guarantees progress when the derived class is capable
// of creating ReaderSlot
//
// History: 21-Aug-98 Gopalk Created
//
//+-------------------------------------------------------------------
#define RWLOCKFLAG_INITIALIZED 1
#ifdef RWLOCK_FULL_FUNCTIONALITY
#define RWLOCKFLAG_CACHEEVENTS 2
#define RWLOCKFLAG_RETURNERRORS 4
#endif
class CRWLock { public: // Constuctor
CRWLock() : _hWriterEvent(NULL), _hReaderEvent(NULL), _dwState(0), _dwWriterID(0), _dwWriterSeqNum(1), _wFlags(0), _wWriterLevel(0) #ifdef RWLOCK_STATISTICS
, _dwReaderEntryCount(0), _dwReaderContentionCount(0), _dwWriterEntryCount(0), _dwWriterContentionCount(0) #endif
#if DBG==1
, _dwDeadLockCounter(0) #endif
{ }
// Initialize
virtual void Initialize() { _wFlags |= RWLOCKFLAG_INITIALIZED; } BOOL IsInitialized() { return(_wFlags & RWLOCKFLAG_INITIALIZED); }
// Cleanup
virtual void Cleanup();
// Lock functions
HRESULT AcquireReaderLock( #ifdef RWLOCK_FULL_FUNCTIONALITY
BOOL fReturnErrors = FALSE, DWORD dwDesiredTimeout = gdwDefaultTimeout #if LOCK_PERF==1
, #endif
#endif
#if LOCK_PERF==1
const char *pszFile = "Unknown File", DWORD dwLine = 0, const char *pszLockName = "Unknown RWLock" #endif
); HRESULT AcquireWriterLock( #ifdef RWLOCK_FULL_FUNCTIONALITY
BOOL fReturnErrors = FALSE, DWORD dwDesiredTimeout = gdwDefaultTimeout #if LOCK_PERF==1
, #endif
#endif
#if LOCK_PERF==1
const char *pszFile = "Unknown File", DWORD dwLine = 0, const char *pszLockName = "Unknown RWLock" #endif
);
HRESULT ReleaseReaderLock(); HRESULT ReleaseWriterLock(); HRESULT UpgradeToWriterLock(LockCookie *pLockCookie, BOOL *pfInterveningWrites = NULL #ifdef RWLOCK_FULL_FUNCTIONALITY
, BOOL fReturnErrors = FALSE, DWORD dwDesiredTimeout = gdwDefaultTimeout #endif
#if LOCK_PERF==1
, const char *pszFile = "Unknown File", DWORD dwLine = 0, const char *pszLockName = "Unknown RWLock" #endif
); HRESULT DowngradeFromWriterLock(LockCookie *pLockCookie #if LOCK_PERF==1
, const char *pszFile = "Unknown File", DWORD dwLine = 0, const char *pszLockName = "Unknown RWLock" #endif
); HRESULT ReleaseLock(LockCookie *pLockCookie); HRESULT RestoreLock(LockCookie *pLockCookie, BOOL *pfInterveningWrites = NULL #if LOCK_PERF==1
, const char *pszFile = "Unknown File", DWORD dwLine = 0, const char *pszLockName = "Unknown RWLock" #endif
); // Assert functions
#if DBG==1
BOOL AssertWriterLockHeld(); BOOL AssertWriterLockNotHeld(); BOOL AssertReaderLockHeld(); BOOL AssertReaderLockNotHeld(); BOOL AssertReaderOrWriterLockHeld(); void AssertHeld() { AssertWriterLockHeld(); } void AssertNotHeld() { AssertWriterLockNotHeld(); AssertReaderLockNotHeld(); } #else
void AssertWriterLockHeld() { } void AssertWriterLockNotHeld() { } void AssertReaderLockHeld() { } void AssertReaderLockNotHeld() { } void AssertReaderOrWriterLockHeld() { } void AssertHeld() { } void AssertNotHeld() { } #endif
// Helper functions
WriterCookie GetWriterCookie() { return(_dwWriterSeqNum); } BOOL IntermediateWrites(WriterCookie cookie) { return((cookie+1) != _dwWriterSeqNum); } BOOL HeldExclusive() { return _dwWriterID == GetCurrentThreadId(); } #ifdef RWLOCK_FULL_FUNCTIONALITY
void ReturnErrors() { _wFlags |= RWLOCKFLAG_RETURNERRORS; } void CacheEvents() { _wFlags |= RWLOCKFLAG_CACHEEVENTS; } #endif
#ifdef RWLOCK_STATISTICS
DWORD GetReaderEntryCount() { return(_dwReaderEntryCount); } DWORD GetReaderContentionCount() { return(_dwReaderContentionCount); } DWORD GetWriterEntryCount() { return(_dwWriterEntryCount); } DWORD GetWriterContentionCount() { return(_dwWriterContentionCount); } #endif
// Static functions
static void InitDefaults(); static void SetTimeout(DWORD dwTimeout) { gdwDefaultTimeout = dwTimeout; } static DWORD GetTimeout() { return(gdwDefaultTimeout); } static void SetSpinCount(DWORD dwSpinCount) { gdwDefaultSpinCount = gdwNumberOfProcessors > 1 ? dwSpinCount : 0; } static DWORD GetSpinCount() { return(gdwDefaultSpinCount); }
private: virtual HRESULT GetTLSLockData(WORD **ppwReaderLevel) = 0;
HANDLE GetReaderEvent(); HANDLE GetWriterEvent(); ULONG ModifyState(ULONG dwModifyState); #ifdef RWLOCK_FULL_FUNCTIONALITY
void ReleaseEvents(); #endif
static void RWSetEvent(HANDLE event); static void RWResetEvent(HANDLE event); static void RWSleep(DWORD dwTime);
HANDLE _hWriterEvent; HANDLE _hReaderEvent; DWORD _dwState; DWORD _dwWriterID; DWORD _dwWriterSeqNum; WORD _wFlags; WORD _wWriterLevel; #ifdef RWLOCK_STATISTICS
DWORD _dwReaderEntryCount; DWORD _dwReaderContentionCount; DWORD _dwWriterEntryCount; DWORD _dwWriterContentionCount; #endif
#if DBG==1
DWORD _dwDeadLockCounter; #endif
};
//+-------------------------------------------------------------------
//
// Class: CStaticRWLock
//
// Synopsis: Instances of this class are statically created. It
// manages creation and allocation of ReaderSlots
//
// History: 21-Aug-98 Gopalk Created
//
//+-------------------------------------------------------------------
class CStaticRWLock : public CRWLock { public: // Constructor
CStaticRWLock() : _dwLockNum(-1) { }
// Initialize
void Initialize();
// Compatibility functions
void Request( const char *pszFile = "Unknown File", DWORD dwLine = 0, const char* pszLockName = "Unknown RWLock" ) { AcquireWriterLock( #if LOCK_PERF==1
#ifdef RWLOCK_FULL_FUNCTIONALITY
FALSE, gdwDefaultTimeout, #endif
pszFile, dwLine, pszLockName #endif
); } void Release() { ReleaseWriterLock(); }
private: HRESULT GetTLSLockData(WORD **ppwReaderLevel);
DWORD _dwLockNum; };
//+-------------------------------------------------------------------
//
// Class: CStaticWriteLock
//
// Synopsis: Helper class that simulates acquiring/releasing
// write lock as constructing/destroying an object
// on the stack
//
// History: 21-Aug-98 Gopalk Created
//
//+-------------------------------------------------------------------
class CStaticWriteLock { public: CStaticWriteLock( CStaticRWLock &rwLock, const char *pszFile = "Unknown File", DWORD dwLine = 0, const char* pszLockName = "Unknown RWLock") : _rwLock(rwLock) { _rwLock.Request(pszFile, dwLine, pszLockName); } ~CStaticWriteLock() { _rwLock.Release(); }
private: CStaticRWLock &_rwLock; }; #endif // _RWLOCK_HXX_
|