Added Memento class to prove strong exception safety. Removed useless lines.

git-svn-id: svn://svn.code.sf.net/p/loki-lib/code/trunk@1176 7ec92016-0320-0410-acc4-a06ded1c099a
This commit is contained in:
rich_sposato 2011-11-07 23:55:30 +00:00
parent 1a45777e77
commit 07c621c3d4

View file

@ -232,6 +232,38 @@ MutexErrors::Type DoMutexesMatchContainer( const LevelMutexInfo::MutexContainer
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
LevelMutexInfo::Memento::Memento( const volatile LevelMutexInfo & mutex ) :
m_level( mutex.m_level ),
m_count( mutex.m_count ),
m_previous( mutex.m_previous ),
m_locked( mutex.IsLockedByCurrentThreadImpl() )
{
assert( this != nullptr );
}
// ----------------------------------------------------------------------------
bool LevelMutexInfo::Memento::operator == ( const volatile LevelMutexInfo & mutex ) const
{
assert( this != nullptr );
if ( m_locked && mutex.IsLockedByCurrentThreadImpl() )
{
// If the current thread still has a lock on the mutex, then make
// sure no values have changed.
if ( m_level != mutex.m_level )
return false;
if ( m_count != mutex.m_count )
return false;
if ( m_previous != mutex.m_previous )
return false;
}
return true;
}
// ----------------------------------------------------------------------------
LevelMutexInfo::MutexUndoer::MutexUndoer( MutexContainer & mutexes ) : LevelMutexInfo::MutexUndoer::MutexUndoer( MutexContainer & mutexes ) :
m_mutexes( mutexes ), m_mutexes( mutexes ),
m_here( mutexes.end() ) m_here( mutexes.end() )
@ -572,6 +604,14 @@ bool LevelMutexInfo::IsValid( void ) const volatile
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
bool LevelMutexInfo::IsValid( void ) const
{
const volatile LevelMutexInfo * pThis = const_cast< const volatile LevelMutexInfo * >( this );
return pThis->IsValid();
}
// ----------------------------------------------------------------------------
void LevelMutexInfo::IncrementCount( void ) volatile void LevelMutexInfo::IncrementCount( void ) volatile
{ {
assert( IsValid() ); assert( IsValid() );
@ -592,8 +632,20 @@ void LevelMutexInfo::DecrementCount( void ) volatile
bool LevelMutexInfo::IsLockedByCurrentThread( void ) const volatile bool LevelMutexInfo::IsLockedByCurrentThread( void ) const volatile
{ {
LOKI_MUTEX_DEBUG_CODE( CheckFor::NoChangeOrThrow checker( this, &IsValid() ); (void)checker; ) // This function could call CheckFor::NoThrowOrChange - except that this function
// gets called by various functions that are called to clean up after an exception
// is thrown
LOKI_MUTEX_DEBUG_CODE(
CheckFor::NoChange checker( this, &LevelMutexInfo::IsValid );
(void)checker;
)
return IsLockedByCurrentThreadImpl();
}
// ----------------------------------------------------------------------------
bool LevelMutexInfo::IsLockedByCurrentThreadImpl( void ) const volatile
{
if ( !IsLocked() ) if ( !IsLocked() )
return false; return false;
const volatile LevelMutexInfo * mutex = s_currentMutex; const volatile LevelMutexInfo * mutex = s_currentMutex;
@ -608,9 +660,30 @@ bool LevelMutexInfo::IsLockedByCurrentThread( void ) const volatile
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
bool LevelMutexInfo::IsNotLockedByCurrentThread( void ) const volatile
{
if ( !IsLocked() )
return true;
const volatile LevelMutexInfo * mutex = s_currentMutex;
while ( nullptr != mutex )
{
if ( this == mutex )
return false;
mutex = mutex->m_previous;
}
return true;
}
// ----------------------------------------------------------------------------
bool LevelMutexInfo::IsRecentLock( void ) const volatile bool LevelMutexInfo::IsRecentLock( void ) const volatile
{ {
LOKI_MUTEX_DEBUG_CODE( CheckFor::NoChangeOrThrow checker( this, &IsValid() ); (void)checker; ) LOKI_MUTEX_DEBUG_CODE(
CheckFor::NoThrowOrChange checker( this, &LevelMutexInfo::IsValid );
(void)checker;
)
if ( 0 == m_count ) if ( 0 == m_count )
return false; return false;
@ -631,7 +704,10 @@ bool LevelMutexInfo::IsRecentLock( void ) const volatile
bool LevelMutexInfo::IsRecentLock( std::size_t count ) const volatile bool LevelMutexInfo::IsRecentLock( std::size_t count ) const volatile
{ {
LOKI_MUTEX_DEBUG_CODE( CheckFor::NoChangeOrThrow checker( this, &IsValid() ); (void)checker; ) LOKI_MUTEX_DEBUG_CODE(
CheckFor::NoThrowOrChange checker( this, &LevelMutexInfo::IsValid );
(void)checker;
)
if ( 0 == count ) if ( 0 == count )
return false; return false;
@ -651,7 +727,10 @@ bool LevelMutexInfo::IsRecentLock( std::size_t count ) const volatile
bool LevelMutexInfo::IsLockedByAnotherThread( void ) const volatile bool LevelMutexInfo::IsLockedByAnotherThread( void ) const volatile
{ {
LOKI_MUTEX_DEBUG_CODE( CheckFor::NoChangeOrThrow checker( this, &IsValid() ); (void)checker; ) LOKI_MUTEX_DEBUG_CODE(
CheckFor::NoThrowOrChange checker( this, &LevelMutexInfo::IsValid );
(void)checker;
)
if ( !IsLocked() ) if ( !IsLocked() )
return false; return false;
@ -664,14 +743,31 @@ bool LevelMutexInfo::IsLockedByAnotherThread( void ) const volatile
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
void LevelMutexInfo::PostLock( void ) volatile bool LevelMutexInfo::PostLockValidator( void ) const volatile
{ {
LOKI_MUTEX_DEBUG_CODE( CheckFor::NoThrow checker( this, &IsValid() ); (void)checker; )
assert( 0 == m_count ); assert( 0 == m_count );
assert( nullptr == m_previous ); assert( nullptr == m_previous );
assert( this != s_currentMutex ); assert( this != s_currentMutex );
assert( !IsLockedByCurrentThread() ); assert( !IsLockedByCurrentThread() );
return true;
}
// ----------------------------------------------------------------------------
void LevelMutexInfo::PostLock( void ) volatile
{
LOKI_MUTEX_DEBUG_CODE(
CheckFor::NoThrow checker( this, &LevelMutexInfo::IsValid,
&LevelMutexInfo::PostLockValidator, &LevelMutexInfo::IsLockedByCurrentThreadImpl );
(void)checker;
)
/** Of the three data members PostLock must modify, it should change the count
before changing the other two. The IsLocked function uses the count to see if
the mutex is locked, so changing this first stops other threads from trying to
lock the object before this thread can modify the other two data members.
*/
m_count = 1; m_count = 1;
m_previous = s_currentMutex; m_previous = s_currentMutex;
s_currentMutex = this; s_currentMutex = this;
@ -679,14 +775,33 @@ void LevelMutexInfo::PostLock( void ) volatile
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
void LevelMutexInfo::PreUnlock( void ) volatile bool LevelMutexInfo::PreUnlockValidator( void ) const volatile
{ {
LOKI_MUTEX_DEBUG_CODE( CheckFor::NoThrow checker( this, &IsValid() ); (void)checker; )
assert( 1 == m_count ); assert( 1 == m_count );
assert( nullptr != s_currentMutex ); assert( nullptr != s_currentMutex );
assert( this == s_currentMutex ); assert( this == s_currentMutex );
assert( IsLockedByCurrentThread() ); assert( IsLockedByCurrentThread() );
return true;
}
// ----------------------------------------------------------------------------
void LevelMutexInfo::PreUnlock( void ) volatile
{
LOKI_MUTEX_DEBUG_CODE(
// This must use CheckFor::Invariants instead of CheckFor::NoThrow because the
// function gets called when MultiLock has to clean up after an exception.
CheckFor::Invariants checker( this, &LevelMutexInfo::IsValid,
&LevelMutexInfo::PreUnlockValidator, &LevelMutexInfo::IsNotLockedByCurrentThread );
(void)checker;
)
/** Of the three data members PostLock must modify, it should change the count
after changing the other two. The IsLocked function uses the count to see if
the mutex is locked, so changing this last stops other threads from trying to
lock the object before this thread can modify the other two data members.
*/
s_currentMutex = m_previous; s_currentMutex = m_previous;
m_previous = nullptr; m_previous = nullptr;
m_count = 0; m_count = 0;
@ -696,7 +811,10 @@ void LevelMutexInfo::PreUnlock( void ) volatile
MutexErrors::Type LevelMutexInfo::PreLockCheck( bool forTryLock ) volatile MutexErrors::Type LevelMutexInfo::PreLockCheck( bool forTryLock ) volatile
{ {
LOKI_MUTEX_DEBUG_CODE( CheckFor::NoThrow checker( this, &IsValid() ); (void)checker; ) LOKI_MUTEX_DEBUG_CODE(
CheckFor::NoThrow checker( this, &LevelMutexInfo::IsValid );
(void)checker;
)
const unsigned int currentLevel = GetCurrentThreadsLevel(); const unsigned int currentLevel = GetCurrentThreadsLevel();
if ( currentLevel < LevelMutexInfo::GetLevel() ) if ( currentLevel < LevelMutexInfo::GetLevel() )
@ -730,7 +848,10 @@ MutexErrors::Type LevelMutexInfo::PreLockCheck( bool forTryLock ) volatile
MutexErrors::Type LevelMutexInfo::PreUnlockCheck( void ) volatile MutexErrors::Type LevelMutexInfo::PreUnlockCheck( void ) volatile
{ {
LOKI_MUTEX_DEBUG_CODE( CheckFor::NoThrow checker( this, &IsValid() ); (void)checker; ) LOKI_MUTEX_DEBUG_CODE(
CheckFor::NoThrow checker( this, &LevelMutexInfo::IsValid );
(void)checker;
)
if ( 0 == m_count ) if ( 0 == m_count )
return MutexErrors::WasntLocked; return MutexErrors::WasntLocked;
@ -803,9 +924,6 @@ SpinLevelMutex::SpinLevelMutex( unsigned int level ) :
switch ( result ) switch ( result )
{ {
case 0: case 0:
//#if defined( DEBUG_LOKI_LEVEL_MUTEX )
// cout << __FUNCTION__ << '\t' << __LINE__ << endl;
//#endif
return; return;
case EBUSY: case EBUSY:
throw MutexException( "pthread mutex already initialized!", throw MutexException( "pthread mutex already initialized!",
@ -842,9 +960,6 @@ SpinLevelMutex::~SpinLevelMutex( void )
#if defined( _MSC_VER ) #if defined( _MSC_VER )
::DeleteCriticalSection( &m_mutex ); ::DeleteCriticalSection( &m_mutex );
#else #else
//#if defined( DEBUG_LOKI_LEVEL_MUTEX )
// cout << __FUNCTION__ << '\t' << __LINE__ << endl;
//#endif
::pthread_mutex_destroy( &m_mutex ); ::pthread_mutex_destroy( &m_mutex );
#endif #endif
} }
@ -868,9 +983,6 @@ MutexErrors::Type SpinLevelMutex::Lock( void ) volatile
switch ( result ) switch ( result )
{ {
case 0: case 0:
//#if defined( DEBUG_LOKI_LEVEL_MUTEX )
// cout << __FUNCTION__ << '\t' << __LINE__ << endl;
//#endif
break; break;
default: default:
case EINVAL: case EINVAL:
@ -911,9 +1023,6 @@ MutexErrors::Type SpinLevelMutex::TryLock( void ) volatile
switch ( result ) switch ( result )
{ {
case 0: case 0:
//#if defined( DEBUG_LOKI_LEVEL_MUTEX )
// cout << __FUNCTION__ << '\t' << __LINE__ << endl;
//#endif
return MutexErrors::Success; return MutexErrors::Success;
default: default:
case EBUSY: case EBUSY:
@ -943,9 +1052,6 @@ MutexErrors::Type SpinLevelMutex::Unlock( void ) volatile
if ( EPERM == result ) if ( EPERM == result )
throw MutexException( "current thread did not lock this pthread mutex!", throw MutexException( "current thread did not lock this pthread mutex!",
GetLevel(), MutexErrors::NotLockedByThread ); GetLevel(), MutexErrors::NotLockedByThread );
//#if defined( DEBUG_LOKI_LEVEL_MUTEX )
// cout << __FUNCTION__ << '\t' << __LINE__ << endl;
//#endif
#endif #endif
return MutexErrors::Success; return MutexErrors::Success;
} }