Added Memento class to prove strong exception safety.

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

View file

@ -144,8 +144,10 @@ class LevelMutexInfo
{ {
public: public:
/** Level for thread that has not locked any mutex. Maximum possible level /** This is the default level for a thread that has not locked any mutex. The
for a mutex is UnlockedLevel-1; No mutex may have a level of UnlockedLevel. maximum possible level for a mutex is UnlockedLevel-1, and I doubt any software
will ever have a call stack of more than 2^32-2 functions. No mutex may have a
level of UnlockedLevel.
*/ */
static const unsigned int UnlockedLevel = 0xFFFFFFFF; static const unsigned int UnlockedLevel = 0xFFFFFFFF;
@ -265,12 +267,35 @@ public:
protected: protected:
/// @class Memento Stores content of LevelMutexInfo so CheckFor can check invariants.
class Memento
{
public:
explicit Memento( const volatile LevelMutexInfo & mutex );
bool operator == ( const volatile LevelMutexInfo & mutex ) const;
private:
/// Level of this mutex.
const unsigned int m_level;
/// How many times this mutex got locked.
const unsigned int m_count;
/// Pointer to mutex locked before this one.
const volatile LevelMutexInfo * const m_previous;
/// True if mutex was locked when Memento was constructed.
const bool m_locked;
};
/** @note CheckFor performs validity checking in many functions to determine if the /** @note CheckFor performs validity checking in many functions to determine if the
code violated any invariants, if any content changed, or if the function threw an code violated any invariants, if any content changed, or if the function threw an
exception. The checkers only get used in debug builds, and get optimized away in exception. The checkers only get used in debug builds, and get optimized away in
release builds. release builds.
*/ */
typedef ::Loki::CheckFor< LevelMutexInfo > CheckFor; typedef ::Loki::CheckFor< volatile LevelMutexInfo, Memento > CheckFor;
/** @class MutexUndoer /** @class MutexUndoer
Undoes actions by MultiLock if an exception occurs. It keeps track of Undoes actions by MultiLock if an exception occurs. It keeps track of
@ -334,6 +359,17 @@ protected:
*/ */
bool IsValid( void ) const volatile; bool IsValid( void ) const volatile;
/** Returns true if no class invariant broken, otherwise asserts. This function
only gets called in debug builds.
*/
bool IsValid( void ) const;
/// Returns true if all pre-conditions for PostLock function are valid.
bool PostLockValidator( void ) const volatile;
/// Returns true if all pre-conditions for PreUnlock function are valid.
bool PreUnlockValidator( void ) const volatile;
private: private:
/// Copy constructor is not implemented. /// Copy constructor is not implemented.
@ -359,6 +395,16 @@ private:
/// Called only by MultiUnlock to unlock each particular mutex within a container. /// Called only by MultiUnlock to unlock each particular mutex within a container.
virtual MutexErrors::Type UnlockThis( void ) volatile = 0; virtual MutexErrors::Type UnlockThis( void ) volatile = 0;
/** The actual implementation of IsLockedByCurrentThread. This does not do any
invariant checking because the functions which call it already have.
*/
bool IsLockedByCurrentThreadImpl( void ) const volatile;
/** Does just the opposite of IsLockedByCurrentThread. Called as a post-condition
check by another function.
*/
bool IsNotLockedByCurrentThread( void ) const volatile;
/// Pointer to singly-linked list of mutexes locked by the current thread. /// Pointer to singly-linked list of mutexes locked by the current thread.
static LOKI_THREAD_LOCAL volatile LevelMutexInfo * s_currentMutex; static LOKI_THREAD_LOCAL volatile LevelMutexInfo * s_currentMutex;
@ -765,7 +811,7 @@ public:
virtual MutexErrors::Type TryLock( void ) volatile virtual MutexErrors::Type TryLock( void ) volatile
{ {
LOKI_MUTEX_DEBUG_CODE( CheckFor::Invariants checker( this, &IsValid() ); (void)checker; ) LOKI_MUTEX_DEBUG_CODE( CheckFor::NoChangeOnThrow checker( this, &IsValid ); (void)checker; )
MutexErrors::Type result = LevelMutexInfo::PreLockCheck( true ); MutexErrors::Type result = LevelMutexInfo::PreLockCheck( true );
if ( MutexErrors::Success == result ) if ( MutexErrors::Success == result )
@ -786,7 +832,7 @@ public:
virtual MutexErrors::Type Lock( void ) volatile virtual MutexErrors::Type Lock( void ) volatile
{ {
LOKI_MUTEX_DEBUG_CODE( CheckFor::Invariants checker( this, &IsValid() ); (void)checker; ) LOKI_MUTEX_DEBUG_CODE( CheckFor::NoChangeOnThrow checker( this, &IsValid ); (void)checker; )
MutexErrors::Type result = LevelMutexInfo::PreLockCheck( false ); MutexErrors::Type result = LevelMutexInfo::PreLockCheck( false );
if ( MutexErrors::Success == result ) if ( MutexErrors::Success == result )
@ -805,7 +851,7 @@ public:
virtual MutexErrors::Type Lock( unsigned int milliSeconds ) volatile virtual MutexErrors::Type Lock( unsigned int milliSeconds ) volatile
{ {
LOKI_MUTEX_DEBUG_CODE( CheckFor::Invariants checker( this, &IsValid() ); (void)checker; ) LOKI_MUTEX_DEBUG_CODE( CheckFor::NoChangeOnThrow checker( this, &IsValid ); (void)checker; )
MutexErrors::Type result = LevelMutexInfo::PreLockCheck( false ); MutexErrors::Type result = LevelMutexInfo::PreLockCheck( false );
if ( MutexErrors::Success == result ) if ( MutexErrors::Success == result )
@ -840,7 +886,7 @@ public:
virtual MutexErrors::Type Unlock( void ) volatile virtual MutexErrors::Type Unlock( void ) volatile
{ {
LOKI_MUTEX_DEBUG_CODE( CheckFor::Invariants checker( this, &IsValid() ); (void)checker; ) LOKI_MUTEX_DEBUG_CODE( CheckFor::NoChangeOnThrow checker( this, &IsValid ); (void)checker; )
MutexErrors::Type result = LevelMutexInfo::PreUnlockCheck(); MutexErrors::Type result = LevelMutexInfo::PreUnlockCheck();
if ( MutexErrors::Success == result ) if ( MutexErrors::Success == result )
@ -885,7 +931,7 @@ private:
*/ */
virtual MutexErrors::Type LockThis( void ) volatile virtual MutexErrors::Type LockThis( void ) volatile
{ {
LOKI_MUTEX_DEBUG_CODE( CheckFor::Invariants checker( this, &IsValid() ); (void)checker; ) LOKI_MUTEX_DEBUG_CODE( CheckFor::NoChangeOnThrow checker( this, &IsValid ); (void)checker; )
assert( this != LevelMutexInfo::GetCurrentMutex() ); assert( this != LevelMutexInfo::GetCurrentMutex() );
const MutexErrors::Type result = m_mutex.Lock(); const MutexErrors::Type result = m_mutex.Lock();
@ -905,7 +951,7 @@ private:
*/ */
virtual MutexErrors::Type LockThis( unsigned int milliSeconds ) volatile virtual MutexErrors::Type LockThis( unsigned int milliSeconds ) volatile
{ {
LOKI_MUTEX_DEBUG_CODE( CheckFor::Invariants checker( this, &IsValid() ); (void)checker; ) LOKI_MUTEX_DEBUG_CODE( CheckFor::NoChangeOnThrow checker( this, &IsValid ); (void)checker; )
clock_t timeOut = clock() + milliSeconds; clock_t timeOut = clock() + milliSeconds;
while ( clock() < timeOut ) while ( clock() < timeOut )
@ -930,7 +976,7 @@ private:
*/ */
virtual MutexErrors::Type UnlockThis( void ) volatile virtual MutexErrors::Type UnlockThis( void ) volatile
{ {
LOKI_MUTEX_DEBUG_CODE( CheckFor::Invariants checker( this, &IsValid() ); (void)checker; ) LOKI_MUTEX_DEBUG_CODE( CheckFor::NoChangeOnThrow checker( this, &IsValid ); (void)checker; )
assert( NULL != LevelMutexInfo::GetCurrentMutex() ); assert( NULL != LevelMutexInfo::GetCurrentMutex() );
if ( 1 < LevelMutexInfo::GetLockCount() ) if ( 1 < LevelMutexInfo::GetLockCount() )