From: <ric...@us...> - 2011-11-07 23:55:37
|
Revision: 1176 http://loki-lib.svn.sourceforge.net/loki-lib/?rev=1176&view=rev Author: rich_sposato Date: 2011-11-07 23:55:30 +0000 (Mon, 07 Nov 2011) Log Message: ----------- Added Memento class to prove strong exception safety. Removed useless lines. Modified Paths: -------------- trunk/src/LevelMutex.cpp Modified: trunk/src/LevelMutex.cpp =================================================================== --- trunk/src/LevelMutex.cpp 2011-11-07 23:54:58 UTC (rev 1175) +++ trunk/src/LevelMutex.cpp 2011-11-07 23:55:30 UTC (rev 1176) @@ -232,6 +232,38 @@ // ---------------------------------------------------------------------------- +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 ) : m_mutexes( mutexes ), m_here( mutexes.end() ) @@ -572,6 +604,14 @@ // ---------------------------------------------------------------------------- +bool LevelMutexInfo::IsValid( void ) const +{ + const volatile LevelMutexInfo * pThis = const_cast< const volatile LevelMutexInfo * >( this ); + return pThis->IsValid(); +} + +// ---------------------------------------------------------------------------- + void LevelMutexInfo::IncrementCount( void ) volatile { assert( IsValid() ); @@ -592,8 +632,20 @@ 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() ) return false; const volatile LevelMutexInfo * mutex = s_currentMutex; @@ -608,9 +660,30 @@ // ---------------------------------------------------------------------------- +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 { - 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 ) return false; @@ -631,7 +704,10 @@ 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 ) return false; @@ -651,7 +727,10 @@ 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() ) return false; @@ -664,14 +743,31 @@ // ---------------------------------------------------------------------------- -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( nullptr == m_previous ); assert( this != s_currentMutex ); 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_previous = s_currentMutex; s_currentMutex = this; @@ -679,14 +775,33 @@ // ---------------------------------------------------------------------------- -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( nullptr != s_currentMutex ); assert( this == s_currentMutex ); 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; m_previous = nullptr; m_count = 0; @@ -696,7 +811,10 @@ 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(); if ( currentLevel < LevelMutexInfo::GetLevel() ) @@ -730,7 +848,10 @@ 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 ) return MutexErrors::WasntLocked; @@ -803,9 +924,6 @@ switch ( result ) { case 0: -//#if defined( DEBUG_LOKI_LEVEL_MUTEX ) -// cout << __FUNCTION__ << '\t' << __LINE__ << endl; -//#endif return; case EBUSY: throw MutexException( "pthread mutex already initialized!", @@ -842,9 +960,6 @@ #if defined( _MSC_VER ) ::DeleteCriticalSection( &m_mutex ); #else -//#if defined( DEBUG_LOKI_LEVEL_MUTEX ) -// cout << __FUNCTION__ << '\t' << __LINE__ << endl; -//#endif ::pthread_mutex_destroy( &m_mutex ); #endif } @@ -868,9 +983,6 @@ switch ( result ) { case 0: -//#if defined( DEBUG_LOKI_LEVEL_MUTEX ) -// cout << __FUNCTION__ << '\t' << __LINE__ << endl; -//#endif break; default: case EINVAL: @@ -911,9 +1023,6 @@ switch ( result ) { case 0: -//#if defined( DEBUG_LOKI_LEVEL_MUTEX ) -// cout << __FUNCTION__ << '\t' << __LINE__ << endl; -//#endif return MutexErrors::Success; default: case EBUSY: @@ -943,9 +1052,6 @@ if ( EPERM == result ) throw MutexException( "current thread did not lock this pthread mutex!", GetLevel(), MutexErrors::NotLockedByThread ); -//#if defined( DEBUG_LOKI_LEVEL_MUTEX ) -// cout << __FUNCTION__ << '\t' << __LINE__ << endl; -//#endif #endif return MutexErrors::Success; } This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |