504 lines
16 KiB
C++
504 lines
16 KiB
C++
#ifndef _SHARELOCK_HPP_
|
|
#define _SHARELOCK_HPP_
|
|
// Ruler
|
|
// 1 2 3 4 5 6 7 8
|
|
//345678901234567890123456789012345678901234567890123456789012345678901234567890
|
|
|
|
/********************************************************************/
|
|
/* */
|
|
/* The standard layout. */
|
|
/* */
|
|
/* The standard layout for 'hpp' files for this code is as */
|
|
/* follows: */
|
|
/* */
|
|
/* 1. Include files. */
|
|
/* 2. Constants exported from the class. */
|
|
/* 3. Data structures exported from the class. */
|
|
/* 4. Forward references to other data structures. */
|
|
/* 5. Class specifications (including inline functions). */
|
|
/* 6. Additional large inline functions. */
|
|
/* */
|
|
/* Any portion that is not required is simply omitted. */
|
|
/* */
|
|
/********************************************************************/
|
|
|
|
#include "Global.hpp"
|
|
|
|
#include "Environment.hpp"
|
|
|
|
/********************************************************************/
|
|
/* */
|
|
/* Sharelock and Semaphore locking. */
|
|
/* */
|
|
/* This class provides a very conservative locking scheme. */
|
|
/* The assumption behind the code is that locks will be */
|
|
/* held for a very short time. A lock can be obtained in */
|
|
/* either exclusive mode or shared mode. If the lock is not */
|
|
/* available the caller waits by spinning or if that fails */
|
|
/* by sleeping. */
|
|
/* */
|
|
/********************************************************************/
|
|
|
|
class SHARELOCK : public ENVIRONMENT
|
|
{
|
|
//
|
|
// Private data.
|
|
//
|
|
SBIT32 MaxSpins;
|
|
SBIT32 MaxUsers;
|
|
|
|
VOLATILE SBIT32 ExclusiveUsers;
|
|
VOLATILE SBIT32 TotalUsers;
|
|
|
|
HANDLE NormalSemaphore;
|
|
VOLATILE SBIT32 NormalWaiting;
|
|
HANDLE PrioritySemaphore;
|
|
VOLATILE SBIT32 PriorityWaiting;
|
|
|
|
#ifdef ENABLE_RECURSIVE_LOCKS
|
|
SBIT32 Owner;
|
|
SBIT32 Recursive;
|
|
|
|
#endif
|
|
#ifdef ENABLE_LOCK_STATISTICS
|
|
//
|
|
// Counters for debugging builds.
|
|
//
|
|
VOLATILE SBIT32 TotalExclusiveLocks;
|
|
VOLATILE SBIT32 TotalShareLocks;
|
|
VOLATILE SBIT32 TotalSleeps;
|
|
VOLATILE SBIT32 TotalSpins;
|
|
VOLATILE SBIT32 TotalTimeouts;
|
|
VOLATILE SBIT32 TotalWaits;
|
|
#endif
|
|
|
|
public:
|
|
//
|
|
// Public functions.
|
|
//
|
|
SHARELOCK( SBIT32 NewMaxSpins = 4096, SBIT32 NewMaxUsers = 256 );
|
|
|
|
INLINE VOID ChangeExclusiveLockToSharedLock( VOID );
|
|
|
|
INLINE BOOLEAN ChangeSharedLockToExclusiveLock( SBIT32 Sleep = INFINITE );
|
|
|
|
INLINE BOOLEAN ClaimExclusiveLock( SBIT32 Sleep = INFINITE );
|
|
|
|
INLINE BOOLEAN ClaimShareLock( SBIT32 Sleep = INFINITE );
|
|
|
|
INLINE VOID ReleaseExclusiveLock( VOID );
|
|
|
|
INLINE VOID ReleaseShareLock( VOID );
|
|
|
|
BOOLEAN UpdateMaxSpins( SBIT32 NewMaxSpins );
|
|
|
|
BOOLEAN UpdateMaxUsers( SBIT32 NewMaxUsers );
|
|
|
|
~SHARELOCK( VOID );
|
|
|
|
//
|
|
// Public inline functions.
|
|
//
|
|
INLINE SBIT32 ActiveUsers( VOID )
|
|
{ return (SBIT32) TotalUsers; }
|
|
|
|
private:
|
|
//
|
|
// Private functions.
|
|
//
|
|
INLINE VOID DeleteExclusiveOwner( VOID );
|
|
|
|
INLINE VOID NewExclusiveOwner( SBIT32 NewOwner );
|
|
|
|
BOOLEAN SleepWaitingForLock
|
|
(
|
|
HANDLE *Semaphore,
|
|
SBIT32 Sleep,
|
|
VOLATILE SBIT32 *Waiting
|
|
);
|
|
|
|
BOOLEAN UpdateSemaphore( HANDLE *Semaphore );
|
|
|
|
BOOLEAN WaitForExclusiveLock( SBIT32 Sleep );
|
|
|
|
BOOLEAN WaitForShareLock( SBIT32 Sleep );
|
|
|
|
VOID WakeAllSleepers( VOID );
|
|
|
|
//
|
|
// Disabled operations.
|
|
//
|
|
SHARELOCK( CONST SHARELOCK & Copy );
|
|
|
|
VOID operator=( CONST SHARELOCK & Copy );
|
|
};
|
|
|
|
/********************************************************************/
|
|
/* */
|
|
/* Change an exclusive lock to a shared lock. */
|
|
/* */
|
|
/* Downgrade the existing exclusive lock to a shared lock. */
|
|
/* */
|
|
/********************************************************************/
|
|
|
|
INLINE VOID SHARELOCK::ChangeExclusiveLockToSharedLock( VOID )
|
|
{
|
|
#ifdef ENABLE_RECURSIVE_LOCKS
|
|
//
|
|
// When we have recursive lock calls we do not
|
|
// release the lock until we have exited to the
|
|
// top level.
|
|
//
|
|
if ( Recursive <= 0 )
|
|
{
|
|
//
|
|
// Delete the exclusive owner information.
|
|
//
|
|
DeleteExclusiveOwner();
|
|
|
|
#endif
|
|
//
|
|
// Simply decrement the exclusive count.
|
|
// This allows the lock to be shared.
|
|
//
|
|
(VOID) AtomicDecrement( & ExclusiveUsers );
|
|
#ifdef ENABLE_RECURSIVE_LOCKS
|
|
}
|
|
#endif
|
|
#ifdef ENABLE_LOCK_STATISTICS
|
|
|
|
//
|
|
// Update the statistics.
|
|
//
|
|
(VOID) AtomicIncrement( & TotalShareLocks );
|
|
#endif
|
|
}
|
|
|
|
/********************************************************************/
|
|
/* */
|
|
/* Change a shared lock to an exclusive lock. */
|
|
/* */
|
|
/* Upgrade the existing shared lock to an exclusive lock. */
|
|
/* */
|
|
/********************************************************************/
|
|
|
|
INLINE BOOLEAN SHARELOCK::ChangeSharedLockToExclusiveLock( SBIT32 Sleep )
|
|
{
|
|
#ifdef ENABLE_RECURSIVE_LOCKS
|
|
REGISTER SBIT32 ThreadId = GetThreadId();
|
|
|
|
//
|
|
// We may already own an exclusive lock. If so
|
|
// we increment the recursive count otherwise
|
|
// we have to wait.
|
|
//
|
|
if ( Owner != ThreadId )
|
|
{
|
|
#endif
|
|
//
|
|
// We need to increment the exclusive count
|
|
// to prevent the lock from being shared.
|
|
//
|
|
(VOID) AtomicIncrement( & ExclusiveUsers );
|
|
|
|
//
|
|
// If the total number of users is one then
|
|
// we have the lock exclusively otherwise we
|
|
// may need to wait.
|
|
//
|
|
if ( TotalUsers != 1 )
|
|
{
|
|
//
|
|
// We have to wait. If we are not allowed
|
|
// to sleep or we have timed out then exit.
|
|
//
|
|
if ( ! WaitForExclusiveLock( Sleep ) )
|
|
{ return False; }
|
|
}
|
|
#ifdef ENABLE_RECURSIVE_LOCKS
|
|
|
|
//
|
|
// Register the new exclusive owner
|
|
// of the lock.
|
|
//
|
|
NewExclusiveOwner( ThreadId );
|
|
}
|
|
#endif
|
|
#ifdef ENABLE_LOCK_STATISTICS
|
|
|
|
//
|
|
// Update the statistics.
|
|
//
|
|
(VOID) AtomicIncrement( & TotalExclusiveLocks );
|
|
#endif
|
|
|
|
return True;
|
|
}
|
|
|
|
/********************************************************************/
|
|
/* */
|
|
/* Claim an exclusive lock. */
|
|
/* */
|
|
/* Claim an exclusive lock if available else wait or exit. */
|
|
/* */
|
|
/********************************************************************/
|
|
|
|
INLINE BOOLEAN SHARELOCK::ClaimExclusiveLock( SBIT32 Sleep )
|
|
{
|
|
#ifdef ENABLE_RECURSIVE_LOCKS
|
|
REGISTER SBIT32 ThreadId = GetThreadId();
|
|
|
|
//
|
|
// We may already own an exclusive lock. If so
|
|
// we increment the recursive count otherwise
|
|
// we have to wait.
|
|
//
|
|
if ( Owner != ThreadId )
|
|
{
|
|
#endif
|
|
//
|
|
// We need to increment the exclusive count
|
|
// to prevent the lock from being shared and
|
|
// the total number of users count. We need
|
|
// update the total number of users first to
|
|
// avoid a deadlock when real-time priorites
|
|
// are being used. If not then an interupt
|
|
// can occur after the update of the
|
|
// 'ExclusiveUsers' count but before the
|
|
// 'TotalUsers' count that will prevent
|
|
// another thread from getting the lock or
|
|
// sleeping (i.e. a live lock).
|
|
//
|
|
(VOID) AtomicIncrement( & TotalUsers );
|
|
(VOID) AtomicIncrement( & ExclusiveUsers );
|
|
|
|
if ( TotalUsers != 1 )
|
|
{
|
|
//
|
|
// We have to wait. If we are not allowed
|
|
// to sleep or we have timed out then exit.
|
|
//
|
|
if ( ! WaitForExclusiveLock( Sleep ) )
|
|
{ return False; }
|
|
}
|
|
#ifdef ENABLE_RECURSIVE_LOCKS
|
|
|
|
//
|
|
// Register the new exclusive owner
|
|
// of the lock.
|
|
//
|
|
NewExclusiveOwner( ThreadId );
|
|
}
|
|
else
|
|
{ Recursive ++; }
|
|
#endif
|
|
#ifdef ENABLE_LOCK_STATISTICS
|
|
|
|
//
|
|
// Update the statistics.
|
|
//
|
|
(VOID) AtomicIncrement( & TotalExclusiveLocks );
|
|
#endif
|
|
|
|
return True;
|
|
}
|
|
|
|
/********************************************************************/
|
|
/* */
|
|
/* Claim a shared lock. */
|
|
/* */
|
|
/* Claim a shared lock if available else wait or exit. */
|
|
/* */
|
|
/********************************************************************/
|
|
|
|
INLINE BOOLEAN SHARELOCK::ClaimShareLock( SBIT32 Sleep )
|
|
{
|
|
#ifdef ENABLE_RECURSIVE_LOCKS
|
|
REGISTER SBIT32 ThreadId = GetThreadId();
|
|
|
|
//
|
|
// We may already own an exclusive lock. If so
|
|
// we increment the recursive count otherwise
|
|
// we have to wait.
|
|
//
|
|
if ( Owner != ThreadId )
|
|
{
|
|
#endif
|
|
//
|
|
// We need to increment the total number of
|
|
// users count to prevent the lock from being
|
|
// claimed for exclusive use.
|
|
//
|
|
(VOID) AtomicIncrement( & TotalUsers );
|
|
|
|
if ( (ExclusiveUsers > 0) || (TotalUsers > MaxUsers) )
|
|
{
|
|
//
|
|
// We have to wait. If we are not allowed
|
|
// to sleep or we have timed out then exit.
|
|
//
|
|
if ( ! WaitForShareLock( Sleep ) )
|
|
{ return False; }
|
|
}
|
|
#ifdef ENABLE_RECURSIVE_LOCKS
|
|
}
|
|
else
|
|
{ Recursive ++; }
|
|
#endif
|
|
#ifdef ENABLE_LOCK_STATISTICS
|
|
|
|
//
|
|
// Update the statistics.
|
|
//
|
|
(VOID) AtomicIncrement( & TotalShareLocks );
|
|
#endif
|
|
|
|
return True;
|
|
}
|
|
#ifdef ENABLE_RECURSIVE_LOCKS
|
|
|
|
/********************************************************************/
|
|
/* */
|
|
/* New exclusive owner. */
|
|
/* */
|
|
/* Delete the exclusive lock owner information. */
|
|
/* */
|
|
/********************************************************************/
|
|
|
|
INLINE VOID SHARELOCK::DeleteExclusiveOwner( VOID )
|
|
{
|
|
#ifdef DEBUGGING
|
|
if ( Owner != NULL )
|
|
{
|
|
#endif
|
|
Owner = NULL;
|
|
#ifdef DEBUGGING
|
|
}
|
|
else
|
|
{ Failure( "Sharelock has no owner in DeleteExclusiveOwner" ); }
|
|
#endif
|
|
}
|
|
|
|
/********************************************************************/
|
|
/* */
|
|
/* New exclusive owner. */
|
|
/* */
|
|
/* Register new exclusive lock owner information. */
|
|
/* */
|
|
/********************************************************************/
|
|
|
|
INLINE VOID SHARELOCK::NewExclusiveOwner( SBIT32 NewOwner )
|
|
{
|
|
#ifdef DEBUGGING
|
|
if ( Owner == NULL )
|
|
{
|
|
#endif
|
|
Owner = NewOwner;
|
|
#ifdef DEBUGGING
|
|
}
|
|
else
|
|
{ Failure( "Already exclusive in NewExclusiveOwner" ); }
|
|
#endif
|
|
}
|
|
#endif
|
|
|
|
/********************************************************************/
|
|
/* */
|
|
/* Release an exclusive lock. */
|
|
/* */
|
|
/* Release an exclusive lock and if needed wakeup any sleepers. */
|
|
/* */
|
|
/********************************************************************/
|
|
|
|
INLINE VOID SHARELOCK::ReleaseExclusiveLock( VOID )
|
|
{
|
|
#ifdef ENABLE_RECURSIVE_LOCKS
|
|
//
|
|
// When we have recursive lock calls we do not
|
|
// release the lock until we have exited to the
|
|
// top level.
|
|
//
|
|
if ( Recursive <= 0 )
|
|
{
|
|
//
|
|
// Delete the exclusive owner information.
|
|
//
|
|
DeleteExclusiveOwner();
|
|
|
|
#endif
|
|
//
|
|
// Release an exclusive lock.
|
|
//
|
|
#ifdef DEBUGGING
|
|
if
|
|
(
|
|
(AtomicDecrement( & ExclusiveUsers ) < 0)
|
|
||
|
|
(AtomicDecrement( & TotalUsers ) < 0)
|
|
)
|
|
{ Failure( "Negative lock count in ReleaseExclusiveLock" ); }
|
|
#else
|
|
AtomicDecrement( & ExclusiveUsers );
|
|
AtomicDecrement( & TotalUsers );
|
|
#endif
|
|
|
|
//
|
|
// Wakeup anyone who is asleep waiting. We
|
|
// need to be vaery careful here as in very
|
|
// rare situations the waiting counts can
|
|
// be negative for very breif periods.
|
|
//
|
|
if ( (PriorityWaiting > 0) || (NormalWaiting > 0) )
|
|
{ WakeAllSleepers(); }
|
|
#ifdef ENABLE_RECURSIVE_LOCKS
|
|
}
|
|
else
|
|
{ Recursive --; }
|
|
#endif
|
|
}
|
|
|
|
/********************************************************************/
|
|
/* */
|
|
/* Release a shared lock. */
|
|
/* */
|
|
/* Release a shared lock and if needed wakeup any sleepers. */
|
|
/* */
|
|
/********************************************************************/
|
|
|
|
INLINE VOID SHARELOCK::ReleaseShareLock( VOID )
|
|
{
|
|
#ifdef ENABLE_RECURSIVE_LOCKS
|
|
//
|
|
// When we have recursive lock calls we do not
|
|
// release the lock until we have exited to the
|
|
// top level.
|
|
//
|
|
if ( Recursive <= 0 )
|
|
{
|
|
#endif
|
|
#ifdef DEBUGGING
|
|
//
|
|
// Release a shared lock.
|
|
//
|
|
if ( AtomicDecrement( & TotalUsers ) < 0 )
|
|
{ Failure( "Negative lock count in ReleaseShareLock" ); }
|
|
#else
|
|
AtomicDecrement( & TotalUsers );
|
|
#endif
|
|
|
|
//
|
|
// Wakeup anyone who is asleep waiting. We
|
|
// need to be vaery careful here as in very
|
|
// rare situations the waiting counts can
|
|
// be negative for very breif periods.
|
|
//
|
|
if ( (PriorityWaiting > 0) || (NormalWaiting > 0) )
|
|
{ WakeAllSleepers(); }
|
|
#ifdef ENABLE_RECURSIVE_LOCKS
|
|
}
|
|
else
|
|
{ Recursive --; }
|
|
#endif
|
|
}
|
|
#endif
|