658 lines
12 KiB
C++
658 lines
12 KiB
C++
|
/*++
|
||
|
|
||
|
Copyright (c) 1994 Microsoft Corporation
|
||
|
All rights reserved.
|
||
|
|
||
|
Module Name:
|
||
|
|
||
|
State.hxx
|
||
|
|
||
|
Abstract:
|
||
|
|
||
|
Handles state objects and critical sections.
|
||
|
|
||
|
|
||
|
Author:
|
||
|
|
||
|
Albert Ting (AlbertT) 28-May-1994
|
||
|
|
||
|
Revision History:
|
||
|
|
||
|
--*/
|
||
|
|
||
|
#ifndef _STATE_HXX
|
||
|
#define _STATE_HXX
|
||
|
|
||
|
typedef DWORD STATEVAR;
|
||
|
|
||
|
/********************************************************************
|
||
|
|
||
|
State object (DWORD) for bitfields.
|
||
|
|
||
|
********************************************************************/
|
||
|
|
||
|
class TState {
|
||
|
|
||
|
SIGNATURE( 'stat' )
|
||
|
ALWAYS_VALID
|
||
|
SAFE_NEW
|
||
|
|
||
|
public:
|
||
|
|
||
|
#if DBG
|
||
|
TState( VOID );
|
||
|
TState( STATEVAR StateVar );
|
||
|
|
||
|
~TState( VOID );
|
||
|
|
||
|
STATEVAR operator|=( STATEVAR StateVarOn );
|
||
|
STATEVAR operator&=( STATEVAR StateVarMask );
|
||
|
|
||
|
STATEVAR operator|=( INT iStateVarOn )
|
||
|
{ return operator|=( (STATEVAR)iStateVarOn ); }
|
||
|
|
||
|
STATEVAR operator&=( INT iStateVarMask )
|
||
|
{ return operator&=( (STATEVAR)iStateVarMask ); }
|
||
|
|
||
|
#else
|
||
|
TState(VOID) : _StateVar(0)
|
||
|
{ }
|
||
|
|
||
|
TState(STATEVAR StateVar) : _StateVar( StateVar )
|
||
|
{ }
|
||
|
|
||
|
~TState(VOID)
|
||
|
{ }
|
||
|
|
||
|
STATEVAR operator|=( STATEVAR StateVarOn )
|
||
|
{ return _StateVar |= StateVarOn; }
|
||
|
|
||
|
STATEVAR operator&=( STATEVAR StateVarMask )
|
||
|
{ return _StateVar &= StateVarMask; }
|
||
|
|
||
|
STATEVAR operator|=( INT iStateVarOn )
|
||
|
{ return operator|=( (STATEVAR)iStateVarOn ); }
|
||
|
|
||
|
STATEVAR operator&=( INT iStateVarMask )
|
||
|
{ return operator&=( (STATEVAR)iStateVarMask ); }
|
||
|
#endif
|
||
|
|
||
|
BOOL bBit( STATEVAR StateVarBit )
|
||
|
{ return _StateVar & StateVarBit ? TRUE : FALSE; }
|
||
|
|
||
|
STATEVAR operator&( TState& State )
|
||
|
{ return _StateVar & State._StateVar; }
|
||
|
|
||
|
STATEVAR operator=( STATEVAR StateVarNew )
|
||
|
{ return _StateVar = StateVarNew; }
|
||
|
|
||
|
TState& operator=( const TState& StateNew )
|
||
|
{
|
||
|
_StateVar = StateNew._StateVar;
|
||
|
return *this;
|
||
|
}
|
||
|
|
||
|
operator STATEVAR() const
|
||
|
{ return _StateVar; }
|
||
|
|
||
|
private:
|
||
|
|
||
|
STATEVAR _StateVar;
|
||
|
|
||
|
#if DBG
|
||
|
|
||
|
TBackTraceMem _BackTrace;
|
||
|
|
||
|
virtual BOOL bValidateState() const
|
||
|
{ return TRUE; }
|
||
|
|
||
|
virtual BOOL bValidateSet(STATEVAR StateVarOn) const
|
||
|
{
|
||
|
UNREFERENCED_PARAMETER( StateVarOn );
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
virtual BOOL bValidateMask(STATEVAR StateVarMask) const
|
||
|
{
|
||
|
UNREFERENCED_PARAMETER( StateVarMask );
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
#endif
|
||
|
};
|
||
|
|
||
|
|
||
|
/********************************************************************
|
||
|
|
||
|
Critical section implementation
|
||
|
|
||
|
Avoid using vEnter and vLeave, since there may be a codepath
|
||
|
that forgets to call one or the other. Instead, use
|
||
|
TCritSec{Hard}Lock and TCritSecUnlock.
|
||
|
|
||
|
MCritSec:
|
||
|
|
||
|
This is the basic critical section. It has vEnter and vLeave,
|
||
|
but these should be used sparingly--use TCritSec*Lock instead.
|
||
|
|
||
|
TCritSecLock:
|
||
|
|
||
|
Used to acquire a critical section for the lifetime of the
|
||
|
TCritSecLock object.
|
||
|
|
||
|
{
|
||
|
foo();
|
||
|
|
||
|
{
|
||
|
TCritSecLock CSL( CritSec ); // CritSec acquired here.
|
||
|
|
||
|
bar();
|
||
|
} // CritSec released here since
|
||
|
// CSL is destroyed.
|
||
|
}
|
||
|
|
||
|
-----------------------------------------------------------------
|
||
|
|
||
|
TCritSecUnlock:
|
||
|
|
||
|
Used to release an acquired critical section for the lifetime of
|
||
|
the TCritSecUnlock object.
|
||
|
|
||
|
Note: The critical section _must_ already be acquired.
|
||
|
|
||
|
{
|
||
|
TCritSecLock CSL( CritSec );
|
||
|
|
||
|
while( bDoIt ){
|
||
|
|
||
|
{
|
||
|
TCritSecLock CSU( CritSec );
|
||
|
Sleep( 10000 );
|
||
|
}
|
||
|
|
||
|
vTouchProtectedData();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
-----------------------------------------------------------------
|
||
|
|
||
|
TCritSecHardLock:
|
||
|
|
||
|
** Acts same as TCritSecLock on free builds, but on CHK builds it
|
||
|
** asserts if lock is freed while lock is held.
|
||
|
|
||
|
{
|
||
|
TCritSecLock CSL( CritSec ); // <- *** (A)
|
||
|
|
||
|
TBar *pBar = gData.pBar;
|
||
|
|
||
|
foo();
|
||
|
|
||
|
pBar->UseMe();
|
||
|
}
|
||
|
|
||
|
In the above snippet, the author wants the entire block to be
|
||
|
within the CritSec. However, he/she didn't realize foo leaves
|
||
|
CritSec because it needs to hit the net--and in doing so, gData.pBar
|
||
|
may have been deleted/changed by another thread. For example,
|
||
|
foo() may do the following:
|
||
|
|
||
|
VOID
|
||
|
foo(
|
||
|
VOID
|
||
|
)
|
||
|
{
|
||
|
{
|
||
|
TCritSecUnlock( CritSec );
|
||
|
vLongOperationOverNet(); // <- BUG! Callee assumes
|
||
|
// CritSec _not_ left!
|
||
|
}
|
||
|
//
|
||
|
// Back inside CritSec.
|
||
|
//
|
||
|
}
|
||
|
|
||
|
To catch this in debug builds, (A) above should use TCritSecHardLock.
|
||
|
This will assert when we try and leave CritSec. Care must still
|
||
|
be taken, however, since leaving CritSec may be a rare codepath
|
||
|
and not hit during testing.
|
||
|
|
||
|
-----------------------------------------------------------------
|
||
|
|
||
|
Note: TCrit*Lock uses space to store the critical section. In
|
||
|
cases where this is known or already stored somewhere, this is
|
||
|
wasted space. We should change this to use templates to avoid
|
||
|
this extra reference.
|
||
|
|
||
|
********************************************************************/
|
||
|
|
||
|
class MCritSec;
|
||
|
|
||
|
class TCritSecLock {
|
||
|
|
||
|
SIGNATURE( 'cslk' )
|
||
|
ALWAYS_VALID
|
||
|
SAFE_NEW
|
||
|
|
||
|
public:
|
||
|
|
||
|
TCritSecLock(
|
||
|
MCritSec& CritSec
|
||
|
);
|
||
|
~TCritSecLock(
|
||
|
VOID
|
||
|
);
|
||
|
|
||
|
private:
|
||
|
|
||
|
MCritSec& _CritSec;
|
||
|
};
|
||
|
|
||
|
|
||
|
class TCritSecUnlock {
|
||
|
|
||
|
SIGNATURE( 'csul' )
|
||
|
ALWAYS_VALID
|
||
|
SAFE_NEW
|
||
|
|
||
|
public:
|
||
|
|
||
|
TCritSecUnlock(
|
||
|
MCritSec& CritSec
|
||
|
);
|
||
|
~TCritSecUnlock(
|
||
|
VOID
|
||
|
);
|
||
|
|
||
|
private:
|
||
|
|
||
|
MCritSec& _CritSec;
|
||
|
|
||
|
};
|
||
|
|
||
|
|
||
|
class TCritSecHardLock {
|
||
|
friend MCritSec;
|
||
|
|
||
|
SIGNATURE( 'cshl' )
|
||
|
ALWAYS_VALID
|
||
|
SAFE_NEW
|
||
|
|
||
|
public:
|
||
|
|
||
|
TCritSecHardLock(
|
||
|
MCritSec& CritSec
|
||
|
);
|
||
|
~TCritSecHardLock(
|
||
|
VOID
|
||
|
);
|
||
|
|
||
|
private:
|
||
|
|
||
|
MCritSec& _CritSec;
|
||
|
|
||
|
#if DBG
|
||
|
DWORD _dwEntryCountMarker;
|
||
|
DLINK( TCritSecHardLock, CritSecHardLock );
|
||
|
#endif
|
||
|
};
|
||
|
|
||
|
|
||
|
/********************************************************************
|
||
|
|
||
|
MCritSec
|
||
|
|
||
|
********************************************************************/
|
||
|
|
||
|
class MCritSec {
|
||
|
friend TDebugExt;
|
||
|
friend TCritSecHardLock;
|
||
|
|
||
|
SIGNATURE( 'crsc' )
|
||
|
ALWAYS_VALID
|
||
|
SAFE_NEW
|
||
|
|
||
|
public:
|
||
|
|
||
|
#if DBG
|
||
|
MCritSec();
|
||
|
~MCritSec();
|
||
|
|
||
|
VOID vEnter();
|
||
|
VOID vLeave();
|
||
|
|
||
|
BOOL bInside() const;
|
||
|
BOOL bOutside() const;
|
||
|
|
||
|
#else
|
||
|
|
||
|
MCritSec()
|
||
|
{ InitializeCriticalSection(&_CritSec); }
|
||
|
|
||
|
~MCritSec()
|
||
|
{ DeleteCriticalSection(&_CritSec); }
|
||
|
|
||
|
VOID vEnter()
|
||
|
{ EnterCriticalSection(&_CritSec); }
|
||
|
|
||
|
VOID vLeave()
|
||
|
{ LeaveCriticalSection(&_CritSec); }
|
||
|
#endif
|
||
|
|
||
|
private:
|
||
|
|
||
|
CRITICAL_SECTION _CritSec;
|
||
|
|
||
|
#if DBG
|
||
|
//
|
||
|
// Current owner, entry count, and tickcount at entry time. These
|
||
|
// values change for each entrance.
|
||
|
//
|
||
|
DWORD _dwThreadOwner;
|
||
|
DWORD _dwEntryCount;
|
||
|
DWORD _dwTickCountEntered;
|
||
|
|
||
|
//
|
||
|
// Statistics gathering.
|
||
|
//
|
||
|
DWORD _dwTickCountBlockedTotal;
|
||
|
DWORD _dwTickCountInsideTotal;
|
||
|
DWORD _dwEntryCountTotal;
|
||
|
|
||
|
DLINK_BASE( TCritSecHardLock, CritSecHardLock, CritSecHardLock );
|
||
|
|
||
|
TBackTraceMem _BackTrace;
|
||
|
#endif
|
||
|
};
|
||
|
|
||
|
|
||
|
/********************************************************************
|
||
|
|
||
|
MRef and TRefLock: Locking objects used for ref counting.
|
||
|
|
||
|
class TFoo : public MRef, public MGenWin {
|
||
|
...
|
||
|
};
|
||
|
|
||
|
class TUserOfFoo {
|
||
|
private:
|
||
|
|
||
|
REF_LOCK( TFoo, Foo );
|
||
|
...
|
||
|
};
|
||
|
|
||
|
//
|
||
|
// Acquires lock on foo by initialization.
|
||
|
//
|
||
|
TUserOfFoo( TFoo* pFoo ) : RL_Foo( pFoo ) {}
|
||
|
|
||
|
//
|
||
|
// During destruction of TUserOfFoo, the reference count
|
||
|
// of pFoo is automatically decremented.
|
||
|
//
|
||
|
|
||
|
//
|
||
|
// Acquires lock on foo after initialization.
|
||
|
//
|
||
|
vGetLock()
|
||
|
{
|
||
|
Foo.vAcquire( pFoo1 );
|
||
|
...
|
||
|
|
||
|
//
|
||
|
// Get the pFoo pointer from RLFoo.
|
||
|
//
|
||
|
pFoo1 = pFoo();
|
||
|
|
||
|
Foo.vRelease( pFoo1 );
|
||
|
}
|
||
|
|
||
|
********************************************************************/
|
||
|
|
||
|
inline
|
||
|
TCritSecLock::
|
||
|
TCritSecLock(
|
||
|
MCritSec& CritSec
|
||
|
) : _CritSec( CritSec )
|
||
|
{
|
||
|
_CritSec.vEnter();
|
||
|
}
|
||
|
|
||
|
inline
|
||
|
TCritSecLock::
|
||
|
~TCritSecLock(
|
||
|
VOID
|
||
|
)
|
||
|
{
|
||
|
_CritSec.vLeave();
|
||
|
}
|
||
|
|
||
|
inline
|
||
|
TCritSecUnlock::
|
||
|
TCritSecUnlock(
|
||
|
MCritSec& CritSec
|
||
|
) : _CritSec( CritSec )
|
||
|
{
|
||
|
_CritSec.vLeave();
|
||
|
SPLASSERT( _CritSec.bOutside( ));
|
||
|
}
|
||
|
|
||
|
inline
|
||
|
TCritSecUnlock::
|
||
|
~TCritSecUnlock( VOID )
|
||
|
{
|
||
|
_CritSec.vEnter();
|
||
|
}
|
||
|
|
||
|
#if !DBG
|
||
|
|
||
|
inline
|
||
|
TCritSecHardLock::
|
||
|
TCritSecHardLock(
|
||
|
MCritSec& CritSec
|
||
|
) : _CritSec( CritSec )
|
||
|
{
|
||
|
_CritSec.vEnter();
|
||
|
}
|
||
|
|
||
|
inline
|
||
|
TCritSecHardLock::
|
||
|
~TCritSecHardLock(
|
||
|
VOID
|
||
|
)
|
||
|
{
|
||
|
_CritSec.vLeave();
|
||
|
}
|
||
|
|
||
|
#endif
|
||
|
|
||
|
class TRefLock;
|
||
|
|
||
|
/************************************************************
|
||
|
|
||
|
VRef: virtual base class for all ref counting.
|
||
|
|
||
|
************************************************************/
|
||
|
|
||
|
class VRef {
|
||
|
|
||
|
SAFE_NEW
|
||
|
|
||
|
public:
|
||
|
|
||
|
virtual ~VRef()
|
||
|
{ }
|
||
|
|
||
|
//
|
||
|
// Virtuals that must be overwritten by the reference classes.
|
||
|
//
|
||
|
virtual VOID vIncRef() = 0;
|
||
|
virtual LONG cDecRef() = 0;
|
||
|
virtual VOID vDecRefDelete() = 0;
|
||
|
|
||
|
protected:
|
||
|
|
||
|
//
|
||
|
// Clients of the reference class should override this class
|
||
|
// to perform cleanup. Generally clients will implement vRefZeroed
|
||
|
// to delete themselves.
|
||
|
//
|
||
|
virtual VOID vRefZeroed() = 0;
|
||
|
};
|
||
|
|
||
|
|
||
|
/************************************************************
|
||
|
|
||
|
MRefNone: quick ref counter, no sync done.
|
||
|
|
||
|
************************************************************/
|
||
|
|
||
|
class MRefQuick : public VRef {
|
||
|
|
||
|
SIGNATURE( 'refq' )
|
||
|
ALWAYS_VALID
|
||
|
|
||
|
protected:
|
||
|
|
||
|
VAR( LONG, cRef );
|
||
|
|
||
|
public:
|
||
|
|
||
|
#if DBG
|
||
|
TBackTraceMem _BackTrace;
|
||
|
|
||
|
MRefQuick( VOID ) : _cRef( 0 )
|
||
|
{ }
|
||
|
|
||
|
~MRefQuick( VOID )
|
||
|
{
|
||
|
SPLASSERT( !_cRef );
|
||
|
}
|
||
|
|
||
|
#else
|
||
|
|
||
|
MRefQuick( VOID ) : _cRef( 0 )
|
||
|
{ }
|
||
|
|
||
|
#endif
|
||
|
|
||
|
VOID vIncRef();
|
||
|
LONG cDecRef();
|
||
|
VOID vDecRefDelete();
|
||
|
};
|
||
|
|
||
|
|
||
|
/************************************************************
|
||
|
|
||
|
MRefCom: Refcount with interlocks
|
||
|
|
||
|
************************************************************/
|
||
|
|
||
|
class MRefCom : public VRef {
|
||
|
friend BOOL bSplLibInit( VOID );
|
||
|
friend VOID vSplLibFree( VOID );
|
||
|
|
||
|
SIGNATURE( 'refc' )
|
||
|
ALWAYS_VALID
|
||
|
|
||
|
public:
|
||
|
|
||
|
#if DBG
|
||
|
TBackTraceMem _BackTrace;
|
||
|
|
||
|
MRefCom( VOID ) : _cRef( 0 )
|
||
|
{ }
|
||
|
|
||
|
~MRefCom( VOID )
|
||
|
{
|
||
|
SPLASSERT( !_cRef );
|
||
|
}
|
||
|
|
||
|
#else
|
||
|
|
||
|
MRefCom( VOID ) : _cRef( 0 )
|
||
|
{ }
|
||
|
|
||
|
#endif
|
||
|
|
||
|
VOID vIncRef();
|
||
|
LONG cDecRef();
|
||
|
VOID vDecRefDelete();
|
||
|
|
||
|
protected:
|
||
|
|
||
|
//
|
||
|
// Must be LONG, not REFCOUNT for Interlocked* apis.
|
||
|
//
|
||
|
VAR( LONG, cRef );
|
||
|
|
||
|
private:
|
||
|
|
||
|
#if DBG
|
||
|
static MCritSec* gpcsCom;
|
||
|
#endif
|
||
|
};
|
||
|
|
||
|
|
||
|
/************************************************************
|
||
|
|
||
|
TRefLock: used to lock a VRef.
|
||
|
|
||
|
************************************************************/
|
||
|
|
||
|
class TRefLock {
|
||
|
|
||
|
SIGNATURE( 'refl' )
|
||
|
ALWAYS_VALID
|
||
|
SAFE_NEW
|
||
|
|
||
|
public:
|
||
|
|
||
|
VRef* _pRef;
|
||
|
|
||
|
TRefLock(
|
||
|
VOID
|
||
|
) : _pRef( NULL )
|
||
|
{ }
|
||
|
|
||
|
TRefLock(
|
||
|
VRef* pRef
|
||
|
)
|
||
|
{
|
||
|
_pRef = pRef;
|
||
|
_pRef->vIncRef( );
|
||
|
}
|
||
|
|
||
|
~TRefLock( )
|
||
|
{
|
||
|
if( _pRef )
|
||
|
_pRef->cDecRef( );
|
||
|
}
|
||
|
|
||
|
VOID vAcquire( VRef* pRef )
|
||
|
{
|
||
|
SPLASSERT( !_pRef );
|
||
|
_pRef = pRef;
|
||
|
_pRef->vIncRef( );
|
||
|
}
|
||
|
|
||
|
VOID vRelease( VOID )
|
||
|
{
|
||
|
SPLASSERT( _pRef );
|
||
|
_pRef->cDecRef( );
|
||
|
|
||
|
_pRef = NULL;
|
||
|
}
|
||
|
};
|
||
|
|
||
|
#define REF_LOCK( type, name ) \
|
||
|
type* p##name() \
|
||
|
{ return (type*)RL_##name._pRef; } \
|
||
|
VOID name##_vAcquire( type* ptr ) \
|
||
|
{ RL_##name.vAcquire( ptr ); } \
|
||
|
VOID name##_vRelease( VOID ) \
|
||
|
{ RL_##name.vRelease(); } \
|
||
|
TRefLock RL_##name
|
||
|
|
||
|
#endif // ndef _STATE_HXX
|