home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Tricks of the Windows Gam…ming Gurus (2nd Edition)
/
Disc2.iso
/
vc98
/
include
/
utsem.h
< prev
next >
Wrap
C/C++ Source or Header
|
1998-04-25
|
24KB
|
859 lines
/* ----------------------------------------------------------------------------
Microsoft D.T.C (Distributed Transaction Coordinator)
(c) 1995 Microsoft Corporation. All Rights Reserved
@doc
@module UTSem.H |
SYNCHRO - C++ class to provide synchronization object encapsulation
This implementation of the SYNCHRO class provides multi-read XOR single-write
(a.k.a. shared vs. exclusive) lock capabilities, with protection against
writer starvation. Code origianly borrowed from
(c) 1994 by Daniel S. Glasser and then modified for our needs.
@devnote None
@rev 3 | 1st-Aug-1996 | GaganC | Added the spin lock code and class
@rev 2 | 31-May-1996 | GaganC | Removed the special code for x86
@rev 1 | 18th Jan, 96 | GaganC | Special cased UTGuard for X86
@rev 0 | 4th Feb,95 | GaganC | Created
---------------------------------------------------------------------------- */
#ifndef __UTSEM_H__
#define __UTSEM_H__
// -------------------------------------------------------------
// INCLUDES
// -------------------------------------------------------------
#include "UTAssert.H"
// -------------------------------------------------------------
// CONSTANTS AND TYPES
// -------------------------------------------------------------
typedef enum {SLC_WRITE, SLC_READWRITE, SLC_READWRITEPROMOTE}
SYNCH_LOCK_CAPS;
typedef enum {SLT_READ, SLT_READPROMOTE, SLT_WRITE}
SYNCH_LOCK_TYPE;
const int NUM_SYNCH_LOCK_TYPES = SLT_WRITE + 1;
// -------------------------------------------------------------
// FORWARDS
// -------------------------------------------------------------
class CSemExclusive;
class CSemExclusiveSL;
class UTGuard;
class UTSemReadWrite;
class UTSemCheapReadWrite;
// -------------------------------------------------------------
// GLOBAL HELPER FUNCTIONS
// -------------------------------------------------------------
/* ----------------------------------------------------------------------------
@func Description:<nl>
Guarantees isolated increments of *pl.<nl><nl>
Usage:<nl>
Use instead of InterlockedIncrement for Win16/Win32 portability.<nl><nl>
@rev 0 | 3/21/95 | Rcraig | Created.
---------------------------------------------------------------------------- */
inline LONG SafeIncrement ( LPLONG pl )
{
return (InterlockedIncrement (pl));
} // SafeIncrement
/* ----------------------------------------------------------------------------
@func Description:<nl>
Win16/Win32 abstraction wrapper:
Guarantees isolated decrements of *pl.<nl><nl>
Usage:<nl>
Use instead of InterlockedDecrement for Win16/Win32 portability.<nl><nl>
@rev 0 | 3/21/95 | Rcraig | Created.
---------------------------------------------------------------------------- */
inline LONG SafeDecrement ( LPLONG pl )
{
return (InterlockedDecrement (pl));
} // SafeDecrement
/* ----------------------------------------------------------------------------
@class UTGuard
This object represents a guard that can be acquired or released. The
advantage with useing this instead of a critical section is that this
is non blocking. If AcquireGuard fails, it will return false and will not
block.
@rev 0 | 4th Feb,95 | GaganC | Created
---------------------------------------------------------------------------- */
class UTGuard
{
private:
long m_lVal;
public:
//@cmember Constructor
UTGuard (void);
//@cmember Destructor
~UTGuard (void);
//@cmember Acquires the guard
BOOL AcquireGuard (void);
//@cmember Releases the guard
void ReleaseGuard (void);
//@cmember Initializes the Guard
void Init (void);
} ; //End class UTGuard
//-----------------------------------------------------------------------------
//
// IMPLEMENTATION of class UTGuard
//
//-----------------------------------------------------------------------------
/* ----------------------------------------------------------------------------
@mfunc
---------------------------------------------------------------------------- */
inline UTGuard::UTGuard ( void )
{
m_lVal = 0;
}
/* ----------------------------------------------------------------------------
@mfunc
---------------------------------------------------------------------------- */
inline UTGuard::~UTGuard ( void )
{
//Do nothing
}
/* ----------------------------------------------------------------------------
@mfunc
---------------------------------------------------------------------------- */
inline BOOL UTGuard::AcquireGuard (void)
{
long lVal;
lVal = InterlockedExchange (&m_lVal, 1);
return (lVal == 0);
}
/* ----------------------------------------------------------------------------
@mfunc
---------------------------------------------------------------------------- */
inline void UTGuard::ReleaseGuard (void)
{
m_lVal = 0;
}
/* ----------------------------------------------------------------------------
@mfunc
---------------------------------------------------------------------------- */
void inline UTGuard::Init ( void )
{
m_lVal = 0;
}
/* ----------------------------------------------------------------------------
@class UTSemReadWrite
@rev 0 | 4th Feb,95 | GaganC | Created
---------------------------------------------------------------------------- */
class UTSemReadWrite
{
private:
CRITICAL_SECTION csExclusive; // Critical section object to synchronize writers
CRITICAL_SECTION csReader; // Critical section object to synchronize readers
HANDLE hevReadDone; // Manual-reset event to notify writers of
//reader completion
int cReaders; // Count of current readers
BOOL fInitSucceeded; // TRUE if the constructor function succeeded
public:
UTSemReadWrite(void); // Constructor
~UTSemReadWrite(void); // Destructor
// This implementation supports Read and Write locks
SYNCH_LOCK_CAPS GetCaps(void) { return SLC_READWRITE; };
// This object is valid if it was initialized
BOOL IsValid(void) { return fInitSucceeded; }
BOOL Lock(SYNCH_LOCK_TYPE); // Lock the object
BOOL UnLock(SYNCH_LOCK_TYPE); // Unlock the object
};
//-----------------------------------------------------------------------------
//
// IMPLEMENTATION of class UTSemReadWrite
//
//-----------------------------------------------------------------------------
/* ----------------------------------------------------------------------------
@mfunc
SYNCHRO class - Constructor
Create the event, initialize the critical section objects and reader count,
and return.
---------------------------------------------------------------------------- */
inline UTSemReadWrite::UTSemReadWrite(void)
{
// Create the manual-reset event (the only init that can fail)
hevReadDone = CreateEvent(NULL, TRUE, TRUE, NULL);
fInitSucceeded = hevReadDone != NULL;
// If we created the event, proceed with the risk-free initialization
if (fInitSucceeded)
{
cReaders = 0;
InitializeCriticalSection(&csExclusive);
InitializeCriticalSection(&csReader);
}
return;
}
/* ----------------------------------------------------------------------------
@mfunc
SYNCHRO class - Destructor
Free the event, delete the critical section objects, and return.
---------------------------------------------------------------------------- */
inline UTSemReadWrite::~UTSemReadWrite(void)
{
if (IsValid())
{
CloseHandle(hevReadDone);
DeleteCriticalSection(&csExclusive);
DeleteCriticalSection(&csReader);
}
return;
}
/* ----------------------------------------------------------------------------
@mfunc
SYNCHRO class - Lock member function
---------------------------------------------------------------------------- */
inline BOOL UTSemReadWrite::Lock(SYNCH_LOCK_TYPE lt)
{
// Verify that the object is valid
if (! IsValid())
return FALSE;
// Verify that the specified lock type is supported by this implementation
if (lt == SLT_READPROMOTE)
return FALSE;
// Claim the read lock or write lock as specified
if (lt == SLT_READ)
{
// Claim the <csExclusive> critical section. This call blocks if there's
// an active writer or if there's a writer waiting for active readers to
// complete.
EnterCriticalSection(&csExclusive);
// Claim access to the reader count. If this blocks, it's only for the
// briefest moment, while other threads go through to increment or
// decrement the reader count.
EnterCriticalSection(&csReader);
// Increment the reader count. If this is the first reader, we reset the
// hevReadDone event so that the next writer blocks.
if (cReaders++ == 0)
ResetEvent(hevReadDone);
// Release access to the reader count
LeaveCriticalSection(&csReader);
// Release access to the <csExclusive> critical section. This enables
// other readers to come through and the next writer to wait for active
// readers to complete (which in turn prevents new readers from entering).
LeaveCriticalSection(&csExclusive);
}
else
{
// Verify that since this isn't the read lock, that it's the write lock
Assert(lt == SLT_WRITE);
// Claim the <csExclusive> critical section. This not only prevents other
// threads from claiming the write lock, but also prevents any new threads
// from claiming the read lock.
EnterCriticalSection(&csExclusive);
// Wait for the active readers to release their read locks.
return WaitForSingleObject(hevReadDone, INFINITE) == WAIT_OBJECT_0;
}
return TRUE;
} //End Lock
/* ----------------------------------------------------------------------------
@mfunc
SYNCHRO class - Unlock member function
---------------------------------------------------------------------------- */
inline BOOL UTSemReadWrite::UnLock(SYNCH_LOCK_TYPE lt)
{
// Verify that the object is valid
if (! IsValid())
return FALSE;
// Verify that the specified lock type is supported by this implementation
if (lt == SLT_READPROMOTE)
return FALSE;
// Release the read lock or write lock as specified
if (lt == SLT_READ)
{
// Claim access to the reader count. If this blocks, it's only for the
// briefest moment, while other threads go through to increment or
// decrement the reader count.
EnterCriticalSection(&csReader);
// Decrement the reader count. If this is the last reader, set
// <hevReadDone>, which allows the first waiting writer to proceed.
if (--cReaders == 0)
SetEvent(hevReadDone);
// Release access to the reader count
LeaveCriticalSection(&csReader);
}
else
{
// Verify that since this isn't the read lock, that it's the write lock
Assert(lt == SLT_WRITE);
// Make <csExclusive> available to one other writer or to the first reader
LeaveCriticalSection(&csExclusive);
}
return TRUE;
} //End Unlock
/* ----------------------------------------------------------------------------
@class UTSemCheapReadWrite
@rev 0 | 5/1/97 | Shaiwals | Created using UTSemReadWrite, reader
| | | access to the lock is cheaper if
| | | there is already a reader present
---------------------------------------------------------------------------- */
class UTSemCheapReadWrite
{
private:
CRITICAL_SECTION csExclusive; // Critical section object to provide core locking
CRITICAL_SECTION csReader; // Critical section object to synchronize readers
CRITICAL_SECTION csWriter; // Critical section object to synchronize writers
HANDLE hevReadDone; // Manual-reset event to notify writers of
// reader completion
HANDLE hevReaderPresent; // Readers are currently present
HANDLE hevWriterPresent; // Writers are currently present
long cReaders; // Count of current readers
long cWriters; // Count of writers waiting
BOOL fInitSucceeded; // TRUE if the constructor function succeeded
BOOL m_fReader; // Flag to signal that readers presence has
// been notifed
public:
UTSemCheapReadWrite(void); // Constructor
~UTSemCheapReadWrite(void); // Destructor
// This implementation supports Read and Write locks
SYNCH_LOCK_CAPS GetCaps(void) { return SLC_READWRITE; };
// This object is valid if it was initialized
BOOL IsValid(void) { return fInitSucceeded; }
BOOL Lock(SYNCH_LOCK_TYPE); // Lock the object
BOOL UnLock(SYNCH_LOCK_TYPE); // Unlock the object
};
//-----------------------------------------------------------------------------
//
// IMPLEMENTATION of class UTSemCheapReadWrite
//
//-----------------------------------------------------------------------------
/* ----------------------------------------------------------------------------
@mfunc
SYNCHRO class - Constructor
Create the event, initialize the critical section objects and reader count,
and return.
---------------------------------------------------------------------------- */
inline UTSemCheapReadWrite::UTSemCheapReadWrite(void)
{
// Create the manual-reset event (the only init that can fail)
hevReadDone = CreateEvent(NULL, TRUE, TRUE, NULL);
hevReaderPresent = CreateEvent(NULL, TRUE, FALSE, NULL);
hevWriterPresent = CreateEvent(NULL, TRUE, FALSE, NULL);
fInitSucceeded = (hevReadDone != NULL) &&
(hevReaderPresent != NULL) &&
(hevWriterPresent != NULL);
// If we created the event, proceed with the risk-free initialization
if (fInitSucceeded)
{
cReaders = -1;
cWriters = -1;
m_fReader = FALSE;
InitializeCriticalSection(&csExclusive);
InitializeCriticalSection(&csReader);
InitializeCriticalSection(&csWriter);
}
return;
}
/* ----------------------------------------------------------------------------
@mfunc
SYNCHRO class - Destructor
Free the event, delete the critical section objects, and return.
---------------------------------------------------------------------------- */
inline UTSemCheapReadWrite::~UTSemCheapReadWrite(void)
{
if (IsValid())
{
CloseHandle(hevReadDone);
DeleteCriticalSection(&csExclusive);
DeleteCriticalSection(&csReader);
}
return;
}
/* ----------------------------------------------------------------------------
@mfunc
SYNCHRO class - Lock member function
---------------------------------------------------------------------------- */
inline BOOL UTSemCheapReadWrite::Lock(SYNCH_LOCK_TYPE lt)
{
// Verify that the object is valid
if (! IsValid())
return FALSE;
// Verify that the specified lock type is supported by this implementation
if (lt == SLT_READPROMOTE)
return FALSE;
// Claim the read lock or write lock as specified
if (lt == SLT_READ)
{
// First try to get the reader lock cheaply by incrementing
// the reader count. Only if reader count is positive
// do we try and test that a reader is present
if (InterlockedIncrement (&cReaders) > 0)
{
// Test that there is actually a reader holding on the the lock.
// It is possible to that the reader count is greater that -1
// but still there are no readers who have actually acquired the
// lock
if (WaitForSingleObject (hevReaderPresent, 0) == WAIT_OBJECT_0)
{
// Only if there are no writers waiting to acquire the lock
// do we try to acquire the lock. Without this we cannot
// guarantee that the writer wont starve.
if (WaitForSingleObject (hevWriterPresent, 0) == WAIT_TIMEOUT)
return TRUE;
}
}
// Decrement extra reader count
InterlockedDecrement (&cReaders);
// Claim the <csExclusive> critical section. This call blocks if there's
// an active writer or if there's a writer waiting for active readers to
// complete.
EnterCriticalSection(&csExclusive);
// Claim access to the reader count. If this blocks, it's only for the
// briefest moment, while other threads go through to increment or
// decrement the reader count.
EnterCriticalSection(&csReader);
// Increment the reader count. If this is the first reader, we reset the
// hevReadDone event so that the next writer blocks.
if (InterlockedIncrement (&cReaders) >= 0)
{
if (!m_fReader)
{
SetEvent (hevReaderPresent);
ResetEvent(hevReadDone);
m_fReader = TRUE;
}
}
// Release access to the reader count
LeaveCriticalSection(&csReader);
// Release access to the <csExclusive> critical section. This enables
// other readers to come through and the next writer to wait for active
// readers to complete (which in turn prevents new readers from entering).
LeaveCriticalSection(&csExclusive);
}
else
{
// Verify that since this isn't the read lock, that it's the write lock
Assert(lt == SLT_WRITE);
// Gain access to the writer count
EnterCriticalSection(&csWriter);
// Increment the writer count. If this is the writer reader, we set the
// hevWriterPresent event so that new readers give way to the writer.
if (InterlockedIncrement (&cWriters) == 0)
{
SetEvent (hevWriterPresent);
}
// Release access to the writer count
LeaveCriticalSection(&csWriter);
// Claim the <csExclusive> critical section. This not only prevents other
// threads from claiming the write lock, but also prevents any new threads
// from claiming the read lock.
EnterCriticalSection(&csExclusive);
// Wait for the active readers to release their read locks.
return WaitForSingleObject(hevReadDone, INFINITE) == WAIT_OBJECT_0;
}
return TRUE;
} //End Lock
/* ----------------------------------------------------------------------------
@mfunc
SYNCHRO class - Unlock member function
---------------------------------------------------------------------------- */
inline BOOL UTSemCheapReadWrite::UnLock(SYNCH_LOCK_TYPE lt)
{
// Verify that the object is valid
if (! IsValid())
return FALSE;
// Verify that the specified lock type is supported by this implementation
if (lt == SLT_READPROMOTE)
return FALSE;
// Release the read lock or write lock as specified
if (lt == SLT_READ)
{
// Claim access to the reader count. If this blocks, it's only for the
// briefest moment, while other threads go through to increment or
// decrement the reader count.
EnterCriticalSection(&csReader);
// Decrement the reader count. If this is the last reader, set
// <hevReadDone>, which allows the first waiting writer to proceed.
if (InterlockedDecrement (&cReaders) < 0)
{
ResetEvent (hevReaderPresent);
SetEvent(hevReadDone);
m_fReader = FALSE;
}
// Release access to the reader count
LeaveCriticalSection(&csReader);
}
else
{
// Verify that since this isn't the read lock, that it's the write lock
Assert(lt == SLT_WRITE);
// Gain access to the writer count
EnterCriticalSection(&csWriter);
// Decrement the writer count. If this is the last writer, we reset the
// hevWriterPresent event.
if (InterlockedDecrement (&cWriters) < 0)
{
ResetEvent (hevWriterPresent);
}
// Release access to the writer count
LeaveCriticalSection(&csWriter);
// Make <csExclusive> available to one other writer or to the first reader
LeaveCriticalSection(&csExclusive);
}
return TRUE;
} //End Unlock
/* ----------------------------------------------------------------------------
@class CSemExclusive:
@rev 0 | 4th Feb,95 | GaganC | Created
---------------------------------------------------------------------------- */
class CSemExclusive
{
public:
CSemExclusive (void);
~CSemExclusive (void);
void Lock (void);
void UnLock (void);
private:
CRITICAL_SECTION m_csx;
}; //end class CSemExclusive
//-----------------------------------------------------------------------------
//
// IMPLEMENTATION of class CSemExclusive
//
//-----------------------------------------------------------------------------
/* ----------------------------------------------------------------------------
@mfunc
---------------------------------------------------------------------------- */
inline CSemExclusive::CSemExclusive
(
void
)
{
InitializeCriticalSection (&m_csx);
}
/* ----------------------------------------------------------------------------
@mfunc
---------------------------------------------------------------------------- */
inline CSemExclusive::~CSemExclusive
(
void
)
{
DeleteCriticalSection (&m_csx);
}
/* ----------------------------------------------------------------------------
@mfunc
---------------------------------------------------------------------------- */
inline void CSemExclusive::Lock
(
void
)
{
EnterCriticalSection (&m_csx);
}
/* ----------------------------------------------------------------------------
@mfunc
---------------------------------------------------------------------------- */
inline void CSemExclusive::UnLock
(
void
)
{
LeaveCriticalSection (&m_csx);
}
/* ----------------------------------------------------------------------------
@class CSemExclusiveSL:
---------------------------------------------------------------------------- */
class CSemExclusiveSL
{
public:
CSemExclusiveSL (void);
~CSemExclusiveSL (void);
void Lock (void);
void UnLock (void);
private:
DWORD volatile m_dwLock;
DWORD volatile *m_pdwLock;
DWORD volatile m_dwOwningThread;
ULONG volatile m_ulRecursionCount;
};
/* ----------------------------------------------------------------------------
@mfunc
---------------------------------------------------------------------------- */
inline CSemExclusiveSL::CSemExclusiveSL ( void )
{
m_dwOwningThread = 0;
m_ulRecursionCount = 0;
m_dwLock = 0;
m_pdwLock = &m_dwLock;
}
/* ----------------------------------------------------------------------------
@mfunc
---------------------------------------------------------------------------- */
inline CSemExclusiveSL::~CSemExclusiveSL ( void )
{
//Nothing to do
}
/* ----------------------------------------------------------------------------
@mfunc
** Lock -- Obtains an SMP safe lock on the address given.
** WARNING: Does not release any semaphore or critsec when waiting.
**
@rev 0 Created 04/20/93 by LaleD
@rev 1 modified 7/13/96 by shaiwals
---------------------------------------------------------------------------- */
inline void CSemExclusiveSL::Lock ( void )
{
DWORD i;
DWORD n = 0;
int m = 0;
startover:
if (InterlockedExchange((long *)m_pdwLock, 1) == 0)
{
m_dwOwningThread = GetCurrentThreadId();
return;
}
if (m_dwOwningThread == GetCurrentThreadId())
{
m_ulRecursionCount++;
return;
}
// Give away my time slice to another thread as the probability
// of my getting this lock right away are low - shaiwals
//Sleep (1);
/* retry using safe test only after cheaper, unsafe test succeeds */
for (i = 0 ; i < (DWORD)(10000) ; i++)
{
if (*m_pdwLock == 0)
{
goto startover;
}
}
/*
** Yield after hitting the cspinctr limit
** Sleep(0) only yields to threads of same priority
** if hit limit 10000 times, sleep 5sec and test for kill
** this provides a chance to CTRL-C it if stuck in loop
*/
m++;
if( ( m % 10000 ) == 0)
{
Sleep(5000);
}
else
{
Sleep(0);
}
goto startover;
/* try again */
}
/* ----------------------------------------------------------------------------
@mfunc
---------------------------------------------------------------------------- */
inline void CSemExclusiveSL::UnLock ( void )
{
AssertSz (m_dwOwningThread == GetCurrentThreadId(), \
"Lock released by someone who doesnt own the lock!!!");
if (m_ulRecursionCount > 0)
{
m_ulRecursionCount--;
return;
}
m_dwOwningThread = 0;
*m_pdwLock = 0;
}
#endif __UTSEM_H__