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.
 
 
 
 
 
 

345 lines
11 KiB

// Ruler
// 1 2 3 4 5 6 7 8
//345678901234567890123456789012345678901234567890123456789012345678901234567890
/********************************************************************/
/* */
/* The standard layout. */
/* */
/* The standard layout for 'cpp' files in this code 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. */
/* */
/* The constructor is typically the first function, class */
/* member functions appear in alphabetical order with the */
/* destructor appearing at the end of the file. Any section */
/* or function this is not required is simply omitted. */
/* */
/********************************************************************/
#include "LibraryPCH.hpp"
#include "Spinlock.hpp"
/********************************************************************/
/* */
/* 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. */
/* */
/********************************************************************/
SPINLOCK::SPINLOCK( SBIT32 NewMaxSpins,SBIT32 NewMaxUsers )
{
//
// Set the initial state.
//
MaxSpins = NewMaxSpins;
MaxUsers = NewMaxUsers;
#ifdef ENABLE_RECURSIVE_LOCKS
Owner = NULL;
Recursive = 0;
#endif
Spinlock = LockOpen;
Semaphore = NULL;
Waiting = 0;
#ifdef ENABLE_LOCK_STATISTICS
//
// Zero the lock statistics.
//
TotalLocks = 0;
TotalSleeps = 0;
TotalSpins = 0;
TotalTimeouts = 0;
TotalWaits = 0;
#endif
}
/********************************************************************/
/* */
/* Update the semahore. */
/* */
/* We only create the semaphore on first use. So when we need */
/* need to create a new semaphore any thread that is trying */
/* to sleep on it comes here. */
/* */
/********************************************************************/
VOID SPINLOCK::UpdateSemaphore( VOID )
{
STATIC SBIT32 Active = 0;
//
// We verify that there is still no semaphore
// otherwise we exit.
//
while ( Semaphore == NULL )
{
//
// We increment the active count and if we
// are first we are selected for special duty.
//
if ( (AtomicIncrement( & Active ) == 1) && (Semaphore == NULL) )
{
//
// We try to create a new semaphore. If
// we fail we still exit.
//
Semaphore = CreateSemaphore( NULL,0,MaxUsers,NULL );
//
// Decrement the active count and exit.
//
AtomicDecrement( & Active );
return;
}
else
{
//
// Decrement the active count and exit.
//
AtomicDecrement( & Active );
Sleep( 1 );
}
}
}
/********************************************************************/
/* */
/* Wait for the spinlock. */
/* */
/* Wait for the spinlock to become free and then claim it. */
/* */
/********************************************************************/
BOOLEAN SPINLOCK::WaitForLock( SBIT32 Sleep )
{
REGISTER LONG Cpus = ((LONG) NumberOfCpus());
#ifdef ENABLE_LOCK_STATISTICS
REGISTER SBIT32 Sleeps = 0;
REGISTER SBIT32 Spins = 0;
REGISTER SBIT32 Waits = 0;
#endif
do
{
REGISTER SBIT32 Count;
//
// If there are already more threads waiting
// than the number of CPUs then the odds of
// getting the lock by spinning are slim, when
// there is only one CPU the chance is zero, so
// just bypass this step.
//
if ( (Cpus > 1) && (Cpus > Waiting) )
{
//
// Wait by spinning and repeatedly testing the
// spinlock. We exit when the lock becomes free
// or the spin limit is exceeded.
//
for
(
Count = MaxSpins;
(Count > 0) && (Spinlock != LockOpen);
Count --
);
#ifdef ENABLE_LOCK_STATISTICS
//
// Update the statistics.
//
Spins += (MaxSpins - Count);
Waits ++;
#endif
}
else
{ Count = 0; }
//
// We have exhusted our spin count so it is time to
// sleep waiting for the lock to clear.
//
if ( Count == 0 )
{
//
// We do not create the semaphore until
// somebody tries to sleep on it for the
// first time.
//
if ( Semaphore == NULL )
{ UpdateSemaphore(); }
//
// We would normally hope to find a semaphore
// avaiable ready for a sleep but the OS may
// decline the request. If this is the case
// try the lock again.
//
if ( Semaphore != NULL )
{
//
// The lock is still closed so lets go to sleep on
// a semaphore. However, we must first increment
// the waiting count and test the lock one last time
// to make sure it is still busy and there is someone
// to wake us up later.
//
(VOID) AtomicIncrement( & Waiting );
if ( ! ClaimSpinlock( & Spinlock ) )
{
if
(
WaitForSingleObject( Semaphore, Sleep )
!=
WAIT_OBJECT_0
)
{
#ifdef ENABLE_LOCK_STATISTICS
//
// Count the number of times we have
// timed out on this lock.
//
(VOID) AtomicIncrement( & TotalTimeouts );
#endif
return False;
}
#ifdef ENABLE_LOCK_STATISTICS
//
// Update the statistics.
//
Sleeps ++;
#endif
}
else
{
//
// Lucky - got the lock on the last attempt.
// Hence, lets decrement the sleep count and
// exit.
//
(VOID) AtomicDecrement( & Waiting );
break;
}
}
}
}
while ( ! ClaimSpinlock( & Spinlock ) );
#ifdef ENABLE_LOCK_STATISTICS
//
// Update the statistics.
//
TotalSleeps += Sleeps;
TotalSpins += Spins;
TotalWaits += Waits;
#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 SPINLOCK::WakeAllSleepers( VOID )
{
REGISTER LONG Wakeup = AtomicExchange( & Waiting, 0 );
//
// We make sure there is still someone to be woken
// up if not we check that the count has not become
// negative.
//
if ( Wakeup > 0 )
{
REGISTER LONG Cpus = ((LONG) NumberOfCpus());
//
// We will only wake enough threads to ensure that
// there is one active thread per CPU. So if an
// application has hundreds of threads we will try
// prevent the system from becoming swampped.
//
if ( Wakeup > Cpus )
{
(VOID) AtomicAdd( & Waiting,(Wakeup - Cpus) );
Wakeup = Cpus;
}
//
// 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( Semaphore, Wakeup, NULL ) )
{ Failure( "Wakeup failed in ReleaseLock()" ); }
}
else
{
//
// When multiple threads pass through the critical
// section it is possible for the 'Waiting' count
// to become negative. This should be very rare but
// such a negative value needs to be preserved.
//
if ( Wakeup < 0 )
{ (VOID) AtomicAdd( & Waiting, Wakeup ); }
}
}
/********************************************************************/
/* */
/* Class destructor. */
/* */
/* Destory a lock. This call is not thread safe and should */
/* only be made in a single thread environment. */
/* */
/********************************************************************/
SPINLOCK::~SPINLOCK( VOID )
{
#ifdef ENABLE_LOCK_STATISTICS
//
// Print the lock statistics.
//
DebugPrint
(
"Spinlock: %d locks, %d timouts, "
"%d locks per wait, %d spins per wait, %d waits per sleep.\n",
TotalLocks,
TotalTimeouts,
(TotalLocks / ((TotalWaits <= 0) ? 1 : TotalWaits)),
(TotalSpins / ((TotalWaits <= 0) ? 1 : TotalWaits)),
(TotalWaits / ((TotalSleeps <= 0) ? 1 : TotalSleeps))
);
#endif
//
// Close the semaphore handle.
//
if ( (Semaphore != NULL) && (! CloseHandle( Semaphore )) )
{ Failure( "Close semaphore in destructor for SPINLOCK" ); }
}