Windows2003-3790/inetcore/outlookexpress/inetcomm/exrwlck/exrwlck.cpp
2020-09-30 16:53:55 +02:00

170 lines
5.3 KiB
C++

//
// This file contains test implmentations of reader and writer locks.
// These are intended to be used with the template class in rw.h so that
// different implementations can be plugged in and tested.
//
// The semantics of the read/write classes should be as follows :
// Functions CAN NOT be recursively called,
// Multiple Readers should be able to enter the lock
// Only a single writer may execute at a time.
//
#ifndef WIN16
#include <windows.h>
#include <limits.h>
#define Assert(x) // Just define a dummy Assert, so we don't get
// compilation errors from exrwlck.h
#include "badstrfunctions.h"
#include "exrwlck.h"
#ifdef DEBUG
#ifndef _VALIDATE
#define _VALIDATE( f ) if( (f) ) ; else DebugBreak()
#endif
#else
#ifndef _VALIDATE
#define _VALIDATE( f )
#endif
#endif
long const BlockValue = (-LONG_MAX) / 2;
// Large in magnitude, negative value. Used to
// indicate a waiting writer in cReadLock
CExShareLock::CExShareLock( ) : cReadLock( 0 ), cOutRdrs( 0 ) {
InitializeCriticalSection( &critWriters ) ;
hWaitingWriters = CreateSemaphore( NULL, 0, 1, NULL ) ;
hWaitingReaders = CreateSemaphore( NULL, 0, LONG_MAX, NULL ) ;
}
CExShareLock::~CExShareLock( ) {
CloseHandle( hWaitingWriters ) ;
CloseHandle( hWaitingReaders ) ;
DeleteCriticalSection( &critWriters ) ;
}
void
CExShareLock::ShareLock( ) {
long sign = InterlockedIncrement( &cReadLock ) ;
if( sign > 0 ) {
return ;
} else {
// There must be a writer in the lock. Wait for him to leave.
// The InterlockedIncrement recorded our presence so that the writer
// can later release the correct number of threads.
WaitForSingleObject( hWaitingReaders, INFINITE ) ;
}
}
void
CExShareLock::ShareUnlock( ) {
//
// Leave the lock. The return value will be negative if there is a writer
// waiting.
BOOL fWriterWaiting = InterlockedDecrement( &cReadLock ) < 0 ;
if( fWriterWaiting ) {
//
// The following increment occurs when there is writer waiting, but
// readers own the lock. So although cReadLock is temporarily inaccurate
// about the number of readers waiting for the lock, it is not inaccurate
// when it matters in WriteUnlock (which assumes a writer owns the lock.)
//
long junk = InterlockedIncrement( &cReadLock ) ; // restore the value in cReadLock, so that we
// end up with an accurate count of readers waiting
// for entry.
long sign = InterlockedDecrement( &cOutRdrs ) ; // Make sure we don't lose track of the
// number for readers who have left the lock.
//
// Are we the last reader out of the lock ?
//
if( sign == 0 ) {
//
// Definately the last reader out !
//
ReleaseSemaphore( hWaitingWriters, 1, &junk ) ;
}
}
}
void
CExShareLock::ExclusiveLock( ) {
// Only one writer allowed to try for the lock at a time.
//
EnterCriticalSection( &critWriters ) ;
//
// Need to track the number of readers who leave the lock while we
// are trying to grab it.
//
cOutRdrs = 0 ;
// Grab the lock
long oldsign = InterlockedExchange( &cReadLock, BlockValue ) ;
// How many readers left while we grabbed the lock ??
long oldval = InterlockedExchange( &cOutRdrs, oldsign ) ;
//
// Accurately track all the readers who left the lock.
//
long cursign = 1 ; // Initialize to 1 so that if while loop not executed
// following if statement works correctly.
while( oldval++ )
cursign = InterlockedDecrement( &cOutRdrs ) ;
//
// Do we own the lock ? Only if there were no readers, or they have all left already.
//
if( oldsign == 0 || cursign == 0 ) {
// We have the lock
} else {
// Wait for a reader to signal us.
WaitForSingleObject( hWaitingWriters, INFINITE ) ;
}
}
void
CExShareLock::ExclusiveUnlock( ) {
// Estimate how many readers are waiting for the lock
long cWaiting = cReadLock - BlockValue ;
// This Exchange allows any readers who have just arrived to grab the lock.
// Also, it accounts for cWaiting of the blocked readers.
long cNewWaiting = InterlockedExchange( &cReadLock, cWaiting ) - BlockValue ;
// cNewWaiting is the EXACT number of blocked readers - we will increment cReadLock
// until we have accounted for the difference between our estimate and the correct
// number !
long cTotal = cNewWaiting ; // Save cNewWaiting for later use
while( cNewWaiting-- > cWaiting )
InterlockedIncrement( &cReadLock ) ;
if( cTotal > 0 ) {
long junk = 0 ;
ReleaseSemaphore( hWaitingReaders, cTotal, &junk ) ; // let all those readers go!
}
// Let the next writer take his shot at the lock!
LeaveCriticalSection( &critWriters ) ;
}
BOOL
CExShareLock::SharedToExclusive( ) {
// tbd - implement this!
return( FALSE ) ;
}
#endif