Source code of Windows XP (NT5)
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.
|
|
///////////////////////////////////////////////////////////////////////////////
//
// The include files for supporting classes.
//
// The include files for supporting classes consists of
// classes refered to or used in this class. The structure
// of each source module is as follows:
// 1. Include files.
// 2. Constants local to the class.
// 3. Data structures local to the class.
// 4. Data initializations.
// 5. Static functions.
// 6. Class functions.
// Sections that are not required are omitted.
//
///////////////////////////////////////////////////////////////////////////////
#include "precomp.hxx"
#define DLL_IMPLEMENTATION
#define IMPLEMENTATION_EXPORT
#include <Sharelok.h>
//////////////////////////////////////////////////////////////////////
//
// Class constructor.
//
// Create a new lock and initialize it. This call is not
// thread safe and should only be made in a single thread
// environment.
//
//////////////////////////////////////////////////////////////////////
CSharelock::CSharelock( SBIT32 lNewMaxSpins, SBIT32 lNewMaxUsers ) { //
// Set the initial state.
//
m_lExclusive = 0; m_lTotalUsers = 0; m_lWaiting = 0;
//
// Check the configurable values.
//
if ( lNewMaxSpins > 0 ) { m_lMaxSpins = lNewMaxSpins; } else { throw (TEXT("Maximum spins invalid in constructor for CSharelock")); }
if ( (lNewMaxUsers > 0) && (lNewMaxUsers <= m_MaxShareLockUsers) ) { m_lMaxUsers = lNewMaxUsers; } else { throw (TEXT("Maximum share invalid in constructor for CSharelock")); }
//
// Create a semaphore to sleep on when the spin count exceeds
// its maximum.
//
if ( (m_hSemaphore = CreateSemaphore( NULL, 0, m_MaxShareLockUsers, NULL )) == NULL) { throw (TEXT("Create semaphore in constructor for CSharelock")); }
#ifdef _DEBUG
//
// Set the initial state of any debug variables.
//
m_lTotalExclusiveLocks = 0; m_lTotalShareLocks = 0; m_lTotalSleeps = 0; m_lTotalSpins = 0; m_lTotalTimeouts = 0; m_lTotalWaits = 0; #endif
}
//////////////////////////////////////////////////////////////////////
//
// Sleep waiting for the lock.
//
// We have decided it is time to sleep waiting for the lock
// to become free.
//
//////////////////////////////////////////////////////////////////////
BOOLEAN CSharelock::SleepWaitingForLock( SBIT32 lSleep ) { //
// We have been spinning waiting for the lock but it
// has not become free. Hence, it is now time to
// give up and sleep for a while.
//
(void) InterlockedIncrement( (LPLONG) & m_lWaiting );
//
// Just before we go to sleep we do one final check
// to make sure that the lock is still busy and that
// there is someone to wake us up when it becomes free.
//
if ( m_lTotalUsers > 0 ) { #ifdef _DEBUG
//
// Count the number of times we have slept on this lock.
//
(void) InterlockedIncrement( (LPLONG) & m_lTotalSleeps );
#endif
//
// When we sleep we awoken when the lock becomes free
// or when we timeout. If we timeout we simply exit
// after decrementing various counters.
if (WaitForSingleObject( m_hSemaphore, lSleep ) != WAIT_OBJECT_0 ) { #ifdef _DEBUG
//
// Count the number of times we have timed out
// on this lock.
//
(void) InterlockedIncrement( (LPLONG) & m_lTotalTimeouts );
#endif
return FALSE; } } else { //
// Lucky - the lock was just freed so lets
// decrement the sleep count and exit without
// sleeping.
//
(void) InterlockedDecrement( (LPLONG) & m_lWaiting ); } return TRUE; }
//////////////////////////////////////////////////////////////////////
//
// Update the spin limit.
//
// Update the maximum number of spins while waiting for the lock.
//
//////////////////////////////////////////////////////////////////////
BOOLEAN CSharelock::UpdateMaxSpins( SBIT32 lNewMaxSpins ) { if ( lNewMaxSpins > 0 ) { m_lMaxSpins = lNewMaxSpins;
return TRUE; } else { return FALSE; } }
//////////////////////////////////////////////////////////////////////
//
// Update the sharing limit.
//
// Update the maximum number of users that can share the lock.
//
//////////////////////////////////////////////////////////////////////
BOOLEAN CSharelock::UpdateMaxUsers( SBIT32 lNewMaxUsers ) { if ( (lNewMaxUsers > 0) && (lNewMaxUsers <= m_MaxShareLockUsers) ) { ClaimExclusiveLock();
m_lMaxUsers = lNewMaxUsers; ReleaseExclusiveLock();
return TRUE; } else { return FALSE; } }
//////////////////////////////////////////////////////////////////////
//
// Wait for an exclusive lock.
//
// Wait for the spinlock to become free and then claim it.
//
//////////////////////////////////////////////////////////////////////
BOOLEAN CSharelock::WaitForExclusiveLock( SBIT32 lSleep ) { #ifdef _DEBUG
register SBIT32 lSpins = 0; register SBIT32 lWaits = 0;
#endif
while ( m_lTotalUsers != 1 ) { //
// The lock is busy so release it and spin waiting
// for it to become free.
//
(void) InterlockedDecrement( (LPLONG) & m_lTotalUsers ); //
// Find out if we are allowed to spin and sleep if
// necessary.
//
if ( (lSleep > 0) || (lSleep == INFINITE) ) { register SBIT32 lCount;
//
// Wait by spinning and repeatedly testing the lock.
// We exit when the lock becomes free or the spin limit
// is exceeded.
//
for (lCount = (NumProcessors() < 2) ? 0 : m_lMaxSpins; (lCount > 0) && (m_lTotalUsers > 0); lCount -- ) ; #ifdef _DEBUG
lSpins += (m_lMaxSpins - lCount); lWaits ++; #endif
//
// We have exhusted our spin count so it is time to
// sleep waiting for the lock to clear.
//
if ( lCount == 0 ) { //
// We have decide that we need to sleep but are
// still holding an exclusive lock so lets drop it
// before sleeping.
//
(void) InterlockedDecrement( (LPLONG) & m_lExclusive );
//
// We have decide to go to sleep. If the sleep time
// is not 'INFINITE' then we must subtract the time
// we sleep from our maximum sleep time. If the
// sleep time is 'INFINITE' then we can just skip
// this step.
//
if ( lSleep != INFINITE ) { register DWORD dwStartTime = GetTickCount();
if ( ! SleepWaitingForLock( lSleep ) ) { return FALSE; }
lSleep -= ((GetTickCount() - dwStartTime) + 1); lSleep = (lSleep > 0) ? lSleep : 0; } else { if ( ! SleepWaitingForLock( lSleep ) ) { return FALSE; } }
//
// We have woken up again so lets reclaim the
// exclusive lock we had earlier.
//
(void) InterlockedIncrement( (LPLONG) & m_lExclusive ); } } else { //
// We have decide that we need to exit but are still
// holding an exclusive lock. so lets drop it and leave.
//
(void) InterlockedDecrement( (LPLONG) & m_lExclusive );
return FALSE; }
//
// Lets test the lock again.
//
InterlockedIncrement( (LPLONG) & m_lTotalUsers ); } #ifdef _DEBUG
(void) InterlockedExchangeAdd( (LPLONG) & m_lTotalSpins, (LONG) lSpins ); (void) InterlockedExchangeAdd( (LPLONG) & m_lTotalWaits, (LONG) lWaits ); #endif
return TRUE; }
//////////////////////////////////////////////////////////////////////
//
// Wait for a shared lock.
//
// Wait for the lock to become free and then claim it.
//
//////////////////////////////////////////////////////////////////////
BOOLEAN CSharelock::WaitForShareLock( SBIT32 lSleep ) { #ifdef _DEBUG
register SBIT32 lSpins = 0; register SBIT32 lWaits = 0;
#endif
while ( (m_lExclusive > 0) || (m_lTotalUsers > m_lMaxUsers) ) { //
// The lock is busy so release it and spin waiting
// for it to become free.
//
(void) InterlockedDecrement( (LPLONG) & m_lTotalUsers );
if ( (lSleep > 0) || (lSleep == INFINITE) ) { register SBIT32 lCount;
//
// Wait by spinning and repeatedly testing the lock.
// We exit when the lock becomes free or the spin limit
// is exceeded.
//
for (lCount = (NumProcessors() < 2) ? 0 : m_lMaxSpins; (lCount > 0) && ((m_lExclusive > 0) || (m_lTotalUsers >= m_lMaxUsers)); lCount -- ) ; #ifdef _DEBUG
lSpins += (m_lMaxSpins - lCount); lWaits ++; #endif
//
// We have exhusted our spin count so it is time to
// sleep waiting for the lock to clear.
//
if ( lCount == 0 ) { //
// We have decide to go to sleep. If the sleep time
// is not 'INFINITE' then we must subtract the time
// we sleep from our maximum sleep time. If the
// sleep time is 'INFINITE' then we can just skip
// this step.
//
if ( lSleep != INFINITE ) { register DWORD dwStartTime = GetTickCount();
if ( ! SleepWaitingForLock( lSleep ) ) { return FALSE; }
lSleep -= ((GetTickCount() - dwStartTime) + 1); lSleep = (lSleep > 0) ? lSleep : 0; } else { if ( ! SleepWaitingForLock( lSleep ) ) { return FALSE; } } } } else { return FALSE; }
//
// Lets test the lock again.
//
(void) InterlockedIncrement( (LPLONG) & m_lTotalUsers ); } #ifdef _DEBUG
(void) InterlockedExchangeAdd( (LPLONG) & m_lTotalSpins, (LONG) lSpins ); (void) InterlockedExchangeAdd( (LPLONG) & m_lTotalWaits, (LONG) lWaits ); #endif
return TRUE; }
//////////////////////////////////////////////////////////////////////
//
// Wake all sleepers.
//
// Wake all the sleepers who are waiting for the spinlock.
// All sleepers are woken because this is much more efficent
// and it is known that the lock latency is short.
//
//////////////////////////////////////////////////////////////////////
void CSharelock::WakeAllSleepers( void ) { register LONG lWakeup = InterlockedExchange( (LPLONG) & m_lWaiting, 0 );
if ( lWakeup > 0 ) { //
// Wake up all sleepers as the lock has just been freed.
// It is a straight race to decide who gets the lock next.
//
if ( ! ReleaseSemaphore( m_hSemaphore, lWakeup, NULL ) ) { throw (TEXT("Wakeup failed in ReleaseLock()")); } } else { //
// When multiple threads pass through the critical section rapidly
// it is possible for the 'Waiting' count to become negative.
// This should be very rare but such a negative value needs to be
// preserved.
//
InterlockedExchangeAdd( (LPLONG) & m_lWaiting, lWakeup ); } }
//////////////////////////////////////////////////////////////////////
//
// Class destructor.
//
// Destroy a lock. This call is not thread safe and should
// only be made in a single thread environment.
//
//////////////////////////////////////////////////////////////////////
CSharelock::~CSharelock( void ) { if ( ! CloseHandle( m_hSemaphore ) ) { throw (TEXT("Close semaphore in destructor for CSharelock")); } }
|