#include "windows.h" #include "sync.hxx" // system max spin count int cSpinMax = 0; // Page Memory Allocation void* PvPageAlloc( const size_t cbSize, void* const pv ); void PageFree( void* const pv ); #ifdef SYNC_ANALYZE_PERFORMANCE // Performance Data Dump CPRINTFSYNC* pcprintfPerfData; #endif // SYNC_ANALYZE_PERFORMANCE // Kernel Semaphore Pool // ctor CKernelSemaphorePool::CKernelSemaphorePool() { } // dtor CKernelSemaphorePool::~CKernelSemaphorePool() { } // init const BOOL CKernelSemaphorePool::FInit() { // semaphore pool should be terminated Assert( !FInitialized() ); // reset members m_cksem = 0; m_mpirksemrksem = 0; m_irksemTop = irksemNil; m_irksemNext = irksemUnknown; // allocate kernel semaphore array if ( !( m_mpirksemrksem = (CReferencedKernelSemaphore*)PvPageAlloc( sizeof( CReferencedKernelSemaphore ) * 65536, NULL ) ) ) { Term(); return fFalse; } Assert( m_cksem == 0 ); Assert( m_irksemTop == irksemNil ); Assert( m_irksemNext == irksemUnknown ); // init successful return fTrue; } // term void CKernelSemaphorePool::Term() { // the kernel semaphore array is allocated if ( m_mpirksemrksem ) { // terminate all initialized kernel semaphores for ( m_cksem-- ; m_cksem >= 0; m_cksem-- ) { m_mpirksemrksem[m_cksem].Term(); m_mpirksemrksem[m_cksem].~CReferencedKernelSemaphore(); } // delete the kernel semaphore array PageFree( m_mpirksemrksem ); } // reset data members m_cksem = 0; m_mpirksemrksem = 0; m_irksemTop = irksemNil; m_irksemNext = irksemUnknown; } // Referenced Kernel Semaphore // ctor CKernelSemaphorePool::CReferencedKernelSemaphore::CReferencedKernelSemaphore() : CKernelSemaphore( CSyncBasicInfo( _T( "CKernelSemaphorePool::CReferencedKernelSemaphore" ) ) ) { // reset data members m_cReference = 0; m_fInUse = 0; m_irksemNext = irksemNil; #ifdef DEBUG m_psyncobjUser = 0; #endif // DEBUG } // dtor CKernelSemaphorePool::CReferencedKernelSemaphore::~CReferencedKernelSemaphore() { } // init const BOOL CKernelSemaphorePool::CReferencedKernelSemaphore::FInit() { // reset data members m_cReference = 0; m_fInUse = 0; m_irksemNext = irksemNil; #ifdef DEBUG m_psyncobjUser = 0; #endif // DEBUG // initialize the kernel semaphore return CKernelSemaphore::FInit(); } // term void CKernelSemaphorePool::CReferencedKernelSemaphore::Term() { // terminate the kernel semaphore CKernelSemaphore::Term(); // reset data members m_cReference = 0; m_fInUse = 0; m_irksemNext = irksemNil; #ifdef DEBUG m_psyncobjUser = 0; #endif // DEBUG } // Global Kernel Semaphore Pool CKernelSemaphorePool ksempoolGlobal; // Synchronization Object Performance: Acquisition // ctor CSyncPerfAcquire::CSyncPerfAcquire() { #ifdef SYNC_ANALYZE_PERFORMANCE m_cAcquire = 0; m_cContend = 0; #endif // SYNC_ANALYZE_PERFORMANCE } // dtor CSyncPerfAcquire::~CSyncPerfAcquire() { } // Semaphore // ctor CSemaphore::CSemaphore( const CSyncBasicInfo& sbi ) : CEnhancedStateContainer< CSemaphoreState, CSyncStateInitNull, CSyncComplexPerfInfo, CSyncBasicInfo >( syncstateNull, sbi ) { #ifdef SYNC_ENHANCED_STATE // further init of CSyncBasicInfo State().SetTypeName( "CSemaphore" ); State().SetInstance( (CSyncObject*)this ); #endif // SYNC_ENHANCED_STATE } // dtor CSemaphore::~CSemaphore() { #ifdef SYNC_ANALYZE_PERFORMANCE // dump performance data on destruction if enabled Dump( pcprintfPerfData ); #endif // SYNC_ANALYZE_PERFORMANCE } // attempts to acquire a count from the semaphore, returning fFalse if unsuccessful // in the time permitted. Infinite and Test-Only timeouts are supported. const BOOL CSemaphore::_FAcquire( const int cmsecTimeout ) { // if we spin, we will spin for the full amount recommended by the OS int cSpin = cSpinMax; // we start with no kernel semaphore allocated CKernelSemaphorePool::IRKSEM irksemAlloc = CKernelSemaphorePool::irksemNil; // try forever until we successfully change the state of the semaphore SYNC_FOREVER { // read the current state of the semaphore const CSemaphoreState stateCur = (CSemaphoreState&) State(); // there is an available count if ( stateCur.FAvail() ) { // we successfully took a count if ( State().FChange( stateCur, CSemaphoreState( stateCur.CAvail() - 1 ) ) ) { // if we allocated a kernel semaphore, release it if ( irksemAlloc != CKernelSemaphorePool::irksemNil ) { ksempoolGlobal.Unreference( irksemAlloc ); } // return success State().SetAcquire(); return fTrue; } } // there is no available count and we still have spins left else if ( cSpin ) { // spin once and try again cSpin--; continue; } // there are no waiters and no available counts else if ( stateCur.FNoWaitAndNoAvail() ) { // allocate and reference a kernel semaphore if we haven't already if ( irksemAlloc == CKernelSemaphorePool::irksemNil ) { irksemAlloc = ksempoolGlobal.Allocate( this ); } // we successfully installed ourselves as the first waiter if ( State().FChange( stateCur, CSemaphoreState( 1, irksemAlloc ) ) ) { // wait for next available count on semaphore State().StartWait(); const BOOL fCompleted = ksempoolGlobal.Ksem( irksemAlloc, this ).FAcquire( cmsecTimeout ); State().StopWait(); // our wait completed if ( fCompleted ) { // unreference the kernel semaphore ksempoolGlobal.Unreference( irksemAlloc ); // we successfully acquired a count State().SetAcquire(); return fTrue; } // our wait timed out else { // try forever until we successfully change the state of the semaphore SYNC_FOREVER { // read the current state of the semaphore const CSemaphoreState stateAfterWait = (CSemaphoreState&) State(); // there are no waiters or the kernel semaphore currently // in the semaphore is not the same as the one we allocated if ( stateAfterWait.FNoWait() || stateAfterWait.Irksem() != irksemAlloc ) { // the kernel semaphore we allocated is no longer in // use, so another context released it. this means that // there is a count on the kernel semaphore that we must // absorb, so we will // NOTE: we could end up blocking because the releasing // context may not have released the semaphore yet ksempoolGlobal.Ksem( irksemAlloc, this ).Acquire(); // unreference the kernel semaphore ksempoolGlobal.Unreference( irksemAlloc ); // we successfully acquired a count return fTrue; } // there is one waiter and the kernel semaphore currently // in the semaphore is the same as the one we allocated else if ( stateAfterWait.CWait() == 1 ) { Assert( stateAfterWait.FWait() ); Assert( stateAfterWait.Irksem() == irksemAlloc ); // we successfully changed the semaphore to have no // available counts and no waiters if ( State().FChange( stateAfterWait, CSemaphoreState( 0 ) ) ) { // unreference the kernel semaphore ksempoolGlobal.Unreference( irksemAlloc ); // we did not successfully acquire a count return fFalse; } } // there are many waiters and the kernel semaphore currently // in the semaphore is the same as the one we allocated else { Assert( stateAfterWait.CWait() > 1 ); Assert( stateAfterWait.FWait() ); Assert( stateAfterWait.Irksem() == irksemAlloc ); // we successfully reduced the number of waiters on the // semaphore by one if ( State().FChange( stateAfterWait, CSemaphoreState( stateAfterWait.CWait() - 1, stateAfterWait.Irksem() ) ) ) { // unreference the kernel semaphore ksempoolGlobal.Unreference( irksemAlloc ); // we did not successfully acquire a count return fFalse; } } } } } } // there are waiters else { Assert( stateCur.FWait() ); // reference the kernel semaphore already in use ksempoolGlobal.Reference( stateCur.Irksem() ); // we successfully added ourself as another waiter if ( State().FChange( stateCur, CSemaphoreState( stateCur.CWait() + 1, stateCur.Irksem() ) ) ) { // if we allocated a kernel semaphore, unreference it if ( irksemAlloc != CKernelSemaphorePool::irksemNil ) { ksempoolGlobal.Unreference( irksemAlloc ); } // wait for next available count on semaphore State().StartWait(); const BOOL fCompleted = ksempoolGlobal.Ksem( stateCur.Irksem(), this ).FAcquire( cmsecTimeout ); State().StopWait(); // our wait completed if ( fCompleted ) { // unreference the kernel semaphore ksempoolGlobal.Unreference( stateCur.Irksem() ); // we successfully acquired a count State().SetAcquire(); return fTrue; } // our wait timed out else { // try forever until we successfully change the state of the semaphore SYNC_FOREVER { // read the current state of the semaphore const CSemaphoreState stateAfterWait = (CSemaphoreState&) State(); // there are no waiters or the kernel semaphore currently // in the semaphore is not the same as the one we waited on if ( stateAfterWait.FNoWait() || stateAfterWait.Irksem() != stateCur.Irksem() ) { // the kernel semaphore we waited on is no longer in // use, so another context released it. this means that // there is a count on the kernel semaphore that we must // absorb, so we will // NOTE: we could end up blocking because the releasing // context may not have released the semaphore yet ksempoolGlobal.Ksem( stateCur.Irksem(), this ).Acquire(); // unreference the kernel semaphore ksempoolGlobal.Unreference( stateCur.Irksem() ); // we successfully acquired a count return fTrue; } // there is one waiter and the kernel semaphore currently // in the semaphore is the same as the one we waited on else if ( stateAfterWait.CWait() == 1 ) { Assert( stateAfterWait.FWait() ); Assert( stateAfterWait.Irksem() == stateCur.Irksem() ); // we successfully changed the semaphore to have no // available counts and no waiters if ( State().FChange( stateAfterWait, CSemaphoreState( 0 ) ) ) { // unreference the kernel semaphore ksempoolGlobal.Unreference( stateCur.Irksem() ); // we did not successfully acquire a count return fFalse; } } // there are many waiters and the kernel semaphore currently // in the semaphore is the same as the one we waited on else { Assert( stateAfterWait.CWait() > 1 ); Assert( stateAfterWait.FWait() ); Assert( stateAfterWait.Irksem() == stateCur.Irksem() ); // we successfully reduced the number of waiters on the // semaphore by one if ( State().FChange( stateAfterWait, CSemaphoreState( stateAfterWait.CWait() - 1, stateAfterWait.Irksem() ) ) ) { // unreference the kernel semaphore ksempoolGlobal.Unreference( stateCur.Irksem() ); // we did not successfully acquire a count return fFalse; } } } } } // unreference the kernel semaphore ksempoolGlobal.Unreference( stateCur.Irksem() ); } } } // releases the given number of counts to the semaphore, waking the appropriate // number of waiters void CSemaphore::_Release( const int cToRelease ) { // try forever until we successfully change the state of the semaphore SYNC_FOREVER { // read the current state of the semaphore const CSemaphoreState stateCur = State(); // there are no waiters if ( stateCur.FNoWait() ) { // we successfully added the count to the semaphore if ( State().FChange( stateCur, CSemaphoreState( stateCur.CAvail() + cToRelease ) ) ) { // we're done return; } } // there are waiters else { Assert( stateCur.FWait() ); // we are releasing more counts than waiters (or equal to) if ( stateCur.CWait() <= cToRelease ) { // we successfully changed the semaphore to have an available count // that is equal to the specified release count minus the number of // waiters to release if ( State().FChange( stateCur, CSemaphoreState( cToRelease - stateCur.CWait() ) ) ) { // release all waiters ksempoolGlobal.Ksem( stateCur.Irksem(), this ).Release( stateCur.CWait() ); // we're done return; } } // we are releasing less counts than waiters else { Assert( stateCur.CWait() > cToRelease ); // we successfully reduced the number of waiters on the semaphore by // the number specified if ( State().FChange( stateCur, CSemaphoreState( stateCur.CWait() - cToRelease, stateCur.Irksem() ) ) ) { // release the specified number of waiters ksempoolGlobal.Ksem( stateCur.Irksem(), this ).Release( cToRelease ); // we're done return; } } } } } // Auto-Reset Signal // ctor CAutoResetSignal::CAutoResetSignal( const CSyncBasicInfo& sbi ) : CEnhancedStateContainer< CAutoResetSignalState, CSyncStateInitNull, CSyncComplexPerfInfo, CSyncBasicInfo >( syncstateNull, sbi ) { #ifdef SYNC_ENHANCED_STATE // further init of CSyncBasicInfo State().SetTypeName( "CAutoResetSignal" ); State().SetInstance( (CSyncObject*)this ); #endif // SYNC_ENHANCED_STATE } // dtor CAutoResetSignal::~CAutoResetSignal() { #ifdef SYNC_ANALYZE_PERFORMANCE // dump performance data on destruction if enabled Dump( pcprintfPerfData ); #endif // SYNC_ANALYZE_PERFORMANCE } // waits for the signal to be set, returning fFalse if unsuccessful in the time // permitted. Infinite and Test-Only timeouts are supported. const BOOL CAutoResetSignal::_FWait( const int cmsecTimeout ) { // if we spin, we will spin for the full amount recommended by the OS int cSpin = cSpinMax; // we start with no kernel semaphore allocated CKernelSemaphorePool::IRKSEM irksemAlloc = CKernelSemaphorePool::irksemNil; // try forever until we successfully change the state of the signal SYNC_FOREVER { // read the current state of the signal const CAutoResetSignalState stateCur = (CAutoResetSignalState&) State(); // the signal is set if ( stateCur.FNoWaitAndSet() ) { // we successfully changed the signal state to reset with no waiters if ( State().FChange( stateCur, CAutoResetSignalState( 0 ) ) ) { // if we allocated a kernel semaphore, release it if ( irksemAlloc != CKernelSemaphorePool::irksemNil ) { ksempoolGlobal.Unreference( irksemAlloc ); } // return success State().SetAcquire(); return fTrue; } } // the signal is not set and we still have spins left else if ( cSpin ) { // spin once and try again cSpin--; continue; } // the signal is not set and there are no waiters else if ( stateCur.FNoWaitAndNotSet() ) { // allocate and reference a kernel semaphore if we haven't already if ( irksemAlloc == CKernelSemaphorePool::irksemNil ) { irksemAlloc = ksempoolGlobal.Allocate( this ); } // we successfully installed ourselves as the first waiter if ( State().FChange( stateCur, CAutoResetSignalState( 1, irksemAlloc ) ) ) { // wait for signal to be set State().StartWait(); const BOOL fCompleted = ksempoolGlobal.Ksem( irksemAlloc, this ).FAcquire( cmsecTimeout ); State().StopWait(); // our wait completed if ( fCompleted ) { // unreference the kernel semaphore ksempoolGlobal.Unreference( irksemAlloc ); // we successfully waited for the signal State().SetAcquire(); return fTrue; } // our wait timed out else { // try forever until we successfully change the state of the signal SYNC_FOREVER { // read the current state of the signal const CAutoResetSignalState stateAfterWait = (CAutoResetSignalState&) State(); // there are no waiters or the kernel semaphore currently // in the signal is not the same as the one we allocated if ( stateAfterWait.FNoWait() || stateAfterWait.Irksem() != irksemAlloc ) { // the kernel semaphore we allocated is no longer in // use, so another context released it. this means that // there is a count on the kernel semaphore that we must // absorb, so we will // NOTE: we could end up blocking because the releasing // context may not have released the semaphore yet ksempoolGlobal.Ksem( irksemAlloc, this ).Acquire(); // unreference the kernel semaphore ksempoolGlobal.Unreference( irksemAlloc ); // we successfully waited for the signal return fTrue; } // there is one waiter and the kernel semaphore currently // in the signal is the same as the one we allocated else if ( stateAfterWait.CWait() == 1 ) { Assert( stateAfterWait.FWait() ); Assert( stateAfterWait.Irksem() == irksemAlloc ); // we successfully changed the signal to the reset with // no waiters state if ( State().FChange( stateAfterWait, CAutoResetSignalState( 0 ) ) ) { // unreference the kernel semaphore ksempoolGlobal.Unreference( irksemAlloc ); // we did not successfully wait for the signal return fFalse; } } // there are many waiters and the kernel semaphore currently // in the signal is the same as the one we allocated else { Assert( stateAfterWait.CWait() > 1 ); Assert( stateAfterWait.FWait() ); Assert( stateAfterWait.Irksem() == irksemAlloc ); // we successfully reduced the number of waiters on the // signal by one if ( State().FChange( stateAfterWait, CAutoResetSignalState( stateAfterWait.CWait() - 1, stateAfterWait.Irksem() ) ) ) { // unreference the kernel semaphore ksempoolGlobal.Unreference( irksemAlloc ); // we did not successfully wait for the signal return fFalse; } } } } } } // there are waiters else { Assert( stateCur.FWait() ); // reference the kernel semaphore already in use ksempoolGlobal.Reference( stateCur.Irksem() ); // we successfully added ourself as another waiter if ( State().FChange( stateCur, CAutoResetSignalState( stateCur.CWait() + 1, stateCur.Irksem() ) ) ) { // if we allocated a kernel semaphore, unreference it if ( irksemAlloc != CKernelSemaphorePool::irksemNil ) { ksempoolGlobal.Unreference( irksemAlloc ); } // wait for signal to be set State().StartWait(); const BOOL fCompleted = ksempoolGlobal.Ksem( stateCur.Irksem(), this ).FAcquire( cmsecTimeout ); State().StopWait(); // our wait completed if ( fCompleted ) { // unreference the kernel semaphore ksempoolGlobal.Unreference( stateCur.Irksem() ); // we successfully waited for the signal State().SetAcquire(); return fTrue; } // our wait timed out else { // try forever until we successfully change the state of the signal SYNC_FOREVER { // read the current state of the signal const CAutoResetSignalState stateAfterWait = (CAutoResetSignalState&) State(); // there are no waiters or the kernel semaphore currently // in the signal is not the same as the one we waited on if ( stateAfterWait.FNoWait() || stateAfterWait.Irksem() != stateCur.Irksem() ) { // the kernel semaphore we waited on is no longer in // use, so another context released it. this means that // there is a count on the kernel semaphore that we must // absorb, so we will // NOTE: we could end up blocking because the releasing // context may not have released the semaphore yet ksempoolGlobal.Ksem( stateCur.Irksem(), this ).Acquire(); // unreference the kernel semaphore ksempoolGlobal.Unreference( stateCur.Irksem() ); // we successfully waited for the signal return fTrue; } // there is one waiter and the kernel semaphore currently // in the signal is the same as the one we waited on else if ( stateAfterWait.CWait() == 1 ) { Assert( stateAfterWait.FWait() ); Assert( stateAfterWait.Irksem() == stateCur.Irksem() ); // we successfully changed the signal to the reset with // no waiters state if ( State().FChange( stateAfterWait, CAutoResetSignalState( 0 ) ) ) { // unreference the kernel semaphore ksempoolGlobal.Unreference( stateCur.Irksem() ); // we did not successfully wait for the signal return fFalse; } } // there are many waiters and the kernel semaphore currently // in the signal is the same as the one we waited on else { Assert( stateAfterWait.CWait() > 1 ); Assert( stateAfterWait.FWait() ); Assert( stateAfterWait.Irksem() == stateCur.Irksem() ); // we successfully reduced the number of waiters on the // signal by one if ( State().FChange( stateAfterWait, CAutoResetSignalState( stateAfterWait.CWait() - 1, stateAfterWait.Irksem() ) ) ) { // unreference the kernel semaphore ksempoolGlobal.Unreference( stateCur.Irksem() ); // we did not successfully wait for the signal return fFalse; } } } } } // unreference the kernel semaphore ksempoolGlobal.Unreference( stateCur.Irksem() ); } } } // 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 void CAutoResetSignal::_Set() { // try forever until we successfully change the state of the signal SYNC_FOREVER { // read the current state of the signal const CAutoResetSignalState stateCur = (CAutoResetSignalState&) State(); // there are no waiters if ( stateCur.FNoWait() ) { // we successfully changed the signal state from reset with no // waiters to set or from set to set (a nop) if ( State().FSimpleSet() ) { // we're done return; } } // there are waiters else { Assert( stateCur.FWait() ); // there is only one waiter if ( stateCur.CWait() == 1 ) { // we successfully changed the signal to the reset with no waiters state if ( State().FChange( stateCur, CAutoResetSignalState( 0 ) ) ) { // release the lone waiter ksempoolGlobal.Ksem( stateCur.Irksem(), this ).Release(); // we're done return; } } // there is more than one waiter else { Assert( stateCur.CWait() > 1 ); // we successfully reduced the number of waiters on the signal by one if ( State().FChange( stateCur, CAutoResetSignalState( stateCur.CWait() - 1, stateCur.Irksem() ) ) ) { // release one waiter ksempoolGlobal.Ksem( stateCur.Irksem(), this ).Release(); // we're done return; } } } } } // resets the signal, releasing up to one waiter void CAutoResetSignal::_Pulse() { // try forever until we successfully change the state of the signal SYNC_FOREVER { // read the current state of the signal const CAutoResetSignalState stateCur = (CAutoResetSignalState&) State(); // there are no waiters if ( stateCur.FNoWait() ) { // we successfully changed 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() ) { // we're done return; } } // there are waiters else { Assert( stateCur.FWait() ); // there is only one waiter if ( stateCur.CWait() == 1 ) { // we successfully changed the signal to the reset with no waiters state if ( State().FChange( stateCur, CAutoResetSignalState( 0 ) ) ) { // release the lone waiter ksempoolGlobal.Ksem( stateCur.Irksem(), this ).Release(); // we're done return; } } // there is more than one waiter else { Assert( stateCur.CWait() > 1 ); // we successfully reduced the number of waiters on the signal by one if ( State().FChange( stateCur, CAutoResetSignalState( stateCur.CWait() - 1, stateCur.Irksem() ) ) ) { // release one waiter ksempoolGlobal.Ksem( stateCur.Irksem(), this ).Release(); // we're done return; } } } } } // Manual-Reset Signal // ctor CManualResetSignal::CManualResetSignal( const CSyncBasicInfo& sbi ) : CEnhancedStateContainer< CManualResetSignalState, CSyncStateInitNull, CSyncComplexPerfInfo, CSyncBasicInfo >( syncstateNull, sbi ) { #ifdef SYNC_ENHANCED_STATE // further init of CSyncBasicInfo State().SetTypeName( "CManualResetSignal" ); State().SetInstance( (CSyncObject*)this ); #endif // SYNC_ENHANCED_STATE } // dtor CManualResetSignal::~CManualResetSignal() { #ifdef SYNC_ANALYZE_PERFORMANCE // dump performance data on destruction if enabled Dump( pcprintfPerfData ); #endif // SYNC_ANALYZE_PERFORMANCE } // waits for the signal to be set, returning fFalse if unsuccessful in the time // permitted. Infinite and Test-Only timeouts are supported. const BOOL CManualResetSignal::_FWait( const int cmsecTimeout ) { // if we spin, we will spin for the full amount recommended by the OS int cSpin = cSpinMax; // we start with no kernel semaphore allocated CKernelSemaphorePool::IRKSEM irksemAlloc = CKernelSemaphorePool::irksemNil; // try forever until we successfully change the state of the signal SYNC_FOREVER { // read the current state of the signal const CManualResetSignalState stateCur = (CManualResetSignalState&) State(); // the signal is set if ( stateCur.FNoWaitAndSet() ) { // if we allocated a kernel semaphore, release it if ( irksemAlloc != CKernelSemaphorePool::irksemNil ) { ksempoolGlobal.Unreference( irksemAlloc ); } // we successfully waited for the signal State().SetAcquire(); return fTrue; } // the signal is not set and we still have spins left else if ( cSpin ) { // spin once and try again cSpin--; continue; } // the signal is not set and there are no waiters else if ( stateCur.FNoWaitAndNotSet() ) { // allocate and reference a kernel semaphore if we haven't already if ( irksemAlloc == CKernelSemaphorePool::irksemNil ) { irksemAlloc = ksempoolGlobal.Allocate( this ); } // we successfully installed ourselves as the first waiter if ( State().FChange( stateCur, CManualResetSignalState( 1, irksemAlloc ) ) ) { // wait for signal to be set State().StartWait(); const BOOL fCompleted = ksempoolGlobal.Ksem( irksemAlloc, this ).FAcquire( cmsecTimeout ); State().StopWait(); // our wait completed if ( fCompleted ) { // unreference the kernel semaphore ksempoolGlobal.Unreference( irksemAlloc ); // we successfully waited for the signal State().SetAcquire(); return fTrue; } // our wait timed out else { // try forever until we successfully change the state of the signal SYNC_FOREVER { // read the current state of the signal const CManualResetSignalState stateAfterWait = (CManualResetSignalState&) State(); // there are no waiters or the kernel semaphore currently // in the signal is not the same as the one we allocated if ( stateAfterWait.FNoWait() || stateAfterWait.Irksem() != irksemAlloc ) { // the kernel semaphore we allocated is no longer in // use, so another context released it. this means that // there is a count on the kernel semaphore that we must // absorb, so we will // NOTE: we could end up blocking because the releasing // context may not have released the semaphore yet ksempoolGlobal.Ksem( irksemAlloc, this ).Acquire(); // unreference the kernel semaphore ksempoolGlobal.Unreference( irksemAlloc ); // we successfully waited for the signal return fTrue; } // there is one waiter and the kernel semaphore currently // in the signal is the same as the one we allocated else if ( stateAfterWait.CWait() == 1 ) { Assert( stateAfterWait.FWait() ); Assert( stateAfterWait.Irksem() == irksemAlloc ); // we successfully changed the signal to the reset with // no waiters state if ( State().FChange( stateAfterWait, CManualResetSignalState( 0 ) ) ) { // unreference the kernel semaphore ksempoolGlobal.Unreference( irksemAlloc ); // we did not successfully wait for the signal return fFalse; } } // there are many waiters and the kernel semaphore currently // in the signal is the same as the one we allocated else { Assert( stateAfterWait.CWait() > 1 ); Assert( stateAfterWait.FWait() ); Assert( stateAfterWait.Irksem() == irksemAlloc ); // we successfully reduced the number of waiters on the // signal by one if ( State().FChange( stateAfterWait, CManualResetSignalState( stateAfterWait.CWait() - 1, stateAfterWait.Irksem() ) ) ) { // unreference the kernel semaphore ksempoolGlobal.Unreference( irksemAlloc ); // we did not successfully wait for the signal return fFalse; } } } } } } // there are waiters else { Assert( stateCur.FWait() ); // reference the kernel semaphore already in use ksempoolGlobal.Reference( stateCur.Irksem() ); // we successfully added ourself as another waiter if ( State().FChange( stateCur, CManualResetSignalState( stateCur.CWait() + 1, stateCur.Irksem() ) ) ) { // if we allocated a kernel semaphore, unreference it if ( irksemAlloc != CKernelSemaphorePool::irksemNil ) { ksempoolGlobal.Unreference( irksemAlloc ); } // wait for signal to be set State().StartWait(); const BOOL fCompleted = ksempoolGlobal.Ksem( stateCur.Irksem(), this ).FAcquire( cmsecTimeout ); State().StopWait(); // our wait completed if ( fCompleted ) { // unreference the kernel semaphore ksempoolGlobal.Unreference( stateCur.Irksem() ); // we successfully waited for the signal State().SetAcquire(); return fTrue; } // our wait timed out else { // try forever until we successfully change the state of the signal SYNC_FOREVER { // read the current state of the signal const CManualResetSignalState stateAfterWait = (CManualResetSignalState&) State(); // there are no waiters or the kernel semaphore currently // in the signal is not the same as the one we waited on if ( stateAfterWait.FNoWait() || stateAfterWait.Irksem() != stateCur.Irksem() ) { // the kernel semaphore we waited on is no longer in // use, so another context released it. this means that // there is a count on the kernel semaphore that we must // absorb, so we will // NOTE: we could end up blocking because the releasing // context may not have released the semaphore yet ksempoolGlobal.Ksem( stateCur.Irksem(), this ).Acquire(); // unreference the kernel semaphore ksempoolGlobal.Unreference( stateCur.Irksem() ); // we successfully waited for the signal return fTrue; } // there is one waiter and the kernel semaphore currently // in the signal is the same as the one we waited on else if ( stateAfterWait.CWait() == 1 ) { Assert( stateAfterWait.FWait() ); Assert( stateAfterWait.Irksem() == stateCur.Irksem() ); // we successfully changed the signal to the reset with // no waiters state if ( State().FChange( stateAfterWait, CManualResetSignalState( 0 ) ) ) { // unreference the kernel semaphore ksempoolGlobal.Unreference( stateCur.Irksem() ); // we did not successfully wait for the signal return fFalse; } } // there are many waiters and the kernel semaphore currently // in the signal is the same as the one we waited on else { Assert( stateAfterWait.CWait() > 1 ); Assert( stateAfterWait.FWait() ); Assert( stateAfterWait.Irksem() == stateCur.Irksem() ); // we successfully reduced the number of waiters on the // signal by one if ( State().FChange( stateAfterWait, CManualResetSignalState( stateAfterWait.CWait() - 1, stateAfterWait.Irksem() ) ) ) { // unreference the kernel semaphore ksempoolGlobal.Unreference( stateCur.Irksem() ); // we did not successfully wait for the signal return fFalse; } } } } } // unreference the kernel semaphore ksempoolGlobal.Unreference( stateCur.Irksem() ); } } } // Lock Object Basic Information // ctor CLockBasicInfo::CLockBasicInfo( const CSyncBasicInfo& sbi, const int rank, const int subrank ) : CSyncBasicInfo( sbi ) { #ifdef SYNC_DEADLOCK_DETECTION m_rank = rank; m_subrank = subrank; #endif // SYNC_DEADLOCK_DETECTION } // dtor CLockBasicInfo::~CLockBasicInfo() { } // Lock Object Performance: Hold // ctor CLockPerfHold::CLockPerfHold() { #ifdef SYNC_ANALYZE_PERFORMANCE m_cHold = 0; m_qwHRTHoldElapsed = 0; #endif // SYNC_ANALYZE_PERFORMANCE } // dtor CLockPerfHold::~CLockPerfHold() { } // Lock Owner Record // ctor COwner::COwner() { #ifdef SYNC_DEADLOCK_DETECTION m_pclsOwner = NULL; m_pownerContextNext = NULL; m_plddiOwned = NULL; m_pownerLockNext = NULL; m_group = 0; #endif // SYNC_DEADLOCK_DETECTION } // dtor COwner::~COwner() { } // Lock Object Deadlock Detection Information // ctor CLockDeadlockDetectionInfo::CLockDeadlockDetectionInfo( const CLockBasicInfo& lbi ) #ifdef SYNC_DEADLOCK_DETECTION : m_semOwnerList( (CSyncBasicInfo&) lbi ) #endif // SYNC_DEADLOCK_DETECTION { #ifdef SYNC_DEADLOCK_DETECTION m_plbiParent = &lbi; m_semOwnerList.Release(); #endif // SYNC_DEADLOCK_DETECTION } // dtor CLockDeadlockDetectionInfo::~CLockDeadlockDetectionInfo() { } // Critical Section (non-nestable) State // ctor CCriticalSectionState::CCriticalSectionState( const CSyncBasicInfo& sbi ) : m_sem( sbi ) { } // dtor CCriticalSectionState::~CCriticalSectionState() { } // Critical Section (non-nestable) // ctor CCriticalSection::CCriticalSection( const CLockBasicInfo& lbi ) : CEnhancedStateContainer< CCriticalSectionState, CSyncBasicInfo, CLockSimpleInfo, CLockBasicInfo >( (CSyncBasicInfo&) lbi, lbi ) { #ifdef SYNC_ENHANCED_STATE // further init of CSyncBasicInfo State().SetTypeName( "CCriticalSection" ); State().SetInstance( (CSyncObject*)this ); #endif // SYNC_ENHANCED_STATE // release semaphore State().Semaphore().Release(); } // dtor CCriticalSection::~CCriticalSection() { #ifdef SYNC_ANALYZE_PERFORMANCE // dump performance data on destruction if enabled Dump( pcprintfPerfData ); #endif // SYNC_ANALYZE_PERFORMANCE } // Nestable Critical Section State // ctor CNestableCriticalSectionState::CNestableCriticalSectionState( const CSyncBasicInfo& sbi ) : m_sem( sbi ), m_pclsOwner( 0 ), m_cEntry( 0 ) { } // dtor CNestableCriticalSectionState::~CNestableCriticalSectionState() { } // Nestable Critical Section // ctor CNestableCriticalSection::CNestableCriticalSection( const CLockBasicInfo& lbi ) : CEnhancedStateContainer< CNestableCriticalSectionState, CSyncBasicInfo, CLockSimpleInfo, CLockBasicInfo >( (CSyncBasicInfo&) lbi, lbi ) { #ifdef SYNC_ENHANCED_STATE // further init of CSyncBasicInfo State().SetTypeName( "CNestableCriticalSection" ); State().SetInstance( (CSyncObject*)this ); #endif // SYNC_ENHANCED_STATE // release semaphore State().Semaphore().Release(); } // dtor CNestableCriticalSection::~CNestableCriticalSection() { #ifdef SYNC_ANALYZE_PERFORMANCE // dump performance data on destruction if enabled Dump( pcprintfPerfData ); #endif // SYNC_ANALYZE_PERFORMANCE } // Gate State // ctor CGateState::CGateState( const int cWait, const int irksem ) { // validate IN args Assert( cWait >= 0 ); Assert( cWait <= 0x7FFF ); Assert( irksem >= 0 ); Assert( irksem <= 0xFFFE ); // set waiter count m_cWait = (unsigned short) cWait; // set semaphore m_irksem = (unsigned short) irksem; } // Gate // ctor CGate::CGate( const CSyncBasicInfo& sbi ) : CEnhancedStateContainer< CGateState, CSyncStateInitNull, CSyncSimplePerfInfo, CSyncBasicInfo >( syncstateNull, sbi ) { #ifdef SYNC_ENHANCED_STATE // further init of CSyncBasicInfo State().SetTypeName( "CGate" ); State().SetInstance( (CSyncObject*)this ); #endif // SYNC_ENHANCED_STATE } // dtor CGate::~CGate() { // no one should be waiting Assert( State().CWait() == 0 ); Assert( State().Irksem() == CKernelSemaphorePool::irksemNil ); #ifdef SYNC_ANALYZE_PERFORMANCE // dump performance data on destruction if enabled Dump( pcprintfPerfData ); #endif // SYNC_ANALYZE_PERFORMANCE } // waits forever on the gate until released by someone else. this function // expects to be called while in the specified critical section. when the // function returns, the caller will NOT be in the critical section void CGate::Wait( CCriticalSection& crit ) { // we must be in the specified critical section Assert( crit.FOwner() ); // there can not be too many waiters on the gate Assert( State().CWait() < 0x7FFF ); // add ourselves as a waiter const int cWait = State().CWait() + 1; State().SetWaitCount( cWait ); // we are the first waiter CKernelSemaphorePool::IRKSEM irksem; #ifdef DEBUG irksem = CKernelSemaphorePool::irksemNil; #endif // DEBUG if ( cWait == 1 ) { // allocate a semaphore for the gate and remember it before leaving // the critical section Assert( State().Irksem() == CKernelSemaphorePool::irksemNil ); irksem = ksempoolGlobal.Allocate( this ); State().SetIrksem( irksem ); } // we are not the first waiter else { // reference the semaphore already in the gate and remember it before // leaving the critical section Assert( State().Irksem() != CKernelSemaphorePool::irksemNil ); irksem = State().Irksem(); ksempoolGlobal.Reference( irksem ); } Assert( irksem != CKernelSemaphorePool::irksemNil ); // leave critical section, never to return crit.Leave(); // wait to be released State().StartWait(); ksempoolGlobal.Ksem( irksem, this ).Acquire(); State().StopWait(); // unreference the semaphore ksempoolGlobal.Unreference( irksem ); } // releases the specified number of waiters from the gate. this function // expects to be called while in the specified critical section. when the // function returns, the caller will NOT be in the critical section // // NOTE: it is illegal to release more waiters than are waiting on the gate // and it is also illegal to release less than one waiter void CGate::Release( CCriticalSection& crit, const int cToRelease ) { // we must be in the specified critical section Assert( crit.FOwner() ); // you must release at least one waiter Assert( cToRelease > 0 ); // we cannot release more waiters than are waiting on the gate Assert( cToRelease <= State().CWait() ); // reduce the waiter count State().SetWaitCount( State().CWait() - cToRelease ); // remember semaphore to release before leaving the critical section const CKernelSemaphorePool::IRKSEM irksem = State().Irksem(); #ifdef DEBUG // we released all the waiters if ( State().CWait() == 0 ) { // set the semaphore to nil State().SetIrksem( CKernelSemaphorePool::irksemNil ); } #endif // DEBUG // leave critical section, never to return crit.Leave(); // release the specified number of waiters ksempoolGlobal.Ksem( irksem, this ).Release( cToRelease ); } // releases the specified number of waiters from the gate. this function // expects to be called while in the specified critical section. it is // guaranteed that the caller will remain in the critical section at all times // // NOTE: it is illegal to release more waiters than are waiting on the gate // and it is also illegal to release less than one waiter void CGate::ReleaseAndHold( CCriticalSection& crit, const int cToRelease ) { // we must be in the specified critical section Assert( crit.FOwner() ); // you must release at least one waiter Assert( cToRelease > 0 ); // we cannot release more waiters than are waiting on the gate Assert( cToRelease <= State().CWait() ); // reduce the waiter count State().SetWaitCount( State().CWait() - cToRelease ); // remember semaphore to release before leaving the critical section const CKernelSemaphorePool::IRKSEM irksem = State().Irksem(); #ifdef DEBUG // we released all the waiters if ( State().CWait() == 0 ) { // set the semaphore to nil State().SetIrksem( CKernelSemaphorePool::irksemNil ); } #endif // DEBUG // release the specified number of waiters ksempoolGlobal.Ksem( irksem, this ).Release( cToRelease ); } // Null Lock Object State Initializer CLockStateInitNull lockstateNull; // Binary Lock State // ctor CBinaryLockState::CBinaryLockState( const CSyncBasicInfo& sbi ) : m_cw( 0 ), m_cOwner( 0 ), m_sem1( sbi ), m_sem2( sbi ) { } // dtor CBinaryLockState::~CBinaryLockState() { } // Binary Lock // ctor CBinaryLock::CBinaryLock( const CLockBasicInfo& lbi ) : CEnhancedStateContainer< CBinaryLockState, CSyncBasicInfo, CGroupLockComplexInfo< 2 >, CLockBasicInfo >( (CSyncBasicInfo&) lbi, lbi ) { #ifdef SYNC_ENHANCED_STATE // further init of CSyncBasicInfo State().SetTypeName( "CBinaryLock" ); State().SetInstance( (CSyncObject*)this ); #endif // SYNC_ENHANCED_STATE } // dtor CBinaryLock::~CBinaryLock() { #ifdef SYNC_ANALYZE_PERFORMANCE // dump performance data on destruction if enabled Dump( pcprintfPerfData ); #endif // SYNC_ANALYZE_PERFORMANCE } // maps an arbitrary combination of zero and non-zero components into a // valid state number of the invalid state number (-1) const int mpindexstate[16] = { 0, -1, -1, -1, -1, -1, 1, -1, -1, 2, -1, 3, -1, -1, 4, 5, }; // returns the state number of the specified control word or -1 if it is not // a legal state int CBinaryLock::_StateFromControlWord( const ControlWord cw ) { // convert the control word into a state index int index = 0; index = index | ( ( cw & 0x80000000 ) ? 8 : 0 ); index = index | ( ( cw & 0x7FFF0000 ) ? 4 : 0 ); index = index | ( ( cw & 0x00008000 ) ? 2 : 0 ); index = index | ( ( cw & 0x00007FFF ) ? 1 : 0 ); // convert the state index into a state number const int state = mpindexstate[index]; // return the computed state number return state; } // state transition reachability matrix (starting state is the major axis) // // each entry contains bits representing valid reasons for making the // transition (made by oring together the valid TransitionReasons) #define NO CBinaryLock::trIllegal #define E1 CBinaryLock::trEnter1 #define L1 CBinaryLock::trLeave1 #define E2 CBinaryLock::trEnter2 #define L2 CBinaryLock::trLeave2 const DWORD mpstatestatetrmask[6][6] = { { NO, E2, E1, NO, NO, NO, }, { L2, E2 | L2, NO, E1, NO, NO, }, { L1, NO, E1 | L1, NO, E2, NO, }, { NO, NO, L2, E1 | L2, NO, E2, }, { NO, L1, NO, NO, L1 | E2, E1, }, { NO, NO, NO, L1, L2, E1 | L1 | E2 | L2, }, }; #undef NO #undef E1 #undef L1 #undef E2 #undef L2 // returns fTrue if the specified control word is in a legal state BOOL CBinaryLock::_FValidStateTransition( const ControlWord cwBI, const ControlWord cwAI, const TransitionReason tr ) { // convert the specified control words into state numbers const int stateBI = _StateFromControlWord( cwBI ); const int stateAI = _StateFromControlWord( cwAI ); // if either state is invalid, the transition is invalid if ( stateBI < 0 || stateAI < 0 ) { return fFalse; } // verify that cOOW2 and cOOW1 only change by +1, 0, -1, or go to 0 const long dcOOW2 = ( ( cwAI & 0x7FFF0000 ) >> 16 ) - ( ( cwBI & 0x7FFF0000 ) >> 16 ); if ( ( dcOOW2 < -1 || dcOOW2 > 1 ) && ( cwAI & 0x7FFF0000 ) != 0 ) { return fFalse; } const long dcOOW1 = ( cwAI & 0x00007FFF ) - ( cwBI & 0x00007FFF ); if ( ( dcOOW1 < -1 || dcOOW1 > 1 ) && ( cwAI & 0x00007FFF ) != 0 ) { return fFalse; } // return the reachability of stateAI from stateBI Assert( tr == trEnter1 || tr == trLeave1 || tr == trEnter2 || tr == trLeave2 ); return ( mpstatestatetrmask[stateBI][stateAI] & tr ) != 0; } // wait for ownership of the lock as a member of Group 1 void CBinaryLock::_Enter1( const ControlWord cwBIOld ) { // we just jumped from state 1 to state 3 if ( ( cwBIOld & 0x80008000 ) == 0x00008000 ) { // update the quiesced owner count with the owner count that we displaced from // the control word, possibly releasing waiters. we update the count as if we // were a member of Group 2 as members of Group 1 can be released _UpdateQuiescedOwnerCountAsGroup2( ( cwBIOld & 0x7FFF0000 ) >> 16 ); } // wait for ownership of the lock on our semaphore State().StartWait( 0 ); State().m_sem1.Acquire(); State().StopWait( 0 ); } // wait for ownership of the lock as a member of Group 2 void CBinaryLock::_Enter2( const ControlWord cwBIOld ) { // we just jumped from state 2 to state 4 if ( ( cwBIOld & 0x80008000 ) == 0x80000000 ) { // update the quiesced owner count with the owner count that we displaced from // the control word, possibly releasing waiters. we update the count as if we // were a member of Group 1 as members of Group 2 can be released _UpdateQuiescedOwnerCountAsGroup1( cwBIOld & 0x00007FFF ); } // wait for ownership of the lock on our semaphore State().StartWait( 1 ); State().m_sem2.Acquire(); State().StopWait( 1 ); } // updates the quiesced owner count as a member of Group 1 void CBinaryLock::_UpdateQuiescedOwnerCountAsGroup1( const DWORD cOwnerDelta ) { // update the quiesced owner count using the provided delta const DWORD cOwnerBI = AtomicExchangeAdd( (long*)&State().m_cOwner, cOwnerDelta ); const DWORD cOwnerAI = cOwnerBI + cOwnerDelta; // our update resulted in a zero quiesced owner count if ( !cOwnerAI ) { // we must release the waiters for Group 2 because we removed the last // quiesced owner count // try forever until we successfully change the lock state ControlWord cwBI; SYNC_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 such that we jump from state // state 4 to state 1 or from state 5 to state 3, whichever is appropriate const ControlWord cwAI = cwBIExpected & ( ( ( long( ( cwBIExpected + 0xFFFF7FFF ) << 16 ) >> 15 ) & 0xFFFF0000 ) ^ 0x8000FFFF ); // validate the transaction Assert( _FValidStateTransition( cwBIExpected, cwAI, trLeave1 ) ); // attempt to perform the transacted state transition on the control word cwBI = AtomicCompareExchange( (long*)&State().m_cw, cwBIExpected, cwAI ); // the transaction failed because another context changed the control word if ( cwBI != cwBIExpected ) { // try again continue; } // the transaction succeeded else { // we're done break; } } // we just jumped from state 5 to state 3 if ( cwBI & 0x00007FFF ) { // update the quiesced owner count with the owner count that we displaced // from the control word // // NOTE: we do not have to worry about releasing any more waiters because // either this context owns one of the owner counts or at least one context // that owns an owner count are currently blocked on the semaphore const DWORD cOwnerDelta = ( cwBI & 0x7FFF0000 ) >> 16; AtomicExchangeAdd( (long*)&State().m_cOwner, cOwnerDelta ); } // release the waiters for Group 2 that we removed from the lock state State().m_sem2.Release( ( cwBI & 0x7FFF0000 ) >> 16 ); } } // updates the quiesced owner count as a member of Group 2 void CBinaryLock::_UpdateQuiescedOwnerCountAsGroup2( const DWORD cOwnerDelta ) { // update the quiesced owner count using the provided delta const DWORD cOwnerBI = AtomicExchangeAdd( (long*)&State().m_cOwner, cOwnerDelta ); const DWORD cOwnerAI = cOwnerBI + cOwnerDelta; // our update resulted in a zero quiesced owner count if ( !cOwnerAI ) { // we must release the waiters for Group 1 because we removed the last // quiesced owner count // try forever until we successfully change the lock state ControlWord cwBI; SYNC_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 such that we jump from state // state 3 to state 2 or from state 5 to state 4, whichever is appropriate const ControlWord cwAI = cwBIExpected & ( ( ( long( cwBIExpected + 0x7FFF0000 ) >> 31 ) & 0x0000FFFF ) ^ 0xFFFF8000 ); // validate the transaction Assert( _FValidStateTransition( cwBIExpected, cwAI, trLeave2 ) ); // attempt to perform the transacted state transition on the control word cwBI = AtomicCompareExchange( (long*)&State().m_cw, cwBIExpected, cwAI ); // the transaction failed because another context changed the control word if ( cwBI != cwBIExpected ) { // try again continue; } // the transaction succeeded else { // we're done break; } } // we just jumped from state 5 to state 4 if ( cwBI & 0x7FFF0000 ) { // update the quiesced owner count with the owner count that we displaced // from the control word // // NOTE: we do not have to worry about releasing any more waiters because // either this context owns one of the owner counts or at least one context // that owns an owner count are currently blocked on the semaphore const DWORD cOwnerDelta = cwBI & 0x00007FFF; AtomicExchangeAdd( (long*)&State().m_cOwner, cOwnerDelta ); } // release the waiters for Group 1 that we removed from the lock state State().m_sem1.Release( cwBI & 0x00007FFF ); } } // Reader / Writer Lock State // ctor CReaderWriterLockState::CReaderWriterLockState( const CSyncBasicInfo& sbi ) : m_cw( 0 ), m_cOwner( 0 ), m_semWriter( sbi ), m_semReader( sbi ) { } // dtor CReaderWriterLockState::~CReaderWriterLockState() { } // Reader / Writer Lock // ctor CReaderWriterLock::CReaderWriterLock( const CLockBasicInfo& lbi ) : CEnhancedStateContainer< CReaderWriterLockState, CSyncBasicInfo, CGroupLockComplexInfo< 2 >, CLockBasicInfo >( (CSyncBasicInfo&) lbi, lbi ) { #ifdef SYNC_ENHANCED_STATE // further init of CSyncBasicInfo State().SetTypeName( "CReaderWriterLock" ); State().SetInstance( (CSyncObject*)this ); #endif // SYNC_ENHANCED_STATE } // dtor CReaderWriterLock::~CReaderWriterLock() { #ifdef SYNC_ANALYZE_PERFORMANCE // dump performance data on destruction if enabled Dump( pcprintfPerfData ); #endif // SYNC_ANALYZE_PERFORMANCE } // maps an arbitrary combination of zero and non-zero components into a // valid state number of the invalid state number (-1) const int mpindexstateRW[16] = { 0, -1, -1, -1, -1, -1, 1, -1, -1, 2, -1, 3, -1, -1, 4, 5, }; // returns the state number of the specified control word or -1 if it is not // a legal state int CReaderWriterLock::_StateFromControlWord( const ControlWord cw ) { // convert the control word into a state index int index = 0; index = index | ( ( cw & 0x80000000 ) ? 8 : 0 ); index = index | ( ( cw & 0x7FFF0000 ) ? 4 : 0 ); index = index | ( ( cw & 0x00008000 ) ? 2 : 0 ); index = index | ( ( cw & 0x00007FFF ) ? 1 : 0 ); // convert the state index into a state number const int state = mpindexstateRW[index]; // return the computed state number return state; } // state transition reachability matrix (starting state is the major axis) // // each entry contains bits representing valid reasons for making the // transition (made by oring together the valid TransitionReasons) #define NO CReaderWriterLock::trIllegal #define EW CReaderWriterLock::trEnterAsWriter #define LW CReaderWriterLock::trLeaveAsWriter #define ER CReaderWriterLock::trEnterAsReader #define LR CReaderWriterLock::trLeaveAsReader const DWORD mpstatestatetrmaskRW[6][6] = { { NO, ER, EW, NO, NO, NO, }, { LR, ER | LR, NO, EW, NO, NO, }, { LW, NO, EW | LW, NO, ER, ER, }, { NO, NO, LR, EW | LR, NO, ER, }, { NO, LW, NO, NO, ER, EW, }, { NO, NO, NO, LW, LR, EW | ER | LR, }, }; #undef NO #undef EW #undef LW #undef ER #undef LR // returns fTrue if the specified control word is in a legal state BOOL CReaderWriterLock::_FValidStateTransition( const ControlWord cwBI, const ControlWord cwAI, const TransitionReason tr ) { // convert the specified control words into state numbers const int stateBI = _StateFromControlWord( cwBI ); const int stateAI = _StateFromControlWord( cwAI ); // if either state is invalid, the transition is invalid if ( stateBI < 0 || stateAI < 0 ) { return fFalse; } // verify that cOOW2 and cOOW1 only change by +1, 0, -1, or cOOW2 can go to 0 const long dcOOW2 = ( ( cwAI & 0x7FFF0000 ) >> 16 ) - ( ( cwBI & 0x7FFF0000 ) >> 16 ); if ( ( dcOOW2 < -1 || dcOOW2 > 1 ) && ( cwAI & 0x7FFF0000 ) != 0 ) { return fFalse; } const long dcOOW1 = ( cwAI & 0x00007FFF ) - ( cwBI & 0x00007FFF ); if ( dcOOW1 < -1 || dcOOW1 > 1 ) { return fFalse; } // return the reachability of stateAI from stateBI Assert( tr == trEnterAsWriter || tr == trLeaveAsWriter || tr == trEnterAsReader || tr == trLeaveAsReader ); return ( mpstatestatetrmaskRW[stateBI][stateAI] & tr ) != 0; } // wait for ownership of the lock as a writer void CReaderWriterLock::_EnterAsWriter( const ControlWord cwBIOld ) { // we just jumped from state 1 to state 3 if ( ( cwBIOld & 0x80008000 ) == 0x00008000 ) { // update the quiesced owner count with the owner count that we displaced from // the control word, possibly releasing a waiter. we update the count as if we // were a reader as a writer can be released _UpdateQuiescedOwnerCountAsReader( ( cwBIOld & 0x7FFF0000 ) >> 16 ); } // wait for ownership of the lock on our semaphore State().StartWait( 0 ); State().m_semWriter.Acquire(); State().StopWait( 0 ); } // wait for ownership of the lock as a reader void CReaderWriterLock::_EnterAsReader( const ControlWord cwBIOld ) { // we just jumped from state 2 to state 4 or from state 2 to state 5 if ( ( cwBIOld & 0x80008000 ) == 0x80000000 ) { // update the quiesced owner count with the owner count that we displaced from // the control word, possibly releasing waiters. we update the count as if we // were a writer as readers can be released _UpdateQuiescedOwnerCountAsWriter( 0x00000001 ); } // wait for ownership of the lock on our semaphore State().StartWait( 1 ); State().m_semReader.Acquire(); State().StopWait( 1 ); } // updates the quiesced owner count as a writer void CReaderWriterLock::_UpdateQuiescedOwnerCountAsWriter( const DWORD cOwnerDelta ) { // update the quiesced owner count using the provided delta const DWORD cOwnerBI = AtomicExchangeAdd( (long*)&State().m_cOwner, cOwnerDelta ); const DWORD cOwnerAI = cOwnerBI + cOwnerDelta; // our update resulted in a zero quiesced owner count if ( !cOwnerAI ) { // we must release the waiting readers because we removed the last // quiesced owner count // try forever until we successfully change the lock state ControlWord cwBI; SYNC_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 such that we jump from state // state 4 to state 1 or from state 5 to state 3, whichever is appropriate const ControlWord cwAI = cwBIExpected & ( ( ( long( ( cwBIExpected + 0xFFFF7FFF ) << 16 ) >> 15 ) & 0xFFFF0000 ) ^ 0x8000FFFF ); // validate the transaction Assert( _FValidStateTransition( cwBIExpected, cwAI, trLeaveAsWriter ) ); // attempt to perform the transacted state transition on the control word cwBI = AtomicCompareExchange( (long*)&State().m_cw, cwBIExpected, cwAI ); // the transaction failed because another context changed the control word if ( cwBI != cwBIExpected ) { // try again continue; } // the transaction succeeded else { // we're done break; } } // we just jumped from state 5 to state 3 if ( cwBI & 0x00007FFF ) { // update the quiesced owner count with the owner count that we displaced // from the control word // // NOTE: we do not have to worry about releasing any more waiters because // either this context owns one of the owner counts or at least one context // that owns an owner count are currently blocked on the semaphore const DWORD cOwnerDelta = ( cwBI & 0x7FFF0000 ) >> 16; AtomicExchangeAdd( (long*)&State().m_cOwner, cOwnerDelta ); } // release the waiting readers that we removed from the lock state State().m_semReader.Release( ( cwBI & 0x7FFF0000 ) >> 16 ); } } // updates the quiesced owner count as a reader void CReaderWriterLock::_UpdateQuiescedOwnerCountAsReader( const DWORD cOwnerDelta ) { // update the quiesced owner count using the provided delta const DWORD cOwnerBI = AtomicExchangeAdd( (long*)&State().m_cOwner, cOwnerDelta ); const DWORD cOwnerAI = cOwnerBI + cOwnerDelta; // our update resulted in a zero quiesced owner count if ( !cOwnerAI ) { // we must release a waiting writer because we removed the last // quiesced owner count // try forever until we successfully change the lock state ControlWord cwBI; SYNC_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 such that we jump from state // state 3 to state 2, from state 5 to state 4, or from state 5 to state 5, // whichever is appropriate const ControlWord cwAI = cwBIExpected + ( ( cwBIExpected & 0x7FFF0000 ) ? 0xFFFFFFFF : 0xFFFF8000 ); // validate the transaction Assert( _FValidStateTransition( cwBIExpected, cwAI, trLeaveAsReader ) ); // attempt to perform the transacted state transition on the control word cwBI = AtomicCompareExchange( (long*)&State().m_cw, cwBIExpected, cwAI ); // the transaction failed because another context changed the control word if ( cwBI != cwBIExpected ) { // try again continue; } // the transaction succeeded else { // we're done break; } } // we just jumped from state 5 to state 4 or from state 5 to state 5 if ( cwBI & 0x7FFF0000 ) { // update the quiesced owner count with the owner count that we displaced // from the control word // // NOTE: we do not have to worry about releasing any more waiters because // either this context owns one of the owner counts or at least one context // that owns an owner count are currently blocked on the semaphore AtomicExchangeAdd( (long*)&State().m_cOwner, 1 ); } // release the waiting writer that we removed from the lock state State().m_semWriter.Release(); } } ////////////////////////////////////////////////// // Everything below this line is OS dependent // Global Synchronization Constants // wait time used for testing the state of the kernel object const int cmsecTest = 0; // wait time used for infinite wait on a kernel object const int cmsecInfinite = INFINITE; // maximum wait time on a kernel object before a deadlock is suspected const int cmsecDeadlock = 600000; // wait time used for infinite wait on a kernel object without deadlock const int cmsecInfiniteNoDeadlock = INFINITE - 1; // Page Memory Allocation // reserves and commits a range of virtual addresses of the specifed size, // returning NULL if there is insufficient address space or backing store to // satisfy the request. Note that the page reserve granularity applies to // this range void* PvPageAlloc( const size_t cbSize, void* const pv ) { // allocate address space and backing store of the specified size void* const pvRet = VirtualAlloc( pv, cbSize, MEM_COMMIT, PAGE_READWRITE ); if ( !pvRet ) { return pvRet; } Assert( !pv || pvRet == pv ); return pvRet; } // free the reserved range of virtual addresses starting at the specified // address, freeing any backing store committed to this range void PageFree( void* const pv ) { if ( pv ) { // free backing store and address space for the specified range BOOL fMemFreed = VirtualFree( pv, 0, MEM_RELEASE ); Assert( fMemFreed ); } } // Context Local Storage // Global CLS List CRITICAL_SECTION csClsSyncGlobal; CLS* pclsSyncGlobal; // Allocated CLS Entry DWORD dwClsSyncIndex; // registers the given CLS structure as the CLS for this context void OSSyncIClsRegister( CLS* pcls ) { // we are the first to register CLS EnterCriticalSection( &csClsSyncGlobal ); if ( pclsSyncGlobal == NULL ) { // allocate a new CLS entry dwClsSyncIndex = TlsAlloc(); Assert( dwClsSyncIndex != 0xFFFFFFFF ); } // save the pointer to the given CLS BOOL fTLSPointerSet = TlsSetValue( dwClsSyncIndex, pcls ); Assert( fTLSPointerSet ); // add this CLS into the global list pcls->pclsNext = pclsSyncGlobal; if ( pcls->pclsNext ) { pcls->pclsNext->ppclsNext = &pcls->pclsNext; } pcls->ppclsNext = &pclsSyncGlobal; pclsSyncGlobal = pcls; LeaveCriticalSection( &csClsSyncGlobal ); } // unregisters the given CLS structure as the CLS for this context void OSSyncIClsUnregister( CLS* pcls ) { // there should be CLSs registered EnterCriticalSection( &csClsSyncGlobal ); Assert( pclsSyncGlobal != NULL ); // remove our CLS from the global CLS list if( pcls->pclsNext ) { pcls->pclsNext->ppclsNext = pcls->ppclsNext; } *( pcls->ppclsNext ) = pcls->pclsNext; // we are the last to unregister our CLS if ( pclsSyncGlobal == NULL ) { // deallocate CLS entry BOOL fTLSFreed = TlsFree( dwClsSyncIndex ); Assert( fTLSFreed ); } LeaveCriticalSection( &csClsSyncGlobal ); } // attaches to the current context, returning fFalse on failure const BOOL FOSSyncAttach() { // allocate memory for this context's CLS CLS* const pcls = (CLS*) GlobalAlloc( GMEM_ZEROINIT, sizeof( CLS ) ); if ( !pcls ) { return fFalse; } // initialize internal CLS fields pcls->dwContextId = GetCurrentThreadId(); // register our CLS OSSyncIClsRegister( pcls ); return fTrue; } // returns the pointer to the current context's local storage. if the context // does not yet have CLS, allocate it. this can happen if the context was // created before the subsystem was initialized CLS* const Pcls() { CLS* pcls = reinterpret_cast( TlsGetValue( dwClsSyncIndex ) ); if ( !pcls ) { while ( !FOSSyncAttach() ) { Sleep( 1000 ); } pcls = reinterpret_cast( TlsGetValue( dwClsSyncIndex ) ); } return pcls; } // High Resolution Timer // QWORDX - used for 32 bit access to a 64 bit integer union QWORDX { QWORD qw; struct { DWORD l; DWORD h; }; }; // High Resolution Timer Type enum HRTType { hrttNone, hrttWin32, #if defined( _M_IX86 ) && defined( SYNC_USE_X86_ASM ) hrttPentium, #endif // _M_IX86 && SYNC_USE_X86_ASM } hrttSync; // HRT Frequency QWORD qwSyncHRTFreq; #if defined( _M_IX86 ) && defined( SYNC_USE_X86_ASM ) // Pentium Time Stamp Counter Fetch #define rdtsc __asm _emit 0x0f __asm _emit 0x31 #endif // _MM_IX86 && SYNC_USE_X86_ASM // initializes the HRT subsystem void OSTimeHRTInit() { // if we have already been initialized, we're done if ( qwSyncHRTFreq ) { return; } #ifdef _M_ALPHA #else // !_M_ALPHA // Win32 high resolution counter is available if ( QueryPerformanceFrequency( (LARGE_INTEGER *) &qwSyncHRTFreq ) ) { hrttSync = hrttWin32; } // Win32 high resolution counter is not available else #endif // _M_ALPHA { // fall back on GetTickCount() (ms since Windows has started) QWORDX qwx; qwx.l = 1000; qwx.h = 0; qwSyncHRTFreq = qwx.qw; hrttSync = hrttNone; } #if defined( _M_IX86 ) && defined( SYNC_USE_X86_ASM ) // this is a Pentium or compatible CPU SYSTEM_INFO siSystemConfig; GetSystemInfo( &siSystemConfig ); if ( siSystemConfig.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_INTEL && siSystemConfig.wProcessorLevel >= 5 ) { // use pentium TSC register, but first find clock frequency experimentally QWORDX qwxTime1a; QWORDX qwxTime1b; QWORDX qwxTime2a; QWORDX qwxTime2b; if ( hrttSync == hrttWin32 ) { __asm xchg eax, edx // HACK: cl 11.00.7022 needs this __asm rdtsc __asm mov qwxTime1a.l,eax //lint !e530 __asm mov qwxTime1a.h,edx //lint !e530 QueryPerformanceCounter( (LARGE_INTEGER*) &qwxTime1b.qw ); Sleep( 50 ); __asm xchg eax, edx // HACK: cl 11.00.7022 needs this __asm rdtsc __asm mov qwxTime2a.l,eax //lint !e530 __asm mov qwxTime2a.h,edx //lint !e530 QueryPerformanceCounter( (LARGE_INTEGER*) &qwxTime2b.qw ); qwSyncHRTFreq = ( qwSyncHRTFreq * ( qwxTime2a.qw - qwxTime1a.qw ) ) / ( qwxTime2b.qw - qwxTime1b.qw ); qwSyncHRTFreq = ( ( qwSyncHRTFreq + 50000 ) / 100000 ) * 100000; } else { __asm xchg eax, edx // HACK: cl 11.00.7022 needs this __asm rdtsc __asm mov qwxTime1a.l,eax __asm mov qwxTime1a.h,edx qwxTime1b.l = GetTickCount(); qwxTime1b.h = 0; Sleep( 2000 ); __asm xchg eax, edx // HACK: cl 11.00.7022 needs this __asm rdtsc __asm mov qwxTime2a.l,eax __asm mov qwxTime2a.h,edx qwxTime2b.l = GetTickCount(); qwxTime2b.h = 0; qwSyncHRTFreq = ( qwSyncHRTFreq * ( qwxTime2a.qw - qwxTime1a.qw ) ) / ( qwxTime2b.qw - qwxTime1b.qw ); qwSyncHRTFreq = ( ( qwSyncHRTFreq + 500000 ) / 1000000 ) * 1000000; } hrttSync = hrttPentium; } #endif // _M_IX86 && SYNC_USE_X86_ASM } // returns the current HRT frequency QWORD QwOSTimeHRTFreq() { return qwSyncHRTFreq; } // returns the current HRT count QWORD QwOSTimeHRTCount() { QWORDX qwx; switch ( hrttSync ) { case hrttNone: qwx.l = GetTickCount(); qwx.h = 0; break; case hrttWin32: QueryPerformanceCounter( (LARGE_INTEGER*) &qwx.qw ); break; #if defined( _M_IX86 ) && defined( SYNC_USE_X86_ASM ) case hrttPentium: __asm xchg eax, edx // HACK: cl 11.00.7022 needs this __asm rdtsc __asm mov qwx.l,eax __asm mov qwx.h,edx break; #endif // _M_IX86 && SYNC_USE_X86_ASM } return qwx.qw; } // Atomic Memory Manipulations #if defined( _M_IX86 ) && defined( SYNC_USE_X86_ASM ) #elif ( defined( _M_MRX000 ) || defined( _M_ALPHA ) || ( defined( _M_PPC ) && ( _MSC_VER >= 1000 ) ) ) #else // returns fTrue if the given data is properly aligned for atomic modification const BOOL IsAtomicallyModifiable( long* plTarget ) { return long( plTarget ) % sizeof( long ) == 0; } // 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 const long AtomicCompareExchange( long* plTarget, const long lInitial, const long lFinal ) { Assert( IsAtomicallyModifiable( plTarget ) ); return long( InterlockedCompareExchange( (void**) plTarget, (void*) lFinal, (void*) lInitial ) ); } // atomically sets the target to the specified value, returning the target's // initial value. the target must be aligned to a four byte boundary const long AtomicExchange( long* plTarget, const long lValue ) { Assert( IsAtomicallyModifiable( plTarget ) ); return InterlockedExchange( plTarget, lValue ); } // atomically adds the specified value to the target, returning the target's // initial value. the target must be aligned to a four byte boundary const long AtomicExchangeAdd( long* plTarget, const long lValue ) { Assert( IsAtomicallyModifiable( plTarget ) ); return InterlockedExchangeAdd( plTarget, lValue ); } #endif // Enhanced Synchronization Object State Container struct MemoryBlock { MemoryBlock* pmbNext; MemoryBlock** ppmbNext; DWORD cAlloc; DWORD ibFreeMic; }; DWORD g_cbMemoryBlock; MemoryBlock* g_pmbRoot; MemoryBlock g_mbSentry; CRITICAL_SECTION g_csESMemory; void* ESMemoryNew( size_t cb ) { if ( !FOSSyncInit() ) { return NULL; } cb += sizeof( long ) - 1; cb -= cb % sizeof( long ); EnterCriticalSection( &g_csESMemory ); MemoryBlock* pmb = g_pmbRoot; if ( pmb->ibFreeMic + cb > g_cbMemoryBlock ) { if ( !( pmb = (MemoryBlock*) VirtualAlloc( NULL, g_cbMemoryBlock, MEM_COMMIT, PAGE_READWRITE ) ) ) { LeaveCriticalSection( &g_csESMemory ); OSSyncTerm(); return pmb; } pmb->pmbNext = g_pmbRoot; pmb->ppmbNext = &g_pmbRoot; pmb->cAlloc = 0; pmb->ibFreeMic = sizeof( MemoryBlock ); g_pmbRoot->ppmbNext = &pmb->pmbNext; g_pmbRoot = pmb; } void* pv = (BYTE*)pmb + pmb->ibFreeMic; pmb->cAlloc++; pmb->ibFreeMic += cb; LeaveCriticalSection( &g_csESMemory ); return pv; } void ESMemoryDelete( void* pv ) { if ( pv ) { EnterCriticalSection( &g_csESMemory ); MemoryBlock* const pmb = (MemoryBlock*) ( (DWORD_PTR)( pv ) - (DWORD_PTR)( pv ) % g_cbMemoryBlock ); if ( !( --pmb->cAlloc ) ) { *pmb->ppmbNext = pmb->pmbNext; pmb->pmbNext->ppmbNext = pmb->ppmbNext; BOOL fMemFreed = VirtualFree( pmb, 0, MEM_RELEASE ); Assert( fMemFreed ); } LeaveCriticalSection( &g_csESMemory ); OSSyncTerm(); } } // Synchronization Object Basic Information // ctor CSyncBasicInfo::CSyncBasicInfo( const _TCHAR* szInstanceName ) { #ifdef SYNC_ENHANCED_STATE m_szInstanceName = szInstanceName; m_szTypeName = NULL; m_psyncobj = NULL; #endif // SYNC_ENHANCED_STATE } // dtor CSyncBasicInfo::~CSyncBasicInfo() { } // Synchronization Object Performance: Wait Times // ctor CSyncPerfWait::CSyncPerfWait() { #ifdef SYNC_ANALYZE_PERFORMANCE m_cWait = 0; m_qwHRTWaitElapsed = 0; #endif // SYNC_ANALYZE_PERFORMANCE } // dtor CSyncPerfWait::~CSyncPerfWait() { } // Null Synchronization Object State Initializer CSyncStateInitNull syncstateNull; // Kernel Semaphore // ctor CKernelSemaphore::CKernelSemaphore( const CSyncBasicInfo& sbi ) : CEnhancedStateContainer< CKernelSemaphoreState, CSyncStateInitNull, CSyncSimplePerfInfo, CSyncBasicInfo >( syncstateNull, sbi ) { Assert( sizeof( long ) >= sizeof( HANDLE ) ); #ifdef SYNC_ENHANCED_STATE // further init of CSyncBasicInfo State().SetTypeName( "CKernelSemaphore" ); State().SetInstance( (CSyncObject*)this ); #endif // SYNC_ENHANCED_STATE } // dtor CKernelSemaphore::~CKernelSemaphore() { // semaphore should not be initialized Assert( !FInitialized() ); } // initialize the semaphore, returning 0 on failure const BOOL CKernelSemaphore::FInit() { // semaphore should not be initialized Assert( !FInitialized() ); // allocate kernel semaphore object State().SetHandle( (LONG_PTR)( CreateSemaphore( 0, 0, 0x7FFFFFFFL, 0 ) ) ); // semaphore should have no available counts, if allocated Assert( State().Handle() == 0 || FReset() ); // return result of init return State().Handle() != 0; } // terminate the semaphore void CKernelSemaphore::Term() { // semaphore should be initialized Assert( FInitialized() ); // semaphore should have no available counts Assert( FReset() ); #ifdef SYNC_ANALYZE_PERFORMANCE // dump performance data on termination if enabled Dump( pcprintfPerfData ); #endif // SYNC_ANALYZE_PERFORMANCE // deallocate kernel semaphore object int fSuccess = CloseHandle( HANDLE( State().Handle() ) ); Assert( fSuccess ); // reset state State().CKernelSemaphoreState::~CKernelSemaphoreState(); State().CKernelSemaphoreState::CKernelSemaphoreState( syncstateNull ); // reset information // UNDONE: clean this up (yuck!) #ifdef SYNC_ENHANCED_STATE const _TCHAR* szInstanceName = State().SzInstanceName(); #else // !SYNC_ENHANCED_STATE const _TCHAR* szInstanceName = _T( "" ); #endif // SYNC_ENHANCED_STATE State().CSyncSimplePerfInfo::~CSyncSimplePerfInfo(); State().CSyncSimplePerfInfo::CSyncSimplePerfInfo( CSyncBasicInfo( szInstanceName ) ); } // acquire one count of the semaphore, waiting only for the specified interval. // returns 0 if the wait timed out before a count could be acquired const BOOL CKernelSemaphore::FAcquire( const int cmsecTimeout ) { // semaphore should be initialized Assert( FInitialized() ); // wait for semaphore DWORD dwResult; State().StartWait(); #ifdef SYNC_DEADLOCK_DETECTION if ( cmsecTimeout == cmsecInfinite || cmsecTimeout > cmsecDeadlock ) { while ( ( dwResult = WaitForSingleObjectEx( HANDLE( State().Handle() ), cmsecDeadlock, FALSE ) ) == WAIT_IO_COMPLETION ); Assert( dwResult == WAIT_OBJECT_0 || dwResult == WAIT_TIMEOUT ); AssertRTLSz( dwResult == WAIT_OBJECT_0, _T( "Potential Deadlock Detected" ) ); if ( dwResult == WAIT_TIMEOUT ) { dwResult = WaitForSingleObjectEx( HANDLE( State().Handle() ), cmsecTimeout == cmsecInfinite ? cmsecInfinite : cmsecTimeout - cmsecDeadlock, FALSE ); } } else { Assert( cmsecTimeout == cmsecInfiniteNoDeadlock || cmsecTimeout <= cmsecDeadlock ); const int cmsecWait = cmsecTimeout == cmsecInfiniteNoDeadlock ? cmsecInfinite : cmsecTimeout; while ( ( dwResult = WaitForSingleObjectEx( HANDLE( State().Handle() ), cmsecWait, FALSE ) ) == WAIT_IO_COMPLETION ); } #else // !SYNC_DEADLOCK_DETECTION const int cmsecWait = cmsecTimeout == cmsecInfiniteNoDeadlock ? cmsecInfinite : cmsecTimeout; while ( ( dwResult = WaitForSingleObjectEx( HANDLE( State().Handle() ), cmsecWait, FALSE ) ) == WAIT_IO_COMPLETION ); #endif // SYNC_DEADLOCK_DETECTION State().StopWait(); Assert( dwResult == WAIT_OBJECT_0 || dwResult == WAIT_TIMEOUT ); return dwResult == WAIT_OBJECT_0; } // releases the given number of counts to the semaphore, waking the appropriate // number of waiters void CKernelSemaphore::Release( const int cToRelease ) { // semaphore should be initialized Assert( FInitialized() ); // release semaphore const BOOL fSuccess = ReleaseSemaphore( HANDLE( State().Handle() ), cToRelease, 0 ); Assert( fSuccess ); } // performance data dumping #include // ================================================================ class CPRINTFFILE : public CPRINTFSYNC // ================================================================ { public: CPRINTFFILE( const _TCHAR* szFile ) : m_szFile( szFile ), m_file( _tfopen( szFile, _T( "a+" ) ) ) { } ~CPRINTFFILE() { fclose( m_file ); } void __cdecl operator()( const _TCHAR* szFormat, ... ) const { va_list arg_ptr; va_start( arg_ptr, szFormat ); _vftprintf( m_file, szFormat, arg_ptr ); va_end( arg_ptr ); } private: const _TCHAR* const m_szFile; FILE* m_file; }; // init sync subsystem volatile DWORD cOSSyncInit; // assumed init to 0 by the loader const BOOL FOSSyncInit() { if ( AtomicIncrement( (long*)&cOSSyncInit ) == 1 ) { // reset all pointers #ifdef SYNC_ANALYZE_PERFORMANCE pcprintfPerfData = NULL; #endif // SYNC_ANALYZE_PERFORMANCE // reset global CLS list InitializeCriticalSection( &csClsSyncGlobal ); pclsSyncGlobal = NULL; dwClsSyncIndex = 0xFFFFFFFF; // iniitalize the HRT OSTimeHRTInit(); // initialize the CEnhancedState allocator SYSTEM_INFO sinf; GetSystemInfo( &sinf ); g_cbMemoryBlock = sinf.dwAllocationGranularity; g_pmbRoot = &g_mbSentry; g_mbSentry.pmbNext = NULL; g_mbSentry.ppmbNext = &g_pmbRoot; g_mbSentry.cAlloc = 1; g_mbSentry.ibFreeMic = g_cbMemoryBlock; InitializeCriticalSection( &g_csESMemory ); // initialize the Kernel Semaphore Pool if ( !ksempoolGlobal.FInit() ) { goto HandleError; } // cache system max spin count // // NOTE: spins heavily as WaitForSingleObject() is extremely expensive // // CONSIDER: get spin count from persistent configuration DWORD cProcessor; DWORD_PTR maskProcess; DWORD_PTR maskSystem; BOOL fGotAffinityMask; fGotAffinityMask = GetProcessAffinityMask( GetCurrentProcess(), &maskProcess, &maskSystem ); Assert( fGotAffinityMask ); for ( cProcessor = 0; maskProcess != 0; maskProcess >>= 1 ) { if ( maskProcess & 1 ) { cProcessor++; } } cSpinMax = cProcessor == 1 ? 0 : 256; #ifdef SYNC_ANALYZE_PERFORMANCE // initialize performance data dumping cprintf if ( !( pcprintfPerfData = new CPRINTFFILE( _T( "SyncPerfData.TXT" ) ) ) ) { goto HandleError; } #endif // SYNC_ANALYZE_PERFORMANCE } return fTrue; HandleError: OSSyncTerm(); return fFalse; } // terminate sync subsystem void OSSyncTerm() { if ( !AtomicDecrement( (long*)&cOSSyncInit ) ) { #ifdef SYNC_ANALYZE_PERFORMANCE // terminate performance data dumping cprintf delete (CPRINTFFILE*)pcprintfPerfData; pcprintfPerfData = NULL; #endif // SYNC_ANALYZE_PERFORMANCE // terminate the Kernel Semaphore Pool if ( ksempoolGlobal.FInitialized() ) { ksempoolGlobal.Term(); } // terminate the CEnhancedState allocator DeleteCriticalSection( &g_csESMemory ); // remove any remaining CLS allocated by contexts that were already created // when the subsystem was initialized while ( pclsSyncGlobal ) { // get the current head of the global CLS list CLS* pcls = pclsSyncGlobal; // unregister our CLS OSSyncIClsUnregister( pcls ); // free our CLS storage BOOL fFreedCLSOK = !GlobalFree( pcls ); Assert( fFreedCLSOK ); } DeleteCriticalSection( &csClsSyncGlobal ); } } #ifdef DEBUGGER_EXTENSION #include #include #include #include #include #include #include #define LOCAL static LOCAL WINDBG_EXTENSION_APIS ExtensionApis; LOCAL BOOL fVerbose = fFalse; LOCAL HANDLE ghDbgProcess; // ================================================================ class CPRINTFWINDBG : public CPRINTFSYNC // ================================================================ { public: VOID __cdecl operator()( const char * szFormat, ... ) const { va_list arg_ptr; va_start( arg_ptr, szFormat ); vsprintf( szBuf, szFormat, arg_ptr ); va_end( arg_ptr ); szBuf[sizeof(szBuf)-1] = 0; dprintf( "%s", szBuf ); } static CPRINTFSYNC * PcprintfInstance(); ~CPRINTFWINDBG() {} private: CPRINTFWINDBG() {} static CHAR szBuf[1024]; // WARNING: not multi-threaded safe! }; CHAR CPRINTFWINDBG::szBuf[1024]; // ================================================================ CPRINTFSYNC * CPRINTFWINDBG::PcprintfInstance() // ================================================================ { static CPRINTFWINDBG CPrintfWindbg; return &CPrintfWindbg; } // ================================================================ class CDUMP // ================================================================ { public: CDUMP() {} virtual ~CDUMP() {} virtual VOID Dump( HANDLE, HANDLE, DWORD, PWINDBG_EXTENSION_APIS, INT, const CHAR * const [] ) = 0; }; // ================================================================ template< class _STRUCT> class CDUMPA : public CDUMP // ================================================================ { public: VOID Dump( HANDLE hCurrentProcess, HANDLE hCurrentThread, DWORD dwCurrentPc, PWINDBG_EXTENSION_APIS lpExtensionApis, INT argc, const CHAR * const argv[] ); static CDUMPA instance; }; template< class _STRUCT> CDUMPA<_STRUCT> CDUMPA<_STRUCT>::instance; // **************************************************************** // PROTOTYPES // **************************************************************** #define DEBUG_EXT( name ) \ LOCAL VOID name( \ const HANDLE hCurrentProcess, \ const HANDLE hCurrentThread, \ const DWORD dwCurrentPc, \ const PWINDBG_EXTENSION_APIS lpExtensionApis, \ const INT argc, \ const CHAR * const argv[] ) DEBUG_EXT( EDBGDump ); #ifdef SYNC_DEADLOCK_DETECTION DEBUG_EXT( EDBGLocks ); #endif // SYNC_DEADLOCK_DETECTION DEBUG_EXT( EDBGHelp ); DEBUG_EXT( EDBGHelpDump ); DEBUG_EXT( EDBGVerbose ); LOCAL VOID * PvReadDBGMemory( const ULONG ulAddr, const ULONG cbSize ); LOCAL VOID FreeDBGMemory( VOID * const pv ); // **************************************************************** // COMMAND DISPATCH // **************************************************************** typedef VOID (*EDBGFUNC)( const HANDLE hCurrentProcess, const HANDLE hCurrentThread, const DWORD dwCurrentPc, const PWINDBG_EXTENSION_APIS lpExtensionApis, const INT argc, const CHAR * const argv[] ); // ================================================================ struct EDBGFUNCMAP // ================================================================ { const char * szCommand; EDBGFUNC function; const char * szHelp; }; // ================================================================ struct CDUMPMAP // ================================================================ { const char * szCommand; CDUMP * pcdump; const char * szHelp; }; // ================================================================ LOCAL EDBGFUNCMAP rgfuncmap[] = { // ================================================================ { "Help", EDBGHelp, "Help - Print this help message" }, { "Dump", EDBGDump, "Dump
- Dump a given synchronization object's state" }, #ifdef SYNC_DEADLOCK_DETECTION { "Locks", EDBGLocks, "Locks [] - List all locks owned by any thread or all locks\n\t" " owned by the thread with the specified Thread ID" }, #endif // SYNC_DEADLOCK_DETECTION { "Verbose", EDBGVerbose, "Verbose - Toggle verbose mode" }, }; LOCAL const int cfuncmap = sizeof( rgfuncmap ) / sizeof( EDBGFUNCMAP ); #define DUMPA(_struct) { #_struct, &(CDUMPA<_struct>::instance), #_struct "
" } // ================================================================ LOCAL CDUMPMAP rgcdumpmap[] = { // ================================================================ DUMPA( CAutoResetSignal ), DUMPA( CBinaryLock ), DUMPA( CCriticalSection ), DUMPA( CGate ), DUMPA( CKernelSemaphore ), DUMPA( CManualResetSignal ), DUMPA( CNestableCriticalSection ), DUMPA( CReaderWriterLock ), DUMPA( CSemaphore ), }; LOCAL const int ccdumpmap = sizeof( rgcdumpmap ) / sizeof( CDUMPMAP ); // ================================================================ LOCAL BOOL FArgumentMatch( const CHAR * const sz, const CHAR * const szCommand ) // ================================================================ { const BOOL fMatch = ( ( strlen( sz ) == strlen( szCommand ) ) && !( _strnicmp( sz, szCommand, strlen( szCommand ) ) ) ); if( fVerbose ) { dprintf( "FArgumentMatch( %s, %s ) = %d\n", sz, szCommand, fMatch ); } return fMatch; } // ================================================================ LOCAL BOOL FUlFromArg( const CHAR * const sz, ULONG * const pul, const INT base = 16 ) // ================================================================ { if( sz && *sz ) { CHAR * pchEnd; *pul = strtoul( sz, &pchEnd, base ); return fTrue; } return fFalse; } // ================================================================ LOCAL BOOL FUlFromExpr( const CHAR * const sz, ULONG * const pul ) // ================================================================ { if( sz && *sz ) { *pul = ULONG( GetExpression( sz ) ); return fTrue; } return fFalse; } // ================================================================ LOCAL VOID * PvReadDBGMemory( const ULONG ulAddr, const ULONG cbSize ) // ================================================================ { if( 0 == cbSize ) { return NULL; } if( fVerbose ) { dprintf( "HeapAlloc(0x%x)\n", cbSize ); } VOID * const pv = HeapAlloc( GetProcessHeap(), 0, cbSize ); if ( NULL == pv ) { dprintf( "Memory allocation error for %x bytes\n", cbSize ); return NULL; } if( fVerbose ) { dprintf( "ReadProcessMemory(0x%x @ %p)\n", cbSize, ulAddr ); } DWORD cRead; if ( !ReadProcessMemory( ghDbgProcess, (VOID *)ulAddr, pv, cbSize, &cRead ) ) { FreeDBGMemory(pv); dprintf( "ReadProcessMemory error %x (%x@%p)\n", GetLastError(), cbSize, ulAddr ); return NULL; } if ( cbSize != cRead ) { FreeDBGMemory( pv ); dprintf( "ReadProcessMemory size error - off by %x bytes\n", (cbSize > cRead) ? cbSize - cRead : cRead - cbSize ); return NULL; } return pv; } // ================================================================ LOCAL VOID FreeDBGMemory( VOID * const pv ) // ================================================================ { if ( fVerbose ) { dprintf( "HeapFree(%p)\n", pv ); } if ( NULL != pv ) { if ( !HeapFree( GetProcessHeap(), 0, pv ) ) { dprintf( "Error %x freeing memory at %p\n", GetLastError(), pv ); } } } // ================================================================ LOCAL VOID DBUTLSprintHex( CHAR * const szDest, const BYTE * const rgbSrc, const INT cbSrc, const INT cbWidth, const INT cbChunk, const INT cbAddress, const INT cbStart) // ================================================================ { static const CHAR rgchConvert[] = { '0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f' }; const BYTE * const pbMax = rgbSrc + cbSrc; const INT cchHexWidth = ( cbWidth * 2 ) + ( cbWidth / cbChunk ); const BYTE * pb = rgbSrc; CHAR * sz = szDest; while( pbMax != pb ) { sz += ( 0 == cbAddress ) ? 0 : sprintf( sz, "%*.*lx ", cbAddress, cbAddress, pb - rgbSrc + cbStart ); CHAR * szHex = sz; CHAR * szText = sz + cchHexWidth; do { for( INT cb = 0; cbChunk > cb && pbMax != pb; ++cb, ++pb ) { *szHex++ = rgchConvert[ *pb >> 4 ]; *szHex++ = rgchConvert[ *pb & 0x0F ]; *szText++ = isprint( *pb ) ? *pb : '.'; } *szHex++ = ' '; } while( ( ( pb - rgbSrc ) % cbWidth ) && pbMax > pb ); while( szHex != sz + cchHexWidth ) { *szHex++ = ' '; } *szText++ = '\n'; *szText = '\0'; sz = szText; } } // ================================================================ LOCAL const CHAR * SzEDBGHexDump( const VOID * const pv, const INT cb ) // ================================================================ // // WARNING: not multi-threaded safe // { static CHAR rgchBuf[1024]; rgchBuf[0] = '\n'; rgchBuf[1] = '\0'; if( NULL == pv ) { if( fVerbose ) { dprintf( "SzEDBGHexDump: NULL pointer\n" ); } return rgchBuf; } DBUTLSprintHex( rgchBuf, (BYTE *)pv, cb, cb + 1, 4, 0, 0 ); return rgchBuf; } // ================================================================ DEBUG_EXT( EDBGDump ) // ================================================================ { if( argc < 2 ) { EDBGHelpDump( hCurrentProcess, hCurrentThread, dwCurrentPc, lpExtensionApis, argc, argv ); return; } for( int icdumpmap = 0; icdumpmap < ccdumpmap; ++icdumpmap ) { if( FArgumentMatch( argv[0], rgcdumpmap[icdumpmap].szCommand ) ) { (rgcdumpmap[icdumpmap].pcdump)->Dump( hCurrentProcess, hCurrentThread, dwCurrentPc, lpExtensionApis, argc - 1, argv + 1 ); return; } } EDBGHelpDump( hCurrentProcess, hCurrentThread, dwCurrentPc, lpExtensionApis, argc, argv ); } // ================================================================ DEBUG_EXT( EDBGHelp ) // ================================================================ { for( int ifuncmap = 0; ifuncmap < cfuncmap; ifuncmap++ ) { dprintf( "\t%s\n", rgfuncmap[ifuncmap].szHelp ); } } // ================================================================ DEBUG_EXT( EDBGHelpDump ) // ================================================================ { dprintf( "Supported objects:\n\n" ); for( int icdumpmap = 0; icdumpmap < ccdumpmap; icdumpmap++ ) { dprintf( "\t%s\n", rgcdumpmap[icdumpmap].szHelp ); } } // ================================================================ DEBUG_EXT( EDBGVerbose ) // ================================================================ { if( fVerbose ) { dprintf( "Changing to non-verbose mode....\n" ); fVerbose = fFalse; } else { dprintf( "Changing to verbose mode....\n" ); fVerbose = fTrue; } } #ifdef SYNC_DEADLOCK_DETECTION // ================================================================ DEBUG_EXT( EDBGLocks ) // ================================================================ { DWORD tid = 0; if ( argc > 1 || ( argc == 1 && !FUlFromArg( argv[0], &tid, 10 ) ) ) { printf( "Usage: Locks []" ); return; } BOOL fFoundLock = fFalse; CLS* pcls = (CLS*)GetExpression( "ESE!pclsSyncGlobal" ); while ( pcls ) { if ( !( pcls = (CLS*)PvReadDBGMemory( DWORD( pcls ), sizeof( CLS ) ) ) ) { dprintf( "An error occurred while scanning Thread IDs.\n" ); break; } if ( fVerbose ) { dprintf( "Inspecting TLS for TID %d...\n", pcls->dwContextId ); } if ( !tid || pcls->dwContextId == tid ) { COwner* powner = pcls->pownerLockHead; while ( powner ) { if ( !( powner = (COwner*)PvReadDBGMemory( DWORD( powner ), sizeof( COwner ) ) ) ) { dprintf( "An error occurred while scanning the lock chain for Thread ID %d.\n", pcls->dwContextId ); break; } CLockDeadlockDetectionInfo* plddi = powner->m_plddiOwned; if ( !( plddi = (CLockDeadlockDetectionInfo*)PvReadDBGMemory( DWORD( plddi ), sizeof( CLockDeadlockDetectionInfo ) ) ) ) { dprintf( "An error occurred while scanning the lock chain for Thread ID %d.\n", pcls->dwContextId ); break; } const CLockBasicInfo* plbi = &plddi->Info(); if ( !( plbi = (CLockBasicInfo*)PvReadDBGMemory( DWORD( plbi ), sizeof( CLockBasicInfo ) ) ) ) { dprintf( "An error occurred while scanning the lock chain for Thread ID %d.\n", pcls->dwContextId ); break; } const int cbTypeNameMax = 256; char* pszTypeName = (char*)plbi->SzTypeName(); if ( !( pszTypeName = (char*)PvReadDBGMemory( DWORD( pszTypeName ), cbTypeNameMax ) ) ) { dprintf( "An error occurred while scanning the lock chain for Thread ID %d.\n", pcls->dwContextId ); break; } pszTypeName[ cbTypeNameMax - 1 ] = '\0'; const int cbInstanceNameMax = 256; char* pszInstanceName = (char*)plbi->SzInstanceName(); if ( !( pszInstanceName = (char*)PvReadDBGMemory( DWORD( pszInstanceName ), cbInstanceNameMax ) ) ) { dprintf( "An error occurred while scanning the lock chain for Thread ID %d.\n", pcls->dwContextId ); break; } pszInstanceName[ cbInstanceNameMax - 1 ] = '\0'; fFoundLock = fTrue; dprintf( "TID %d owns %s 0x%08x ( \"%s\", %d, %d )", pcls->dwContextId, pszTypeName, plbi->Instance(), pszInstanceName, plbi->Rank(), plbi->SubRank() ); if ( powner->m_group != -1 ) { dprintf( " as Group %d", powner->m_group ); } dprintf( ".\n" ); FreeDBGMemory( pszInstanceName ); FreeDBGMemory( pszTypeName ); FreeDBGMemory( (void*)plbi ); FreeDBGMemory( plddi ); COwner* pownerToFree = powner; powner = powner->m_pownerLockNext; FreeDBGMemory( pownerToFree ); } } CLS* pclsToFree = pcls; pcls = pcls->pclsNext; FreeDBGMemory( pclsToFree ); } if ( !fFoundLock ) { if ( !tid ) { dprintf( "No threads own any locks.\n" ); } else { dprintf( "This thread does not own any locks.\n" ); } } } #endif // SYNC_DEADLOCK_DETECTION // ================================================================ template< class _STRUCT> VOID CDUMPA<_STRUCT>::Dump( HANDLE hCurrentProcess, HANDLE hCurrentThread, DWORD dwCurrentPc, PWINDBG_EXTENSION_APIS lpExtensionApis, INT argc, const CHAR * const argv[] ) // ================================================================ { DWORD dwAddress; if( 1 != argc || !FUlFromExpr( argv[0], &dwAddress ) ) { printf( "Usage: Dump
" ); return; } _STRUCT * const p = (_STRUCT *)PvReadDBGMemory( dwAddress, sizeof( _STRUCT ) ); if( NULL != p ) { dprintf( "0x%x bytes @ 0x%08x\n", sizeof( _STRUCT ), dwAddress ); if ( sizeof( _STRUCT ) < p->CbEnhancedState() ) { dwAddress = *((ULONG*)p); *((void**)p) = PvReadDBGMemory( dwAddress, p->CbEnhancedState() ); if ( NULL == *((void**)p) ) { FreeDBGMemory( p ); return; } dprintf( "0x%x bytes @ 0x%08x [Enhanced State]\n", p->CbEnhancedState(), dwAddress ); p->Dump( CPRINTFWINDBG::PcprintfInstance(), dwAddress - *((DWORD*)p) ); FreeDBGMemory( *((void**)p) ); } else { p->Dump( CPRINTFWINDBG::PcprintfInstance(), dwAddress - DWORD( p ) ); } FreeDBGMemory( p ); } } VOID __cdecl OSSyncDebuggerExtension( HANDLE hCurrentProcess, HANDLE hCurrentThread, DWORD dwCurrentPc, PWINDBG_EXTENSION_APIS lpExtensionApis, const INT argc, const CHAR * const argv[] ) { ExtensionApis = *lpExtensionApis; // we can't use dprintf until this is set ghDbgProcess = hCurrentProcess; if( argc < 1 ) { EDBGHelp( hCurrentProcess, hCurrentThread, dwCurrentPc, lpExtensionApis, argc, (const CHAR **)argv ); return; } INT ifuncmap; for( ifuncmap = 0; ifuncmap < cfuncmap; ifuncmap++ ) { if( FArgumentMatch( argv[0], rgfuncmap[ifuncmap].szCommand ) ) { (rgfuncmap[ifuncmap].function)( hCurrentProcess, hCurrentThread, dwCurrentPc, lpExtensionApis, argc - 1, (const CHAR **)( argv + 1 ) ); return; } } EDBGHelp( hCurrentProcess, hCurrentThread, dwCurrentPc, lpExtensionApis, argc, (const CHAR **)argv ); } // class and member size and offset functions #define sizeof_class_bits( c ) ( sizeof( c ) * 8 ) #define sizeof_class_bytes( c ) ( sizeof( c ) ) LOCAL void* pvGS; #define sizeof_member_bits( c, m ) ( \ ( (c *)( pvGS = calloc( 1, sizeof_class_bytes( c ) ) ) )->m = -1, \ _sizeof_member_bits( (char*) pvGS, sizeof_class_bytes( c ) ) ) LOCAL inline int _sizeof_member_bits( char* pb, int cb ) { for ( int ib = 0; pb[ib] == 0; ib++ ); for ( int off = ib * 8, mask = 1; ( pb[ib] & mask ) == 0; off++, mask <<= 1 ); for ( int ib2 = cb - 1; pb[ib2] == 0; ib2-- ); for ( int off2 = ib2 * 8 + 7, mask2 = 128; ( pb[ib2] & mask2 ) == 0; off2--, mask2 >>= 1 ); free( pb ); return off2 - off + 1; } #define sizeof_member_bytes( c, m ) ( ( sizeof_member_bits( c, m ) + 7 ) / 8 ) LOCAL void* pvGO; #define offsetof_member_bits_abs( c, m ) ( \ ( (c *)( pvGO = calloc( 1, sizeof_class_bytes( c ) ) ) )->m = -1, \ _offsetof_member_bits_abs( (char*) pvGO, sizeof_class_bytes( c ) ) ) LOCAL inline int _offsetof_member_bits_abs( char* pb, int cb ) { for ( int ib = 0; pb[ib] == 0; ib++ ); for ( int off = ib * 8, mask = 1; ( pb[ib] & mask ) == 0; off++, mask <<= 1 ); free( pb ); return off; } #define offsetof_member_bits( c, m ) ( _offsetof_member_bits( offsetof_member_bits_abs( c, m ), sizeof_member_bits( c, m ) ) ) LOCAL inline int _offsetof_member_bits( int ibit, int cbit ) { for ( int cbit2 = 8; cbit2 < cbit; cbit2 <<= 1 ); return ibit % cbit2; } #define offsetof_member_bytes( c, m ) ( ( offsetof_member_bits_abs( c, m ) - offsetof_member_bits( c, m ) ) / 8 ) #define is_member_bitfield( c, m ) ( offsetof_member_bits( c, m ) != 0 || sizeof_member_bytes( c, m ) * 8 > sizeof_member_bits( c, m ) ) // member dumping functions #include #define SYMBOL_LEN_MAX 15 #define VOID_CB_DUMP 8 #define FORMAT_VOID( CLASS, pointer, member, offset ) \ "\t%*.*s <0x%08x ,%3i> %s", \ SYMBOL_LEN_MAX, \ SYMBOL_LEN_MAX, \ #member, \ (char*)pointer + offset + offsetof( CLASS, member ), \ sizeof( (pointer)->member ), \ SzEDBGHexDump( (VOID *)(&((pointer)->member)), ( VOID_CB_DUMP > sizeof( (pointer)->member ) ) ? sizeof( (pointer)->member ) : VOID_CB_DUMP ) #define FORMAT_POINTER( CLASS, pointer, member, offset ) \ "\t%*.*s <0x%08x ,%3i>: 0x%08x\n", \ SYMBOL_LEN_MAX, \ SYMBOL_LEN_MAX, \ #member, \ (char*)pointer + offset + offsetof( CLASS, member ), \ sizeof( (pointer)->member ), \ (pointer)->member #define FORMAT_INT( CLASS, pointer, member, offset ) \ "\t%*.*s <0x%08x ,%3i>: %I64i (0x%0*I64x)\n", \ SYMBOL_LEN_MAX, \ SYMBOL_LEN_MAX, \ #member, \ (char*)pointer + offset + offsetof( CLASS, member ), \ sizeof( (pointer)->member ), \ (QWORD)(pointer)->member, \ sizeof( (pointer)->member ) * 2, \ (QWORD)(pointer)->member & ( ( (QWORD)1 << ( sizeof( (pointer)->member ) * 8 ) ) - 1 ) #define FORMAT_UINT( CLASS, pointer, member, offset ) \ "\t%*.*s <0x%08x ,%3i>: %I64u (0x%0*I64x)\n", \ SYMBOL_LEN_MAX, \ SYMBOL_LEN_MAX, \ #member, \ (char*)pointer + offset + offsetof( CLASS, member ), \ sizeof( (pointer)->member ), \ (QWORD)(pointer)->member, \ sizeof( (pointer)->member ) * 2, \ (QWORD)(pointer)->member & ( ( (QWORD)1 << ( sizeof( (pointer)->member ) * 8 ) ) - 1 ) #define FORMAT_BOOL( CLASS, pointer, member, offset ) \ "\t%*.*s <0x%08x ,%3i>: %s (0x%08x)\n", \ SYMBOL_LEN_MAX, \ SYMBOL_LEN_MAX, \ #member, \ (char*)pointer + offset + offsetof( CLASS, member ), \ sizeof( (pointer)->member ), \ (pointer)->member ? \ "fTrue" : \ "fFalse", \ (pointer)->member #define FORMAT_INT_BF( CLASS, pointer, member, offset ) \ "\t%*.*s <0x%08x%s%-2.*i,%3i>: %I64i (0x%0*I64x)\n", \ SYMBOL_LEN_MAX, \ SYMBOL_LEN_MAX, \ #member, \ (char*)pointer + offset + offsetof_member_bytes( CLASS, member ), \ is_member_bitfield( CLASS, member ) ? \ ":" : \ " ", \ is_member_bitfield( CLASS, member ) ? \ 1 : \ 0, \ offsetof_member_bits( CLASS, member ), \ is_member_bitfield( CLASS, member ) ? \ sizeof_member_bits( CLASS, member ) : \ sizeof_member_bytes( CLASS, member ), \ (QWORD)(pointer)->member, \ ( sizeof_member_bits( CLASS, member ) + 3 ) / 4, \ (QWORD)(pointer)->member & ( ( (QWORD)1 << sizeof_member_bits( CLASS, member ) ) - 1 ) #define FORMAT_UINT_BF( CLASS, pointer, member, offset ) \ "\t%*.*s <0x%08x%s%-2.*i,%3i>: %I64u (0x%0*I64x)\n", \ SYMBOL_LEN_MAX, \ SYMBOL_LEN_MAX, \ #member, \ (char*)pointer + offset + offsetof_member_bytes( CLASS, member ), \ is_member_bitfield( CLASS, member ) ? \ ":" : \ " ", \ is_member_bitfield( CLASS, member ) ? \ 1 : \ 0, \ offsetof_member_bits( CLASS, member ), \ is_member_bitfield( CLASS, member ) ? \ sizeof_member_bits( CLASS, member ) : \ sizeof_member_bytes( CLASS, member ), \ (QWORD)(pointer)->member, \ ( sizeof_member_bits( CLASS, member ) + 3 ) / 4, \ (QWORD)(pointer)->member & ( ( (QWORD)1 << sizeof_member_bits( CLASS, member ) ) - 1 ) #define FORMAT_BOOL_BF( CLASS, pointer, member, offset ) \ "\t%*.*s <0x%08x%s%-2.*i,%3i>: %s (0x%0*I64x)\n", \ SYMBOL_LEN_MAX, \ SYMBOL_LEN_MAX, \ #member, \ (char*)pointer + offset + offsetof_member_bytes( CLASS, member ), \ is_member_bitfield( CLASS, member ) ? \ ":" : \ " ", \ is_member_bitfield( CLASS, member ) ? \ 1 : \ 0, \ offsetof_member_bits( CLASS, member ), \ is_member_bitfield( CLASS, member ) ? \ sizeof_member_bits( CLASS, member ) : \ sizeof_member_bytes( CLASS, member ), \ (pointer)->member ? \ "fTrue" : \ "fFalse", \ ( sizeof_member_bits( CLASS, member ) + 3 ) / 4, \ (QWORD)(pointer)->member & ( ( (QWORD)1 << sizeof_member_bits( CLASS, member ) ) - 1 ) #endif // DEBUGGER_EXTENSION void CSyncBasicInfo::Dump( CPRINTFSYNC* pcprintf, DWORD dwOffset ) { #if defined( DEBUGGER_EXTENSION ) || defined( SYNC_ANALYZE_PERFORMANCE ) #ifdef SYNC_ENHANCED_STATE (*pcprintf)( FORMAT_POINTER( CSyncBasicInfo, this, m_szInstanceName, dwOffset ) ); (*pcprintf)( FORMAT_POINTER( CSyncBasicInfo, this, m_szTypeName, dwOffset ) ); (*pcprintf)( FORMAT_POINTER( CSyncBasicInfo, this, m_psyncobj, dwOffset ) ); #endif // SYNC_ENHANCED_STATE #endif // DEBUGGER_EXTENSION || SYNC_ANALYZE_PERFORMANCE } void CSyncPerfWait::Dump( CPRINTFSYNC* pcprintf, DWORD dwOffset ) { #if defined( DEBUGGER_EXTENSION ) || defined( SYNC_ANALYZE_PERFORMANCE ) #ifdef SYNC_ANALYZE_PERFORMANCE (*pcprintf)( FORMAT_UINT( CSyncPerfWait, this, m_cWait, dwOffset ) ); (*pcprintf)( FORMAT_UINT( CSyncPerfWait, this, m_qwHRTWaitElapsed, dwOffset ) ); #endif // SYNC_ANALYZE_PERFORMANCE #endif // DEBUGGER_EXTENSION || SYNC_ANALYZE_PERFORMANCE } void CKernelSemaphoreState::Dump( CPRINTFSYNC* pcprintf, DWORD dwOffset ) { #if defined( DEBUGGER_EXTENSION ) || defined( SYNC_ANALYZE_PERFORMANCE ) (*pcprintf)( FORMAT_UINT( CKernelSemaphoreState, this, m_handle, dwOffset ) ); #endif // DEBUGGER_EXTENSION || SYNC_ANALYZE_PERFORMANCE } void CSyncPerfAcquire::Dump( CPRINTFSYNC* pcprintf, DWORD dwOffset ) { #if defined( DEBUGGER_EXTENSION ) || defined( SYNC_ANALYZE_PERFORMANCE ) #ifdef SYNC_ANALYZE_PERFORMANCE (*pcprintf)( FORMAT_UINT( CSyncPerfAcquire, this, m_cAcquire, dwOffset ) ); (*pcprintf)( FORMAT_UINT( CSyncPerfAcquire, this, m_cContend, dwOffset ) ); #endif // SYNC_ANALYZE_PERFORMANCE #endif // DEBUGGER_EXTENSION || SYNC_ANALYZE_PERFORMANCE } void CSemaphoreState::Dump( CPRINTFSYNC* pcprintf, DWORD dwOffset ) { #if defined( DEBUGGER_EXTENSION ) || defined( SYNC_ANALYZE_PERFORMANCE ) if ( FNoWait() ) { (*pcprintf)( FORMAT_UINT( CSemaphoreState, this, m_cAvail, dwOffset ) ); } else { (*pcprintf)( FORMAT_UINT( CSemaphoreState, this, m_irksem, dwOffset ) ); (*pcprintf)( FORMAT_INT( CSemaphoreState, this, m_cWaitNeg, dwOffset ) ); } #endif // DEBUGGER_EXTENSION || SYNC_ANALYZE_PERFORMANCE } void CAutoResetSignalState::Dump( CPRINTFSYNC* pcprintf, DWORD dwOffset ) { #if defined( DEBUGGER_EXTENSION ) || defined( SYNC_ANALYZE_PERFORMANCE ) if ( FNoWait() ) { (*pcprintf)( FORMAT_BOOL( CAutoResetSignalState, this, m_fSet, dwOffset ) ); } else { (*pcprintf)( FORMAT_UINT( CAutoResetSignalState, this, m_irksem, dwOffset ) ); (*pcprintf)( FORMAT_INT( CAutoResetSignalState, this, m_cWaitNeg, dwOffset ) ); } #endif // DEBUGGER_EXTENSION || SYNC_ANALYZE_PERFORMANCE } void CManualResetSignalState::Dump( CPRINTFSYNC* pcprintf, DWORD dwOffset ) { #if defined( DEBUGGER_EXTENSION ) || defined( SYNC_ANALYZE_PERFORMANCE ) if ( FNoWait() ) { (*pcprintf)( FORMAT_BOOL( CManualResetSignalState, this, m_fSet, dwOffset ) ); } else { (*pcprintf)( FORMAT_UINT( CManualResetSignalState, this, m_irksem, dwOffset ) ); (*pcprintf)( FORMAT_INT( CManualResetSignalState, this, m_cWaitNeg, dwOffset ) ); } #endif // DEBUGGER_EXTENSION || SYNC_ANALYZE_PERFORMANCE } void CLockBasicInfo::Dump( CPRINTFSYNC* pcprintf, DWORD dwOffset ) { #if defined( DEBUGGER_EXTENSION ) || defined( SYNC_ANALYZE_PERFORMANCE ) CSyncBasicInfo::Dump( pcprintf, dwOffset ); #ifdef SYNC_DEADLOCK_DETECTION (*pcprintf)( FORMAT_UINT( CLockBasicInfo, this, m_rank, dwOffset ) ); (*pcprintf)( FORMAT_UINT( CLockBasicInfo, this, m_subrank, dwOffset ) ); #endif // SYNC_DEADLOCK_DETECTION #endif // DEBUGGER_EXTENSION || SYNC_ANALYZE_PERFORMANCE } void CLockPerfHold::Dump( CPRINTFSYNC* pcprintf, DWORD dwOffset ) { #if defined( DEBUGGER_EXTENSION ) || defined( SYNC_ANALYZE_PERFORMANCE ) #ifdef SYNC_ANALYZE_PERFORMANCE (*pcprintf)( FORMAT_UINT( CLockPerfHold, this, m_cHold, dwOffset ) ); (*pcprintf)( FORMAT_UINT( CLockPerfHold, this, m_qwHRTHoldElapsed, dwOffset ) ); #endif // SYNC_ANALYZE_PERFORMANCE #endif // DEBUGGER_EXTENSION || SYNC_ANALYZE_PERFORMANCE } void CLockDeadlockDetectionInfo::Dump( CPRINTFSYNC* pcprintf, DWORD dwOffset ) { #if defined( DEBUGGER_EXTENSION ) || defined( SYNC_ANALYZE_PERFORMANCE ) #ifdef SYNC_DEADLOCK_DETECTION (*pcprintf)( FORMAT_POINTER( CLockDeadlockDetectionInfo, this, m_plbiParent, dwOffset ) ); (*pcprintf)( FORMAT_VOID( CLockDeadlockDetectionInfo, this, m_semOwnerList, dwOffset ) ); (*pcprintf)( FORMAT_VOID( CLockDeadlockDetectionInfo, this, m_ownerHead, dwOffset ) ); #endif // SYNC_DEADLOCK_DETECTION #endif // DEBUGGER_EXTENSION || SYNC_ANALYZE_PERFORMANCE } void CCriticalSectionState::Dump( CPRINTFSYNC* pcprintf, DWORD dwOffset ) { #if defined( DEBUGGER_EXTENSION ) || defined( SYNC_ANALYZE_PERFORMANCE ) (*pcprintf)( FORMAT_VOID( CCriticalSectionState, this, m_sem, dwOffset ) ); #endif // DEBUGGER_EXTENSION || SYNC_ANALYZE_PERFORMANCE } void CNestableCriticalSectionState::Dump( CPRINTFSYNC* pcprintf, DWORD dwOffset ) { #if defined( DEBUGGER_EXTENSION ) || defined( SYNC_ANALYZE_PERFORMANCE ) (*pcprintf)( FORMAT_VOID( CNestableCriticalSectionState, this, m_sem, dwOffset ) ); (*pcprintf)( FORMAT_POINTER( CNestableCriticalSectionState, this, m_pclsOwner, dwOffset ) ); (*pcprintf)( FORMAT_UINT( CNestableCriticalSectionState, this, m_cEntry, dwOffset ) ); #endif // DEBUGGER_EXTENSION || SYNC_ANALYZE_PERFORMANCE } void CGateState::Dump( CPRINTFSYNC* pcprintf, DWORD dwOffset ) { #if defined( DEBUGGER_EXTENSION ) || defined( SYNC_ANALYZE_PERFORMANCE ) (*pcprintf)( FORMAT_INT( CGateState, this, m_cWait, dwOffset ) ); (*pcprintf)( FORMAT_UINT( CGateState, this, m_irksem, dwOffset ) ); #endif // DEBUGGER_EXTENSION || SYNC_ANALYZE_PERFORMANCE } void CBinaryLockState::Dump( CPRINTFSYNC* pcprintf, DWORD dwOffset ) { #if defined( DEBUGGER_EXTENSION ) || defined( SYNC_ANALYZE_PERFORMANCE ) (*pcprintf)( FORMAT_UINT( CBinaryLockState, this, m_cw, dwOffset ) ); (*pcprintf)( FORMAT_UINT_BF( CBinaryLockState, this, m_cOOW1, dwOffset ) ); (*pcprintf)( FORMAT_BOOL_BF( CBinaryLockState, this, m_fQ1, dwOffset ) ); (*pcprintf)( FORMAT_UINT_BF( CBinaryLockState, this, m_cOOW2, dwOffset ) ); (*pcprintf)( FORMAT_BOOL_BF( CBinaryLockState, this, m_fQ2, dwOffset ) ); (*pcprintf)( FORMAT_UINT( CBinaryLockState, this, m_cOwner, dwOffset ) ); (*pcprintf)( FORMAT_VOID( CBinaryLockState, this, m_sem1, dwOffset ) ); (*pcprintf)( FORMAT_VOID( CBinaryLockState, this, m_sem2, dwOffset ) ); #endif // DEBUGGER_EXTENSION || SYNC_ANALYZE_PERFORMANCE } void CReaderWriterLockState::Dump( CPRINTFSYNC* pcprintf, DWORD dwOffset ) { #if defined( DEBUGGER_EXTENSION ) || defined( SYNC_ANALYZE_PERFORMANCE ) (*pcprintf)( FORMAT_UINT( CReaderWriterLockState, this, m_cw, dwOffset ) ); (*pcprintf)( FORMAT_UINT_BF( CReaderWriterLockState, this, m_cOAOWW, dwOffset ) ); (*pcprintf)( FORMAT_BOOL_BF( CReaderWriterLockState, this, m_fQW, dwOffset ) ); (*pcprintf)( FORMAT_UINT_BF( CReaderWriterLockState, this, m_cOOWR, dwOffset ) ); (*pcprintf)( FORMAT_BOOL_BF( CReaderWriterLockState, this, m_fQR, dwOffset ) ); (*pcprintf)( FORMAT_UINT( CReaderWriterLockState, this, m_cOwner, dwOffset ) ); (*pcprintf)( FORMAT_VOID( CReaderWriterLockState, this, m_semWriter, dwOffset ) ); (*pcprintf)( FORMAT_VOID( CReaderWriterLockState, this, m_semReader, dwOffset ) ); #endif // DEBUGGER_EXTENSION || SYNC_ANALYZE_PERFORMANCE } void CAutoResetSignal::Dump( CPRINTFSYNC* pcprintf, DWORD dwOffset ) { #if defined( DEBUGGER_EXTENSION ) || defined( SYNC_ANALYZE_PERFORMANCE ) State().CAutoResetSignalState::Dump( pcprintf, dwOffset ); State().CSyncComplexPerfInfo::Dump( pcprintf, dwOffset ); #endif // DEBUGGER_EXTENSION || SYNC_ANALYZE_PERFORMANCE } void CBinaryLock::Dump( CPRINTFSYNC* pcprintf, DWORD dwOffset ) { #if defined( DEBUGGER_EXTENSION ) || defined( SYNC_ANALYZE_PERFORMANCE ) State().CBinaryLockState::Dump( pcprintf, dwOffset ); State().CGroupLockComplexInfo< 2 >::Dump( pcprintf, dwOffset ); #endif // DEBUGGER_EXTENSION || SYNC_ANALYZE_PERFORMANCE } void CCriticalSection::Dump( CPRINTFSYNC* pcprintf, DWORD dwOffset ) { #if defined( DEBUGGER_EXTENSION ) || defined( SYNC_ANALYZE_PERFORMANCE ) State().CCriticalSectionState::Dump( pcprintf, dwOffset ); State().CLockSimpleInfo::Dump( pcprintf, dwOffset ); #endif // DEBUGGER_EXTENSION || SYNC_ANALYZE_PERFORMANCE } void CGate::Dump( CPRINTFSYNC* pcprintf, DWORD dwOffset ) { #if defined( DEBUGGER_EXTENSION ) || defined( SYNC_ANALYZE_PERFORMANCE ) State().CGateState::Dump( pcprintf, dwOffset ); State().CSyncSimplePerfInfo::Dump( pcprintf, dwOffset ); #endif // DEBUGGER_EXTENSION || SYNC_ANALYZE_PERFORMANCE } void CKernelSemaphore::Dump( CPRINTFSYNC* pcprintf, DWORD dwOffset ) { #if defined( DEBUGGER_EXTENSION ) || defined( SYNC_ANALYZE_PERFORMANCE ) State().CKernelSemaphoreState::Dump( pcprintf, dwOffset ); State().CSyncSimplePerfInfo::Dump( pcprintf, dwOffset ); #endif // DEBUGGER_EXTENSION || SYNC_ANALYZE_PERFORMANCE } void CManualResetSignal::Dump( CPRINTFSYNC* pcprintf, DWORD dwOffset ) { #if defined( DEBUGGER_EXTENSION ) || defined( SYNC_ANALYZE_PERFORMANCE ) State().CManualResetSignalState::Dump( pcprintf, dwOffset ); State().CSyncComplexPerfInfo::Dump( pcprintf, dwOffset ); #endif // DEBUGGER_EXTENSION || SYNC_ANALYZE_PERFORMANCE } void CNestableCriticalSection::Dump( CPRINTFSYNC* pcprintf, DWORD dwOffset ) { #if defined( DEBUGGER_EXTENSION ) || defined( SYNC_ANALYZE_PERFORMANCE ) State().CNestableCriticalSectionState::Dump( pcprintf, dwOffset ); State().CLockSimpleInfo::Dump( pcprintf, dwOffset ); #endif // DEBUGGER_EXTENSION || SYNC_ANALYZE_PERFORMANCE } void CReaderWriterLock::Dump( CPRINTFSYNC* pcprintf, DWORD dwOffset ) { #if defined( DEBUGGER_EXTENSION ) || defined( SYNC_ANALYZE_PERFORMANCE ) State().CReaderWriterLockState::Dump( pcprintf, dwOffset ); State().CGroupLockComplexInfo< 2 >::Dump( pcprintf, dwOffset ); #endif // DEBUGGER_EXTENSION || SYNC_ANALYZE_PERFORMANCE } void CSemaphore::Dump( CPRINTFSYNC* pcprintf, DWORD dwOffset ) { #if defined( DEBUGGER_EXTENSION ) || defined( SYNC_ANALYZE_PERFORMANCE ) State().CSemaphoreState::Dump( pcprintf, dwOffset ); State().CSyncComplexPerfInfo::Dump( pcprintf, dwOffset ); #endif // DEBUGGER_EXTENSION || SYNC_ANALYZE_PERFORMANCE } // Define Stuff for sync.hxx void AssertFail( const _TCHAR* szMessage, const _TCHAR* szFilename, long lLine ) { #ifdef DEBUG DebugBreak(); #endif return; } void EnforceFail( const _TCHAR* szMessage, const _TCHAR* szFilename, long lLine ) { #ifdef DEBUG DebugBreak(); #endif return; }