/* ---------------------------------------------------------------------------- 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: Guarantees isolated increments of *pl. Usage: Use instead of InterlockedIncrement for Win16/Win32 portability. @rev 0 | 3/21/95 | Rcraig | Created. ---------------------------------------------------------------------------- */ inline LONG SafeIncrement ( LPLONG pl ) { return (InterlockedIncrement (pl)); } // SafeIncrement /* ---------------------------------------------------------------------------- @func Description: Win16/Win32 abstraction wrapper: Guarantees isolated decrements of *pl. Usage: Use instead of InterlockedDecrement for Win16/Win32 portability. @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 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 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 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 // , 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 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 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 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 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 // , 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 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__