415 lines
7.9 KiB
C
415 lines
7.9 KiB
C
|
/*++
|
||
|
|
||
|
rwnew.h
|
||
|
|
||
|
This file defines several variations of Reader/Writer locks
|
||
|
with different properties regarding handles used, and other
|
||
|
implementation details.
|
||
|
|
||
|
Also defined are some variations of CRITICAL_SECTIONS which use
|
||
|
fewer or no handles.
|
||
|
|
||
|
--*/
|
||
|
|
||
|
|
||
|
|
||
|
#ifndef _RWNEW_H
|
||
|
#define _RWNEW_H
|
||
|
|
||
|
#ifdef _RW_IMPLEMENTATION_
|
||
|
#define _RW_INTERFACE_ __declspec( dllexport )
|
||
|
#else
|
||
|
#define _RW_INTERFACE_ __declspec( dllimport )
|
||
|
#endif
|
||
|
|
||
|
#pragma warning( disable:4251 )
|
||
|
|
||
|
#include <limits.h>
|
||
|
#include "lockq.h"
|
||
|
#include "rwintrnl.h"
|
||
|
|
||
|
|
||
|
class _RW_INTERFACE_ CCritSection {
|
||
|
private :
|
||
|
|
||
|
//
|
||
|
// Handle of thread owning the lock !
|
||
|
//
|
||
|
HANDLE m_hOwner ;
|
||
|
|
||
|
//
|
||
|
// Count of Recursive Calls
|
||
|
//
|
||
|
long m_RecursionCount ;
|
||
|
|
||
|
//
|
||
|
// Count used to see who gets the lock next !
|
||
|
//
|
||
|
long m_lock ;
|
||
|
|
||
|
//
|
||
|
// Queue of waiting threads
|
||
|
//
|
||
|
CSingleReleaseQueue m_queue ;
|
||
|
|
||
|
//
|
||
|
// Copying of these objects is not allowed !!!!
|
||
|
//
|
||
|
CCritSection( CCritSection& ) ;
|
||
|
CCritSection& operator=( CCritSection& ) ;
|
||
|
|
||
|
public :
|
||
|
|
||
|
#ifdef DEBUG
|
||
|
DWORD m_dwThreadOwner ;
|
||
|
#endif
|
||
|
|
||
|
//
|
||
|
// Construct a critical section object
|
||
|
//
|
||
|
CCritSection( ) :
|
||
|
m_queue( FALSE ),
|
||
|
m_hOwner( INVALID_HANDLE_VALUE ),
|
||
|
m_RecursionCount( 0 ),
|
||
|
m_lock( -1 ) {
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Acquire the critical section
|
||
|
//
|
||
|
void
|
||
|
Enter(
|
||
|
CWaitingThread& myself
|
||
|
) ;
|
||
|
|
||
|
//
|
||
|
// Another version which acquires the critical section -
|
||
|
// creates its own CWaitingThread object !
|
||
|
//
|
||
|
void
|
||
|
Enter() ;
|
||
|
|
||
|
//
|
||
|
// REturns TRUE if the lock is available right now !
|
||
|
//
|
||
|
BOOL
|
||
|
TryEnter(
|
||
|
CWaitingThread& myself
|
||
|
) ;
|
||
|
|
||
|
//
|
||
|
// Returns TRUE if we can get the lock right now !
|
||
|
//
|
||
|
BOOL
|
||
|
TryEnter() {
|
||
|
CWaitingThread myself ;
|
||
|
return TryEnter( myself ) ;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Release the critical section !
|
||
|
//
|
||
|
void
|
||
|
Leave() ;
|
||
|
|
||
|
|
||
|
} ;
|
||
|
|
||
|
|
||
|
//
|
||
|
// This version of critical section is more like an event - doesn't
|
||
|
// care who releases locks - and doesn't handle recursive grabs !
|
||
|
//
|
||
|
class _RW_INTERFACE_ CSimpleCritSection {
|
||
|
private :
|
||
|
|
||
|
//
|
||
|
// Count used to see who gets the lock next !
|
||
|
//
|
||
|
long m_lock ;
|
||
|
|
||
|
//
|
||
|
// Queue of waiting threads
|
||
|
//
|
||
|
CSingleReleaseQueue m_queue ;
|
||
|
|
||
|
//
|
||
|
// Copying of these objects is not allowed !!!!
|
||
|
//
|
||
|
CSimpleCritSection( CCritSection& ) ;
|
||
|
CSimpleCritSection& operator=( CCritSection& ) ;
|
||
|
|
||
|
public :
|
||
|
|
||
|
#ifdef DEBUG
|
||
|
DWORD m_dwThreadOwner ;
|
||
|
#endif
|
||
|
|
||
|
//
|
||
|
// Construct a critical section object
|
||
|
//
|
||
|
CSimpleCritSection( ) :
|
||
|
m_queue( FALSE ),
|
||
|
#ifdef DEBUG
|
||
|
m_dwThreadOwner( 0 ),
|
||
|
#endif
|
||
|
m_lock( -1 ) {
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Acquire the critical section
|
||
|
//
|
||
|
void
|
||
|
Enter(
|
||
|
CWaitingThread& myself
|
||
|
) ;
|
||
|
|
||
|
//
|
||
|
// Another version which acquires the critical section -
|
||
|
// creates its own CWaitingThread object !
|
||
|
//
|
||
|
void
|
||
|
Enter() ;
|
||
|
|
||
|
//
|
||
|
// REturns TRUE if the lock is available right now !
|
||
|
//
|
||
|
BOOL
|
||
|
TryEnter(
|
||
|
CWaitingThread& myself
|
||
|
) ;
|
||
|
|
||
|
//
|
||
|
// Returns TRUE if we can get the lock right now !
|
||
|
//
|
||
|
BOOL
|
||
|
TryEnter() {
|
||
|
CWaitingThread myself ;
|
||
|
return TryEnter( myself ) ;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Release the critical section !
|
||
|
//
|
||
|
void
|
||
|
Leave() ;
|
||
|
|
||
|
} ;
|
||
|
|
||
|
//
|
||
|
// Another class which tries to create Reader/Write locks with
|
||
|
// no handles !!
|
||
|
//
|
||
|
|
||
|
class _RW_INTERFACE_ CShareLockNH {
|
||
|
private :
|
||
|
|
||
|
//
|
||
|
// Lock grabbed by writers to have exclusive access
|
||
|
//
|
||
|
CSimpleCritSection m_lock ;
|
||
|
|
||
|
//
|
||
|
// Number of readers who have grabbed the Read Lock -
|
||
|
// Negative if a writer is waiting !
|
||
|
//
|
||
|
volatile long m_cReadLock ;
|
||
|
|
||
|
//
|
||
|
// Number of Readers who have left the lock since a
|
||
|
// writer tried to grab it !
|
||
|
//
|
||
|
volatile long m_cOutReaders ;
|
||
|
|
||
|
//
|
||
|
// Number of readers who are entering the lock after
|
||
|
// being blocked !!!
|
||
|
//
|
||
|
volatile long m_cOutAcquiringReaders ;
|
||
|
|
||
|
//
|
||
|
// Number of threads needing m_lock to be held right now !
|
||
|
//
|
||
|
volatile long m_cExclusiveRefs ;
|
||
|
|
||
|
//
|
||
|
// Handle that all the readers who are waiting try to grab !
|
||
|
//
|
||
|
volatile HANDLE m_hWaitingReaders ;
|
||
|
|
||
|
//
|
||
|
// Handle that the single writer waiting for the lock is trying
|
||
|
// to grab !
|
||
|
//
|
||
|
volatile HANDLE m_hWaitingWriters ;
|
||
|
|
||
|
void inline
|
||
|
WakeReaders() ;
|
||
|
|
||
|
//
|
||
|
// The internal work of ShareLock - does a lot more of the stuff required
|
||
|
// when a writer is present !!!
|
||
|
//
|
||
|
void
|
||
|
ShareLockInternal() ;
|
||
|
|
||
|
//
|
||
|
// The internal work of ShareLock - does a lot more of the stuff required
|
||
|
// when a writer is present !!!
|
||
|
//
|
||
|
void
|
||
|
ShareUnlockInternal() ;
|
||
|
|
||
|
|
||
|
//
|
||
|
// You may not copy these objects - so this lock is private !
|
||
|
//
|
||
|
CShareLockNH( CShareLockNH& ) ;
|
||
|
|
||
|
//
|
||
|
// You may not copy through assignment - so this operator is private !
|
||
|
//
|
||
|
CShareLockNH& operator=( CShareLockNH& ) ;
|
||
|
|
||
|
public :
|
||
|
|
||
|
//
|
||
|
// Construction of CShareLockNH() objects always succeeds and there
|
||
|
// are no error cases !
|
||
|
//
|
||
|
CShareLockNH() ;
|
||
|
|
||
|
//
|
||
|
// Grab the lock Shared - other threads may pass through ShareLock() as well
|
||
|
//
|
||
|
void ShareLock() ;
|
||
|
|
||
|
//
|
||
|
// Releases the lock - if we are the last reader to leave writers may
|
||
|
// start to enter the lock !
|
||
|
//
|
||
|
void ShareUnlock() ;
|
||
|
|
||
|
//
|
||
|
// Grab the lock Exclusively - no other readers or writers may enter !!
|
||
|
//
|
||
|
void ExclusiveLock() ;
|
||
|
|
||
|
//
|
||
|
// Release the Exclusive Locks - if there are readers waiting they
|
||
|
// will enter before other waiting writers !
|
||
|
//
|
||
|
void ExclusiveUnlock() ;
|
||
|
|
||
|
//
|
||
|
// Convert an ExclusiveLock to a Shared - this cannot fail !
|
||
|
//
|
||
|
void ExclusiveToShared() ;
|
||
|
|
||
|
//
|
||
|
// Convert a Shared Lock to an Exclusive one - this can fail - returns
|
||
|
// TRUE if successfull !
|
||
|
//
|
||
|
BOOL SharedToExclusive() ;
|
||
|
|
||
|
//
|
||
|
// Return TRUE if we can get the lock shared. Only fails when
|
||
|
// somebody is attempting or has the lock exclusively, we will enter
|
||
|
// if there are only other readers in the lock.
|
||
|
//
|
||
|
BOOL TryShareLock() ;
|
||
|
|
||
|
//
|
||
|
// Return TRUE if we can get the lock Exclusively. Only succeeds
|
||
|
// when nobody else is near the lock.
|
||
|
//
|
||
|
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 : NYI in CShareLockNH - will always return FALSE !
|
||
|
//
|
||
|
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() ;
|
||
|
|
||
|
} ;
|
||
|
|
||
|
//
|
||
|
// This is a utility function to get an Event Handle we save
|
||
|
// for each thread - the handle is Created as:
|
||
|
// CreateEvent( NULL,
|
||
|
// FALSE,
|
||
|
// FALSE,
|
||
|
// NULL ) ;
|
||
|
// This results in an auto-reset event which goes back to non signalled whenever a thread
|
||
|
// is released.
|
||
|
//
|
||
|
HANDLE
|
||
|
_RW_INTERFACE_
|
||
|
GetPerThreadEvent() ;
|
||
|
|
||
|
|
||
|
|
||
|
#endif // _RWNEW_H_
|