|
|
#ifndef _SYNC_HXX_INCLUDED
#define _SYNC_HXX_INCLUDED
#include "nt.h"
#include "ntrtl.h"
#include "nturtl.h"
#include "windows.h"
#pragma warning ( disable : 4786 ) // we allow huge symbol names
// Build Options
#define SYNC_USE_X86_ASM // use x86 assembly for atomic memory manipulation
//#define SYNC_ANALYZE_PERFORMANCE // analyze performance of synchronization objects
#ifdef SYNC_ANALYZE_PERFORMANCE
#define SYNC_DUMP_PERF_DATA // dump performance analysis of synchronization objects
#endif // SYNC_ANALYZE_PERFORMANCE
//#define SYNC_DEADLOCK_DETECTION // perform deadlock detection
//#define SYNC_VALIDATE_IRKSEM_USAGE // validate IRKSEM (CReferencedKernelSemaphore) usage
#ifdef DEBUG
#ifdef DBG
#else // !DBG
#define SYNC_DEADLOCK_DETECTION // always perform deadlock detection in DEBUG
#define SYNC_VALIDATE_IRKSEM_USAGE // always validate IRKSEM (CReferencedKernelSemaphore) usage in DEBUG
#endif // DBG
#endif // DEBUG
// copied from basestd.h to make LONG_PTR available.
#ifdef __cplusplus
extern "C" { #endif
//
// The following types are guaranteed to be signed and 32 bits wide.
//
typedef int LONG32, *PLONG32; typedef int INT32, *PINT32;
//
// The following types are guaranteed to be unsigned and 32 bits wide.
//
typedef unsigned int ULONG32, *PULONG32; typedef unsigned int DWORD32, *PDWORD32; typedef unsigned int UINT32, *PUINT32;
//
// The INT_PTR is guaranteed to be the same size as a pointer. Its
// size with change with pointer size (32/64). It should be used
// anywhere that a pointer is cast to an integer type. UINT_PTR is
// the unsigned variation.
//
// __int3264 is intrinsic to 64b MIDL but not to old MIDL or to C compiler.
//
#if ( 501 < __midl )
typedef __int3264 INT_PTR, *PINT_PTR; typedef unsigned __int3264 UINT_PTR, *PUINT_PTR;
typedef __int3264 LONG_PTR, *PLONG_PTR; typedef unsigned __int3264 ULONG_PTR, *PULONG_PTR;
#else // midl64
// old midl and C++ compiler
#ifdef _WIN64
typedef __int64 INT_PTR, *PINT_PTR; typedef unsigned __int64 UINT_PTR, *PUINT_PTR;
typedef __int64 LONG_PTR, *PLONG_PTR; typedef unsigned __int64 ULONG_PTR, *PULONG_PTR;
#define __int3264 __int64
#else
typedef int INT_PTR, *PINT_PTR; typedef unsigned int UINT_PTR, *PUINT_PTR;
typedef long LONG_PTR, *PLONG_PTR; typedef unsigned long ULONG_PTR, *PULONG_PTR;
#define __int3264 __int32
#endif
#endif //midl64
typedef ULONG_PTR DWORD_PTR, *PDWORD_PTR;
//
// The following types are guaranteed to be signed and 64 bits wide.
//
typedef __int64 LONG64, *PLONG64; typedef __int64 INT64, *PINT64;
//
// The following types are guaranteed to be unsigned and 64 bits wide.
//
typedef unsigned __int64 ULONG64, *PULONG64; typedef unsigned __int64 DWORD64, *PDWORD64; typedef unsigned __int64 UINT64, *PUINT64;
//
// SIZE_T used for counts or ranges which need to span the range of
// of a pointer. SSIZE_T is the signed variation.
//
typedef ULONG_PTR SIZE_T, *PSIZE_T; typedef LONG_PTR SSIZE_T, *PSSIZE_T;
//
// useful macros for both 32/64
//
#define OffsetOf(s,m) (SIZE_T)&(((s *)0)->m)
#ifdef __cplusplus
} #endif
#pragma warning ( disable : 4355 )
#include <limits.h>
#include <new.h>
#include <stdarg.h>
#include <stdlib.h>
// calling convention
#define OSSYNCAPI __stdcall
// basic types
typedef int BOOL; #define fFalse BOOL( 0 )
#define fTrue BOOL( !0 )
typedef unsigned char BYTE; typedef unsigned short WORD; typedef unsigned long DWORD; typedef unsigned __int64 QWORD;
// Assertions
// Assertion Failure action
//
// called to indicate to the developer that an assumption is not true
void OSSYNCAPI AssertFail( const char* szMessage, const char* szFilename, long lLine );
// Assert Macros
// asserts that the given expression is true or else fails with the specified message
#define OSSYNCAssertSzRTL( exp, sz ) ( ( exp ) ? (void) 0 : AssertFail( sz, __FILE__, __LINE__ ) )
#ifdef DEBUG
#define OSSYNCAssertSz( exp, sz ) OSSYNCAssertSzRTL( exp, sz )
#else // !DEBUG
#define OSSYNCAssertSz( exp, sz )
#endif // DEBUG
// asserts that the given expression is true or else fails with that expression
#define OSSYNCAssertRTL( exp ) OSSYNCAssertSzRTL( exp, #exp )
#define OSSYNCAssert( exp ) OSSYNCAssertSz( exp, #exp )
// Enforces
// Enforce Failure action
//
// called when a strictly enforced condition has been violated
void OSSYNCAPI EnforceFail( const char* szMessage, const char* szFilename, long lLine );
// Enforce Macros
// the given expression MUST be true or else fails with the specified message
#define OSSYNCEnforceSz( exp, sz ) ( ( exp ) ? (void) 0 : EnforceFail( sz, __FILE__, __LINE__ ) )
// the given expression MUST be true or else fails with that expression
#define OSSYNCEnforce( exp ) OSSYNCEnforceSz( exp, #exp )
#ifdef SYNC_VALIDATE_IRKSEM_USAGE
#define OSSYNCEnforceIrksem( exp, sz ) OSSYNCEnforceSz( exp, sz )
#else // !SYNC_VALIDATE_IRKSEM_USAGE
#define OSSYNCEnforceIrksem( exp, sz )
#endif // SYNC_VALIDATE_IRKSEM_USAGE
// OSSYNC_FOREVER marks all convergence loops
#if defined( _M_IX86 ) && defined( SYNC_USE_X86_ASM )
inline void OSSyncPause() { __asm rep nop } #else // !_M_IX86 || !SYNC_USE_X86_ASM
inline void OSSyncPause() {} #endif // _M_IX86 && SYNC_USE_X86_ASM
#ifdef DEBUG
#define OSSYNC_FOREVER for ( int cLoop = 0; ; cLoop++, OSSyncPause() )
#else // !DEBUG
#define OSSYNC_FOREVER for ( ; ; OSSyncPause() )
#endif // DEBUG
namespace OSSYNC {
class CDumpContext;
// Context Local Storage
class COwner; class CLockDeadlockDetectionInfo;
struct CLS { #ifdef SYNC_DEADLOCK_DETECTION
COwner* pownerLockHead; // list of locks owned by this context
DWORD cDisableOwnershipTracking; // lock ownerships are not tracked for this context
BOOL fOverrideDeadlock; // next lock ownership will not be a deadlock
CLockDeadlockDetectionInfo* plddiLockWait; // lock for which this context is waiting
DWORD groupLockWait; // lock group for which this context is waiting
#endif // SYNC_DEADLOCK_DETECTION
};
// returns the pointer to the current context's local storage
CLS* const OSSYNCAPI Pcls();
// Processor Information
// returns the maximum number of processors this process can utilize
int OSSYNCAPI OSSyncGetProcessorCountMax();
// returns the current number of processors this process can utilize
int OSSYNCAPI OSSyncGetProcessorCount();
// returns the processor number that the current context _MAY_ be executing on
//
// NOTE: the current context may change processors at any time
int OSSYNCAPI OSSyncGetCurrentProcessor();
// sets the processor number returned by OSSyncGetCurrentProcessor()
void OSSYNCAPI OSSyncSetCurrentProcessor( const int iProc );
// Processor Local Storage
// configures the size of processor local storage
BOOL OSSYNCAPI FOSSyncConfigureProcessorLocalStorage( const size_t cbPLS );
// retrieves a pointer to the current context's processor local storage
void* OSSYNCAPI OSSyncGetProcessorLocalStorage();
// retrieves a pointer to a given processor's local storage
void* OSSYNCAPI OSSyncGetProcessorLocalStorage( const size_t iProc );
// High Resolution Timer
// returns the current HRT frequency
QWORD OSSYNCAPI QwOSTimeHRTFreq();
// returns the current HRT count
QWORD OSSYNCAPI QwOSTimeHRTCount();
// Timer
// returns the current tick count where one tick is one millisecond
DWORD OSSYNCAPI DwOSTimeGetTickCount();
// Global Synchronization Constants
// wait time used for testing the state of the kernel object
extern const int cmsecTest;
// wait time used for infinite wait on a kernel object
extern const int cmsecInfinite;
// maximum wait time on a kernel object before a deadlock is suspected
extern const int cmsecDeadlock;
// wait time used for infinite wait on a kernel object without deadlock
extern const int cmsecInfiniteNoDeadlock;
// cache line size
extern const int cbCacheLine;
// Atomic Memory Manipulations
// returns fTrue if the given data is properly aligned for atomic modification
inline const BOOL IsAtomicallyModifiable( long* plTarget ) { return ULONG_PTR( plTarget ) % sizeof( long ) == 0; }
inline const BOOL IsAtomicallyModifiablePointer( void*const* ppvTarget ) { return ULONG_PTR( ppvTarget ) % sizeof( void* ) == 0; }
#if defined( _M_IX86 ) && ( defined( SYNC_USE_X86_ASM ) || _MSC_FULL_VER > 13009037 )
#if _MSC_FULL_VER <= 13009037
#pragma warning( disable: 4035 )
// atomically compares the current value of the target with the specified
// initial value and if equal sets the target to the specified final value.
// the initial value of the target is returned. the exchange is successful
// if the value returned equals the specified initial value. the target
// must be aligned to a four byte boundary
inline long AtomicCompareExchange( long* const plTarget, const long lInitial, const long lFinal ) { OSSYNCAssert( IsAtomicallyModifiable( plTarget ) );
__asm mov ecx, plTarget __asm mov edx, lFinal __asm mov eax, lInitial __asm lock cmpxchg [ecx], edx }
inline void* AtomicCompareExchangePointer( void** const ppvTarget, void* const pvInitial, void* const pvFinal ) { OSSYNCAssert( IsAtomicallyModifiablePointer( ppvTarget ) );
return (void*) AtomicCompareExchange( (long* const) ppvTarget, (const long) pvInitial, (const long) pvFinal ); }
// atomically sets the target to the specified value, returning the target's
// initial value. the target must be aligned to a four byte boundary
inline long AtomicExchange( long* const plTarget, const long lValue ) { OSSYNCAssert( IsAtomicallyModifiable( plTarget ) );
__asm mov ecx, plTarget __asm mov eax, lValue __asm lock xchg [ecx], eax }
inline void* AtomicExchangePointer( void* const * ppvTarget, void* const pvValue ) { OSSYNCAssert( IsAtomicallyModifiablePointer( ppvTarget ) );
return (void*) AtomicExchange( (long* const) ppvTarget, (const long) pvValue ); }
// atomically adds the specified value to the target, returning the target's
// initial value. the target must be aligned to a four byte boundary
inline long AtomicExchangeAdd( long* const plTarget, const long lValue ) { OSSYNCAssert( IsAtomicallyModifiable( plTarget ) );
__asm mov ecx, plTarget __asm mov eax, lValue __asm lock xadd [ecx], eax }
#pragma warning( default: 4035 )
#else
extern "C" { LONG __cdecl _InterlockedExchange( IN OUT LONG volatile *Target, IN LONG Value );
#pragma intrinsic (_InterlockedExchange)
}
inline long AtomicExchange( long* const plTarget, const long lValue ) { OSSYNCAssert( IsAtomicallyModifiable( plTarget ) );
return _InterlockedExchange( plTarget, lValue ); }
extern "C" { LONG __cdecl _InterlockedExchangeAdd( IN OUT LONG volatile *Addend, IN LONG Increment );
#pragma intrinsic (_InterlockedExchangeAdd)
}
inline long AtomicExchangeAdd( long* const plTarget, const long lValue ) { OSSYNCAssert( IsAtomicallyModifiable( plTarget ) );
return _InterlockedExchangeAdd( plTarget, lValue ); }
extern "C" { LONG __cdecl _InterlockedCompareExchange ( IN OUT LONG volatile *Destination, IN LONG ExChange, IN LONG Comperand );
#pragma intrinsic (_InterlockedCompareExchange)
}
inline long AtomicCompareExchange( long* const plTarget, const long lInitial, const long lFinal ) { OSSYNCAssert( IsAtomicallyModifiable( plTarget ) );
return _InterlockedCompareExchange( plTarget, lFinal, lInitial ); }
inline void* AtomicExchangePointer( void* const * ppvTarget, void* const pvValue ) { return (void*)AtomicExchange( (long *)ppvTarget, (long)pvValue ); }
inline void* AtomicCompareExchangePointer( void** const ppvTarget, void* const pvInitial, void* const pvFinal ) { return (void*)AtomicCompareExchange( (long *)ppvTarget, (long)pvInitial, (long)pvFinal ); }
#endif
#elif defined( _WIN64 )
inline long AtomicExchange( long* const plTarget, const long lValue ) { OSSYNCAssert( IsAtomicallyModifiable( plTarget ) );
return InterlockedExchange( plTarget, lValue ); }
inline long AtomicExchangeAdd( long* const plTarget, const long lValue ) { OSSYNCAssert( IsAtomicallyModifiable( plTarget ) );
return InterlockedExchangeAdd( plTarget, lValue ); }
inline long AtomicCompareExchange( long* const plTarget, const long lInitial, const long lFinal ) { OSSYNCAssert( IsAtomicallyModifiable( plTarget ) );
return InterlockedCompareExchange( plTarget, lFinal, lInitial ); }
inline void* AtomicExchangePointer( void* const * ppvTarget, void* const pvValue ) { OSSYNCAssert( IsAtomicallyModifiablePointer( ppvTarget ) );
return InterlockedExchangePointer( (void **)ppvTarget, pvValue ); }
inline void* AtomicCompareExchangePointer( void** const ppvTarget, void* const pvInitial, void* const pvFinal ) { OSSYNCAssert( IsAtomicallyModifiablePointer( ppvTarget ) );
return InterlockedCompareExchangePointer( ppvTarget, pvFinal, pvInitial ); }
#else
long OSSYNCAPI AtomicCompareExchange( long* const plTarget, const long lInitial, const long lFinal ); void* OSSYNCAPI AtomicCompareExchangePointer( void** const ppvTarget, void* const pvInitial, void* const pvFinal ); long OSSYNCAPI AtomicExchange( long* const plTarget, const long lValue ); void* OSSYNCAPI AtomicExchangePointer( void* const * ppvTarget, void* const pvValue ); long OSSYNCAPI AtomicExchangeAdd( long* const plTarget, const long lValue );
#endif
// atomically adds the specified value to the target, returning the target's
// initial value. the target must be aligned to a pointer boundary.
inline void* AtomicExchangeAddPointer( void** const ppvTarget, void* const pvValue ) { void* pvInitial; void* pvFinal; void* pvResult;
OSSYNCAssert( IsAtomicallyModifiablePointer( ppvTarget ) );
OSSYNC_FOREVER { pvInitial = *((void* volatile *)ppvTarget); pvFinal = (void*)( ULONG_PTR( pvInitial ) + ULONG_PTR( pvValue ) ); pvResult = AtomicCompareExchangePointer( ppvTarget, pvInitial, pvFinal );
if ( pvResult == pvInitial ) { break; } }
return pvResult; }
// atomically increments the target, returning the incremented value. the
// target must be aligned to a four byte boundary
inline long AtomicIncrement( long* const plTarget ) { return AtomicExchangeAdd( plTarget, 1 ) + 1; }
// atomically decrements the target, returning the decremented value. the
// target must be aligned to a four byte boundary
inline long AtomicDecrement( long* const plTarget ) { return AtomicExchangeAdd( plTarget, -1 ) - 1; }
// atomically adds the specified value to the target. the target must be
// aligned to a four byte boundary
inline void AtomicAdd( QWORD* const pqwTarget, const QWORD qwValue ) { #ifdef _WIN64
AtomicExchangeAddPointer( (VOID **)pqwTarget, (VOID *)qwValue ); #else
DWORD* const pdwTargetLow = (DWORD*)pqwTarget; DWORD* const pdwTargetHigh = pdwTargetLow + 1;
const DWORD dwValueLow = DWORD( qwValue ); DWORD dwValueHigh = DWORD( qwValue >> 32 );
if ( dwValueLow ) { if ( DWORD( AtomicExchangeAdd( (long*)pdwTargetLow, dwValueLow ) ) + dwValueLow < dwValueLow ) { dwValueHigh++; } } if ( dwValueHigh ) { AtomicExchangeAdd( (long*)pdwTargetHigh, dwValueHigh ); } #endif
}
// Atomically increments a DWORD counter, returning TRUE if the final
// value is less than or equal to a specified maximum, or FALSE otherwise.
// The pre-incremented value is returned in *pdwInitial
// WARNING: to determine if the maximum value has been reached, an UNSIGNED
// comparison is performed
inline BOOL FAtomicIncrementMax( volatile DWORD * const pdw, DWORD * const pdwInitial, const DWORD dwMax ) { OSSYNC_FOREVER { const DWORD dwInitial = *pdw; if ( dwInitial < dwMax ) { const DWORD dwFinal = dwInitial + 1; if ( dwInitial == (DWORD)AtomicCompareExchange( (LONG *)pdw, (LONG)dwInitial, (LONG)dwFinal ) ) { *pdwInitial = dwInitial; return fTrue; } } else return fFalse; }
// should be impossible
OSSYNCAssert( fFalse ); return fFalse; }
// Atomically increments a pointer-sized counter, returning TRUE if the final
// value is less than or equal to a specified maximum, or FALSE otherwise.
// The pre-incremented value is returned in *ppvInitial
// WARNING: to determine if the maximum value has been reached, an UNSIGNED
// comparison is performed
inline BOOL FAtomicIncrementPointerMax( volatile VOID ** const ppv, VOID ** const ppvInitial, const VOID * const pvMax ) { OSSYNC_FOREVER { const QWORD qwInitial = QWORD( *ppv ); if ( qwInitial < (QWORD)pvMax ) { const QWORD qwFinal = qwInitial + 1; if ( qwInitial == (QWORD)AtomicCompareExchangePointer( (VOID **)ppv, (VOID *)qwInitial, (VOID *)qwFinal ) ) { *ppvInitial = (VOID *)qwInitial; return fTrue; } } else return fFalse; }
// should be impossible
OSSYNCAssert( fFalse ); return fFalse; }
// Enhanced Synchronization Object State Container
//
// This class manages a "simple" or normal state for an arbitrary sync object
// and its "enhanced" counterpart. Which type is used depends on the build.
// The goal is to maintain a footprint equal to the normal state so that other
// classes that contain this object do not fluctuate in size depending on what
// build options have been selected. For example, a performance build might
// need extra storage to collect performance stats on the object. This data
// will force the object to be allocated elsewhere in memory but will not change
// the size of the object in its containing class.
//
// Template Arguments:
//
// CState sync object state class
// CStateInit sync object state class ctor arg type
// CInformation sync object information class
// CInformationInit sync object information class ctor arg type
void* OSSYNCAPI ESMemoryNew( size_t cb ); void OSSYNCAPI ESMemoryDelete( void* pv );
// determine when enhanced state is needed
#if defined( SYNC_ANALYZE_PERFORMANCE ) || defined( SYNC_DEADLOCK_DETECTION )
#define SYNC_ENHANCED_STATE
#endif // SYNC_ANALYZE_PERFORMANCE || SYNC_DEADLOCK_DETECTION
template< class CState, class CStateInit, class CInformation, class CInformationInit > class CEnhancedStateContainer { public:
// types
// enhanced state
class CEnhancedState : public CState, public CInformation { public:
CEnhancedState( const CStateInit& si, const CInformationInit& ii ) : CState( si ), CInformation( ii )
{ }
void* operator new( size_t cb ) { return ESMemoryNew( cb ); } void operator delete( void* pv ) { ESMemoryDelete( pv ); } };
// member functions
// ctors / dtors
CEnhancedStateContainer( const CStateInit& si, const CInformationInit& ii ) { #ifdef SYNC_ENHANCED_STATE
m_pes = new CEnhancedState( si, ii );
#else // !SYNC_ENHANCED_STATE
new( (CState*) m_rgbState ) CState( si );
#endif // SYNC_ENHANCED_STATE
}
~CEnhancedStateContainer() { #ifdef SYNC_ENHANCED_STATE
delete m_pes; #ifdef DEBUG
m_pes = NULL; #endif // DEBUG
#else // !SYNC_ENHANCED_STATE
( (CState*) m_rgbState )->~CState();
#endif // SYNC_ENHANCED_STATE
}
// accessors
CEnhancedState& State() const { #ifdef SYNC_ENHANCED_STATE
return *m_pes;
#else // !SYNC_ENHANCED_STATE
// NOTE: this assumes that CInformation has no storage!
return *( (CEnhancedState*) m_rgbState );
#endif // SYNC_ENHANCED_STATE
}
// debugging support
void Dump( CDumpContext& dc ) const;
private:
// data members
// either a pointer to the enhanced state elsewhere in memory or the
// actual state data, depending on the mode of the sync object
union { CEnhancedState* m_pes; BYTE m_rgbState[ sizeof( CState ) ]; }; };
// Synchronization Object Base Class
//
// All Synchronization Objects are derived from this class
class CSyncObject { public:
// member functions
// ctors / dtors
CSyncObject() {} ~CSyncObject() {}
private:
// member functions
// operators
CSyncObject& operator=( CSyncObject& ); // disallowed
};
// Synchronization Object Basic Information
class CSyncBasicInfo { public:
// member functions
// ctors / dtors
CSyncBasicInfo( const char* szInstanceName ); ~CSyncBasicInfo();
// manipulators
void SetTypeName( const char* szTypeName ); void SetInstance( const CSyncObject* const psyncobj );
// accessors
#ifdef SYNC_ENHANCED_STATE
const char* SzInstanceName() const { return m_szInstanceName; } const char* SzTypeName() const { return m_szTypeName; } const CSyncObject* const Instance() const { return m_psyncobj; }
#endif // SYNC_ENHANCED_STATE
// debugging support
void Dump( CDumpContext& dc ) const;
private:
// member functions
// operators
CSyncBasicInfo& operator=( CSyncBasicInfo& ); // disallowed
// data members
#ifdef SYNC_ENHANCED_STATE
// Instance Name
const char* m_szInstanceName;
// Type Name
const char* m_szTypeName;
// Instance
const CSyncObject* m_psyncobj;
#endif // SYNC_ENHANCED_STATE
};
// sets the type name for the synchronization object
inline void CSyncBasicInfo::SetTypeName( const char* szTypeName ) { #ifdef SYNC_ENHANCED_STATE
m_szTypeName = szTypeName;
#endif // SYNC_ENHANCED_STATE
}
// sets the instance pointer for the synchronization object
inline void CSyncBasicInfo::SetInstance( const CSyncObject* const psyncobj ) { #ifdef SYNC_ENHANCED_STATE
m_psyncobj = psyncobj;
#endif // SYNC_ENHANCED_STATE
}
// Synchronization Object Performance: Wait Times
class CSyncPerfWait { public:
// member functions
// ctors / dtors
CSyncPerfWait(); ~CSyncPerfWait();
// member functions
// manipulators
void StartWait(); void StopWait();
// accessors
#ifdef SYNC_ANALYZE_PERFORMANCE
QWORD CWaitTotal() const { return m_cWait; } double CsecWaitElapsed() const { return (double)(signed __int64)m_qwHRTWaitElapsed / (double)(signed __int64)QwOSTimeHRTFreq(); }
#endif // SYNC_ANALYZE_PERFORMANCE
// debugging support
void Dump( CDumpContext& dc ) const;
private:
// member functions
// operators
CSyncPerfWait& operator=( CSyncPerfWait& ); // disallowed
// data members
#ifdef SYNC_ANALYZE_PERFORMANCE
// wait count
volatile QWORD m_cWait;
// elapsed wait time
volatile QWORD m_qwHRTWaitElapsed;
#endif // SYNC_ANALYZE_PERFORMANCE
};
// starts the wait timer for the sync object
inline void CSyncPerfWait::StartWait() { #ifdef SYNC_ANALYZE_PERFORMANCE
// increment the wait count
AtomicAdd( (QWORD*)&m_cWait, 1 );
// subtract the start wait time from the elapsed wait time. this starts
// an elapsed time computation for this context. StopWait() will later
// add the end wait time to the elapsed time, causing the following net
// effect:
//
// m_qwHRTWaitElapsed += <end time> - <start time>
//
// we simply choose to go ahead and do the subtraction now to save storage
AtomicAdd( (QWORD*)&m_qwHRTWaitElapsed, QWORD( -__int64( QwOSTimeHRTCount() ) ) );
#endif // SYNC_ANALYZE_PERFORMANCE
}
// stops the wait timer for the sync object
inline void CSyncPerfWait::StopWait() { #ifdef SYNC_ANALYZE_PERFORMANCE
// add the end wait time to the elapsed wait time. this completes the
// computation started in StartWait()
AtomicAdd( (QWORD*)&m_qwHRTWaitElapsed, QwOSTimeHRTCount() );
#endif // SYNC_ANALYZE_PERFORMANCE
}
// Null Synchronization Object State Initializer
class CSyncStateInitNull { };
extern const CSyncStateInitNull syncstateNull;
// Kernel Semaphore Information
class CKernelSemaphoreInfo : public CSyncBasicInfo, public CSyncPerfWait { public:
// member functions
// ctors / dtors
CKernelSemaphoreInfo( const CSyncBasicInfo& sbi ) : CSyncBasicInfo( sbi ) { }
// debugging support
void Dump( CDumpContext& dc ) const; };
// Kernel Semaphore State
class CKernelSemaphoreState { public:
// member functions
// ctors / dtors
CKernelSemaphoreState( const CSyncStateInitNull& null ) : m_handle( 0 ) {}
// manipulators
void SetHandle( void * handle ) { m_handle = handle; }
// accessors
void* Handle() { return m_handle; }
// debugging support
void Dump( CDumpContext& dc ) const;
private:
// member functions
// operators
CKernelSemaphoreState& operator=( CKernelSemaphoreState& ); // disallowed
// data members
// kernel semaphore handle
void* m_handle; };
// Kernel Semaphore
class CKernelSemaphore : private CSyncObject, private CEnhancedStateContainer< CKernelSemaphoreState, CSyncStateInitNull, CKernelSemaphoreInfo, CSyncBasicInfo > { public:
// member functions
// ctors / dtors
CKernelSemaphore( const CSyncBasicInfo& sbi ); ~CKernelSemaphore();
// init / term
const BOOL FInit(); void Term();
// manipulators
void Acquire(); const BOOL FTryAcquire(); const BOOL FAcquire( const int cmsecTimeout ); void Release( const int cToRelease = 1 );
// accessors
const BOOL FReset();
// debugging support
void Dump( CDumpContext& dc ) const;
private:
// member functions
// operators
CKernelSemaphore& operator=( CKernelSemaphore& ); // disallowed
// accessors
const BOOL FInitialized(); };
// acquire one count of the semaphore, waiting forever if necessary
inline void CKernelSemaphore::Acquire() { // semaphore should be initialized
OSSYNCAssert( FInitialized() );
// wait for the semaphore
const BOOL fAcquire = FAcquire( cmsecInfinite ); OSSYNCAssert( fAcquire ); }
// try to acquire one count of the semaphore without waiting. returns 0 if a
// count could not be acquired
inline const BOOL CKernelSemaphore::FTryAcquire() { // semaphore should be initialized
OSSYNCAssert( FInitialized() );
// test the semaphore
return FAcquire( cmsecTest ); }
// returns fTrue if the semaphore has no available counts
inline const BOOL CKernelSemaphore::FReset() { // semaphore should be initialized
OSSYNCAssert( FInitialized() );
// test the semaphore
return !FTryAcquire(); }
// returns fTrue if the semaphore has been initialized
inline const BOOL CKernelSemaphore::FInitialized() { return State().Handle() != 0; }
// Kernel Semaphore Pool
class CKernelSemaphorePool { public:
// types
// index to a ref counted kernel semaphore
typedef unsigned short IRKSEM; enum { irksemAllocated = 0xFFFE, irksemNil = 0xFFFF };
// member functions
// ctors / dtors
CKernelSemaphorePool(); ~CKernelSemaphorePool();
// init / term
const BOOL FInit(); void Term();
// manipulators
const IRKSEM Allocate( const CSyncObject* const psyncobj ); void Reference( const IRKSEM irksem ); void Unreference( const IRKSEM irksem );
// accessors
CKernelSemaphore& Ksem( const IRKSEM irksem, const CSyncObject* const psyncobj ) const;
const BOOL FInitialized() const;
long CksemAlloc() const { return m_cksem; }
private:
// types
// reference counted kernel semaphore
class CReferencedKernelSemaphore : public CKernelSemaphore { public:
// member functions
// ctors / dtors
CReferencedKernelSemaphore(); ~CReferencedKernelSemaphore();
// init / term
const BOOL FInit(); void Term();
// manipulators
BOOL FAllocate(); void Release();
void SetUser( const CSyncObject* const psyncobj );
void Reference(); const BOOL FUnreference();
// accessors
const BOOL FInUse() const { return m_fInUse; } const int CReference() const { return m_cReference; }
#ifdef SYNC_VALIDATE_IRKSEM_USAGE
const CSyncObject* const PsyncobjUser() const { return m_psyncobjUser; } #endif // SYNC_VALIDATE_IRKSEM_USAGE
private:
// member functions
// operators
CReferencedKernelSemaphore& operator=( CReferencedKernelSemaphore& ); // disallowed
// data members
// transacted state representation
union { volatile long m_l; struct { volatile unsigned short m_cReference:15; // 0 <= m_cReference <= ( 1 << 15 ) - 1
volatile unsigned short m_fInUse:1; // m_fInUse = { 0, 1 }
}; };
volatile long m_fAvailable;
#ifdef SYNC_VALIDATE_IRKSEM_USAGE
// sync object currently using this semaphore
const CSyncObject* volatile m_psyncobjUser;
#else // SYNC_VALIDATE_IRKSEM_USAGE
BYTE m_rgbReserved1[4];
#endif // SYNC_VALIDATE_IRKSEM_USAGE
BYTE m_rgbReserved2[16]; };
// member functions
// operators
CKernelSemaphorePool& operator=( CKernelSemaphorePool& ); // disallowed
// manipulators
const IRKSEM AllocateNew(); void Free( const IRKSEM irksem );
// data members
// semaphore index to semaphore map
CReferencedKernelSemaphore* m_mpirksemrksem;
// semaphore count
volatile long m_cksem; };
// allocates an IRKSEM from the pool on behalf of the specified sync object
//
// NOTE: the returned IRKSEM has one reference count
inline const CKernelSemaphorePool::IRKSEM CKernelSemaphorePool::Allocate( const CSyncObject* const psyncobj ) { // semaphore pool should be initialized
OSSYNCAssert( FInitialized() );
// there are semaphores in the semaphore pool
IRKSEM irksem = irksemNil; if ( m_cksem ) { // hash into the semaphore pool based on this context's CLS and the time
IRKSEM irksemHash = IRKSEM( UINT_PTR( UINT_PTR( Pcls() ) / sizeof( CLS ) + UINT_PTR( QwOSTimeHRTCount() ) ) % m_cksem ); OSSYNCAssert( irksemHash >= 0 && irksemHash < m_cksem );
// try to allocate a semaphore, scanning forwards through the pool
for ( long cLoop = 0; cLoop < m_cksem; cLoop++, irksemHash = IRKSEM( ++irksemHash % m_cksem ) ) { if ( m_mpirksemrksem[ irksemHash ].FAllocate() ) { irksem = irksemHash; break; } } }
// if we do not yet have a semaphore, allocate one
if ( irksem == irksemNil ) { irksem = AllocateNew(); }
// validate irksem retrieved
OSSYNCAssert( irksem != irksemNil ); OSSYNCAssert( irksem >= 0 ); OSSYNCAssert( irksem < m_cksem );
// set the user for this semaphore
m_mpirksemrksem[irksem].SetUser( psyncobj );
// ensure that the semaphore we retrieved is reset
OSSYNCEnforceIrksem( m_mpirksemrksem[irksem].FReset(), "Illegal allocation of a Kernel Semaphore with available counts!" );
// return the allocated semaphore
return irksem; }
// add a reference count to an IRKSEM
inline void CKernelSemaphorePool::Reference( const IRKSEM irksem ) { // validate IN args
OSSYNCAssert( irksem != irksemNil ); OSSYNCAssert( irksem >= 0 ); OSSYNCAssert( irksem < m_cksem );
// semaphore pool should be initialized
OSSYNCAssert( FInitialized() );
// increment the reference count for this IRKSEM
m_mpirksemrksem[irksem].Reference(); }
// remove a reference count from an IRKSEM, freeing it if the reference count
// drops to zero and it is not currently in use
inline void CKernelSemaphorePool::Unreference( const IRKSEM irksem ) { // validate IN args
OSSYNCAssert( irksem != irksemNil ); OSSYNCAssert( irksem >= 0 ); OSSYNCAssert( irksem < m_cksem );
// semaphore pool should be initialized
OSSYNCAssert( FInitialized() );
// decrement the reference count for this IRKSEM
const BOOL fFree = m_mpirksemrksem[irksem].FUnreference();
// we need to free the semaphore
if ( fFree ) { // free the IRKSEM back to the allocation stack
Free( irksem ); } }
// returns the CKernelSemaphore object associated with the given IRKSEM
inline CKernelSemaphore& CKernelSemaphorePool::Ksem( const IRKSEM irksem, const CSyncObject* const psyncobj ) const { // validate IN args
OSSYNCAssert( irksem != irksemNil ); OSSYNCAssert( irksem >= 0 ); OSSYNCAssert( irksem < m_cksem );
// semaphore pool should be initialized
OSSYNCAssert( FInitialized() );
// we had better be retrieving this semaphore for the right sync object
OSSYNCEnforceIrksem( m_mpirksemrksem[irksem].PsyncobjUser() == psyncobj, "Illegal use of a Kernel Semaphore by another Synchronization Object" );
// return kernel semaphore
return m_mpirksemrksem[irksem]; }
// returns fTrue if the semaphore pool has been initialized
inline const BOOL CKernelSemaphorePool::FInitialized() const { return m_mpirksemrksem != NULL; }
// allocates a new irksem and adds it to the stack's irksem pool
inline const CKernelSemaphorePool::IRKSEM CKernelSemaphorePool::AllocateNew() { // atomically allocate a position in the stack's irksem pool for our new
// irksem
const long lDelta = 0x00000001; const long lBI = AtomicExchangeAdd( (long*) &m_cksem, lDelta );
const IRKSEM irksem = IRKSEM( lBI );
// initialize this irksem
new ( &m_mpirksemrksem[irksem] ) CReferencedKernelSemaphore;
BOOL fInitKernelSemaphore = m_mpirksemrksem[irksem].FInit(); OSSYNCEnforceSz( fInitKernelSemaphore, "Could not allocate a Kernel Semaphore" );
// return the irksem for use
return irksem; }
// frees the given IRKSEM back to the allocation stack
inline void CKernelSemaphorePool::Free( const IRKSEM irksem ) { // validate IN args
OSSYNCAssert( irksem != irksemNil ); OSSYNCAssert( irksem >= 0 ); OSSYNCAssert( irksem < m_cksem );
// semaphore pool should be initialized
OSSYNCAssert( FInitialized() );
// the semaphore to free had better not be in use
OSSYNCEnforceIrksem( !m_mpirksemrksem[irksem].FInUse(), "Illegal free of a Kernel Semaphore that is still in use" );
// the semaphore had better not already be freed
OSSYNCEnforceIrksem( !m_mpirksemrksem[irksem].FAllocate(), "Illegal free of a Kernel Semaphore that is already free" );
// ensure that the semaphore to free is reset
OSSYNCEnforceIrksem( m_mpirksemrksem[irksem].FReset(), "Illegal free of a Kernel Semaphore that has available counts" );
// release the semaphore to the pool
m_mpirksemrksem[irksem].Release(); }
// Referenced Kernel Semaphore
// attempts to allocate the semaphore, returning fTrue on success
inline BOOL CKernelSemaphorePool::CReferencedKernelSemaphore::FAllocate() { return m_fAvailable && AtomicExchange( (long*)&m_fAvailable, 0 ); }
// releases the semaphore
inline void CKernelSemaphorePool::CReferencedKernelSemaphore::Release() { AtomicExchange( (long*)&m_fAvailable, 1 ); }
// sets the user for the semaphore and gives the user an initial reference
inline void CKernelSemaphorePool::CReferencedKernelSemaphore::SetUser( const CSyncObject* const psyncobj ) { // this semaphore had better not already be in use
OSSYNCEnforceIrksem( !m_fInUse, "Illegal allocation of a Kernel Semaphore that is already in use" ); OSSYNCEnforceIrksem( !m_psyncobjUser, "Illegal allocation of a Kernel Semaphore that is already in use" );
// mark this semaphore as in use and add an initial reference count for the
// user
AtomicExchangeAdd( (long*) &m_l, 0x00008001 ); #ifdef SYNC_VALIDATE_IRKSEM_USAGE
m_psyncobjUser = psyncobj; #endif // SYNC_VALIDATE_IRKSEM_USAGE
}
// add a reference count to the semaphore
inline void CKernelSemaphorePool::CReferencedKernelSemaphore::Reference() { // increment the reference count
AtomicIncrement( (long*) &m_l );
// there had better be at least one reference count!
OSSYNCAssert( m_cReference > 0 ); }
// remove a reference count from the semaphore, returning fTrue if the last
// reference count on the semaphore was removed and the semaphore was in use
// (this is the condition on which we can free the semaphore to the stack)
inline const BOOL CKernelSemaphorePool::CReferencedKernelSemaphore::FUnreference() { // there had better be at least one reference count!
OSSYNCAssert( m_cReference > 0 );
// try forever until we succeed in removing our reference count
long lBI; OSSYNC_FOREVER { // read the current state of the control word as our expected before image
const long lBIExpected = m_l;
// compute the after image of the control word by decrementing the
// reference count and reseting the In Use bit if and only if we are
// removing the last reference count
const long lAI = lBIExpected + ( lBIExpected == 0x00008001 ? 0xFFFF7FFF : 0xFFFFFFFF );
// attempt to perform the transacted state transition on the control word
lBI = AtomicCompareExchange( (long*)&m_l, lBIExpected, lAI );
// the transaction failed
if ( lBI != lBIExpected ) { // try again
continue; }
// the transaction succeeded
else { // we're done
break; } }
// return fTrue if we removed the last reference count and reset the In Use bit
if ( lBI == 0x00008001 ) { #ifdef SYNC_VALIDATE_IRKSEM_USAGE
m_psyncobjUser = NULL; #endif // SYNC_VALIDATE_IRKSEM_USAGE
return fTrue; } else { return fFalse; } }
// Global Kernel Semaphore Pool
extern CKernelSemaphorePool ksempoolGlobal;
// Synchronization Object Performance: Acquisition
class CSyncPerfAcquire { public:
// member functions
// ctors / dtors
CSyncPerfAcquire(); ~CSyncPerfAcquire();
// member functions
// manipulators
void SetAcquire();
void SetContend();
// accessors
#ifdef SYNC_ANALYZE_PERFORMANCE
QWORD CAcquireTotal() const { return m_cAcquire; } QWORD CContendTotal() const { return m_cContend; }
#endif // SYNC_ANALYZE_PERFORMANCE
// debugging support
void Dump( CDumpContext& dc ) const;
private:
// member functions
// operators
CSyncPerfAcquire& operator=( CSyncPerfAcquire& ); // disallowed
// data members
#ifdef SYNC_ANALYZE_PERFORMANCE
// acquire count
volatile QWORD m_cAcquire;
// contend count
volatile QWORD m_cContend;
#endif // SYNC_ANALYZE_PERFORMANCE
};
// specifies that the sync object was acquired
inline void CSyncPerfAcquire::SetAcquire() { #ifdef SYNC_ANALYZE_PERFORMANCE
AtomicAdd( (QWORD*)&m_cAcquire, 1 );
#endif // SYNC_ANALYZE_PERFORMANCE
}
// specifies that a contention occurred while acquiring the sync object
inline void CSyncPerfAcquire::SetContend() { #ifdef SYNC_ANALYZE_PERFORMANCE
AtomicAdd( (QWORD*)&m_cContend, 1 );
#endif // SYNC_ANALYZE_PERFORMANCE
}
// Semaphore Information
class CSemaphoreInfo : public CSyncBasicInfo, public CSyncPerfWait, public CSyncPerfAcquire { public:
// member functions
// ctors / dtors
CSemaphoreInfo( const CSyncBasicInfo& sbi ) : CSyncBasicInfo( sbi ) { }
// debugging support
void Dump( CDumpContext& dc ) const; };
// Semaphore State
class CSemaphoreState { public:
// member functions
// ctors / dtors
CSemaphoreState( const CSyncStateInitNull& null ) : m_cAvail( 0 ) {} CSemaphoreState( const int cAvail ); CSemaphoreState( const int cWait, const int irksem ); ~CSemaphoreState() {}
// operators
CSemaphoreState& operator=( CSemaphoreState& state ) { m_cAvail = state.m_cAvail; return *this; }
// manipulators
const BOOL FChange( const CSemaphoreState& stateCur, const CSemaphoreState& stateNew ); const BOOL FIncAvail( const int cToInc ); const BOOL FDecAvail();
// accessors
const BOOL FNoWait() const { return m_cAvail >= 0; } const BOOL FWait() const { return m_cAvail < 0; } const BOOL FAvail() const { return m_cAvail > 0; } const BOOL FNoWaitAndNoAvail() const { return m_cAvail == 0; }
const int CAvail() const { OSSYNCAssert( FNoWait() ); return m_cAvail; } const int CWait() const { OSSYNCAssert( FWait() ); return -m_cWaitNeg; } const CKernelSemaphorePool::IRKSEM Irksem() const { OSSYNCAssert( FWait() ); return CKernelSemaphorePool::IRKSEM( m_irksem ); }
// debugging support
void Dump( CDumpContext& dc ) const;
private:
// data members
// transacted state representation (switched on bit 31)
union { // Mode 0: no waiters
volatile long m_cAvail; // 0 <= m_cAvail <= ( 1 << 31 ) - 1
// Mode 1: waiters
struct { volatile unsigned short m_irksem; // 0 <= m_irksem <= ( 1 << 16 ) - 2
volatile short m_cWaitNeg; // -( ( 1 << 15 ) - 1 ) <= m_cWaitNeg <= -1
}; }; };
// ctor
inline CSemaphoreState::CSemaphoreState( const int cAvail ) { // validate IN args
OSSYNCAssert( cAvail >= 0 ); OSSYNCAssert( cAvail <= 0x7FFFFFFF );
// set available count
m_cAvail = long( cAvail ); }
// ctor
inline CSemaphoreState::CSemaphoreState( const int cWait, const int irksem ) { // validate IN args
OSSYNCAssert( cWait > 0 ); OSSYNCAssert( cWait <= 0x7FFF ); OSSYNCAssert( irksem >= 0 ); OSSYNCAssert( irksem <= 0xFFFE );
// set waiter count
m_cWaitNeg = short( -cWait );
// set semaphore
m_irksem = (unsigned short) irksem; }
// changes the transacted state of the semaphore using a transacted memory
// compare/exchange operation, returning fFalse on failure
inline const BOOL CSemaphoreState::FChange( const CSemaphoreState& stateCur, const CSemaphoreState& stateNew ) { return AtomicCompareExchange( (long*)&m_cAvail, stateCur.m_cAvail, stateNew.m_cAvail ) == stateCur.m_cAvail; }
// tries to increase the available count on the semaphore by the count
// given using a transacted memory compare/exchange operation, returning fFalse
// on failure
__forceinline const BOOL CSemaphoreState::FIncAvail( const int cToInc ) { // try forever to change the state of the semaphore
OSSYNC_FOREVER { // get current value
const long cAvail = m_cAvail;
// munge start value such that the transaction will only work if we are in
// mode 0 (we do this to save a branch)
const long cAvailStart = cAvail & 0x7FFFFFFF;
// compute end value relative to munged start value
const long cAvailEnd = cAvailStart + cToInc;
// validate transaction
OSSYNCAssert( cAvail < 0 || ( cAvailStart >= 0 && cAvailEnd <= 0x7FFFFFFF && cAvailEnd == cAvailStart + cToInc ) );
// attempt the transaction
const long cAvailOld = AtomicCompareExchange( (long*)&m_cAvail, cAvailStart, cAvailEnd );
// the transaction succeeded
if ( cAvailOld == cAvailStart ) { // return success
return fTrue; }
// the transaction failed
else { // the transaction failed because of a collision with another context
if ( cAvailOld >= 0 ) { // try again
continue; }
// the transaction failed because there are waiters
else { // return failure
return fFalse; } } } }
// tries to decrease the available count on the semaphore by one using a
// transacted memory compare/exchange operation, returning fFalse on failure
__forceinline const BOOL CSemaphoreState::FDecAvail() { // try forever to change the state of the semaphore
OSSYNC_FOREVER { // get current value
const long cAvail = m_cAvail;
// this function has no effect on 0x80000000, so this MUST be an illegal
// value!
OSSYNCAssert( cAvail != 0x80000000 );
// munge end value such that the transaction will only work if we are in
// mode 0 and we have at least one available count (we do this to save a
// branch)
const long cAvailEnd = ( cAvail - 1 ) & 0x7FFFFFFF;
// compute start value relative to munged end value
const long cAvailStart = cAvailEnd + 1;
// validate transaction
OSSYNCAssert( cAvail <= 0 || ( cAvailStart > 0 && cAvailEnd >= 0 && cAvailEnd == cAvail - 1 ) );
// attempt the transaction
const long cAvailOld = AtomicCompareExchange( (long*)&m_cAvail, cAvailStart, cAvailEnd );
// the transaction succeeded
if ( cAvailOld == cAvailStart ) { // return success
return fTrue; }
// the transaction failed
else { // the transaction failed because of a collision with another context
if ( cAvailOld > 0 ) { // try again
continue; }
// the transaction failed because there are no available counts
else { // return failure
return fFalse; } } } }
// Semaphore
class CSemaphore : private CSyncObject, private CEnhancedStateContainer< CSemaphoreState, CSyncStateInitNull, CSemaphoreInfo, CSyncBasicInfo > { public:
// member functions
// ctors / dtors
CSemaphore( const CSyncBasicInfo& sbi ); ~CSemaphore();
// manipulators
void Acquire(); const BOOL FTryAcquire(); const BOOL FAcquire( const int cmsecTimeout ); void Release( const int cToRelease = 1 );
// accessors
const int CWait() const; const int CAvail() const;
// debugging support
void Dump( CDumpContext& dc ) const;
private:
// member functions
// operators
CSemaphore& operator=( CSemaphore& ); // disallowed
// manipulators
const BOOL _FAcquire( const int cmsecTimeout ); void _Release( const int cToRelease ); };
// acquire one count of the semaphore, waiting forever if necessary
inline void CSemaphore::Acquire() { // we will wait forever, so we should not timeout
int fAcquire = FAcquire( cmsecInfinite ); OSSYNCAssert( fAcquire ); }
// try to acquire one count of the semaphore without waiting or spinning.
// returns fFalse if a count could not be acquired
inline const BOOL CSemaphore::FTryAcquire() { // only try to perform a simple decrement of the available count
const BOOL fAcquire = State().FDecAvail();
// we did not acquire the semaphore
if ( !fAcquire ) { // this is a contention
State().SetContend(); }
// we did acquire the semaphore
else { // note that we acquired a count
State().SetAcquire(); }
return fAcquire; }
// acquire one count of the semaphore, waiting only for the specified interval.
// returns fFalse if the wait timed out before a count could be acquired
inline const BOOL CSemaphore::FAcquire( const int cmsecTimeout ) { // first try to quickly grab an available count. if that doesn't work,
// retry acquire using the full state machine
return FTryAcquire() || _FAcquire( cmsecTimeout ); }
// releases the given number of counts to the semaphore, waking the appropriate
// number of waiters
inline void CSemaphore::Release( const int cToRelease ) { // we failed to perform a simple increment of the available count
if ( !State().FIncAvail( cToRelease ) ) { // retry release using the full state machine
_Release( cToRelease ); } }
// returns the number of execution contexts waiting on the semaphore
inline const int CSemaphore::CWait() const { // read the current state of the semaphore
const CSemaphoreState stateCur = (CSemaphoreState&) State();
// return the waiter count
return stateCur.FWait() ? stateCur.CWait() : 0; }
// returns the number of available counts on the semaphore
inline const int CSemaphore::CAvail() const { // read the current state of the semaphore
const CSemaphoreState stateCur = (CSemaphoreState&) State();
// return the available count
return stateCur.FNoWait() ? stateCur.CAvail() : 0; }
// Auto-Reset Signal Information
class CAutoResetSignalInfo : public CSyncBasicInfo, public CSyncPerfWait, public CSyncPerfAcquire { public:
// member functions
// ctors / dtors
CAutoResetSignalInfo( const CSyncBasicInfo& sbi ) : CSyncBasicInfo( sbi ) { }
// debugging support
void Dump( CDumpContext& dc ) const; };
// Auto-Reset Signal State
class CAutoResetSignalState { public:
// member functions
// ctors / dtors
CAutoResetSignalState( const CSyncStateInitNull& null ) : m_fSet( 0 ) {} CAutoResetSignalState( const int fSet ); CAutoResetSignalState( const int cWait, const int irksem ); ~CAutoResetSignalState() {}
// operators
CAutoResetSignalState& operator=( CAutoResetSignalState& state ) { m_fSet = state.m_fSet; return *this; }
// manipulators
const BOOL FChange( const CAutoResetSignalState& stateCur, const CAutoResetSignalState& stateNew ); const BOOL FSimpleSet(); const BOOL FSimpleReset();
// accessors
const BOOL FNoWait() const { return m_fSet >= 0; } const BOOL FWait() const { return m_fSet < 0; } const BOOL FNoWaitAndSet() const { return m_fSet > 0; } const BOOL FNoWaitAndNotSet() const { return m_fSet == 0; }
const BOOL FSet() const { OSSYNCAssert( FNoWait() ); return m_fSet; } const int CWait() const { OSSYNCAssert( FWait() ); return -m_cWaitNeg; } const CKernelSemaphorePool::IRKSEM Irksem() const { OSSYNCAssert( FWait() ); return CKernelSemaphorePool::IRKSEM( m_irksem ); }
// debugging support
void Dump( CDumpContext& dc ) const;
private:
// data members
// transacted state representation (switched on bit 31)
union { // Mode 0: no waiters
volatile long m_fSet; // m_fSet = { 0, 1 }
// Mode 1: waiters
struct { volatile unsigned short m_irksem; // 0 <= m_irksem <= ( 1 << 16 ) - 2
volatile short m_cWaitNeg; // -( ( 1 << 15 ) - 1 ) <= m_cWaitNeg <= -1
}; }; };
// ctor
inline CAutoResetSignalState::CAutoResetSignalState( const int fSet ) { // validate IN args
OSSYNCAssert( fSet == 0 || fSet == 1 );
// set state
m_fSet = long( fSet ); }
// ctor
inline CAutoResetSignalState::CAutoResetSignalState( const int cWait, const int irksem ) { // validate IN args
OSSYNCAssert( cWait > 0 ); OSSYNCAssert( cWait <= 0x7FFF ); OSSYNCAssert( irksem >= 0 ); OSSYNCAssert( irksem <= 0xFFFE );
// set waiter count
m_cWaitNeg = short( -cWait );
// set semaphore
m_irksem = (unsigned short) irksem; }
// changes the transacted state of the signal using a transacted memory
// compare/exchange operation, returning 0 on failure
inline const BOOL CAutoResetSignalState::FChange( const CAutoResetSignalState& stateCur, const CAutoResetSignalState& stateNew ) { return AtomicCompareExchange( (long*)&m_fSet, stateCur.m_fSet, stateNew.m_fSet ) == stateCur.m_fSet; }
// tries to set the signal state from either the set or reset with no waiters states
// using a transacted memory compare/exchange operation, returning fFalse on failure
__forceinline const BOOL CAutoResetSignalState::FSimpleSet() { // try forever to change the state of the signal
OSSYNC_FOREVER { // get current value
const long fSet = m_fSet;
// munge start value such that the transaction will only work if we are in
// mode 0 (we do this to save a branch)
const long fSetStart = fSet & 0x7FFFFFFF;
// compute end value relative to munged start value
const long fSetEnd = 1;
// validate transaction
OSSYNCAssert( fSet < 0 || ( ( fSetStart == 0 || fSetStart == 1 ) && fSetEnd == 1 ) );
// attempt the transaction
const long fSetOld = AtomicCompareExchange( (long*)&m_fSet, fSetStart, fSetEnd );
// the transaction succeeded
if ( fSetOld == fSetStart ) { // return success
return fTrue; }
// the transaction failed
else { // the transaction failed because of a collision with another context
if ( fSetOld >= 0 ) { // try again
continue; }
// the transaction failed because there are waiters
else { // return failure
return fFalse; } } } }
// tries to reset the signal state from either the set or reset with no waiters states
// using a transacted memory compare/exchange operation, returning fFalse on failure
__forceinline const BOOL CAutoResetSignalState::FSimpleReset() { // try forever to change the state of the signal
OSSYNC_FOREVER { // get current value
const long fSet = m_fSet;
// munge start value such that the transaction will only work if we are in
// mode 0 (we do this to save a branch)
const long fSetStart = fSet & 0x7FFFFFFF;
// compute end value relative to munged start value
const long fSetEnd = 0;
// validate transaction
OSSYNCAssert( fSet < 0 || ( ( fSetStart == 0 || fSetStart == 1 ) && fSetEnd == 0 ) );
// attempt the transaction
const long fSetOld = AtomicCompareExchange( (long*)&m_fSet, fSetStart, fSetEnd );
// the transaction succeeded
if ( fSetOld == fSetStart ) { // return success
return fTrue; }
// the transaction failed
else { // the transaction failed because of a collision with another context
if ( fSetOld >= 0 ) { // try again
continue; }
// the transaction failed because there are waiters
else { // return failure
return fFalse; } } } }
// Auto-Reset Signal
class CAutoResetSignal : private CSyncObject, private CEnhancedStateContainer< CAutoResetSignalState, CSyncStateInitNull, CAutoResetSignalInfo, CSyncBasicInfo > { public:
// member functions
// ctors / dtors
CAutoResetSignal( const CSyncBasicInfo& sbi ); ~CAutoResetSignal();
// manipulators
void Wait(); const BOOL FTryWait(); const BOOL FWait( const int cmsecTimeout );
void Set(); void Reset(); void Pulse();
// debugging support
void Dump( CDumpContext& dc ) const;
private:
// member functions
// operators
CAutoResetSignal& operator=( CAutoResetSignal& ); // disallowed
// manipulators
const BOOL _FWait( const int cmsecTimeout );
void _Set(); void _Pulse(); };
// waits for the signal to be set, forever if necessary. when the wait completes,
// the signal will be reset
inline void CAutoResetSignal::Wait() { // we will wait forever, so we should not timeout
const BOOL fWait = FWait( cmsecInfinite ); OSSYNCAssert( fWait ); }
// tests the state of the signal without waiting or spinning, returning fFalse
// if the signal was not set. if the signal was set, the signal will be reset
inline const BOOL CAutoResetSignal::FTryWait() { // we can satisfy the wait if we can successfully change the state of the
// signal from set to reset with no waiters
const BOOL fSuccess = State().FChange( CAutoResetSignalState( 1 ), CAutoResetSignalState( 0 ) );
// we did not successfully wait for the signal
if ( !fSuccess ) { // this is a contention
State().SetContend(); }
// we did successfully wait for the signal
else { // note that we acquired the signal
State().SetAcquire(); }
return fSuccess; }
// wait for the signal to be set, but only for the specified interval,
// returning fFalse if the wait timed out before the signal was set. if the
// wait completes, the signal will be reset
inline const BOOL CAutoResetSignal::FWait( const int cmsecTimeout ) { // first try to quickly pass through the signal. if that doesn't work,
// retry wait using the full state machine
return FTryWait() || _FWait( cmsecTimeout ); }
// sets the signal, releasing up to one waiter. if a waiter is released, then
// the signal will be reset. if a waiter is not released, the signal will
// remain set
inline void CAutoResetSignal::Set() { // we failed to change the signal state from reset with no waiters to set
// or from set to set (a nop)
if ( !State().FSimpleSet() ) { // retry set using the full state machine
_Set(); } }
// resets the signal
inline void CAutoResetSignal::Reset() { // if and only if the signal is in the set state, change it to the reset state
State().FChange( CAutoResetSignalState( 1 ), CAutoResetSignalState( 0 ) ); }
// resets the signal, releasing up to one waiter
inline void CAutoResetSignal::Pulse() { // wa failed to change the signal state from set to reset with no waiters
// or from reset with no waiters to reset with no waiters (a nop)
if ( !State().FSimpleReset() ) { // retry pulse using the full state machine
_Pulse(); } }
// Manual-Reset Signal Information
class CManualResetSignalInfo : public CSyncBasicInfo, public CSyncPerfWait, public CSyncPerfAcquire { public:
// member functions
// ctors / dtors
CManualResetSignalInfo( const CSyncBasicInfo& sbi ) : CSyncBasicInfo( sbi ) { }
// debugging support
void Dump( CDumpContext& dc ) const; };
// Manual-Reset Signal State
class CManualResetSignalState { public:
// member functions
// ctors / dtors
CManualResetSignalState( const CSyncStateInitNull& null ) : m_fSet( 0 ) {} CManualResetSignalState( const int fSet ); CManualResetSignalState( const int cWait, const int irksem ); ~CManualResetSignalState() {}
// operators
CManualResetSignalState& operator=( CManualResetSignalState& state ) { m_fSet = state.m_fSet; return *this; }
// manipulators
const BOOL FChange( const CManualResetSignalState& stateCur, const CManualResetSignalState& stateNew ); const CManualResetSignalState Set(); const CManualResetSignalState Reset();
// accessors
const BOOL FNoWait() const { return m_fSet >= 0; } const BOOL FWait() const { return m_fSet < 0; } const BOOL FNoWaitAndSet() const { return m_fSet > 0; } const BOOL FNoWaitAndNotSet() const { return m_fSet == 0; }
const BOOL FSet() const { OSSYNCAssert( FNoWait() ); return m_fSet; } const int CWait() const { OSSYNCAssert( FWait() ); return -m_cWaitNeg; } const CKernelSemaphorePool::IRKSEM Irksem() const { OSSYNCAssert( FWait() ); return CKernelSemaphorePool::IRKSEM( m_irksem ); }
// debugging support
void Dump( CDumpContext& dc ) const;
private:
// data members
// transacted state representation (switched on bit 31)
union { // Mode 0: no waiters
volatile long m_fSet; // m_fSet = { 0, 1 }
// Mode 1: waiters
struct { volatile unsigned short m_irksem; // 0 <= m_irksem <= ( 1 << 16 ) - 2
volatile short m_cWaitNeg; // -( ( 1 << 15 ) - 1 ) <= m_cWaitNeg <= -1
}; }; };
// ctor
inline CManualResetSignalState::CManualResetSignalState( const int fSet ) { // set state
m_fSet = long( fSet ); }
// ctor
inline CManualResetSignalState::CManualResetSignalState( const int cWait, const int irksem ) { // validate IN args
OSSYNCAssert( cWait > 0 ); OSSYNCAssert( cWait <= 0x7FFF ); OSSYNCAssert( irksem >= 0 ); OSSYNCAssert( irksem <= 0xFFFE );
// set waiter count
m_cWaitNeg = short( -cWait );
// set semaphore
m_irksem = (unsigned short) irksem; }
// changes the transacted state of the signal using a transacted memory
// compare/exchange operation, returning fFalse on failure
inline const BOOL CManualResetSignalState::FChange( const CManualResetSignalState& stateCur, const CManualResetSignalState& stateNew ) { return AtomicCompareExchange( (long*)&m_fSet, stateCur.m_fSet, stateNew.m_fSet ) == stateCur.m_fSet; }
// changes the transacted state of the signal to set using a transacted memory
// exchange operation and returns the original state of the signal
inline const CManualResetSignalState CManualResetSignalState::Set() { const CManualResetSignalState stateNew( 1 ); return CManualResetSignalState( AtomicExchange( (long*)&m_fSet, stateNew.m_fSet ) ); }
// changes the transacted state of the signal to reset using a transacted memory
// exchange operation and returns the original state of the signal
inline const CManualResetSignalState CManualResetSignalState::Reset() { const CManualResetSignalState stateNew( 0 ); return CManualResetSignalState( AtomicExchange( (long*)&m_fSet, stateNew.m_fSet ) ); }
// Manual-Reset Signal
class CManualResetSignal : private CSyncObject, private CEnhancedStateContainer< CManualResetSignalState, CSyncStateInitNull, CManualResetSignalInfo, CSyncBasicInfo > { public:
// member functions
// ctors / dtors
CManualResetSignal( const CSyncBasicInfo& sbi ); ~CManualResetSignal();
// manipulators
void Wait(); const BOOL FTryWait(); const BOOL FWait( const int cmsecTimeout );
void Set(); void Reset(); void Pulse();
// debugging support
void Dump( CDumpContext& dc ) const;
private:
// member functions
// operators
CManualResetSignal& operator=( CManualResetSignal& ); // disallowed
// manipulators
const BOOL _FWait( const int cmsecTimeout ); };
// waits for the signal to be set, forever if necessary
inline void CManualResetSignal::Wait() { // we will wait forever, so we should not timeout
int fWait = FWait( cmsecInfinite ); OSSYNCAssert( fWait ); }
// tests the state of the signal without waiting or spinning, returning fFalse
// if the signal was not set
inline const BOOL CManualResetSignal::FTryWait() { const BOOL fSuccess = State().FNoWaitAndSet();
// we did not successfully wait for the signal
if ( !fSuccess ) { // this is a contention
State().SetContend(); }
// we did successfully wait for the signal
else { // note that we acquired the signal
State().SetAcquire(); }
return fSuccess; }
// wait for the signal to be set, but only for the specified interval,
// returning fFalse if the wait timed out before the signal was set
inline const BOOL CManualResetSignal::FWait( const int cmsecTimeout ) { // first try to quickly pass through the signal. if that doesn't work,
// retry wait using the full state machine
return FTryWait() || _FWait( cmsecTimeout ); }
// sets the signal, releasing any waiters
inline void CManualResetSignal::Set() { // change the signal state to set
const CManualResetSignalState stateOld = State().Set();
// there were waiters on the signal
if ( stateOld.FWait() ) { // release all the waiters
ksempoolGlobal.Ksem( stateOld.Irksem(), this ).Release( stateOld.CWait() ); } }
// resets the signal
inline void CManualResetSignal::Reset() { // if and only if the signal is in the set state, change it to the reset state
State().FChange( CManualResetSignalState( 1 ), CManualResetSignalState( 0 ) ); }
// resets the signal, releasing any waiters
inline void CManualResetSignal::Pulse() { // change the signal state to reset
const CManualResetSignalState stateOld = State().Reset();
// there were waiters on the signal
if ( stateOld.FWait() ) { // release all the waiters
ksempoolGlobal.Ksem( stateOld.Irksem(), this ).Release( stateOld.CWait() ); } }
// Lock Object Base Class
//
// All Lock Objects are derived from this class
class CLockObject : public CSyncObject { public:
// member functions
// ctors / dtors
CLockObject() {} ~CLockObject() {}
private:
// member functions
// operators
CLockObject& operator=( CLockObject& ); // disallowed
};
// Lock Object Basic Information
class CLockBasicInfo : public CSyncBasicInfo { public:
// member functions
// ctors / dtors
CLockBasicInfo( const CSyncBasicInfo& sbi, const int rank, const int subrank ); ~CLockBasicInfo();
// accessors
#ifdef SYNC_DEADLOCK_DETECTION
const int Rank() const { return m_rank; } const int SubRank() const { return m_subrank; }
#endif // SYNC_DEADLOCK_DETECTION
// debugging support
void Dump( CDumpContext& dc ) const;
private:
// member functions
// operators
CLockBasicInfo& operator=( CLockBasicInfo& ); // disallowed
// data members
#ifdef SYNC_DEADLOCK_DETECTION
// Rank and Subrank
int m_rank; int m_subrank;
#endif // SYNC_DEADLOCK_DETECTION
};
// Lock Object Performance: Hold
class CLockPerfHold { public:
// member functions
// ctors / dtors
CLockPerfHold(); ~CLockPerfHold();
// member functions
// manipulators
void StartHold(); void StopHold();
// accessors
#ifdef SYNC_ANALYZE_PERFORMANCE
QWORD CHoldTotal() const { return m_cHold; } double CsecHoldElapsed() const { return (double)(signed __int64)m_qwHRTHoldElapsed / (double)(signed __int64)QwOSTimeHRTFreq(); }
#endif // SYNC_ANALYZE_PERFORMANCE
// debugging support
void Dump( CDumpContext& dc ) const;
private:
// member functions
// operators
CLockPerfHold& operator=( CLockPerfHold& ); // disallowed
// data members
#ifdef SYNC_ANALYZE_PERFORMANCE
// hold count
volatile QWORD m_cHold;
// elapsed hold time
volatile QWORD m_qwHRTHoldElapsed;
#endif // SYNC_ANALYZE_PERFORMANCE
};
// starts the hold timer for the lock object
inline void CLockPerfHold::StartHold() { #ifdef SYNC_ANALYZE_PERFORMANCE
// increment the hold count
AtomicAdd( (QWORD*)&m_cHold, 1 );
// subtract the start hold time from the elapsed hold time. this starts
// an elapsed time computation for this context. StopHold() will later
// add the end hold time to the elapsed time, causing the following net
// effect:
//
// m_qwHRTHoldElapsed += <end time> - <start time>
//
// we simply choose to go ahead and do the subtraction now to save storage
AtomicAdd( (QWORD*)&m_qwHRTHoldElapsed, QWORD( -__int64( QwOSTimeHRTCount() ) ) );
#endif // SYNC_ANALYZE_PERFORMANCE
}
// stops the hold timer for the lock object
inline void CLockPerfHold::StopHold() { #ifdef SYNC_ANALYZE_PERFORMANCE
// add the end hold time to the elapsed hold time. this completes the
// computation started in StartHold()
AtomicAdd( (QWORD*)&m_qwHRTHoldElapsed, QwOSTimeHRTCount() );
#endif // SYNC_ANALYZE_PERFORMANCE
}
// Lock Owner Record
class CLockDeadlockDetectionInfo;
class COwner { public:
// member functions
// ctors / dtors
COwner(); ~COwner();
void* operator new( size_t cb ) { return ESMemoryNew( cb ); } void operator delete( void* pv ) { ESMemoryDelete( pv ); }
public:
// member functions
// operators
COwner& operator=( COwner& ); // disallowed
// data members
// owning context
CLS* m_pclsOwner;
// next context owning this lock
COwner* m_pownerContextNext;
// owned lock object
CLockDeadlockDetectionInfo* m_plddiOwned;
// next lock owned by this context
COwner* m_pownerLockNext;
// owning group for this context and lock
DWORD m_group; };
// Lock Object Deadlock Detection Information
class CLockDeadlockDetectionInfo { public:
// types
// subrank used to disable deadlock detection at the subrank level
enum { subrankNoDeadlock = INT_MAX };
// member functions
// ctors / dtors
CLockDeadlockDetectionInfo( const CLockBasicInfo& lbi ); ~CLockDeadlockDetectionInfo();
// member functions
// manipulators
void AddAsWaiter( const DWORD group = -1 ); void RemoveAsWaiter( const DWORD group = -1 );
void AddAsOwner( const DWORD group = -1 ); void RemoveAsOwner( const DWORD group = -1 );
static void OSSYNCAPI NextOwnershipIsNotADeadlock(); static void OSSYNCAPI DisableOwnershipTracking(); static void OSSYNCAPI EnableOwnershipTracking();
// accessors
const BOOL FOwner( const DWORD group = -1 ); const BOOL FNotOwner( const DWORD group = -1 ); const BOOL FOwned(); const BOOL FNotOwned();
const BOOL FCanBeWaiter(); const BOOL FWaiter( const DWORD group = -1 ); const BOOL FNotWaiter( const DWORD group = -1 );
#ifdef SYNC_DEADLOCK_DETECTION
const CLockBasicInfo& Info() { return *m_plbiParent; }
#endif // SYNC_DEADLOCK_DETECTION
// debugging support
void Dump( CDumpContext& dc ) const;
private:
// member functions
// operators
CLockDeadlockDetectionInfo& operator=( CLockDeadlockDetectionInfo& ); // disallowed
// data members
#ifdef SYNC_DEADLOCK_DETECTION
// parent lock object information
const CLockBasicInfo* m_plbiParent;
// semaphore protecting owner list
CSemaphore m_semOwnerList;
// owner list head
COwner m_ownerHead;
#endif // SYNC_DEADLOCK_DETECTION
};
// adds the current context as a waiter for the lock object as a member of the
// specified group
inline void CLockDeadlockDetectionInfo::AddAsWaiter( const DWORD group ) { // this context had better not be a waiter for the lock
OSSYNCAssert( FNotWaiter( group ) );
#ifdef SYNC_DEADLOCK_DETECTION
// we had better not already be waiting for something else!
CLS* const pcls = Pcls(); OSSYNCAssert( !pcls->plddiLockWait ); OSSYNCAssert( !pcls->groupLockWait );
// add this context as a waiter for the lock
pcls->plddiLockWait = this; pcls->groupLockWait = group;
#endif // SYNC_DEADLOCK_DETECTION
// this context had better be a waiter for the lock
OSSYNCAssert( FWaiter( group ) ); }
// removes the current context as a waiter for the lock object
inline void CLockDeadlockDetectionInfo::RemoveAsWaiter( const DWORD group ) { // this context had better be a waiter for the lock
OSSYNCAssert( FWaiter( group ) );
#ifdef SYNC_DEADLOCK_DETECTION
// remove this context as a waiter for the lock
CLS* const pcls = Pcls(); pcls->plddiLockWait = NULL; pcls->groupLockWait = 0;
#endif // SYNC_DEADLOCK_DETECTION
// this context had better not be a waiter for the lock anymore
OSSYNCAssert( FNotWaiter( group ) ); }
// adds the current context as an owner of the lock object as a member of the
// specified group
inline void CLockDeadlockDetectionInfo::AddAsOwner( const DWORD group ) { // this context had better not be an owner of the lock
OSSYNCAssert( FNotOwner( group ) );
#ifdef SYNC_DEADLOCK_DETECTION
// add this context as an owner of the lock
CLS* const pcls = Pcls();
if ( !pcls->cDisableOwnershipTracking ) { COwner* powner = &m_ownerHead;
if ( AtomicCompareExchangePointer( (void **)&powner->m_pclsOwner, NULL, pcls ) != NULL ) { powner = new COwner; OSSYNCEnforceSz( powner, "Failed to allocate Deadlock Detection Owner Record" );
m_semOwnerList.Acquire();
powner->m_pclsOwner = pcls; powner->m_pownerContextNext = m_ownerHead.m_pownerContextNext; m_ownerHead.m_pownerContextNext = powner;
m_semOwnerList.Release(); }
powner->m_plddiOwned = this; powner->m_pownerLockNext = pcls->pownerLockHead; pcls->pownerLockHead = powner; powner->m_group = group; }
// reset override
pcls->fOverrideDeadlock = fFalse;
#endif // SYNC_DEADLOCK_DETECTION
// this context had better be an owner of the lock
OSSYNCAssert( FOwner( group ) ); }
// removes the current context as an owner of the lock object
inline void CLockDeadlockDetectionInfo::RemoveAsOwner( const DWORD group ) { // this context had better be an owner of the lock
OSSYNCAssert( FOwner( group ) );
#ifdef SYNC_DEADLOCK_DETECTION
// remove this context as an owner of the lock
CLS* const pcls = Pcls();
if ( !pcls->cDisableOwnershipTracking ) { COwner** ppownerLock = &pcls->pownerLockHead; while ( (*ppownerLock)->m_plddiOwned != this || (*ppownerLock)->m_group != group ) { ppownerLock = &(*ppownerLock)->m_pownerLockNext; }
COwner* pownerLock = *ppownerLock; *ppownerLock = pownerLock->m_pownerLockNext;
pownerLock->m_plddiOwned = NULL; pownerLock->m_pownerLockNext = NULL; pownerLock->m_group = 0;
if ( AtomicCompareExchangePointer( (void**) &m_ownerHead.m_pclsOwner, pcls, NULL ) != pcls ) { m_semOwnerList.Acquire();
COwner** ppownerContext = &m_ownerHead.m_pownerContextNext; while ( (*ppownerContext)->m_pclsOwner != pcls ) { ppownerContext = &(*ppownerContext)->m_pownerContextNext; }
COwner* pownerContext = *ppownerContext; *ppownerContext = pownerContext->m_pownerContextNext;
m_semOwnerList.Release();
delete pownerContext; } }
#endif // SYNC_DEADLOCK_DETECTION
// this context had better not be an owner of the lock anymore
OSSYNCAssert( FNotOwner( group ) ); }
// overrides deadlock detection using ranks for the next ownership
inline void OSSYNCAPI CLockDeadlockDetectionInfo::NextOwnershipIsNotADeadlock() { #ifdef SYNC_DEADLOCK_DETECTION
Pcls()->fOverrideDeadlock = fTrue;
#endif // SYNC_DEADLOCK_DETECTION
}
// disables ownership tracking for this context
inline void OSSYNCAPI CLockDeadlockDetectionInfo::DisableOwnershipTracking() { #ifdef SYNC_DEADLOCK_DETECTION
Pcls()->cDisableOwnershipTracking++;
#endif // SYNC_DEADLOCK_DETECTION
}
// enables ownership tracking for this context
inline void OSSYNCAPI CLockDeadlockDetectionInfo::EnableOwnershipTracking() { #ifdef SYNC_DEADLOCK_DETECTION
Pcls()->cDisableOwnershipTracking--;
#endif // SYNC_DEADLOCK_DETECTION
}
// returns fTrue if the current context is an owner of the lock object
//
// NOTE: if deadlock detection is disabled, this function will always return
// fTrue
inline const BOOL CLockDeadlockDetectionInfo::FOwner( const DWORD group ) { #ifdef SYNC_DEADLOCK_DETECTION
COwner* pownerLock = Pcls()->pownerLockHead; while ( pownerLock && ( pownerLock->m_plddiOwned != this || pownerLock->m_group != group ) ) { pownerLock = pownerLock->m_pownerLockNext; }
return Pcls()->cDisableOwnershipTracking != 0 || pownerLock && pownerLock->m_group == group;
#else // !SYNC_DEADLOCK_DETECTION
return fTrue;
#endif // SYNC_DEADLOCK_DETECTION
}
// returns fTrue if the current context is not an owner of the lock object
//
// NOTE: if deadlock detection is disabled, this function will always return
// fTrue
inline const BOOL CLockDeadlockDetectionInfo::FNotOwner( const DWORD group ) { #ifdef SYNC_DEADLOCK_DETECTION
return Pcls()->cDisableOwnershipTracking != 0 || !FOwner( group );
#else // !SYNC_DEADLOCK_DETECTION
return fTrue;
#endif // SYNC_DEADLOCK_DETECTION
}
// returns fTrue if any context is an owner of the lock object
//
// NOTE: if deadlock detection is disabled, this function will always return
// fTrue
inline const BOOL CLockDeadlockDetectionInfo::FOwned() { #ifdef SYNC_DEADLOCK_DETECTION
return m_ownerHead.m_pclsOwner || m_ownerHead.m_pownerContextNext;
#else // !SYNC_DEADLOCK_DETECTION
return fTrue;
#endif // SYNC_DEADLOCK_DETECTION
}
// returns fTrue if no context is an owner of the lock object
//
// NOTE: if deadlock detection is disabled, this function will always return
// fTrue
inline const BOOL CLockDeadlockDetectionInfo::FNotOwned() { #ifdef SYNC_DEADLOCK_DETECTION
return !FOwned();
#else // !SYNC_DEADLOCK_DETECTION
return fTrue;
#endif // SYNC_DEADLOCK_DETECTION
}
// returns fTrue if the current context can wait for the lock object without
// violating any deadlock constraints
//
// NOTE: if deadlock detection is disabled, this function will always return
// fTrue
inline const BOOL CLockDeadlockDetectionInfo::FCanBeWaiter() { #ifdef SYNC_DEADLOCK_DETECTION
// find the minimum rank, subrank of all locks owned by the current context
CLS* const pcls = Pcls(); COwner* powner = pcls->pownerLockHead; int Rank = INT_MAX; int SubRank = INT_MAX;
while ( powner ) { if ( powner->m_plddiOwned->Info().Rank() < Rank || ( powner->m_plddiOwned->Info().Rank() == Rank && powner->m_plddiOwned->Info().SubRank() < SubRank ) ) { Rank = powner->m_plddiOwned->Info().Rank(); SubRank = powner->m_plddiOwned->Info().SubRank(); }
powner = powner->m_pownerLockNext; }
// test this rank, subrank against our rank, subrank
return Rank > Info().Rank() || ( Rank == Info().Rank() && SubRank > Info().SubRank() ) || ( Rank == Info().Rank() && SubRank == Info().SubRank() && SubRank == subrankNoDeadlock ) || pcls->fOverrideDeadlock;
#else // !SYNC_DEADLOCK_DETECTION
return fTrue;
#endif // SYNC_DEADLOCK_DETECTION
}
// returns fTrue if the current context is a waiter of the lock object
//
// NOTE: if deadlock detection is disabled, this function will always return
// fTrue
inline const BOOL CLockDeadlockDetectionInfo::FWaiter( const DWORD group ) { #ifdef SYNC_DEADLOCK_DETECTION
CLS* const pcls = Pcls(); return pcls->plddiLockWait == this && pcls->groupLockWait == group;
#else // !SYNC_DEADLOCK_DETECTION
return fTrue;
#endif // SYNC_DEADLOCK_DETECTION
}
// returns fTrue if the current context is not a waiter of the lock object
//
// NOTE: if deadlock detection is disabled, this function will always return
// fTrue
inline const BOOL CLockDeadlockDetectionInfo::FNotWaiter( const DWORD group ) { #ifdef SYNC_DEADLOCK_DETECTION
return !FWaiter( group );
#else // !SYNC_DEADLOCK_DETECTION
return fTrue;
#endif // SYNC_DEADLOCK_DETECTION
}
// Critical Section (non-nestable) Information
class CCriticalSectionInfo : public CLockBasicInfo, public CLockPerfHold, public CLockDeadlockDetectionInfo { public:
// member functions
// ctors / dtors
CCriticalSectionInfo( const CLockBasicInfo& lbi ) : CLockBasicInfo( lbi ), CLockDeadlockDetectionInfo( (CLockBasicInfo&) *this ) { }
// debugging support
void Dump( CDumpContext& dc ) const; };
// Critical Section (non-nestable) State
class CCriticalSectionState { public:
// member functions
// ctors / dtors
CCriticalSectionState( const CSyncBasicInfo& sbi ); ~CCriticalSectionState();
// accessors
CSemaphore& Semaphore() { return m_sem; }
// debugging support
void Dump( CDumpContext& dc ) const;
private:
// member functions
// operators
CCriticalSectionState& operator=( CCriticalSectionState& ); // disallowed
// data members
// semaphore
CSemaphore m_sem; };
// Critical Section (non-nestable)
class CCriticalSection : private CLockObject, private CEnhancedStateContainer< CCriticalSectionState, CSyncBasicInfo, CCriticalSectionInfo, CLockBasicInfo > { public:
// member functions
// ctors / dtors
CCriticalSection( const CLockBasicInfo& lbi ); ~CCriticalSection();
// manipulators
void Enter(); const BOOL FTryEnter(); const BOOL FEnter( const int cmsecTimeout ); void Leave();
// accessors
const int CWait() { return State().Semaphore().CWait(); }
const BOOL FOwner() { return State().FOwner(); } const BOOL FNotOwner() { return State().FNotOwner(); }
// debugging support
void Dump( CDumpContext& dc ) const;
private:
// member functions
// operators
CCriticalSection& operator=( CCriticalSection& ); // disallowed
};
// enter the critical section, waiting forever if someone else is currently the
// owner. the critical section can not be re-entered until it has been left
inline void CCriticalSection::Enter() { // check for deadlock
OSSYNCAssertSzRTL( State().FCanBeWaiter(), "Potential Deadlock Detected (Rank Violation)" );
// acquire the semaphore
State().AddAsWaiter();
State().Semaphore().Acquire();
State().RemoveAsWaiter();
// there had better be no available counts on the semaphore
OSSYNCAssert( !State().Semaphore().CAvail() );
// we are now the owner of the critical section
State().AddAsOwner(); State().StartHold(); }
// try to enter the critical section without waiting or spinning, returning
// fFalse if someone else currently is the owner. the critical section can not
// be re-entered until it has been left
inline const BOOL CCriticalSection::FTryEnter() { // try to acquire the semaphore without waiting or spinning
//
// NOTE: there is no potential for deadlock here, so don't bother to check
BOOL fAcquire = State().Semaphore().FTryAcquire();
// we are now the owner of the critical section
if ( fAcquire ) { // there had better be no available counts on the semaphore
OSSYNCAssert( !State().Semaphore().CAvail() );
// add ourself as the owner
State().AddAsOwner(); State().StartHold(); }
return fAcquire; }
// try to enter the critical section waiting only for the specified interval,
// returning fFalse if the wait timed out before the critical section could be
// acquired. the critical section can not be re-entered until it has been left
inline const BOOL CCriticalSection::FEnter( const int cmsecTimeout ) { // check for deadlock if we are waiting forever
OSSYNCAssertSzRTL( cmsecTimeout != cmsecInfinite || State().FCanBeWaiter(), "Potential Deadlock Detected (Rank Violation)" );
// try to acquire the semaphore, timing out as requested
//
// NOTE: there is still a potential for deadlock, but that will be detected
// at the OS level
State().AddAsWaiter();
BOOL fAcquire = State().Semaphore().FAcquire( cmsecTimeout );
State().RemoveAsWaiter();
// we are now the owner of the critical section
if ( fAcquire ) { // there had better be no available counts on the semaphore
OSSYNCAssert( !State().Semaphore().CAvail() );
// add ourself as the owner
State().AddAsOwner(); State().StartHold(); }
return fAcquire; }
// leaves the critical section, releasing it for ownership by someone else
inline void CCriticalSection::Leave() { // remove ourself as the owner
State().RemoveAsOwner();
// we are no longer holding the lock
State().StopHold();
// there had better be no available counts on the semaphore
OSSYNCAssert( !State().Semaphore().CAvail() );
// release the semaphore
State().Semaphore().Release(); }
// Nestable Critical Section Information
class CNestableCriticalSectionInfo : public CLockBasicInfo, public CLockPerfHold, public CLockDeadlockDetectionInfo { public:
// member functions
// ctors / dtors
CNestableCriticalSectionInfo( const CLockBasicInfo& lbi ) : CLockBasicInfo( lbi ), CLockDeadlockDetectionInfo( (CLockBasicInfo&) *this ) { }
// debugging support
void Dump( CDumpContext& dc ) const; };
// Nestable Critical Section State
class CNestableCriticalSectionState { public:
// member functions
// ctors / dtors
CNestableCriticalSectionState( const CSyncBasicInfo& sbi ); ~CNestableCriticalSectionState();
// manipulators
void SetOwner( CLS* const pcls );
void Enter(); void Leave();
// accessors
CSemaphore& Semaphore() { return m_sem; } CLS* PclsOwner() { return m_pclsOwner; } int CEntry() { return m_cEntry; }
// debugging support
void Dump( CDumpContext& dc ) const;
private:
// member functions
// operators
CNestableCriticalSectionState& operator=( CNestableCriticalSectionState& ); // disallowed
// data members
// semaphore
CSemaphore m_sem;
// owning context (protected by the semaphore)
CLS* volatile m_pclsOwner;
// entry count (only valid when the owner id is valid)
volatile int m_cEntry; };
// set the owner
inline void CNestableCriticalSectionState::SetOwner( CLS* const pcls ) { // we had either be clearing the owner or setting a new owner. we should
// never be overwriting another owner
OSSYNCAssert( !pcls || !m_pclsOwner );
// set the new owner
m_pclsOwner = pcls; }
// increment the entry count
inline void CNestableCriticalSectionState::Enter() { // we had better have an owner already!
OSSYNCAssert( m_pclsOwner );
// we should not overflow the entry count
OSSYNCAssert( int( m_cEntry + 1 ) >= 1 );
// increment the entry count
m_cEntry++; }
// decrement the entry count
inline void CNestableCriticalSectionState::Leave() { // we had better have an owner already!
OSSYNCAssert( m_pclsOwner );
// decrement the entry count
m_cEntry--; }
// Nestable Critical Section
class CNestableCriticalSection : private CLockObject, private CEnhancedStateContainer< CNestableCriticalSectionState, CSyncBasicInfo, CNestableCriticalSectionInfo, CLockBasicInfo > { public:
// member functions
// ctors / dtors
CNestableCriticalSection( const CLockBasicInfo& lbi ); ~CNestableCriticalSection();
// manipulators
void Enter(); const BOOL FTryEnter(); const BOOL FEnter( const int cmsecTimeout ); void Leave();
// accessors
const int CWait() { return State().Semaphore().CWait(); }
const BOOL FOwner() { return State().FOwner(); } const BOOL FNotOwner() { return State().FNotOwner(); }
// debugging support
void Dump( CDumpContext& dc ) const;
private:
// member functions
// operators
CNestableCriticalSection& operator=( CNestableCriticalSection& ); // disallowed
};
// enter the critical section, waiting forever if someone else is currently the
// owner. the critical section can be reentered without waiting or deadlocking
inline void CNestableCriticalSection::Enter() { // get our context
CLS* const pcls = Pcls();
// we own the critical section
if ( State().PclsOwner() == pcls ) { // there had better be no available counts on the semaphore
OSSYNCAssert( !State().Semaphore().CAvail() );
// we should have at least one entry count
OSSYNCAssert( State().CEntry() >= 1 );
// increment our entry count
State().Enter(); }
// we do not own the critical section
else { OSSYNCAssert( State().PclsOwner() != pcls );
// check for deadlock
OSSYNCAssertSzRTL( State().FCanBeWaiter(), "Potential Deadlock Detected (Rank Violation)" );
// acquire the semaphore
State().AddAsWaiter();
State().Semaphore().Acquire();
State().RemoveAsWaiter();
// there had better be no available counts on the semaphore
OSSYNCAssert( !State().Semaphore().CAvail() );
// we are now the owner of the critical section
State().AddAsOwner(); State().StartHold();
// save our context as the owner
State().SetOwner( pcls );
// set initial entry count
State().Enter(); } }
// try to enter the critical section without waiting or spinning, returning
// fFalse if someone else currently is the owner. the critical section can be
// reentered without waiting or deadlocking
inline const BOOL CNestableCriticalSection::FTryEnter() { // get our context
CLS* const pcls = Pcls();
// we own the critical section
if ( State().PclsOwner() == pcls ) { // there had better be no available counts on the semaphore
OSSYNCAssert( !State().Semaphore().CAvail() );
// we should have at least one entry count
OSSYNCAssert( State().CEntry() >= 1 );
// increment our entry count
State().Enter();
// return success
return fTrue; }
// we do not own the critical section
else { OSSYNCAssert( State().PclsOwner() != pcls );
// try to acquire the semaphore without waiting or spinning
//
// NOTE: there is no potential for deadlock here, so don't bother to check
const BOOL fAcquired = State().Semaphore().FTryAcquire();
// we now own the critical section
if ( fAcquired ) { // there had better be no available counts on the semaphore
OSSYNCAssert( !State().Semaphore().CAvail() );
// add ourself as the owner
State().AddAsOwner(); State().StartHold();
// save our context as the owner
State().SetOwner( pcls );
// set initial entry count
State().Enter(); }
// return result
return fAcquired; } }
// try to enter the critical section waiting only for the specified interval,
// returning fFalse if the wait timed out before the critical section could be
// acquired. the critical section can be reentered without waiting or
// deadlocking
inline const BOOL CNestableCriticalSection::FEnter( const int cmsecTimeout ) { // get our context
CLS* const pcls = Pcls();
// we own the critical section
if ( State().PclsOwner() == pcls ) { // there had better be no available counts on the semaphore
OSSYNCAssert( !State().Semaphore().CAvail() );
// we should have at least one entry count
OSSYNCAssert( State().CEntry() >= 1 );
// increment our entry count
State().Enter();
// return success
return fTrue; }
// we do not own the critical section
else { OSSYNCAssert( State().PclsOwner() != pcls );
// check for deadlock if we are waiting forever
OSSYNCAssertSzRTL( cmsecTimeout != cmsecInfinite || State().FCanBeWaiter(), "Potential Deadlock Detected (Rank Violation)" );
// try to acquire the semaphore, timing out as requested
//
// NOTE: there is still a potential for deadlock, but that will be detected
// at the OS level
State().AddAsWaiter();
const BOOL fAcquired = State().Semaphore().FAcquire( cmsecTimeout );
State().RemoveAsWaiter();
// we now own the critical section
if ( fAcquired ) { // there had better be no available counts on the semaphore
OSSYNCAssert( !State().Semaphore().CAvail() );
// add ourself as the owner
State().AddAsOwner(); State().StartHold();
// save our context as the owner
State().SetOwner( pcls );
// set initial entry count
State().Enter(); }
// return result
return fAcquired; } }
// leave the critical section. if leave has been called for every enter that
// has completed successfully, the critical section is released for ownership
// by someone else
inline void CNestableCriticalSection::Leave() { // we had better be the current owner
OSSYNCAssert( State().PclsOwner() == Pcls() );
// there had better be no available counts on the semaphore
OSSYNCAssert( !State().Semaphore().CAvail() );
// there had better be at least one entry count
OSSYNCAssert( State().CEntry() >= 1 );
// release one entry count
State().Leave();
// we released the last entry count
if ( !State().CEntry() ) { // reset the owner id
State().SetOwner( 0 );
// remove ourself as the owner
State().RemoveAsOwner();
// we are no longer holding the lock
State().StopHold();
// release the semaphore
State().Semaphore().Release(); } }
// Gate Information
class CGateInfo : public CSyncBasicInfo, public CSyncPerfWait { public:
// member functions
// ctors / dtors
CGateInfo( const CSyncBasicInfo& sbi ) : CSyncBasicInfo( sbi ) { }
// debugging support
void Dump( CDumpContext& dc ) const; };
// Gate State
class CGateState { public:
// member functions
// ctors / dtors
CGateState( const CSyncStateInitNull& null ) : m_cWait( 0 ), m_irksem( CKernelSemaphorePool::irksemNil ) {} CGateState( const int cWait, const int irksem ); ~CGateState() {}
// manipulators
void SetWaitCount( const int cWait ); void SetIrksem( const CKernelSemaphorePool::IRKSEM irksem );
// accessors
const int CWait() const { return m_cWait; } const CKernelSemaphorePool::IRKSEM Irksem() const { return CKernelSemaphorePool::IRKSEM( m_irksem ); }
// debugging support
void Dump( CDumpContext& dc ) const;
private:
// member functions
// operators
CGateState& operator=( CGateState& ); // disallowed
// data members
// waiter count
volatile short m_cWait; // 0 <= m_cWait <= ( 1 << 15 ) - 1
// reference kernel semaphore
volatile unsigned short m_irksem; // 0 <= m_irksem <= ( 1 << 16 ) - 2
};
// sets the wait count for the gate
inline void CGateState::SetWaitCount( const int cWait ) { // it must be a valid wait count
OSSYNCAssert( cWait >= 0 ); OSSYNCAssert( cWait <= 0x7FFF );
// set the wait count
m_cWait = (unsigned short) cWait; }
// sets the referenced kernel semaphore for the gate
inline void CGateState::SetIrksem( const CKernelSemaphorePool::IRKSEM irksem ) { // it must be a valid irksem
OSSYNCAssert( irksem >= 0 ); OSSYNCAssert( irksem <= 0xFFFF );
// set the irksem
m_irksem = (unsigned short) irksem; }
// Gate
class CGate : private CSyncObject, private CEnhancedStateContainer< CGateState, CSyncStateInitNull, CGateInfo, CSyncBasicInfo > { public:
// member functions
// ctors / dtors
CGate( const CSyncBasicInfo& sbi ); ~CGate();
// manipulators
void Wait( CCriticalSection& crit ); void Release( CCriticalSection& crit, const int cToRelease = 1 ); void ReleaseAndHold( CCriticalSection& crit, const int cToRelease = 1 );
// accessors
const int CWait() const { return State().CWait(); }
// debugging support
void Dump( CDumpContext& dc ) const;
private:
// member functions
// operators
CGate& operator=( CGate& ); // disallowed
};
// Null Lock Object State Initializer
class CLockStateInitNull { };
extern const CLockStateInitNull lockstateNull;
// Binary Lock Performance Information
class CBinaryLockPerfInfo : public CSyncPerfWait, public CSyncPerfAcquire, public CLockPerfHold { public:
// member functions
// ctors / dtors
CBinaryLockPerfInfo() { }
// debugging support
void Dump( CDumpContext& dc ) const; };
// Binary Lock Group Information
class CBinaryLockGroupInfo { public:
// member functions
// ctors / dtors
CBinaryLockGroupInfo() {} ~CBinaryLockGroupInfo() {}
// manipulators
void StartWait( const int iGroup ) { m_rginfo[iGroup].StartWait(); } void StopWait( const int iGroup ) { m_rginfo[iGroup].StopWait(); }
void SetAcquire( const int iGroup ) { m_rginfo[iGroup].SetAcquire(); } void SetContend( const int iGroup ) { m_rginfo[iGroup].SetContend(); }
void StartHold( const int iGroup ) { m_rginfo[iGroup].StartHold(); } void StopHold( const int iGroup ) { m_rginfo[iGroup].StopHold(); }
// accessors
#ifdef SYNC_ANALYZE_PERFORMANCE
QWORD CWaitTotal( const int iGroup ) const { return m_rginfo[iGroup].CWaitTotal(); } double CsecWaitElapsed( const int iGroup ) const { return m_rginfo[iGroup].CsecWaitElapsed(); }
QWORD CAcquireTotal( const int iGroup ) const { return m_rginfo[iGroup].CAcquireTotal(); } QWORD CContendTotal( const int iGroup ) const { return m_rginfo[iGroup].CContendTotal(); }
QWORD CHoldTotal( const int iGroup ) const { return m_rginfo[iGroup].CHoldTotal(); } double CsecHoldElapsed( const int iGroup ) const { return m_rginfo[iGroup].CsecHoldElapsed(); }
#endif // SYNC_ANALYZE_PERFORMANCE
// debugging support
void Dump( CDumpContext& dc ) const;
private:
// member functions
// operators
CBinaryLockGroupInfo& operator=( CBinaryLockGroupInfo& ); // disallowed
// data members
// performance info for each group
CBinaryLockPerfInfo m_rginfo[2]; };
// Binary Lock Information
class CBinaryLockInfo : public CLockBasicInfo, public CBinaryLockGroupInfo, public CLockDeadlockDetectionInfo { public:
// member functions
// ctors / dtors
CBinaryLockInfo( const CLockBasicInfo& lbi ) : CLockBasicInfo( lbi ), CLockDeadlockDetectionInfo( (CLockBasicInfo&) *this ) { }
// debugging support
void Dump( CDumpContext& dc ) const; };
// Binary Lock State
class CBinaryLockState { public:
// types
// control word
typedef DWORD ControlWord;
// member functions
// ctors / dtors
CBinaryLockState( const CSyncBasicInfo& sbi ); ~CBinaryLockState();
// debugging support
void Dump( CDumpContext& dc ) const;
// data members
// control word
union { volatile ControlWord m_cw;
struct { volatile DWORD m_cOOW1:15; volatile DWORD m_fQ1:1; volatile DWORD m_cOOW2:15; volatile DWORD m_fQ2:1; }; };
// quiesced owner count
volatile DWORD m_cOwner;
// sempahore used by Group 1 to wait for lock ownership
CSemaphore m_sem1;
// sempahore used by Group 2 to wait for lock ownership
CSemaphore m_sem2;
private:
// member functions
// operators
CBinaryLockState& operator=( CBinaryLockState& ); // disallowed
};
// Binary Lock
class CBinaryLock : private CLockObject, private CEnhancedStateContainer< CBinaryLockState, CSyncBasicInfo, CBinaryLockInfo, CLockBasicInfo > { public:
// types
// control word
typedef CBinaryLockState::ControlWord ControlWord;
// transition reasons for state machine
enum TransitionReason { trIllegal = 0, trEnter1 = 1, trLeave1 = 2, trEnter2 = 4, trLeave2 = 8, };
// member functions
// ctors / dtors
CBinaryLock( const CLockBasicInfo& lbi ); ~CBinaryLock();
// manipulators
void Enter1(); const BOOL FTryEnter1(); void Leave1();
void Enter2(); const BOOL FTryEnter2(); void Leave2();
// accessors
const BOOL FGroup1Quiesced() { return State().m_cw & 0x00008000; } const BOOL FGroup2Quiesced() { return State().m_cw & 0x80000000; }
const BOOL FMemberOfGroup1() { return State().FOwner( 0 ); } const BOOL FNotMemberOfGroup1() { return State().FNotOwner( 0 ); } const BOOL FMemberOfGroup2() { return State().FOwner( 1 ); } const BOOL FNotMemberOfGroup2() { return State().FNotOwner( 1 ); }
// debugging support
void Dump( CDumpContext& dc ) const;
private:
// member functions
// operators
CBinaryLock& operator=( CBinaryLock& ); // disallowed
// verification
int _StateFromControlWord( const ControlWord cw ); BOOL _FValidStateTransition( const ControlWord cwBI, const ControlWord cwAI, const TransitionReason tr );
// manipulators
void _Enter1( const ControlWord cwBIOld ); void _Enter2( const ControlWord cwBIOld );
void _UpdateQuiescedOwnerCountAsGroup1( const DWORD cOwnerDelta ); void _UpdateQuiescedOwnerCountAsGroup2( const DWORD cOwnerDelta ); };
// enters the binary lock as a member of Group 1, waiting forever if necessary
//
// NOTE: trying to enter the lock as a member of Group 1 when you already own
// the lock as a member of Group 2 will cause a deadlock.
inline void CBinaryLock::Enter1() { // we had better not already own this lock as either group
OSSYNCAssert( State().FNotOwner( 0 ) ); OSSYNCAssert( State().FNotOwner( 1 ) );
// check for deadlock
OSSYNCAssertSzRTL( State().FCanBeWaiter(), "Potential Deadlock Detected (Rank Violation)" );
// try forever until we successfully change the lock state
OSSYNC_FOREVER { // read the current state of the control word as our expected before image
const ControlWord cwBIExpected = State().m_cw;
// compute the after image of the control word by performing the global
// transform for the Enter1 state transition
const ControlWord cwAI = ControlWord( ( ( cwBIExpected & ( ( LONG_PTR( long( cwBIExpected ) ) >> 31 ) | 0x0000FFFF ) ) | 0x80000000 ) + 0x00000001 );
// validate the transaction
OSSYNCAssert( _FValidStateTransition( cwBIExpected, cwAI, trEnter1 ) );
// attempt to perform the transacted state transition on the control word
const ControlWord cwBI = AtomicCompareExchange( (long*)&State().m_cw, cwBIExpected, cwAI );
// the transaction failed or Group 1 was quiesced from ownership
if ( ( cwBI ^ cwBIExpected ) | ( cwBI & 0x00008000 ) ) { // the transaction failed because another context changed the control word
if ( cwBI != cwBIExpected ) { // try again
continue; }
// the transaction succeeded but Group 1 was quiesced from ownership
else { // this is a contention for Group 1
State().SetContend( 0 );
// wait to own the lock as a member of Group 1
_Enter1( cwBI );
// we now own the lock, so we're done
break; } }
// the transaction succeeded and Group 1 was not quiesced from ownership
else { // we now own the lock, so we're done
break; } }
// we are now an owner of the lock for Group 1
State().SetAcquire( 0 ); State().AddAsOwner( 0 ); State().StartHold( 0 ); }
// tries to enter the binary lock as a member of Group 1 without waiting or
// spinning, returning fFalse if Group 1 is quiesced from ownership
//
// NOTE: trying to enter the lock as a member of Group 1 when you already own
// the lock as a member of Group 2 will cause a deadlock.
inline const BOOL CBinaryLock::FTryEnter1() { // we had better not already own this lock as either group
OSSYNCAssert( State().FNotOwner( 0 ) ); OSSYNCAssert( State().FNotOwner( 1 ) );
// try forever until we successfully change the lock state
OSSYNC_FOREVER { // read the current state of the control word as our expected before image
ControlWord cwBIExpected = State().m_cw;
// change the expected before image so that the transaction will only work if
// Group 1 ownership is not quiesced
cwBIExpected = cwBIExpected & 0xFFFF7FFF;
// compute the after image of the control word by performing the global
// transform for the Enter1 state transition
const ControlWord cwAI = ControlWord( ( ( cwBIExpected & ( ( LONG_PTR( long( cwBIExpected ) ) >> 31 ) | 0x0000FFFF ) ) | 0x80000000 ) + 0x00000001 );
// validate the transaction
OSSYNCAssert( _StateFromControlWord( cwBIExpected ) < 0 || _FValidStateTransition( cwBIExpected, cwAI, trEnter1 ) );
// attempt to perform the transacted state transition on the control word
const ControlWord cwBI = AtomicCompareExchange( (long*)&State().m_cw, cwBIExpected, cwAI );
// the transaction failed
if ( cwBI != cwBIExpected ) { // the transaction failed because Group 1 ownership is quiesced
if ( cwBI & 0x00008000 ) { // return failure
return fFalse; }
// the transaction failed because another context changed the control word
else { // try again
continue; } }
// the transaction succeeded
else { // we are now an owner of the lock for Group 1
State().SetAcquire( 0 ); State().AddAsOwner( 0 ); State().StartHold( 0 );
// return success
return fTrue; } } }
// leaves the binary lock as a member of Group 1
//
// NOTE: you must leave the lock as a member of the same Group for which you entered
// the lock or deadlocks may occur
inline void CBinaryLock::Leave1() { // we are no longer an owner of the lock
State().RemoveAsOwner( 0 );
// we are no longer holding the lock
State().StopHold( 0 );
// try forever until we successfully change the lock state
OSSYNC_FOREVER { // read the current state of the control word as our expected before image
ControlWord cwBIExpected = State().m_cw;
// change the expected before image so that the transaction will only work if
// Group 1 ownership is not quiesced
cwBIExpected = cwBIExpected & 0xFFFF7FFF;
// compute the after image of the control word by performing the transform that
// will take us either from state 2 to state 0 or state 2 to state 2
ControlWord cwAI = cwBIExpected + 0xFFFFFFFF; cwAI = cwAI - ( ( ( cwAI + 0xFFFFFFFF ) << 16 ) & 0x80000000 );
// validate the transaction
OSSYNCAssert( _StateFromControlWord( cwBIExpected ) < 0 || _FValidStateTransition( cwBIExpected, cwAI, trLeave1 ) );
// attempt to perform the transacted state transition on the control word
const ControlWord cwBI = AtomicCompareExchange( (long*)&State().m_cw, cwBIExpected, cwAI );
// the transaction failed
if ( cwBI != cwBIExpected ) { // the transaction failed because Group 1 ownership is quiesced
if ( cwBI & 0x00008000 ) { // leave the lock as a quiesced owner
_UpdateQuiescedOwnerCountAsGroup1( 0xFFFFFFFF );
// we're done
break; }
// the transaction failed because another context changed the control word
else { // try again
continue; } }
// the transaction succeeded
else { // we're done
break; } } }
// enters the binary lock as a member of Group 2, waiting forever if necessary
//
// NOTE: trying to enter the lock as a member of Group 2 when you already own
// the lock as a member of Group 1 will cause a deadlock.
inline void CBinaryLock::Enter2() { // we had better not already own this lock as either group
OSSYNCAssert( State().FNotOwner( 0 ) ); OSSYNCAssert( State().FNotOwner( 1 ) );
// check for deadlock
OSSYNCAssertSzRTL( State().FCanBeWaiter(), "Potential Deadlock Detected (Rank Violation)" );
// try forever until we successfully change the lock state
OSSYNC_FOREVER { // read the current state of the control word as our expected before image
const ControlWord cwBIExpected = State().m_cw;
// compute the after image of the control word by performing the global
// transform for the Enter2 state transition
const ControlWord cwAI = ControlWord( ( ( cwBIExpected & ( ( LONG_PTR( long( cwBIExpected << 16 ) ) >> 31 ) | 0xFFFF0000 ) ) | 0x00008000 ) + 0x00010000 );
// validate the transaction
OSSYNCAssert( _FValidStateTransition( cwBIExpected, cwAI, trEnter2 ) );
// attempt to perform the transacted state transition on the control word
const ControlWord cwBI = AtomicCompareExchange( (long*)&State().m_cw, cwBIExpected, cwAI );
// the transaction failed or Group 2 was quiesced from ownership
if ( ( cwBI ^ cwBIExpected ) | ( cwBI & 0x80000000 ) ) { // the transaction failed because another context changed the control word
if ( cwBI != cwBIExpected ) { // try again
continue; }
// the transaction succeeded but Group 2 was quiesced from ownership
else { // this is a contention for Group 2
State().SetContend( 1 );
// wait to own the lock as a member of Group 2
_Enter2( cwBI );
// we now own the lock, so we're done
break; } }
// the transaction succeeded and Group 2 was not quiesced from ownership
else { // we now own the lock, so we're done
break; } }
// we are now an owner of the lock for Group 2
State().SetAcquire( 1 ); State().AddAsOwner( 1 ); State().StartHold( 1 ); }
// tries to enter the binary lock as a member of Group 2 without waiting or
// spinning, returning fFalse if Group 2 is quiesced from ownership
//
// NOTE: trying to enter the lock as a member of Group 2 when you already own
// the lock as a member of Group 1 will cause a deadlock.
inline const BOOL CBinaryLock::FTryEnter2() { // we had better not already own this lock as either group
OSSYNCAssert( State().FNotOwner( 0 ) ); OSSYNCAssert( State().FNotOwner( 1 ) );
// try forever until we successfully change the lock state
OSSYNC_FOREVER { // read the current state of the control word as our expected before image
ControlWord cwBIExpected = State().m_cw;
// change the expected before image so that the transaction will only work if
// Group 2 ownership is not quiesced
cwBIExpected = cwBIExpected & 0x7FFFFFFF;
// compute the after image of the control word by performing the global
// transform for the Enter2 state transition
const ControlWord cwAI = ControlWord( ( ( cwBIExpected & ( ( LONG_PTR( long( cwBIExpected << 16 ) ) >> 31 ) | 0xFFFF0000 ) ) | 0x00008000 ) + 0x00010000 );
// validate the transaction
OSSYNCAssert( _StateFromControlWord( cwBIExpected ) < 0 || _FValidStateTransition( cwBIExpected, cwAI, trEnter2 ) );
// attempt to perform the transacted state transition on the control word
const ControlWord cwBI = AtomicCompareExchange( (long*)&State().m_cw, cwBIExpected, cwAI );
// the transaction failed
if ( cwBI != cwBIExpected ) { // the transaction failed because Group 2 ownership is quiesced
if ( cwBI & 0x80000000 ) { // return failure
return fFalse; }
// the transaction failed because another context changed the control word
else { // try again
continue; } }
// the transaction succeeded
else { // we are now an owner of the lock for Group 2
State().SetAcquire( 1 ); State().AddAsOwner( 1 ); State().StartHold( 1 );
// return success
return fTrue; } } }
// leaves the binary lock as a member of Group 2
//
// NOTE: you must leave the lock as a member of the same Group for which you entered
// the lock or deadlocks may occur
inline void CBinaryLock::Leave2() { // we are no longer an owner of the lock
State().RemoveAsOwner( 1 );
// we are no longer holding the lock
State().StopHold( 1 );
// try forever until we successfully change the lock state
OSSYNC_FOREVER { // read the current state of the control word as our expected before image
ControlWord cwBIExpected = State().m_cw;
// change the expected before image so that the transaction will only work if
// Group 2 ownership is not quiesced
cwBIExpected = cwBIExpected & 0x7FFFFFFF;
// compute the after image of the control word by performing the transform that
// will take us either from state 1 to state 0 or state 1 to state 1
ControlWord cwAI = cwBIExpected + 0xFFFF0000; cwAI = cwAI - ( ( ( cwAI + 0xFFFF0000 ) >> 16 ) & 0x00008000 );
// validate the transaction
OSSYNCAssert( _StateFromControlWord( cwBIExpected ) < 0 || _FValidStateTransition( cwBIExpected, cwAI, trLeave2 ) );
// attempt to perform the transacted state transition on the control word
const ControlWord cwBI = AtomicCompareExchange( (long*)&State().m_cw, cwBIExpected, cwAI );
// the transaction failed
if ( cwBI != cwBIExpected ) { // the transaction failed because Group 2 ownership is quiesced
if ( cwBI & 0x80000000 ) { // leave the lock as a quiesced owner
_UpdateQuiescedOwnerCountAsGroup2( 0xFFFFFFFF );
// we're done
break; }
// the transaction failed because another context changed the control word
else { // try again
continue; } }
// the transaction succeeded
else { // we're done
break; } } }
// Reader / Writer Lock Performance Information
class CReaderWriterLockPerfInfo : public CSyncPerfWait, public CSyncPerfAcquire, public CLockPerfHold { public:
// member functions
// ctors / dtors
CReaderWriterLockPerfInfo() { }
// debugging support
void Dump( CDumpContext& dc ) const; };
// Reader / Writer Lock Group Information
class CReaderWriterLockGroupInfo { public:
// member functions
// ctors / dtors
CReaderWriterLockGroupInfo() {} ~CReaderWriterLockGroupInfo() {}
// manipulators
void StartWait( const int iGroup ) { m_rginfo[iGroup].StartWait(); } void StopWait( const int iGroup ) { m_rginfo[iGroup].StopWait(); }
void SetAcquire( const int iGroup ) { m_rginfo[iGroup].SetAcquire(); } void SetContend( const int iGroup ) { m_rginfo[iGroup].SetContend(); }
void StartHold( const int iGroup ) { m_rginfo[iGroup].StartHold(); } void StopHold( const int iGroup ) { m_rginfo[iGroup].StopHold(); }
// accessors
#ifdef SYNC_ANALYZE_PERFORMANCE
QWORD CWaitTotal( const int iGroup ) const { return m_rginfo[iGroup].CWaitTotal(); } double CsecWaitElapsed( const int iGroup ) const { return m_rginfo[iGroup].CsecWaitElapsed(); }
QWORD CAcquireTotal( const int iGroup ) const { return m_rginfo[iGroup].CAcquireTotal(); } QWORD CContendTotal( const int iGroup ) const { return m_rginfo[iGroup].CContendTotal(); }
QWORD CHoldTotal( const int iGroup ) const { return m_rginfo[iGroup].CHoldTotal(); } double CsecHoldElapsed( const int iGroup ) const { return m_rginfo[iGroup].CsecHoldElapsed(); }
#endif // SYNC_ANALYZE_PERFORMANCE
// debugging support
void Dump( CDumpContext& dc ) const;
private:
// member functions
// operators
CReaderWriterLockGroupInfo& operator=( CReaderWriterLockGroupInfo& ); // disallowed
// data members
// performance info for each group
CReaderWriterLockPerfInfo m_rginfo[2]; };
// Reader / Writer Lock Information
class CReaderWriterLockInfo : public CLockBasicInfo, public CReaderWriterLockGroupInfo, public CLockDeadlockDetectionInfo { public:
// member functions
// ctors / dtors
CReaderWriterLockInfo( const CLockBasicInfo& lbi ) : CLockBasicInfo( lbi ), CLockDeadlockDetectionInfo( (CLockBasicInfo&) *this ) { }
// debugging support
void Dump( CDumpContext& dc ) const; };
// Reader / Writer Lock State
class CReaderWriterLockState { public:
// types
// control word
typedef DWORD ControlWord;
// member functions
// ctors / dtors
CReaderWriterLockState( const CSyncBasicInfo& sbi ); ~CReaderWriterLockState();
// debugging support
void Dump( CDumpContext& dc ) const;
// data members
// control word
union { volatile ControlWord m_cw;
struct { volatile DWORD m_cOAOWW:15; volatile DWORD m_fQW:1; volatile DWORD m_cOOWR:15; volatile DWORD m_fQR:1; }; };
// quiesced owner count
volatile DWORD m_cOwner;
// sempahore used by writers to wait for lock ownership
CSemaphore m_semWriter;
// sempahore used by readers to wait for lock ownership
CSemaphore m_semReader;
private:
// member functions
// operators
CReaderWriterLockState& operator=( CReaderWriterLockState& ); // disallowed
};
// Reader / Writer Lock
class CReaderWriterLock : private CLockObject, private CEnhancedStateContainer< CReaderWriterLockState, CSyncBasicInfo, CReaderWriterLockInfo, CLockBasicInfo > { public:
// types
// control word
typedef CReaderWriterLockState::ControlWord ControlWord;
// transition reasons for state machine
enum TransitionReason { trIllegal = 0, trEnterAsWriter = 1, trLeaveAsWriter = 2, trEnterAsReader = 4, trLeaveAsReader = 8, };
// member functions
// ctors / dtors
CReaderWriterLock( const CLockBasicInfo& lbi ); ~CReaderWriterLock();
// manipulators
void EnterAsWriter(); const BOOL FTryEnterAsWriter(); void LeaveAsWriter();
void EnterAsReader(); const BOOL FTryEnterAsReader(); void LeaveAsReader();
// accessors
const BOOL FWritersQuiesced() { return State().m_cw & 0x00008000; } const BOOL FReadersQuiesced() { return State().m_cw & 0x80000000; }
const BOOL FWriter() { return State().FOwner( 0 ); } const BOOL FNotWriter() { return State().FNotOwner( 0 ); } const BOOL FReader() { return State().FOwner( 1 ); } const BOOL FNotReader() { return State().FNotOwner( 1 ); }
// debugging support
void Dump( CDumpContext& dc ) const;
private:
// member functions
// operators
CReaderWriterLock& operator=( CReaderWriterLock& ); // disallowed
// verification
int _StateFromControlWord( const ControlWord cw ); BOOL _FValidStateTransition( const ControlWord cwBI, const ControlWord cwAI, const TransitionReason tr );
// manipulators
void _EnterAsWriter( const ControlWord cwBIOld ); void _EnterAsReader( const ControlWord cwBIOld );
void _UpdateQuiescedOwnerCountAsWriter( const DWORD cOwnerDelta ); void _UpdateQuiescedOwnerCountAsReader( const DWORD cOwnerDelta ); };
// enters the reader / writer lock as a writer, waiting forever if necessary
//
// NOTE: trying to enter the lock as a writer when you already own the lock
// as a reader will cause a deadlock.
inline void CReaderWriterLock::EnterAsWriter() { // we had better not already own this lock as either a reader or a writer
OSSYNCAssert( State().FNotOwner( 0 ) ); OSSYNCAssert( State().FNotOwner( 1 ) );
// check for deadlock
OSSYNCAssertSzRTL( State().FCanBeWaiter(), "Potential Deadlock Detected (Rank Violation)");
// try forever until we successfully change the lock state
OSSYNC_FOREVER { // read the current state of the control word as our expected before image
const ControlWord cwBIExpected = State().m_cw;
// compute the after image of the control word by performing the global
// transform for the EnterAsWriter state transition
const ControlWord cwAI = ControlWord( ( ( cwBIExpected & ( ( LONG_PTR( long( cwBIExpected ) ) >> 31 ) | 0x0000FFFF ) ) | 0x80000000 ) + 0x00000001 );
// validate the transaction
OSSYNCAssert( _FValidStateTransition( cwBIExpected, cwAI, trEnterAsWriter ) );
// attempt to perform the transacted state transition on the control word
const ControlWord cwBI = AtomicCompareExchange( (long*)&State().m_cw, cwBIExpected, cwAI );
// the transaction failed or writers are quiesced from ownership or a
// writer already owns the lock
if ( ( cwBI ^ cwBIExpected ) | ( cwBI & 0x0000FFFF ) ) { // the transaction failed because another context changed the control word
if ( cwBI != cwBIExpected ) { // try again
continue; }
// the transaction succeeded but writers are quiesced from ownership
// or a writer already owns the lock
else { // this is a contention for writers
State().SetContend( 0 );
// wait to own the lock as a writer
_EnterAsWriter( cwBI );
// we now own the lock, so we're done
break; } }
// the transaction succeeded and writers were not quiesced from ownership
// and a writer did not already own the lock
else { // we now own the lock, so we're done
break; } }
// we are now an owner of the lock for writers
State().SetAcquire( 0 ); State().AddAsOwner( 0 ); State().StartHold( 0 ); }
// tries to enter the reader / writer lock as a writer without waiting or
// spinning, returning fFalse if writers are quiesced from ownership or
// another writer already owns the lock
//
// NOTE: trying to enter the lock as a writer when you already own the lock
// as a reader will cause a deadlock.
inline const BOOL CReaderWriterLock::FTryEnterAsWriter() { // we had better not already own this lock as either a reader or a writer
OSSYNCAssert( State().FNotOwner( 0 ) ); OSSYNCAssert( State().FNotOwner( 1 ) );
// try forever until we successfully change the lock state
OSSYNC_FOREVER { // read the current state of the control word as our expected before image
ControlWord cwBIExpected = State().m_cw;
// change the expected before image so that the transaction will only work if
// writers were not quiesced from ownership and another writer doesn't already
// own the lock
cwBIExpected = cwBIExpected & 0xFFFF0000;
// compute the after image of the control word by performing the global
// transform for the EnterAsWriter state transition
const ControlWord cwAI = ControlWord( ( ( cwBIExpected & ( ( LONG_PTR( long( cwBIExpected ) ) >> 31 ) | 0x0000FFFF ) ) | 0x80000000 ) + 0x00000001 );
// validate the transaction
OSSYNCAssert( _StateFromControlWord( cwBIExpected ) < 0 || _FValidStateTransition( cwBIExpected, cwAI, trEnterAsWriter ) );
// attempt to perform the transacted state transition on the control word
const ControlWord cwBI = AtomicCompareExchange( (long*)&State().m_cw, cwBIExpected, cwAI );
// the transaction failed
if ( cwBI != cwBIExpected ) { // the transaction failed because writers were quiesced from ownership
// or another writer already owns the lock
if ( cwBI & 0x0000FFFF ) { // return failure
return fFalse; }
// the transaction failed because another context changed the control word
else { // try again
continue; } }
// the transaction succeeded
else { // we are now an owner of the lock for writers
State().SetAcquire( 0 ); State().AddAsOwner( 0 ); State().StartHold( 0 );
// return success
return fTrue; } } }
// leaves the reader / writer lock as a writer
//
// NOTE: you must leave the lock as a member of the same group for which you entered
// the lock or deadlocks may occur
inline void CReaderWriterLock::LeaveAsWriter() { // we are no longer an owner of the lock
State().RemoveAsOwner( 0 );
// we are no longer holding the lock
State().StopHold( 0 );
// try forever until we successfully change the lock state
OSSYNC_FOREVER { // read the current state of the control word as our expected before image
ControlWord cwBIExpected = State().m_cw;
// change the expected before image so that the transaction will only work if
// writers were not quiesced from ownership
cwBIExpected = cwBIExpected & 0xFFFF7FFF;
// compute the after image of the control word by performing the transform that
// will take us either from state 2 to state 0 or state 2 to state 2
ControlWord cwAI = cwBIExpected + 0xFFFFFFFF; cwAI = cwAI - ( ( ( cwAI + 0xFFFFFFFF ) << 16 ) & 0x80000000 );
// validate the transaction
OSSYNCAssert( _StateFromControlWord( cwBIExpected ) < 0 || _FValidStateTransition( cwBIExpected, cwAI, trLeaveAsWriter ) );
// attempt to perform the transacted state transition on the control word
const ControlWord cwBI = AtomicCompareExchange( (long*)&State().m_cw, cwBIExpected, cwAI );
// the transaction failed
if ( cwBI != cwBIExpected ) { // the transaction failed because writers were quiesced from ownership
if ( cwBI & 0x00008000 ) { // leave the lock as a quiesced owner
_UpdateQuiescedOwnerCountAsWriter( 0xFFFFFFFF );
// we're done
break; }
// the transaction failed because another context changed the control word
else { // try again
continue; } }
// the transaction succeeded
else { // there were other writers waiting for ownership of the lock
if ( cwAI & 0x00007FFF ) { // release the next writer into ownership of the lock
State().m_semWriter.Release(); }
// we're done
break; } } }
// enters the reader / writer lock as a reader, waiting forever if necessary
//
// NOTE: trying to enter the lock as a reader when you already own the lock
// as a writer will cause a deadlock.
inline void CReaderWriterLock::EnterAsReader() { // we had better not already own this lock as either a reader or a writer
OSSYNCAssert( State().FNotOwner( 0 ) ); OSSYNCAssert( State().FNotOwner( 1 ) );
// check for deadlock
OSSYNCAssertSzRTL( State().FCanBeWaiter(), "Potential Deadlock Detected (Rank Violation)" );
// try forever until we successfully change the lock state
OSSYNC_FOREVER { // read the current state of the control word as our expected before image
const ControlWord cwBIExpected = State().m_cw;
// compute the after image of the control word by performing the global
// transform for the EnterAsReader state transition
const ControlWord cwAI = ( cwBIExpected & 0xFFFF7FFF ) + ( ( cwBIExpected & 0x80008000 ) == 0x80000000 ? 0x00017FFF : 0x00018000 );
// validate the transaction
OSSYNCAssert( _FValidStateTransition( cwBIExpected, cwAI, trEnterAsReader ) );
// attempt to perform the transacted state transition on the control word
const ControlWord cwBI = AtomicCompareExchange( (long*)&State().m_cw, cwBIExpected, cwAI );
// the transaction failed or readers were quiesced from ownership
if ( ( cwBI ^ cwBIExpected ) | ( cwBI & 0x80000000 ) ) { // the transaction failed because another context changed the control word
if ( cwBI != cwBIExpected ) { // try again
continue; }
// the transaction succeeded but readers were quiesced from ownership
else { // this is a contention for readers
State().SetContend( 1 );
// wait to own the lock as a reader
_EnterAsReader( cwBI );
// we now own the lock, so we're done
break; } }
// the transaction succeeded and readers were not quiesced from ownership
else { // we now own the lock, so we're done
break; } }
// we are now an owner of the lock for readers
State().SetAcquire( 1 ); State().AddAsOwner( 1 ); State().StartHold( 1 ); }
// tries to enter the reader / writer lock as a reader without waiting or
// spinning, returning fFalse if readers are quiesced from ownership
//
// NOTE: trying to enter the lock as a reader when you already own the lock
// as a writer will cause a deadlock.
inline const BOOL CReaderWriterLock::FTryEnterAsReader() { // we had better not already own this lock as either a reader or a writer
OSSYNCAssert( State().FNotOwner( 0 ) ); OSSYNCAssert( State().FNotOwner( 1 ) );
// try forever until we successfully change the lock state
OSSYNC_FOREVER { // read the current state of the control word as our expected before image
ControlWord cwBIExpected = State().m_cw;
// change the expected before image so that the transaction will only work if
// readers were not quiesced from ownership
cwBIExpected = cwBIExpected & 0x7FFFFFFF;
// compute the after image of the control word by performing the global
// transform for the EnterAsReader state transition
const ControlWord cwAI = ( cwBIExpected & 0xFFFF7FFF ) + ( ( cwBIExpected & 0x80008000 ) == 0x80000000 ? 0x00017FFF : 0x00018000 );
// validate the transaction
OSSYNCAssert( _StateFromControlWord( cwBIExpected ) < 0 || _FValidStateTransition( cwBIExpected, cwAI, trEnterAsReader ) );
// attempt to perform the transacted state transition on the control word
const ControlWord cwBI = AtomicCompareExchange( (long*)&State().m_cw, cwBIExpected, cwAI );
// the transaction failed
if ( cwBI != cwBIExpected ) { // the transaction failed because readers were quiesced from ownership
if ( cwBI & 0x80000000 ) { // return failure
return fFalse; }
// the transaction failed because another context changed the control word
else { // try again
continue; } }
// the transaction succeeded
else { // we are now an owner of the lock for readers
State().SetAcquire( 1 ); State().AddAsOwner( 1 ); State().StartHold( 1 );
// return success
return fTrue; } } }
// leaves the reader / writer lock as a reader
//
// NOTE: you must leave the lock as a member of the same group for which you entered
// the lock or deadlocks may occur
inline void CReaderWriterLock::LeaveAsReader() { // we are no longer an owner of the lock
State().RemoveAsOwner( 1 );
// we are no longer holding the lock
State().StopHold( 1 );
// try forever until we successfully change the lock state
OSSYNC_FOREVER { // read the current state of the control word as our expected before image
ControlWord cwBIExpected = State().m_cw;
// change the expected before image so that the transaction will only work if
// readers were not quiesced from ownership
cwBIExpected = cwBIExpected & 0x7FFFFFFF;
// compute the after image of the control word by performing the transform that
// will take us either from state 1 to state 0 or state 1 to state 1
const ControlWord cwAI = ControlWord( cwBIExpected + 0xFFFF0000 + ( ( LONG_PTR( long( cwBIExpected + 0xFFFE0000 ) ) >> 31 ) & 0xFFFF8000 ) );
// validate the transaction
OSSYNCAssert( _StateFromControlWord( cwBIExpected ) < 0 || _FValidStateTransition( cwBIExpected, cwAI, trLeaveAsReader ) );
// attempt to perform the transacted state transition on the control word
const ControlWord cwBI = AtomicCompareExchange( (long*)&State().m_cw, cwBIExpected, cwAI );
// the transaction failed
if ( cwBI != cwBIExpected ) { // the transaction failed because readers were quiesced from ownership
if ( cwBI & 0x80000000 ) { // leave the lock as a quiesced owner
_UpdateQuiescedOwnerCountAsReader( 0xFFFFFFFF );
// we're done
break; }
// the transaction failed because another context changed the control word
else { // try again
continue; } }
// the transaction succeeded
else { // we're done
break; } } }
// Metered Section
class CMeteredSection : private CSyncObject { public:
// types
// control word
typedef DWORD ControlWord;
// callback used to notify the user when a partition of the current
// group has been completed
typedef void (*PFNPARTITIONCOMPLETE)( const DWORD_PTR dwCompletionKey );
// member functions
// ctors / dtors
CMeteredSection(); ~CMeteredSection();
// manipulators
int Enter(); void Leave( const int group );
void Partition( const PFNPARTITIONCOMPLETE pfnPartitionComplete = NULL, const DWORD_PTR dwCompletionKey = NULL );
// accessors
int ActiveGroup() { return int( m_groupCurrent ); }
// debugging support
void Dump( CDumpContext& dc ) const;
private:
// data members
// partition complete callback
PFNPARTITIONCOMPLETE m_pfnPartitionComplete; DWORD_PTR m_dwPartitionCompleteKey;
// control word
union { volatile ControlWord m_cw;
struct { volatile DWORD m_cCurrent:15; volatile DWORD m_groupCurrent:1; volatile DWORD m_cQuiesced:15; volatile DWORD m_groupQuiesced:1; }; };
// member functions
// operators
CMeteredSection& operator=( CMeteredSection& ); // disallowed
// manipulators
void _PartitionAsync( const PFNPARTITIONCOMPLETE pfnPartitionComplete, const DWORD_PTR dwCompletionKey ); static void _PartitionSyncComplete( CAutoResetSignal* const pasig ); };
// ctor
inline CMeteredSection::CMeteredSection() : m_cw( 0x80000000 ), m_pfnPartitionComplete( NULL ), m_dwPartitionCompleteKey( NULL ) { }
// dtor
inline CMeteredSection::~CMeteredSection() { }
// enter the metered section, returning the group id for which the current
// context has acquired the metered section
inline int CMeteredSection::Enter() { // increment the count for the current group
const DWORD cwDelta = 0x00000001; const DWORD cwBI = AtomicExchangeAdd( (long*) &m_cw, (long) cwDelta );
// there had better not be any overflow!
OSSYNCAssert( ( cwBI & 0x80008000 ) == ( ( cwBI + cwDelta ) & 0x80008000 ) );
// return the group we referenced
return int( ( cwBI >> 15 ) & 1 ); }
// leave the metered section using the specified group id. this group id must
// be the group id returned by the corresponding call to Enter()
inline void CMeteredSection::Leave( const int group ) { // try forever until we successfully leave
OSSYNC_FOREVER { // read the current state of the control word as our expected before image
const ControlWord cwBIExpected = m_cw;
// compute the after image of the control word
const ControlWord cwAI = cwBIExpected - ( ( ( ( cwBIExpected & 0x80008000 ) ^ 0x80008000 ) >> 15 ) ^ ( ( group << 16 ) | group ) );
// there had better not be any underflow!
OSSYNCAssert( ( cwBIExpected & 0x80008000 ) == ( cwAI & 0x80008000 ) );
// attempt to perform the transacted state transition on the control word
const ControlWord cwBI = AtomicCompareExchange( (long*)&m_cw, cwBIExpected, cwAI );
// the transaction failed
if ( cwBI != cwBIExpected ) { // try again
continue; }
// the transaction succeeded
else { // our update resulted in a partition completion
if ( ( cwBI & 0x7FFF0000 ) + ( cwAI & 0x7FFF0000 ) == 0x00010000 ) { // execute the completion function
m_pfnPartitionComplete( m_dwPartitionCompleteKey ); }
// we're done
break; } } }
// partitions all execution contexts entering the metered section into two groups.
// all contexts entering the section after this call are in a different group than
// all the contexts that entered the section before this call. when all contexts
// in the old group have left the metered section, the partition will be completed
//
// there are two ways to complete a partition: asynchronously and synchronously.
// asynchronous operation is selected if a completion function and key are provided.
// the last thread to leave the metered section for the previous group will
// execute asynchronous completions
//
// NOTE: it is illegal to have multiple concurrent partition requests. any attempt
// to do so will result in undefined behavior
inline void CMeteredSection::Partition( const PFNPARTITIONCOMPLETE pfnPartitionComplete, const DWORD_PTR dwCompletionKey ) { // this is an async partition request
if ( pfnPartitionComplete ) { // execute the parititon request
_PartitionAsync( pfnPartitionComplete, dwCompletionKey ); }
// this is a sync partition request
else { // create a signal to wait for completion
CAutoResetSignal asig( CSyncBasicInfo( "CMeteredSection::Partition()::asig" ) );
// issue an async partition request
_PartitionAsync( PFNPARTITIONCOMPLETE( _PartitionSyncComplete ), DWORD_PTR( &asig ) );
// wait for the partition to complete
asig.Wait(); } }
// performs an async partition request
inline void CMeteredSection::_PartitionAsync( const PFNPARTITIONCOMPLETE pfnPartitionComplete, const DWORD_PTR dwCompletionKey ) { // we should not be calling this if there is already a partition pending
OSSYNCAssertSz( !( m_cw & 0x7FFF0000 ), "Illegal concurrent use of Partitioning" );
// save the callback and key for the future completion
m_pfnPartitionComplete = pfnPartitionComplete; m_dwPartitionCompleteKey = dwCompletionKey;
// try forever until we successfully partition
OSSYNC_FOREVER { // read the current state of the control word as our expected before image
const ControlWord cwBIExpected = m_cw;
// compute the after image of the control word
const ControlWord cwAI = ( cwBIExpected >> 16 ) | ( cwBIExpected << 16 );
// attempt to perform the transacted state transition on the control word
const ControlWord cwBI = AtomicCompareExchange( (long*)&m_cw, cwBIExpected, cwAI );
// the transaction failed
if ( cwBI != cwBIExpected ) { // try again
continue; }
// the transaction succeeded
else { // our update resulted in a partition completion
if ( !( cwAI & 0x7FFF0000 ) ) { // execute the completion function
m_pfnPartitionComplete( m_dwPartitionCompleteKey ); }
// we're done
break; } } }
// partition completion function used for sync partition requests
inline void CMeteredSection::_PartitionSyncComplete( CAutoResetSignal* const pasig ) { // set the signal
pasig->Set(); }
// S / X / W Latch Performance Information
class CSXWLatchPerfInfo : public CSyncPerfWait, public CSyncPerfAcquire, public CLockPerfHold { public:
// member functions
// ctors / dtors
CSXWLatchPerfInfo() { }
// debugging support
void Dump( CDumpContext& dc ) const; };
// S / X / W Latch Group Information
class CSXWLatchGroupInfo { public:
// member functions
// ctors / dtors
CSXWLatchGroupInfo() {} ~CSXWLatchGroupInfo() {}
// manipulators
void StartWait( const int iGroup ) { m_rginfo[iGroup].StartWait(); } void StopWait( const int iGroup ) { m_rginfo[iGroup].StopWait(); }
void SetAcquire( const int iGroup ) { m_rginfo[iGroup].SetAcquire(); } void SetContend( const int iGroup ) { m_rginfo[iGroup].SetContend(); }
void StartHold( const int iGroup ) { m_rginfo[iGroup].StartHold(); } void StopHold( const int iGroup ) { m_rginfo[iGroup].StopHold(); }
// accessors
#ifdef SYNC_ANALYZE_PERFORMANCE
QWORD CWaitTotal( const int iGroup ) const { return m_rginfo[iGroup].CWaitTotal(); } double CsecWaitElapsed( const int iGroup ) const { return m_rginfo[iGroup].CsecWaitElapsed(); }
QWORD CAcquireTotal( const int iGroup ) const { return m_rginfo[iGroup].CAcquireTotal(); } QWORD CContendTotal( const int iGroup ) const { return m_rginfo[iGroup].CContendTotal(); }
QWORD CHoldTotal( const int iGroup ) const { return m_rginfo[iGroup].CHoldTotal(); } double CsecHoldElapsed( const int iGroup ) const { return m_rginfo[iGroup].CsecHoldElapsed(); }
#endif // SYNC_ANALYZE_PERFORMANCE
// debugging support
void Dump( CDumpContext& dc ) const;
private:
// member functions
// operators
CSXWLatchGroupInfo& operator=( CSXWLatchGroupInfo& ); // disallowed
// data members
// performance info for each group
CSXWLatchPerfInfo m_rginfo[3]; };
// S / X / W Latch Information
class CSXWLatchInfo : public CLockBasicInfo, public CSXWLatchGroupInfo, public CLockDeadlockDetectionInfo { public:
// member functions
// ctors / dtors
CSXWLatchInfo( const CLockBasicInfo& lbi ) : CLockBasicInfo( lbi ), CLockDeadlockDetectionInfo( (CLockBasicInfo&) *this ) { }
// debugging support
void Dump( CDumpContext& dc ) const; };
// S / X / W Latch State
class CSXWLatchState { public:
// types
// control word
typedef DWORD ControlWord;
// member functions
// ctors / dtors
CSXWLatchState( const CSyncBasicInfo& sbi ); ~CSXWLatchState();
// debugging support
void Dump( CDumpContext& dc ) const;
// data members
// control word
union { volatile ControlWord m_cw;
struct { volatile DWORD m_cOOWS:15; volatile DWORD m_fQS:1; volatile DWORD m_cOAWX:16; }; };
// quiesced share latch count
volatile DWORD m_cQS;
// sempahore used to wait for the shared latch
CSemaphore m_semS;
// sempahore used to wait for the exclusive latch
CSemaphore m_semX;
// sempahore used to wait for the write latch
CSemaphore m_semW;
private:
// member functions
// operators
CSXWLatchState& operator=( CSXWLatchState& ); // disallowed
};
// S / X / W Latch
class CSXWLatch : private CLockObject, private CEnhancedStateContainer< CSXWLatchState, CSyncBasicInfo, CSXWLatchInfo, CLockBasicInfo > { public:
// types
// control word
typedef CSXWLatchState::ControlWord ControlWord;
// API Error Codes
enum ERR { errSuccess, errWaitForSharedLatch, errWaitForExclusiveLatch, errWaitForWriteLatch, errLatchConflict };
// member functions
// ctors / dtors
CSXWLatch( const CLockBasicInfo& lbi ); ~CSXWLatch();
// manipulators
ERR ErrAcquireSharedLatch(); ERR ErrTryAcquireSharedLatch(); ERR ErrAcquireExclusiveLatch(); ERR ErrTryAcquireExclusiveLatch(); ERR ErrTryAcquireWriteLatch();
ERR ErrUpgradeSharedLatchToExclusiveLatch(); ERR ErrUpgradeSharedLatchToWriteLatch(); ERR ErrUpgradeExclusiveLatchToWriteLatch();
ERR ErrTryUpgradeSharedLatchToWriteLatch();
void DowngradeWriteLatchToExclusiveLatch(); void DowngradeWriteLatchToSharedLatch(); void DowngradeExclusiveLatchToSharedLatch();
void ReleaseWriteLatch(); void ReleaseExclusiveLatch(); void ReleaseSharedLatch();
void WaitForSharedLatch(); void WaitForExclusiveLatch(); void WaitForWriteLatch();
void ClaimOwnership( const DWORD group ); void ReleaseOwnership( const DWORD group );
// accessors
BOOL FOwnSharedLatch() { return State().FOwner( 0 ); } BOOL FNotOwnSharedLatch() { return State().FNotOwner( 0 ); } BOOL FOwnExclusiveLatch() { return State().FOwner( 1 ); } BOOL FNotOwnExclusiveLatch() { return State().FNotOwner( 1 ); } BOOL FOwnWriteLatch() { return State().FOwner( 2 ); } BOOL FNotOwnWriteLatch() { return State().FNotOwner( 2 ); }
// debugging support
void Dump( CDumpContext& dc ) const;
private:
// member functions
// operators
CSXWLatch& operator=( CSXWLatch& ); // disallowed
// manipulators
void _UpdateQuiescedSharedLatchCount( const DWORD cQSDelta ); };
// declares the current context as an owner or waiter of a shared latch. if
// the shared latch is acquired immediately, errSuccess will be returned. if
// the shared latch is not acquired immediately, errWaitForSharedLatch will be
// returned and WaitForSharedLatch() must be called to gain ownership of the
// shared latch
inline CSXWLatch::ERR CSXWLatch::ErrAcquireSharedLatch() { // we had better not already have a shared, exclusive, or write latch
OSSYNCAssert( FNotOwnSharedLatch() ); OSSYNCAssert( FNotOwnExclusiveLatch() ); OSSYNCAssert( FNotOwnWriteLatch() );
// add ourself as an owner or waiter for the shared latch
const ControlWord cwDelta = 0x00000001; const ControlWord cwBI = AtomicExchangeAdd( (long*)&State().m_cw, cwDelta );
// shared latches are quiesced
if ( cwBI & 0x00008000 ) { // this is a contention for a shared latch
State().SetContend( 0 );
// we are now a waiter for the shared latch
State().AddAsWaiter( 0 ); State().StartWait( 0 );
// we will need to block
return errWaitForSharedLatch; }
// shared latches are not quiesced
else { // we are now an owner of a shared latch
State().SetAcquire( 0 ); State().AddAsOwner( 0 ); State().StartHold( 0 );
// we now own the shared latch
return errSuccess; } }
// tries to declare the current context as an owner of a shared latch. if
// the shared latch is acquired immediately, errSuccess will be returned. if
// the shared latch is not acquired immediately, errLatchConflict will be
// returned
inline CSXWLatch::ERR CSXWLatch::ErrTryAcquireSharedLatch() { // we had better not already have a shared, exclusive, or write latch
OSSYNCAssert( FNotOwnSharedLatch() ); OSSYNCAssert( FNotOwnExclusiveLatch() ); OSSYNCAssert( FNotOwnWriteLatch() );
// try forever until we successfully change the latch state
OSSYNC_FOREVER { // read the current state of the control word as our expected before image
ControlWord cwBIExpected = State().m_cw;
// change the expected before image so that the transaction will only work if
// shared latches are not quiesced
cwBIExpected = cwBIExpected & 0xFFFF7FFF;
// compute the after image of the control word by performing the transform
// that will acquire a shared latch iff shared latches are not quiesced
const ControlWord cwAI = cwBIExpected + 0x00000001;
// attempt to perform the transacted state transition on the control word
const ControlWord cwBI = AtomicCompareExchange( (long*)&State().m_cw, cwBIExpected, cwAI );
// the transaction failed
if ( cwBI != cwBIExpected ) { // the transaction failed because shared latches were quiesced
if ( cwBI & 0x00008000 ) { // this is a contention for the shared latch
State().SetContend( 0 );
// this is a latch conflict
return errLatchConflict; }
// the transaction failed because another context changed the control
// word
else { // try again
continue; } }
// the transaction succeeded
else { // we are now an owner of a shared latch
State().SetAcquire( 0 ); State().AddAsOwner( 0 ); State().StartHold( 0 );
// we now own the shared latch
return errSuccess; } } }
// declares the current context as an owner or waiter of the exclusive latch.
// if the exclusive latch is acquired immediately, errSuccess will be returned.
// if the exclusive latch is not acquired immediately, errWaitForExclusiveLatch
// will be returned and WaitForExclusiveLatch() must be called to gain ownership
// of the exclusive latch
inline CSXWLatch::ERR CSXWLatch::ErrAcquireExclusiveLatch() { // we had better not already have a shared, exclusive, or write latch
OSSYNCAssert( FNotOwnSharedLatch() ); OSSYNCAssert( FNotOwnExclusiveLatch() ); OSSYNCAssert( FNotOwnWriteLatch() );
// add ourself as an owner or waiter for the exclusive latch
const ControlWord cwDelta = 0x00010000; const ControlWord cwBI = AtomicExchangeAdd( (long*)&State().m_cw, cwDelta );
// we are not the owner of the exclusive latch
if ( cwBI & 0xFFFF0000 ) { // this is a contention for the exclusive latch
State().SetContend( 1 );
// we are now a waiter for the exclusive latch
State().AddAsWaiter( 1 ); State().StartWait( 1 );
// we will need to block
return errWaitForExclusiveLatch; }
// we are the owner of the exclusive latch
else { // we are now an owner of the exclusive latch
State().SetAcquire( 1 ); State().AddAsOwner( 1 ); State().StartHold( 1 );
// we now own the exclusive latch
return errSuccess; } }
// tries to declare the current context as an owner of the exclusive latch. if
// the exclusive latch is acquired immediately, errSuccess will be returned. if
// the exclusive latch is not acquired immediately, errLatchConflict will be
// returned
inline CSXWLatch::ERR CSXWLatch::ErrTryAcquireExclusiveLatch() { // we had better not already have a shared, exclusive, or write latch
OSSYNCAssert( FNotOwnSharedLatch() ); OSSYNCAssert( FNotOwnExclusiveLatch() ); OSSYNCAssert( FNotOwnWriteLatch() );
// try forever until we successfully change the latch state
OSSYNC_FOREVER { // read the current state of the control word as our expected before image
ControlWord cwBIExpected = State().m_cw;
// change the expected before image so that the transaction will only work if
// the exclusive latch is not already owned
cwBIExpected = cwBIExpected & 0x0000FFFF;
// compute the after image of the control word by performing the transform
// that will acquire the exclusive latch iff it is not already owned
const ControlWord cwAI = cwBIExpected + 0x00010000;
// attempt to perform the transacted state transition on the control word
const ControlWord cwBI = AtomicCompareExchange( (long*)&State().m_cw, cwBIExpected, cwAI );
// the transaction failed
if ( cwBI != cwBIExpected ) { // the transaction failed because the exclusive latch was already
// owned
if ( cwBI & 0xFFFF0000 ) { // this is a contention for the exclusive latch
State().SetContend( 1 );
// this is a latch conflict
return errLatchConflict; }
// the transaction failed because another context changed the control
// word
else { // try again
continue; } }
// the transaction succeeded
else { // we are now an owner of the exclusive latch
State().SetAcquire( 1 ); State().AddAsOwner( 1 ); State().StartHold( 1 );
// we now own the exclusive latch
return errSuccess; } } }
// tries to declare the current context as an owner of the write latch. if
// the write latch is acquired immediately, errSuccess will be returned. if
// the write latch is not acquired immediately, errLatchConflict will be
// returned. note that a latch conflict will effectively occur if any other
// context currently owns or is waiting to own any type of latch
inline CSXWLatch::ERR CSXWLatch::ErrTryAcquireWriteLatch() { // we had better not already have a shared, exclusive, or write latch
OSSYNCAssert( FNotOwnSharedLatch() ); OSSYNCAssert( FNotOwnExclusiveLatch() ); OSSYNCAssert( FNotOwnWriteLatch() );
// try forever until we successfully change the latch state
OSSYNC_FOREVER { // set the expected before image so that the transaction will only work if
// no other context currently owns or is waiting to own any type of latch
const ControlWord cwBIExpected = 0x00000000;
// set the after image of the control word to a single write latch
const ControlWord cwAI = 0x00018000;
// attempt to perform the transacted state transition on the control word
const ControlWord cwBI = AtomicCompareExchange( (long*)&State().m_cw, cwBIExpected, cwAI );
// the transaction failed
if ( cwBI != cwBIExpected ) { // this is a contention for the write latch
State().SetContend( 2 );
// this is a latch conflict
return errLatchConflict; }
// the transaction succeeded
else { // we are now an owner of the write latch
State().SetAcquire( 2 ); State().AddAsOwner( 2 ); State().StartHold( 2 );
// we now own the write latch
return errSuccess; } } }
// attempts to upgrade a shared latch to the exclusive latch. if the exclusive
// latch is not available, errLatchConflict will be returned. if the exclusive
// latch is available, it will be acquired and errSuccess will be returned
inline CSXWLatch::ERR CSXWLatch::ErrUpgradeSharedLatchToExclusiveLatch() { // we had better already have only a shared latch
OSSYNCAssert( FOwnSharedLatch() ); OSSYNCAssert( FNotOwnExclusiveLatch() ); OSSYNCAssert( FNotOwnWriteLatch() );
// try forever until we successfully change the latch state
OSSYNC_FOREVER { // read the current state of the control word as our expected before image
ControlWord cwBIExpected = State().m_cw;
// change the expected before image so that the transaction will only work if
// the exclusive latch is not already owned
cwBIExpected = cwBIExpected & 0x0000FFFF;
// compute the after image of the control word by performing the transform
// that will set an exclusive latch iff there is no current owner of the
// exclusive latch and release our shared latch
const ControlWord cwAI = cwBIExpected + 0x0000FFFF;
// attempt to perform the transacted state transition on the control word
const ControlWord cwBI = AtomicCompareExchange( (long*)&State().m_cw, cwBIExpected, cwAI );
// the transaction failed
if ( cwBI != cwBIExpected ) { // the transaction failed because the exclusive latch was already owned
if ( cwBI & 0xFFFF0000 ) { // this is a contention for the exclusive latch
State().SetContend( 1 );
// this is a latch conflict
return errLatchConflict; }
// the transaction failed because another context changed the control
// word
else { // try again
continue; } }
// the transaction succeeded
else { // we are no longer an owner of a shared latch
State().RemoveAsOwner( 0 ); State().StopHold( 0 );
// we are now an owner of the exclusive latch
State().SetAcquire( 1 ); State().AddAsOwner( 1 ); State().StartHold( 1 );
// we now own the exclusive latch
return errSuccess; } } }
// attempts to upgrade a shared latch to the write latch. if the write latch
// is not available, errLatchConflict will be returned. if the write latch is
// available, it will be acquired. if the write latch is acquired immediately,
// errSuccess will be returned. if the write latch is not acquired immediately,
// errWaitForWriteLatch will be returned and WaitForWriteLatch() must be called
// to gain ownership of the write latch
inline CSXWLatch::ERR CSXWLatch::ErrUpgradeSharedLatchToWriteLatch() { // we had better already have only a shared latch
OSSYNCAssert( FOwnSharedLatch() ); OSSYNCAssert( FNotOwnExclusiveLatch() ); OSSYNCAssert( FNotOwnWriteLatch() );
// try forever until we successfully change the latch state
OSSYNC_FOREVER { // read the current state of the control word as our expected before image
ControlWord cwBIExpected = State().m_cw;
// change the expected before image so that the transaction will only work if
// the exclusive latch is not already owned
cwBIExpected = cwBIExpected & 0x0000FFFF;
// compute the after image of the control word by performing the transform
// that will set a write latch iff there is no current owner of the
// exclusive latch, quiescing any remaining shared latches
const ControlWord cwAI = 0x00018000;
// attempt to perform the transacted state transition on the control word
const ControlWord cwBI = AtomicCompareExchange( (long*)&State().m_cw, cwBIExpected, cwAI );
// the transaction failed
if ( cwBI != cwBIExpected ) { // the transaction failed because the write latch was already owned
if ( cwBI & 0xFFFF0000 ) { // this is a contention for the write latch
State().SetContend( 2 );
// this is a latch conflict
return errLatchConflict; }
// the transaction failed because another context changed the control
// word
else { // try again
continue; } }
// the transaction succeeded
else { // shared latches were just quiesced
if ( cwBI != 0x00000001 ) { // we are no longer an owner of a shared latch
State().RemoveAsOwner( 0 ); State().StopHold( 0 );
// update the quiesced shared latch count with the shared latch count
// that we displaced from the control word, possibly releasing waiters.
// we update the count as if we we had a shared latch as a write latch
// (namely ours) can be released. don't forget to deduct our shared
// latch from this count
_UpdateQuiescedSharedLatchCount( cwBI - 1 );
// we are now a waiter for the write latch
State().AddAsWaiter( 2 ); State().StartWait( 2 );
// we will need to block
return errWaitForWriteLatch; }
// shared latches were not just quiesced
else { // we are no longer an owner of a shared latch
State().RemoveAsOwner( 0 ); State().StopHold( 0 );
// we are now an owner of the write latch
State().SetAcquire( 2 ); State().AddAsOwner( 2 ); State().StartHold( 2 );
// we now own the write latch
return errSuccess; } } } }
// upgrades the exclusive latch to the write latch. if the write latch is
// acquired immediately, errSuccess will be returned. if the write latch is
// not acquired immediately, errWaitForWriteLatch is returned and
// WaitForWriteLatch() must be called to gain ownership of the write latch
inline CSXWLatch::ERR CSXWLatch::ErrUpgradeExclusiveLatchToWriteLatch() { // we had better already have only an exclusive latch
OSSYNCAssert( FNotOwnSharedLatch() ); OSSYNCAssert( FOwnExclusiveLatch() ); OSSYNCAssert( FNotOwnWriteLatch() );
// we are no longer an owner of the exclusive latch
State().RemoveAsOwner( 1 ); State().StopHold( 1 );
// try forever until we successfully change the latch state
OSSYNC_FOREVER { // read the current state of the control word as our expected before image
const ControlWord cwBIExpected = State().m_cw;
// compute the after image of the control word by performing the transform that
// will quiesce shared latches by setting the fQS flag and removing the current
// shared latch count from the control word
const ControlWord cwAI = ( cwBIExpected & 0xFFFF0000 ) | 0x00008000;
// attempt to perform the transacted state transition on the control word
const ControlWord cwBI = AtomicCompareExchange( (long*)&State().m_cw, cwBIExpected, cwAI );
// the transaction failed
if ( cwBI != cwBIExpected ) { // try again
continue; }
// the transaction succeeded
else { // shared latches were just quiesced
if ( cwBI & 0x00007FFF ) { // this is a contention for the write latch
State().SetContend( 2 );
// update the quiesced shared latch count with the shared latch
// count that we displaced from the control word, possibly
// releasing waiters. we update the count as if we we had a
// shared latch as a write latch (namely ours) can be released
_UpdateQuiescedSharedLatchCount( cwBI & 0x00007FFF );
// we are now a waiter for the write latch
State().AddAsWaiter( 2 ); State().StartWait( 2 );
// we will need to block
return errWaitForWriteLatch; }
// shared latches were not just quiesced
else { // we are now an owner of the write latch
State().SetAcquire( 2 ); State().AddAsOwner( 2 ); State().StartHold( 2 );
// we now own the write latch
return errSuccess; } } } }
// tries to upgrade a shared latch owned by this context to the write latch.
// if the write latch is acquired immediately, errSuccess will be returned.
// if the write latch is not acquired immediately, errLatchConflict will be
// returned. note that a latch conflict will effectively occur if any other
// context currently owns or is waiting to own any type of latch
inline CSXWLatch::ERR CSXWLatch::ErrTryUpgradeSharedLatchToWriteLatch() { // we had better already have only a shared latch
OSSYNCAssert( FOwnSharedLatch() ); OSSYNCAssert( FNotOwnExclusiveLatch() ); OSSYNCAssert( FNotOwnWriteLatch() );
// try forever until we successfully change the latch state
OSSYNC_FOREVER { // set the expected before image so that the transaction will only work if
// we are the only owner of a latch and it is a share latch
const ControlWord cwBIExpected = 0x00000001;
// set the after image of the control word to a single write latch
const ControlWord cwAI = 0x00018000;
// attempt to perform the transacted state transition on the control word
const ControlWord cwBI = AtomicCompareExchange( (long*)&State().m_cw, cwBIExpected, cwAI );
// the transaction failed
if ( cwBI != cwBIExpected ) { // this is a contention for the write latch
State().SetContend( 2 );
// this is a latch conflict
return errLatchConflict; }
// the transaction succeeded
else { // we are no longer an owner of a shared latch
State().RemoveAsOwner( 0 ); State().StopHold( 0 );
// we are now an owner of the write latch
State().SetAcquire( 2 ); State().AddAsOwner( 2 ); State().StartHold( 2 );
// we now own the write latch
return errSuccess; } } }
// releases the write latch in exchange for the exclusive latch
inline void CSXWLatch::DowngradeWriteLatchToExclusiveLatch() { // we had better already have only a write latch
OSSYNCAssert( FNotOwnSharedLatch() ); OSSYNCAssert( FNotOwnExclusiveLatch() ); OSSYNCAssert( FOwnWriteLatch() );
// stop quiescing shared latches by resetting the fQS flag
const ControlWord cwDelta = 0xFFFF8000; const ControlWord cwBI = AtomicExchangeAdd( (long*)&State().m_cw, cwDelta );
// transfer ownership from the write latch to the exclusive latch
State().RemoveAsOwner( 2 ); State().StopHold( 2 );
State().SetAcquire( 1 ); State().AddAsOwner( 1 ); State().StartHold( 1 );
// release any quiesced shared latches
if ( cwBI & 0x00007FFF ) { State().m_semS.Release( cwBI & 0x00007FFF ); } }
// releases the write latch in exchange for a shared latch
inline void CSXWLatch::DowngradeWriteLatchToSharedLatch() { // we had better already have only a write latch
OSSYNCAssert( FNotOwnSharedLatch() ); OSSYNCAssert( FNotOwnExclusiveLatch() ); OSSYNCAssert( FOwnWriteLatch() );
// stop quiescing shared latches by resetting the fQS flag, release our
// exclusive latch, and acquire a shared latch
const ControlWord cwDelta = 0xFFFE8001; const ControlWord cwBI = AtomicExchangeAdd( (long*)&State().m_cw, cwDelta );
// transfer ownership from the write latch to a shared latch
State().RemoveAsOwner( 2 ); State().StopHold( 2 );
State().SetAcquire( 0 ); State().AddAsOwner( 0 ); State().StartHold( 0 );
// release any quiesced shared latches
if ( cwBI & 0x00007FFF ) { State().m_semS.Release( cwBI & 0x00007FFF ); }
// release a waiter for the exclusive latch, if any
if ( cwBI >= 0x00020000 ) { State().m_semX.Release(); } }
// releases the exclusive latch in exchange for a shared latch
inline void CSXWLatch::DowngradeExclusiveLatchToSharedLatch() { // we had better already have only an exclusive latch
OSSYNCAssert( FNotOwnSharedLatch() ); OSSYNCAssert( FOwnExclusiveLatch() ); OSSYNCAssert( FNotOwnWriteLatch() );
// release our exclusive latch and acquire a shared latch
const ControlWord cwDelta = 0xFFFF0001; const ControlWord cwBI = AtomicExchangeAdd( (long*)&State().m_cw, cwDelta );
// transfer ownership from the exclusive latch to a shared latch
State().RemoveAsOwner( 1 ); State().StopHold( 1 );
State().SetAcquire( 0 ); State().AddAsOwner( 0 ); State().StartHold( 0 );
// release a waiter for the exclusive latch, if any
if ( cwBI >= 0x00020000 ) { State().m_semX.Release(); } }
// releases the write latch
inline void CSXWLatch::ReleaseWriteLatch() { // we had better already have only a write latch
OSSYNCAssert( FNotOwnSharedLatch() ); OSSYNCAssert( FNotOwnExclusiveLatch() ); OSSYNCAssert( FOwnWriteLatch() );
// stop quiescing shared latches by resetting the fQS flag and release our
// exclusive latch
const ControlWord cwDelta = 0xFFFE8000; const ControlWord cwBI = AtomicExchangeAdd( (long*)&State().m_cw, cwDelta );
// release ownership of the write latch
State().RemoveAsOwner( 2 ); State().StopHold( 2 );
// release any quiesced shared latches
if ( cwBI & 0x00007FFF ) { State().m_semS.Release( cwBI & 0x00007FFF ); }
// release a waiter for the exclusive latch, if any
if ( cwBI >= 0x00020000 ) { State().m_semX.Release(); } }
// releases the exclusive latch
inline void CSXWLatch::ReleaseExclusiveLatch() { // we had better already have only an exclusive latch
OSSYNCAssert( FNotOwnSharedLatch() ); OSSYNCAssert( FOwnExclusiveLatch() ); OSSYNCAssert( FNotOwnWriteLatch() );
// release our exclusive latch
const ControlWord cwDelta = 0xFFFF0000; const ControlWord cwBI = AtomicExchangeAdd( (long*)&State().m_cw, cwDelta );
// release ownership of the exclusive latch
State().RemoveAsOwner( 1 ); State().StopHold( 1 );
// release a waiter for the exclusive latch, if any
if ( cwBI >= 0x00020000 ) { State().m_semX.Release(); } }
// releases a shared latch
inline void CSXWLatch::ReleaseSharedLatch() { // we had better already have only a shared latch
OSSYNCAssert( FOwnSharedLatch() ); OSSYNCAssert( FNotOwnExclusiveLatch() ); OSSYNCAssert( FNotOwnWriteLatch() );
// we are no longer an owner of a shared latch
State().RemoveAsOwner( 0 ); State().StopHold( 0 );
// try forever until we successfully change the latch state
OSSYNC_FOREVER { // read the current state of the control word as our expected before image
ControlWord cwBIExpected = State().m_cw;
// change the expected before image so that the transaction will only work if
// shared latches are not quiesced
cwBIExpected = cwBIExpected & 0xFFFF7FFF;
// compute the after image of the control word by performing the transform that
// will release our shared latch
const ControlWord cwAI = cwBIExpected + 0xFFFFFFFF;
// attempt to perform the transacted state transition on the control word
const ControlWord cwBI = AtomicCompareExchange( (long*)&State().m_cw, cwBIExpected, cwAI );
// the transaction failed
if ( cwBI != cwBIExpected ) { // the transaction failed because shared latches were quiesced
if ( cwBI & 0x00008000 ) { // leave the latch as a quiesced shared latch
_UpdateQuiescedSharedLatchCount( 0xFFFFFFFF );
// we're done
break; }
// the transaction failed because another context changed the control word
else { // try again
continue; } }
// the transaction succeeded
else { // we're done
break; } } }
// waits for ownership of a shared latch in response to receiving an
// errWaitForSharedLatch from the API. this function must not be called at any
// other time
inline void CSXWLatch::WaitForSharedLatch() { // check for deadlock
OSSYNCAssertSzRTL( State().FCanBeWaiter(), "Potential Deadlock Detected (Rank Violation)");
// we had better already be declared a waiter
OSSYNCAssert( State().FWaiter( 0 ) );
// wait for ownership of a shared latch on the shared latch semaphore
State().m_semS.Acquire();
State().StopWait( 0 ); State().RemoveAsWaiter( 0 );
State().SetAcquire( 0 ); State().AddAsOwner( 0 ); State().StartHold( 0 ); }
// waits for ownership of the exclusive latch in response to receiving an
// errWaitForExclusiveLatch from the API. this function must not be called at any
// other time
inline void CSXWLatch::WaitForExclusiveLatch() { // check for deadlock
OSSYNCAssertSzRTL( State().FCanBeWaiter(), "Potential Deadlock Detected (Rank Violation)");
// we had better already be declared a waiter
OSSYNCAssert( State().FWaiter( 1 ) );
// wait for ownership of the exclusive latch on the exclusive latch semaphore
State().m_semX.Acquire();
State().StopWait( 1 ); State().RemoveAsWaiter( 1 );
State().SetAcquire( 1 ); State().AddAsOwner( 1 ); State().StartHold( 1 ); }
// waits for ownership of the write latch in response to receiving an
// errWaitForWriteLatch from the API. this function must not be called at any
// other time
inline void CSXWLatch::WaitForWriteLatch() { // check for deadlock
OSSYNCAssertSzRTL( State().FCanBeWaiter(), "Potential Deadlock Detected (Rank Violation)");
// we had better already be declared a waiter
OSSYNCAssert( State().FWaiter( 2 ) );
// wait for ownership of the write latch on the write latch semaphore
State().m_semW.Acquire();
State().StopWait( 2 ); State().RemoveAsWaiter( 2 );
State().SetAcquire( 2 ); State().AddAsOwner( 2 ); State().StartHold( 2 ); }
// claims ownership of the latch for the specified group for deadlock detection
inline void CSXWLatch::ClaimOwnership( const DWORD group ) { State().AddAsOwner( group ); }
// releases ownership of the latch for the specified group for deadlock detection
inline void CSXWLatch::ReleaseOwnership( const DWORD group ) { State().RemoveAsOwner( group ); }
// updates the quiesced shared latch count, possibly releasing a waiter for
// the write latch
inline void CSXWLatch::_UpdateQuiescedSharedLatchCount( const DWORD cQSDelta ) { // update the quiesced shared latch count using the provided delta
const DWORD cQSBI = AtomicExchangeAdd( (long*)&State().m_cQS, cQSDelta ); const DWORD cQSAI = cQSBI + cQSDelta;
// our update resulted in a zero quiesced shared latch count
if ( !cQSAI ) { // release the waiter for the write latch
State().m_semW.Release(); } }
// init sync subsystem
const BOOL OSSYNCAPI FOSSyncPreinit(); #define FOSSyncInit FOSSyncPreinit
// terminate sync subsystem
void OSSYNCAPI OSSyncPostterm(); #define OSSyncTerm OSSyncPostterm
// attach the current context to the sync subsystem
BOOL OSSYNCAPI FOSSyncAttach();
// detach the current context from the sync subsystem
void OSSYNCAPI OSSyncDetach();
// special init/term API's for Enhanced State only
const BOOL OSSYNCAPI FOSSyncInitForES(); void OSSYNCAPI OSSyncTermForES();
}; // namespace OSSYNC
using namespace OSSYNC;
#endif // _SYNC_HXX_INCLUDED
|