#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 #include #include #include // 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 += - // // 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 += - // // 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