367 lines
8.5 KiB
C++
367 lines
8.5 KiB
C++
/*++
|
|
|
|
rw.h
|
|
|
|
This file defines some locks with the following performance characteristics :
|
|
|
|
* A constant number of handles is used per lock instance
|
|
* The lock entry/exit protocols use Interlocked operations and do not
|
|
cause context switches in themselves.
|
|
|
|
|
|
--*/
|
|
|
|
#ifndef _CRW_H
|
|
#define _CRW_H
|
|
|
|
#include <limits.h>
|
|
|
|
//
|
|
// This class contains the meat - does actual locking etc...
|
|
//
|
|
class CShareLock {
|
|
/*++
|
|
|
|
Class Description :
|
|
|
|
This class implements a reader writer lock.
|
|
|
|
Multiple threads can successfully enter the lock by calling
|
|
ShareLock(). Once any thread has entered the lock through
|
|
ShareLock(), all threads calling ExclusiveLock() are blocked
|
|
untill ShareUnlock() is called for each successfull ShareLock().
|
|
|
|
This lock is fair - If multiple threads have passed through
|
|
ShareLock(), and a thread calls ExclusiveLock(), all threads
|
|
that arrive after the call to ExclusiveLock() will block untill
|
|
the ExclusiveLock() thread has acquired and released the lock.
|
|
This property is symetrical with ShareLock() and ExclusiveLock().
|
|
|
|
|
|
--*/
|
|
private :
|
|
|
|
//
|
|
// Number of Readers who have passed through the lock OR
|
|
// the number of readers waiting for the lock (will be negative).
|
|
// A value of 0 means nobody in the lock
|
|
//
|
|
volatile long cReadLock ;
|
|
|
|
//
|
|
// The number of readers remainin in the lock if
|
|
// there is a writer waiting. This can become temporarily negative
|
|
//
|
|
volatile long cOutRdrs ;
|
|
|
|
//
|
|
// Critical section to allow only one writer into the lock at a time
|
|
//
|
|
CRITICAL_SECTION critWriters ;
|
|
|
|
//
|
|
// Semaphore for waiting writers to block on (Only 1 ever, others will
|
|
// be queued on critWriters)
|
|
//
|
|
HANDLE hWaitingWriters ;
|
|
|
|
//
|
|
// Semaphore for waiting readers to block on
|
|
//
|
|
HANDLE hWaitingReaders ;
|
|
|
|
//
|
|
// You may not copy these objects - so this lock is private !
|
|
//
|
|
CShareLock( CShareLock& ) ;
|
|
|
|
//
|
|
// You may not copy through assignment - so this operator is private !
|
|
//
|
|
CShareLock& operator=( CShareLock& ) ;
|
|
|
|
public :
|
|
|
|
//
|
|
// Construct and Destroy a CShareLock !
|
|
//
|
|
CShareLock( ) ;
|
|
~CShareLock( ) ;
|
|
|
|
//
|
|
// Check that the lock is correctly initialized - call only
|
|
// after construction !
|
|
//
|
|
BOOL
|
|
IsValid() ;
|
|
|
|
//
|
|
// Grab the lock for shared mode.
|
|
// each call to ShareLock() must be paired with exactly 1 call
|
|
// to ShareUnlock(). A thread that successfully calls ShareLock()
|
|
// can only call ShareUnlock(), otherwise a deadlock can occur.
|
|
// ( the sequence ShareLock(); ShareLock(); ShareUnlock(); ShareUnlock();
|
|
// can also deadlock)
|
|
//
|
|
void
|
|
ShareLock( ) ;
|
|
|
|
//
|
|
// Release the lock from shared mode !
|
|
//
|
|
void
|
|
ShareUnlock( ) ;
|
|
|
|
//
|
|
// Grab the lock for Exclusive mode.
|
|
// each call to ExclusiveLock() must be paired with exactly 1 call
|
|
// to ExclusiveUnlock(). A thread that successfully calls ExclusiveLock()
|
|
// can only call ExclusiveUnlock(), otherwise a deadlock can occur.
|
|
// ( the sequence ExclusiveLock(); ExclusiveLock(); ExclusiveUnlock(); ExclusiveUnlock();
|
|
// can also deadlock)
|
|
//
|
|
void
|
|
ExclusiveLock( ) ;
|
|
|
|
//
|
|
// Release the lock from Exclusive mode !
|
|
//
|
|
void
|
|
ExclusiveUnlock( ) ;
|
|
|
|
//
|
|
// Given that we've already acquired the lock Exclusively, convert to
|
|
// Shared. This cannot fail. ShareUnlock() must be called after
|
|
// we have done this.
|
|
//
|
|
void
|
|
ExclusiveToShared() ;
|
|
|
|
//
|
|
// Given that we've acquired the lock in shared mode try to get
|
|
// it exclusively.
|
|
// This can fail for two reasons :
|
|
// Another thread is trying to get the lock Exclusively
|
|
// A bunch of other threads are also in the lock in shared mode.
|
|
//
|
|
// The function will return FALSE if it fails, in which case the
|
|
// Shared lock is still held !
|
|
//
|
|
BOOL
|
|
SharedToExclusive() ;
|
|
|
|
//
|
|
// Try to acquire the lock in shared mode.
|
|
// This will only fail if an ExclusiveLock is held or being
|
|
// waited for.
|
|
// TRUE is returned if we get the lock Shared, FALSE otherwise !
|
|
//
|
|
BOOL
|
|
TryShareLock() ;
|
|
|
|
//
|
|
// Try to acquire the lock in Exclusive mode.
|
|
// This will fail if another thread is in the ExclusiveLock()
|
|
// or ShareLock's are held.
|
|
// TRUE is returned if we get the Exclusive Lock, FALSE otherwise !
|
|
//
|
|
BOOL
|
|
TryExclusiveLock() ;
|
|
|
|
|
|
//
|
|
// PartialLocks -
|
|
//
|
|
// Partial Locks are similar to Exclusive Locks - only one thread
|
|
// can successfully call PartialLock(), any other threads calling
|
|
// PartialLock() or ExclusiveLock() will block.
|
|
// HOWEVER - while a PartialLock() is held, Readers (threads calling
|
|
// ShareLock()) may enter the lock.
|
|
//
|
|
void PartialLock() ;
|
|
|
|
//
|
|
// Release the PartialLock - Other Exclusive() or Partial lock acquirers
|
|
// may now enter.
|
|
//
|
|
void PartialUnlock() ;
|
|
|
|
//
|
|
// Convert a Partial Lock to an Exclusive Lock. This function is
|
|
// guaranteed to succeed, HOWEVER a lock can only be converted with
|
|
// this function once, i.e. a thread doing
|
|
// PartialLock() ;
|
|
// FirstPartialToExclusive() ;
|
|
// ExclusiveToPartial() ;
|
|
// FirstPartialToExclusive() ;
|
|
// will have problems - the second call to FirstPartialToExclusive()
|
|
// may mess up the lock state and cause the lock to fail horribly.
|
|
// If a user wishes to convert as above they must have a call sequence like :
|
|
//
|
|
// PartialLock() ;
|
|
// FirstPartialToExclusive() or PartialToExclusive() ;
|
|
// ExclusiveToPartial() ;
|
|
// PartialToExclusive() ;
|
|
//
|
|
// If you change lock states more than once - you take your chances !
|
|
//
|
|
void FirstPartialToExclusive() ;
|
|
|
|
//
|
|
// Returns TRUE if we can get the lock Exclusively, otherwise
|
|
// we return FALSE with the lock remaining in the Partially held state.
|
|
//
|
|
// NOTE : This function will fail in CShareLockNH, but will always
|
|
// succeed for CShareLock() locks !
|
|
//
|
|
BOOL PartialToExclusive() ;
|
|
|
|
//
|
|
// We can always go from an ExclusiveLock() to a PartialLock() state.
|
|
//
|
|
void ExclusiveToPartial() ;
|
|
|
|
//
|
|
// We can always go from a PartialLock() state to a SharedLock() state
|
|
//
|
|
void PartialToShared() ;
|
|
|
|
//
|
|
// Returns TRUE if we can get the lock Partially !
|
|
// If it returns FALSE we remain with the lock held Shared()
|
|
//
|
|
BOOL SharedToPartial() ;
|
|
|
|
//
|
|
// Returns TRUE only if no other threads are trying to get the lock
|
|
// ExclusiveLy or Partially !
|
|
//
|
|
BOOL TryPartialLock() ;
|
|
|
|
} ;
|
|
|
|
|
|
|
|
class CSymLock {
|
|
/*++
|
|
|
|
Class Description :
|
|
|
|
This class implements a symmetric lock, where multiple threads can simultaneously
|
|
acquire the lock if they are all of the same group.
|
|
|
|
For instance, multiple threads can call Group1Lock() and all enter the lock.
|
|
Any thread successfully acquiring the lock through Group1Lock() blocks all threads
|
|
trying to acquire the lock through Group2Lock(), and vice versa.
|
|
|
|
--*/
|
|
private :
|
|
|
|
//
|
|
// Main lock point where all acquiring threads determine who gets the lock !
|
|
//
|
|
volatile long m_lock ;
|
|
|
|
//
|
|
// Two variables for the lock exit procotol - used to determine the last thread
|
|
// to leave the lock !
|
|
//
|
|
volatile long m_Departures ;
|
|
volatile long m_left ;
|
|
|
|
//
|
|
// Handles for blocking threads !
|
|
//
|
|
HANDLE m_hSema4Group1 ;
|
|
HANDLE m_hSema4Group2 ;
|
|
|
|
//
|
|
// Utility function - implements lock exit protocol when there
|
|
// is no contention for the lock !
|
|
//
|
|
BOOL
|
|
InterlockedDecWordAndMask( volatile long* plong,
|
|
long mask,
|
|
long decrement
|
|
) ;
|
|
|
|
//
|
|
// Utility functions - implement lock exit protocol for case where
|
|
// InterlockedDecWordAndMask determines that there is contention for the lock !
|
|
//
|
|
|
|
//
|
|
// How Group1 Leaves the lock under contention
|
|
//
|
|
BOOL
|
|
Group1Departures( long bump ) ;
|
|
|
|
//
|
|
// How Group2 Leaves the lock under contention
|
|
//
|
|
BOOL
|
|
Group2Departures( long bump ) ;
|
|
|
|
//
|
|
// You may not copy these objects - so the copy constructor is private !
|
|
//
|
|
CSymLock( CSymLock& ) ;
|
|
|
|
//
|
|
// You may not copy through assignment - so this operator is private !
|
|
//
|
|
CSymLock& operator=( CSymLock& ) ;
|
|
|
|
public :
|
|
|
|
//
|
|
// Construct and Destruct the asymetric lock !
|
|
//
|
|
CSymLock() ;
|
|
~CSymLock() ;
|
|
|
|
//
|
|
// Check that the lock is correctly initialized - call only
|
|
// after construction !
|
|
//
|
|
BOOL
|
|
IsValid() ;
|
|
|
|
//
|
|
// Grab the lock for a group1 thread.
|
|
// This function may not be called again on the same
|
|
// thread until Group1Unlock() is called.
|
|
// Group1Unlock() must be called exactly once for each
|
|
// Group1Lock() !
|
|
//
|
|
void
|
|
Group1Lock() ;
|
|
|
|
//
|
|
// Release the lock for a group1 thread.
|
|
//
|
|
void
|
|
Group1Unlock() ;
|
|
|
|
//
|
|
// Grab the lock for a group2 thread.
|
|
// This function may not be called again on the same
|
|
// thread until Group2Unlock() is called.
|
|
// Group2Unlock() must be called exactly once for each
|
|
// Group2Lock() !
|
|
//
|
|
void
|
|
Group2Lock() ;
|
|
|
|
//
|
|
// Release the lock for a Group2 thread !
|
|
//
|
|
void
|
|
Group2Unlock() ;
|
|
|
|
} ;
|
|
|
|
|
|
|
|
#endif
|