Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

416 lines
10 KiB

// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//
// SYNCHRO.CPP
//
// Copyright 1986-1997 Microsoft Corporation, All Rights Reserved
//
#include "_synchro.h"
#include <ex\synchro.h>
// ========================================================================
//
// CLASS CMRWLock
//
// EnterRead()/LeaveRead() respectively lets a reader enter or leave
// the lock. If there is a writer or promotable reader in the lock,
// entry is delayed until the writer/promotable reader leaves.
//
// EnterWrite()/LeaveWrite() respectively lets a single writer enter
// or leave the lock. If there are any readers in the lock, entry
// is delayed until they leave. If there is another writer or a
// promotable reader in the lock, entry is delayed until it leaves.
//
// EnterPromote()/LeavePromote() respectively lets a single promotable
// reader enter or leave the lock. If there is a writer or another
// promotable reader in the lock, entry is delayed until the
// writer/promotable reader leaves. Otherwise entry is immediate,
// even if there are other (non-promotable) readers in the lock.
// Promote() promotes the promotable reader to a writer. If there
// are readers in the lock, promotion is delayed until they leave.
//
// Once a writer or promotable reader has entered the lock, it may
// reenter the lock as a reader, writer or promotable reader without
// delay. A reader cannot reenter the lock as a writer or promotable.
//
// ------------------------------------------------------------------------
//
// CMRWLock::CMRWLock()
//
CMRWLock::CMRWLock() :
m_lcReaders(0),
m_dwWriteLockOwner(0),
m_dwPromoterRecursion(0)
{
}
// ------------------------------------------------------------------------
//
// CMRWLock::FInitialize()
//
BOOL
CMRWLock::FInitialize()
{
return m_evtEnableReaders.FCreate( NULL, // default security
TRUE, // manual-reset
FALSE, // initially non-signalled
NULL ) // unnamed
&& m_evtEnableWriter.FCreate( NULL, // default security
FALSE, // auto-reset
FALSE, // initially non-signalled
NULL ); // unnamed
}
// ------------------------------------------------------------------------
//
// CMRWLock::EnterRead()
//
void
CMRWLock::EnterRead()
{
(void) FAcquireReadLock(TRUE); // fBlock
}
// ------------------------------------------------------------------------
//
// CMRWLock::FTryEnterRead()
//
BOOL
CMRWLock::FTryEnterRead()
{
return FAcquireReadLock(FALSE); // fBlock
}
// ------------------------------------------------------------------------
//
// CMRWLock::FAcquireReadLock()
//
BOOL
CMRWLock::FAcquireReadLock(BOOL fAllowCallToBlock)
{
//
// Loop around trying to enter the lock until successful
//
for ( ;; )
{
//
// Poll the reader count/write lock
//
LONG lcReaders = m_lcReaders;
//
// If the write lock is held ...
//
if ( lcReaders & WRITE_LOCKED )
{
//
// ... check whether the writer is on this thread.
// If it is, then let this thread reenter the
// lock as a reader. Do not update the reader
// count in this case.
//
if ( m_dwWriteLockOwner == GetCurrentThreadId() )
break;
//
// If the writer is not on this thread, then wait
// until the writer leaves, then re-poll the
// reader count/write lock and try again.
//
// We only block if the caller allows us to block. If this is
// a FTryEnterRead call, we return FALSE right away.
//
if ( fAllowCallToBlock )
{
m_evtEnableReaders.Wait();
}
else
{
return FALSE;
}
}
//
// Otherwise, the write lock was not held, so
// try to enter the lock as a reader. This only
// succeeds when no readers or writers enter or leave
// the lock between the time the reader count/
// write lock is polled above and now. If what is in
// the lock has changed, the whole operation is retried
// until the lock state doesn't change.
//
else
{
if ( lcReaders == /*reinterpret_cast<LONG>*/(
InterlockedCompareExchange(
(&m_lcReaders),
(lcReaders + 1),
(lcReaders))) )
#ifdef NEVER
reinterpret_cast<PVOID *>(&m_lcReaders),
reinterpret_cast<PVOID>(lcReaders + 1),
reinterpret_cast<PVOID>(lcReaders))) )
#endif // NEVER
{
break;
}
}
}
// If we made it to this point, we have acquired the read lock.
//
return TRUE;
}
// ------------------------------------------------------------------------
//
// CMRWLock::LeaveRead()
//
void
CMRWLock::LeaveRead()
{
//
// If the thread on which the reader is leaving also owns
// the write lock, then the reader leaving has no effect,
// as did entering.
//
if ( m_dwWriteLockOwner == GetCurrentThreadId() )
return;
//
// Otherwise, atomically decrement the reader count and
// check if a writer is waiting to enter the lock.
// If the reader count goes to 0 and a writer is waiting
// to enter the lock, then notify the writer that
// it is safe to enter.
//
if ( WRITE_LOCKED == InterlockedDecrement(&m_lcReaders) )
m_evtEnableWriter.Set();
}
// ------------------------------------------------------------------------
//
// CMRWLock::EnterWrite()
//
void
CMRWLock::EnterWrite()
{
//
// A writer is just a promotable reader that promotes immediately
//
EnterPromote();
Promote();
}
// ------------------------------------------------------------------------
//
// CMRWLock::FTryEnterWrite()
//
BOOL
CMRWLock::FTryEnterWrite()
{
BOOL fSuccess;
//
// Try to enter the lock as a promotable reader.
// Promote to a writer immediately if successful
// and return the status of the operation.
//
fSuccess = FTryEnterPromote();
if ( fSuccess )
Promote();
return fSuccess;
}
// ------------------------------------------------------------------------
//
// CMRWLock::LeaveWrite()
//
void
CMRWLock::LeaveWrite()
{
LeavePromote();
}
// ------------------------------------------------------------------------
//
// CMRWLock::EnterPromote()
//
void
CMRWLock::EnterPromote()
{
//
// Grab the writer critical section to ensure that no other thread
// is already in the lock as a writer or promotable reader.
//
m_csWriter.Enter();
//
// Bump the promoter recursion count
//
++m_dwPromoterRecursion;
}
// ------------------------------------------------------------------------
//
// CMRWLock::FTryEnterPromote()
//
BOOL
CMRWLock::FTryEnterPromote()
{
BOOL fSuccess;
//
// Try to enter the writer critical section.
// Bump the recursion count if successful and
// return the status of the operation.
//
fSuccess = m_csWriter.FTryEnter();
if ( fSuccess )
++m_dwPromoterRecursion;
return fSuccess;
}
// ------------------------------------------------------------------------
//
// CMRWLock::Promote()
//
void
CMRWLock::Promote()
{
//
// If the promotable reader has already been promoted
// then don't promote it again
//
if ( GetCurrentThreadId() == m_dwWriteLockOwner )
return;
//
// Assert that no other writer owns the lock.
//
Assert( 0 == m_dwWriteLockOwner );
Assert( !(m_lcReaders & WRITE_LOCKED) );
//
// Claim the lock
//
m_dwWriteLockOwner = GetCurrentThreadId();
//
// Stop readers from entering the lock
//
m_evtEnableReaders.Reset();
//
// If there are any readers in the lock
// then wait for them to leave. The InterlockedExchangeOr()
// is used to ensure that the test is atomic.
//
if ( InterlockedExchangeOr( &m_lcReaders, WRITE_LOCKED ) )
m_evtEnableWriter.Wait();
//
// Assert that the (promoted) writer is now the only thing in the lock
//
Assert( WRITE_LOCKED == m_lcReaders );
}
// ------------------------------------------------------------------------
//
// CMRWLock::LeavePromote()
//
void
CMRWLock::LeavePromote()
{
//
// No one should attempt to leave a promote block
// that they never entered.
//
Assert( m_dwPromoterRecursion > 0 );
//
// If the promotable reader promoted to a writer
// then start allowing readers back into the lock
// once the promoter recursion count reaches 0
//
if ( --m_dwPromoterRecursion == 0 &&
GetCurrentThreadId() == m_dwWriteLockOwner )
{
//
// Clear the write flag to allow new readers
// to start entering the lock.
//
m_lcReaders = 0;
//
// Unblock any threads with readers that are
// already waiting to enter the lock.
//
m_evtEnableReaders.Set();
//
// Release ownership of the write lock
//
m_dwWriteLockOwner = 0;
}
//
// Release the writer's/promoter's critical section reference.
// When this the last such reference is released, a new
// promoter/writer may enter the lock.
//
m_csWriter.Leave();
}
// ========================================================================
//
// FREE FUNCTIONS
//
// ------------------------------------------------------------------------
//
// InterlockedExchangeOr()
//
// This function performs an atomic logical OR of a value to a variable.
// The function prevents more than one thread from using the same
// variable simultaneously. (Well, actually, it spins until it
// gets a consistent result, but who's counting...)
//
// Returns the value of the variable before the logical OR was performed
//
LONG InterlockedExchangeOr( LONG * plVariable, LONG lOrBits )
{
// The rather cryptic way this works is:
//
// Get the instantaneous value of the variable. Stuff it into a
// local variable so that it cannot be changed by another thread.
// Then try to replace the variable with this value OR'd together
// with the OR bits. But only replace if the variable's value
// is still the same as the local variable. If it isn't, then
// another thread must have changed the value between the two
// operations, so keep trying until they both succeed as if
// they had executed as one. Once the operation succeeds in
// changing the value atomically, return the previous value.
//
for ( ;; )
{
LONG lValue = *plVariable;
if ( lValue == /*reinterpret_cast<LONG>*/(
InterlockedCompareExchange(
(plVariable),
(lValue | lOrBits),
(lValue))) )
#ifdef NEVER
reinterpret_cast<PVOID *>(plVariable),
reinterpret_cast<PVOID>(lValue | lOrBits),
reinterpret_cast<PVOID>(lValue))) )
#endif // NEVER
return lValue;
}
}