diff --git a/test/LevelMutex/LevelMutex_MSVC_9.vcproj b/test/LevelMutex/LevelMutex_MSVC_9.vcproj
new file mode 100644
index 0000000..61181f9
--- /dev/null
+++ b/test/LevelMutex/LevelMutex_MSVC_9.vcproj
@@ -0,0 +1,211 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/test/LevelMutex/MultiThreadTests.cpp b/test/LevelMutex/MultiThreadTests.cpp
new file mode 100644
index 0000000..94e124e
--- /dev/null
+++ b/test/LevelMutex/MultiThreadTests.cpp
@@ -0,0 +1,1179 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+// Part of LevelMutex test program for The Loki Library
+// Copyright (c) 2008 Richard Sposato
+// The copyright on this file is protected under the terms of the MIT license.
+//
+// Permission to use, copy, modify, distribute and sell this software for any
+// purpose is hereby granted without fee, provided that the above copyright
+// notice appear in all copies and that both that copyright notice and this
+// permission notice appear in supporting documentation.
+//
+// The author makes no representations about the suitability of this software
+// for any purpose. It is provided "as is" without express or implied warranty.
+//
+////////////////////////////////////////////////////////////////////////////////
+
+// $Id$
+
+
+// ----------------------------------------------------------------------------
+
+#include "MultiThreadTests.hpp"
+
+#include
+#include
+#include
+#include
+
+#include
+
+#include "ThreadPool.hpp"
+#include "Thing.hpp"
+
+
+// define nullptr even though new compilers will have this keyword just so we
+// have a consistent and easy way of identifying which uses of 0 mean null.
+#define nullptr 0
+
+
+using namespace ::Loki;
+
+static const unsigned int thingCount = 8;
+
+static bool s_redo = false;
+
+
+// ----------------------------------------------------------------------------
+
+void SetToRedoSingleTests( bool redo )
+{
+ s_redo = redo;
+}
+
+// ----------------------------------------------------------------------------
+
+bool WillRedoSingleTests( void )
+{
+ return s_redo;
+}
+
+// ----------------------------------------------------------------------------
+
+#if defined(_WIN32)
+
+ typedef unsigned int ( WINAPI * ThreadFunction_ )( void * );
+
+ #define LOKI_pthread_t HANDLE
+
+ #define LOKI_pthread_create(handle,attr,func,arg) \
+ (int)((*handle=(HANDLE) _beginthreadex (nullptr,0,(ThreadFunction_)func,arg,0,nullptr))==nullptr)
+
+ #define LOKI_pthread_join(thread) \
+ ((::WaitForSingleObject((thread),INFINITE)!=WAIT_OBJECT_0) || !CloseHandle(thread))
+
+#else
+
+ #define LOKI_pthread_t \
+ pthread_t
+ #define LOKI_pthread_create(handle,attr,func,arg) \
+ pthread_create(handle,attr,func,arg)
+ #define LOKI_pthread_join(thread) \
+ pthread_join(thread, NULL)
+
+#endif
+
+using namespace std;
+using namespace Loki;
+
+// ----------------------------------------------------------------------------
+
+//class Thread
+//{
+//public:
+//
+// typedef void * ( * CallFunction )( void * );
+//
+// Thread( CallFunction func, void * parm )
+// : pthread_()
+// , func_( func )
+// , parm_( parm )
+// {
+// }
+//
+// int Start( void )
+// {
+// return LOKI_pthread_create( &pthread_, nullptr, func_, parm_ );
+// }
+//
+// int WaitForThread( void ) const
+// {
+// return LOKI_pthread_join( pthread_ );
+// }
+//
+//private:
+// LOKI_pthread_t pthread_;
+// CallFunction func_;
+// void * parm_;
+//};
+//
+//// ----------------------------------------------------------------------------
+//
+//class ThreadPool
+//{
+//public:
+// ThreadPool( void ) : m_threads()
+// {
+// }
+//
+// void Create( size_t threadCount, Thread::CallFunction function )
+// {
+// for( size_t ii = 0; ii < threadCount; ii++ )
+// {
+// string buffer;
+// SPrintf( buffer, "Creating thread %d\n" )( ii );
+// cout << buffer;
+// Thread * thread = new Thread( function,
+// reinterpret_cast< void * >( ii ) );
+// m_threads.push_back( thread );
+// }
+// }
+//
+// void Start( void )
+// {
+// for ( size_t ii = 0; ii < m_threads.size(); ii++ )
+// {
+// m_threads.at( ii )->Start();
+// }
+// }
+//
+// void Join( void ) const
+// {
+// for ( size_t ii = 0; ii < m_threads.size(); ii++ )
+// m_threads.at( ii )->WaitForThread();
+// }
+//
+// ~ThreadPool( void )
+// {
+// for ( size_t ii = 0; ii < m_threads.size(); ii++ )
+// {
+// delete m_threads.at(ii);
+// }
+// }
+//
+//private:
+// typedef std::vector< Thread * > Threads;
+//
+// Threads m_threads;
+//};
+
+// ----------------------------------------------------------------------------
+
+void * PrintSafeThread( void * p )
+{
+ unsigned int value = reinterpret_cast< unsigned int >( p );
+ volatile Thing & thing = Thing::GetIt();
+ try
+ {
+ for ( unsigned int ii = 0; ii < 5; ++ii )
+ {
+ thing.Print( value, ii, 16 );
+ ::GoToSleep( 2 );
+ if ( WillRedoSingleTests() )
+ {
+ SingleThreadSimpleTest();
+ SingleThreadReentrantTest();
+ SingleThreadSimpleMultiLockTest();
+ SingleThreadComplexMultiLockTest( false );
+ SingleThreadExceptionTest();
+ }
+ }
+ }
+ catch ( ... )
+ {
+ assert( false );
+ }
+
+ return nullptr;
+}
+
+// ----------------------------------------------------------------------------
+
+void * PrintUnsafeThread( void * p )
+{
+ unsigned int value = reinterpret_cast< unsigned int >( p );
+ Thing & thing = const_cast< Thing & >( Thing::GetIt() );
+ try
+ {
+ for ( unsigned int ii = 0; ii < 5; ++ii )
+ {
+ thing.Print( value, ii, 16 );
+ ::GoToSleep( 2 );
+ if ( WillRedoSingleTests() )
+ {
+ SingleThreadSimpleTest();
+ SingleThreadReentrantTest();
+ SingleThreadSimpleMultiLockTest();
+ SingleThreadComplexMultiLockTest( false );
+ SingleThreadExceptionTest();
+ }
+ }
+ }
+ catch ( ... )
+ {
+ assert( false );
+ }
+
+ return nullptr;
+}
+
+// ----------------------------------------------------------------------------
+
+void OutputResults( unsigned int loop, unsigned int value, unsigned int result )
+{
+ static volatile SleepMutex mutex( 2 );
+ static bool initialized = false;
+ if ( !initialized )
+ {
+ initialized = true;
+ #if defined( _MSC_VER )
+ mutex.GetMutexPolicy().SetSleepTime( 5 );
+ mutex.GetMutexPolicy().SetWakable( true );
+ #else
+ mutex.GetMutexPolicy().SetSleepTime( 1 );
+ #endif
+ }
+
+ MutexLocker locker( mutex );
+ (void)locker;
+
+ const char * message = ( result == value ) ? "yes" : "no!";
+ Printf( "Loop = %d \t Value = %d \t Result = %d \t Pass? %s \n" )
+ ( loop )( value )( result )( message );
+}
+
+// ----------------------------------------------------------------------------
+
+void * ValueSafeThread( void * p )
+{
+
+ const unsigned int testCount = 8;
+ unsigned int fails = 0;
+ const unsigned int value = reinterpret_cast< unsigned int >( p );
+ volatile Thing & thing = Thing::GetIt();
+ try
+ {
+ for ( unsigned int ii = 0; ii < testCount; ++ii )
+ {
+ MutexLocker locker( thing.GetMutex() );
+ (void)locker;
+ thing.SetValue( value );
+ ::GoToSleep( 3 );
+ const unsigned int result = thing.GetValue();
+ OutputResults( ii, value, result );
+ if ( result != value )
+ fails++;
+ assert( result == value );
+ }
+ }
+ catch ( ... )
+ {
+ assert( false );
+ }
+ TestResults::GetIt()->SetResult( value, testCount, fails );
+
+ return nullptr;
+}
+
+// ----------------------------------------------------------------------------
+
+void * ValueUnsafeThread( void * p )
+{
+
+ const unsigned int testCount = 8;
+ unsigned int fails = 0;
+ const unsigned int value = reinterpret_cast< unsigned int >( p );
+ // cast away volatility so the mutex doesn't get used by volatile functions.
+ Thing & thing = const_cast< Thing & >( Thing::GetIt() );
+ try
+ {
+ for ( unsigned int ii = 0; ii < testCount; ++ii )
+ {
+ thing.SetValue( value );
+ ::GoToSleep( 3 );
+ const unsigned int result = thing.GetValue();
+ OutputResults( ii, value, result );
+ if ( result != value )
+ fails++;
+ }
+ }
+ catch ( ... )
+ {
+ assert( false );
+ }
+ TestResults::GetIt()->SetResult( value, testCount, fails );
+
+ return nullptr;
+}
+
+// ----------------------------------------------------------------------------
+
+void MultiThreadSimpleTest( void )
+{
+ Thing::Init( 0 );
+ const unsigned int threadCount = 5;
+ ThreadPool pool( threadCount );
+
+ cout << endl << "Doing thread-locked print test. This test should pass. and not deadlock" << endl;
+ ::system( "pause" );
+ for ( unsigned int ii = 0; ii < threadCount; ++ii )
+ {
+ void * p = reinterpret_cast< void * >( ii );
+ pool.Start( PrintSafeThread, p );
+ }
+ pool.JoinAll();
+
+ cout << endl << "Doing thread-unsafe print test. This test may fail." << endl;
+ ::system( "pause" );
+ for ( unsigned int ii = 0; ii < threadCount; ++ii )
+ {
+ void * p = reinterpret_cast< void * >( ii );
+ pool.Start( PrintUnsafeThread, p );
+ }
+ pool.JoinAll();
+
+ Thing::Destroy();
+}
+
+// ----------------------------------------------------------------------------
+
+void * TryLockThread( void * p )
+{
+
+ const unsigned int value = reinterpret_cast< unsigned int >( p );
+ volatile Thing & thing = Thing::GetIt();
+ volatile SleepMutex & mutex = thing.GetMutex();
+ assert( mutex.IsLockedByAnotherThread() );
+
+ for ( unsigned int ii = 0; ii < 5; ++ii )
+ {
+ MutexErrors::Type result = mutex.TryLock();
+ const bool locked = ( result == MutexErrors::Success );
+ assert( !locked );
+ if ( locked )
+ {
+ // let's pretend we actually locked it,
+ // and try to change the value.
+ thing.SetValue( value );
+ result = mutex.Unlock();
+ assert( result == MutexErrors::Success );
+ }
+ const unsigned int gotValue = thing.GetValue();
+ assert( gotValue != value );
+ }
+ assert( mutex.IsLockedByAnotherThread() );
+
+ return nullptr;
+}
+
+// ----------------------------------------------------------------------------
+
+void MultiThreadTryLockTest( void )
+{
+ static const unsigned int threadCount = 3;
+ Thing::Init( 0 );
+ volatile Thing & thing = Thing::GetIt();
+ volatile SleepMutex & mutex = thing.GetMutex();
+
+ cout << endl << "Doing multi-threaded TryLock test. This test should not deadlock." << endl;
+ ::system( "pause" );
+ // First step is to lock the mutex in the main thread so no child thread
+ // can ever lock it, change the value, or anything like that.
+ MutexErrors::Type result = mutex.Lock();
+ assert( MutexErrors::Success == result );
+ bool okay = mutex.IsLockedByCurrentThread();
+ assert( okay );
+ thing.SetValue( threadCount );
+ ThreadPool pool( threadCount );
+ for ( unsigned int ii = 0; ii < threadCount; ++ii )
+ {
+ void * p = reinterpret_cast< void * >( ii );
+ pool.Start( TryLockThread, p );
+ }
+ pool.JoinAll();
+ const unsigned int value = thing.GetValue();
+ assert( value == threadCount );
+ result = mutex.Unlock();
+ assert( MutexErrors::Success == result );
+ okay = !mutex.IsLockedByCurrentThread();
+ assert( okay );
+ okay = !mutex.IsLocked();
+ assert( okay );
+
+ Thing::Destroy();
+}
+
+// ----------------------------------------------------------------------------
+
+void MultiThreadReentrantTest( void )
+{
+ Thing::Init( 0 );
+ const unsigned int threadCount = 8;
+ TestResults::Create( threadCount );
+ ThreadPool pool( threadCount );
+
+ cout << endl << "Doing thread-safe value test. This test should pass and not deadlock." << endl;
+ ::system( "pause" );
+ for ( unsigned int ii = 0; ii < threadCount; ++ii )
+ {
+ void * p = reinterpret_cast< void * >( ii );
+ pool.Start( ValueSafeThread, p );
+ }
+ pool.JoinAll();
+ TestResults::GetIt()->OutputResults();
+
+ cout << endl << "Doing thread-unsafe value test. This test may fail." << endl;
+ ::system( "pause" );
+ for ( unsigned int ii = 0; ii < threadCount; ++ii )
+ {
+ void * p = reinterpret_cast< void * >( ii );
+ pool.Start( ValueUnsafeThread, p );
+ }
+ pool.JoinAll();
+ TestResults::GetIt()->OutputResults();
+
+ TestResults::Destroy();
+ Thing::Destroy();
+}
+
+// ----------------------------------------------------------------------------
+
+bool RandomizeMutexOrder( LevelMutexInfo::MutexContainer & mutexes )
+{
+ unsigned int count = mutexes.size();
+ if ( count < 2 )
+ return false;
+
+ LevelMutexInfo::MutexContainer randomMutexes;
+ for ( unsigned int ii = 0; ii < count; ++ii )
+ {
+ volatile LevelMutexInfo * mutex = nullptr;
+ const unsigned int sizeNow = mutexes.size();
+ if ( 1 < sizeNow )
+ {
+ unsigned int index = ( ::rand() % sizeNow );
+ mutex = mutexes[ index ];
+ if ( index < sizeNow - 1 )
+ mutexes[ index ] = mutexes[ sizeNow - 1 ];
+ }
+ else
+ {
+ mutex = mutexes[ 0 ];
+ }
+ mutexes.pop_back();
+ randomMutexes.push_back( mutex );
+ }
+
+ mutexes = randomMutexes;
+ return true;
+}
+
+// ----------------------------------------------------------------------------
+
+void SingleThreadComplexMultiLockTest( bool doSetup )
+{
+
+ ::srand( static_cast< unsigned int >( time( nullptr ) ) );
+ if ( doSetup )
+ {
+ Thing::MakePool( thingCount );
+ }
+
+ const unsigned int priorLevel = GetCurrentThreadsLevel();
+ const unsigned int priorLockCount = CountLocksInCurrentThread();
+ const unsigned int priorMutexCount = CountMutexesInCurrentThread();
+ const unsigned int priorLevelMutexCount = CountMutexesAtCurrentLevel();
+ if ( LevelMutexInfo::UnlockedLevel == priorLevel )
+ {
+ assert( priorLockCount == 0 );
+ assert( priorMutexCount == 0 );
+ assert( priorLevelMutexCount == 0 );
+ }
+ else
+ {
+ assert( priorLockCount != 0 );
+ assert( priorMutexCount != 0 );
+ assert( priorLevelMutexCount != 0 );
+ assert( priorLevelMutexCount <= priorMutexCount );
+ assert( priorMutexCount <= priorLockCount );
+ }
+
+ volatile Thing * thing0 = Thing::GetFromPool( 0 );
+ volatile Thing * thing1 = Thing::GetFromPool( 1 );
+ volatile Thing * thing2 = Thing::GetFromPool( 2 );
+ volatile Thing * thing3 = Thing::GetFromPool( 3 );
+ volatile Thing * thing4 = Thing::GetFromPool( 4 );
+ volatile Thing * thing5 = Thing::GetFromPool( 5 );
+ volatile Thing * thing6 = Thing::GetFromPool( 6 );
+ volatile Thing * thing7 = Thing::GetFromPool( 7 );
+ assert( nullptr != thing0 );
+ assert( nullptr != thing1 );
+ assert( nullptr != thing2 );
+ assert( nullptr != thing3 );
+ assert( nullptr != thing4 );
+ assert( nullptr != thing5 );
+ assert( nullptr != thing6 );
+ assert( nullptr != thing7 );
+
+ LevelMutexInfo::MutexContainer mutexes;
+ mutexes.reserve( thingCount );
+ MutexErrors::Type result = LevelMutexInfo::MultiLock( mutexes );
+ assert( result == MutexErrors::EmptyContainer );
+
+ mutexes.push_back( &thing0->GetMutex() );
+ mutexes.push_back( &thing1->GetMutex() );
+ mutexes.push_back( &thing2->GetMutex() );
+ mutexes.push_back( &thing3->GetMutex() );
+ mutexes.push_back( &thing4->GetMutex() );
+ mutexes.push_back( &thing5->GetMutex() );
+ mutexes.push_back( &thing6->GetMutex() );
+ mutexes.push_back( &thing7->GetMutex() );
+
+ result = LevelMutexInfo::MultiLock( mutexes );
+ assert( result == MutexErrors::Success );
+ assert( GetCurrentThreadsLevel() == thing0->GetMutex().GetLevel() );
+ assert( CountLocksInCurrentThread() == priorLockCount + thingCount );
+ result = LevelMutexInfo::MultiUnlock( mutexes );
+ assert( result == MutexErrors::Success );
+ assert( GetCurrentThreadsLevel() == priorLevel );
+ assert( CountLocksInCurrentThread() == priorLockCount );
+ assert( CountMutexesInCurrentThread() == priorMutexCount );
+ assert( CountMutexesAtCurrentLevel() == priorLevelMutexCount );
+
+ mutexes.clear();
+ mutexes.push_back( &thing7->GetMutex() );
+ mutexes.push_back( &thing6->GetMutex() );
+ mutexes.push_back( &thing5->GetMutex() );
+ mutexes.push_back( &thing4->GetMutex() );
+ mutexes.push_back( &thing3->GetMutex() );
+ mutexes.push_back( &thing2->GetMutex() );
+ mutexes.push_back( &thing1->GetMutex() );
+ mutexes.push_back( &thing0->GetMutex() );
+ result = LevelMutexInfo::MultiLock( mutexes );
+ assert( result == MutexErrors::Success );
+ assert( GetCurrentThreadsLevel() == thing0->GetMutex().GetLevel() );
+ assert( CountLocksInCurrentThread() == priorLockCount + thingCount );
+ result = LevelMutexInfo::MultiUnlock( mutexes );
+ assert( result == MutexErrors::Success );
+ assert( GetCurrentThreadsLevel() == priorLevel );
+ assert( CountLocksInCurrentThread() == priorLockCount );
+ assert( CountMutexesInCurrentThread() == priorMutexCount );
+ assert( CountMutexesAtCurrentLevel() == priorLevelMutexCount );
+
+ RandomizeMutexOrder( mutexes );
+ result = LevelMutexInfo::MultiLock( mutexes );
+ assert( result == MutexErrors::Success );
+ assert( GetCurrentThreadsLevel() == thing0->GetMutex().GetLevel() );
+ assert( CountLocksInCurrentThread() == priorLockCount + thingCount );
+ result = LevelMutexInfo::MultiUnlock( mutexes );
+ assert( result == MutexErrors::Success );
+ assert( GetCurrentThreadsLevel() == priorLevel );
+ assert( CountLocksInCurrentThread() == priorLockCount );
+ assert( CountMutexesInCurrentThread() == priorMutexCount );
+ assert( CountMutexesAtCurrentLevel() == priorLevelMutexCount );
+
+ if ( doSetup )
+ Thing::DestroyPool();
+}
+
+// ----------------------------------------------------------------------------
+
+void * MultiLockSafeThread( void * p )
+{
+
+ const unsigned int value = reinterpret_cast< unsigned int >( p );
+ LevelMutexInfo::MutexContainer mutexes( thingCount );
+ volatile Thing * thing = nullptr;
+ unsigned int jj = 0;
+ for ( ; jj < thingCount; ++jj )
+ {
+ thing = Thing::GetFromPool( jj );
+ assert( nullptr != thing );
+ mutexes[ jj ] = &thing->GetMutex();
+ }
+
+ unsigned int fails = 0;
+ unsigned int tests = 0;
+ unsigned int randomIndex = 0;
+ try
+ {
+ for ( unsigned int ii = 0; ii < thingCount; ++ii )
+ {
+ MultiMutexLocker locker( mutexes );
+ (void)locker;
+
+ for ( jj = 0; jj < thingCount; ++jj )
+ {
+ thing = Thing::GetFromPool( jj );
+ assert( nullptr != thing );
+ thing->SetValue( value );
+ }
+ ::GoToSleep( 2 );
+
+ if ( WillRedoSingleTests() )
+ {
+ SingleThreadSimpleTest();
+ SingleThreadReentrantTest();
+ SingleThreadSimpleMultiLockTest();
+ SingleThreadComplexMultiLockTest( false );
+ SingleThreadExceptionTest();
+ }
+
+ thing = Thing::GetFromPool( ii );
+ assert( nullptr != thing );
+ thing->Print( value, ii, 7 );
+ randomIndex = ( ::rand() % thingCount );
+ thing = Thing::GetFromPool( randomIndex );
+ assert( nullptr != thing );
+ thing->Print( value, ii + 10, 7 );
+
+ for ( jj = 0; jj < thingCount; ++jj )
+ {
+ thing = Thing::GetFromPool( jj );
+ assert( nullptr != thing );
+ if ( thing->GetValue() != value )
+ fails++;
+ tests++;
+ }
+
+ bool okay = locker.Unlock();
+ assert( okay );
+ ::GoToSleep( 2 );
+ }
+ }
+ catch ( ... )
+ {
+ assert( false );
+ }
+
+ TestResults::GetIt()->SetResult( value, tests, fails );
+
+ return nullptr;
+}
+
+// ----------------------------------------------------------------------------
+
+void * MultiLockUnsafeThread( void * p )
+{
+
+ const unsigned int value = reinterpret_cast< unsigned int >( p );
+ Thing * thing = nullptr;
+ unsigned int jj = 0;
+ unsigned int tests = 0;
+ unsigned int fails = 0;
+ unsigned int randomIndex = 0;
+ try
+ {
+ for ( unsigned int ii = 0; ii < thingCount; ++ii )
+ {
+ for ( jj = 0; jj < thingCount; ++jj )
+ {
+ thing = const_cast< Thing * >( Thing::GetFromPool( jj ) );
+ assert( nullptr != thing );
+ thing->SetValue( value );
+ }
+ ::GoToSleep( 2 );
+
+ if ( WillRedoSingleTests() )
+ {
+ SingleThreadSimpleTest();
+ SingleThreadReentrantTest();
+ SingleThreadSimpleMultiLockTest();
+ SingleThreadComplexMultiLockTest( false );
+ SingleThreadExceptionTest();
+ }
+
+ thing = const_cast< Thing * >( Thing::GetFromPool( ii ) );
+ assert( nullptr != thing );
+ thing->Print( value, ii, 7 );
+ randomIndex = ( ::rand() % thingCount );
+ thing = const_cast< Thing * >( Thing::GetFromPool( randomIndex ) );
+ assert( nullptr != thing );
+ thing->Print( value, ii, 7 );
+
+ for ( jj = 0; jj < thingCount; ++jj )
+ {
+ thing = const_cast< Thing * >( Thing::GetFromPool( jj ) );
+ assert( nullptr != thing );
+ if ( thing->GetValue() != value )
+ fails++;
+ tests++;
+ }
+
+ ::GoToSleep( 2 );
+ }
+ }
+ catch ( ... )
+ {
+ assert( false );
+ }
+
+ TestResults::GetIt()->SetResult( value, tests, fails );
+
+ return nullptr;
+}
+
+// ----------------------------------------------------------------------------
+
+void MultiThreadMultiLockTest( void )
+{
+ Thing::MakePool( thingCount );
+ const unsigned int threadCount = 8;
+ TestResults::Create( threadCount );
+ ThreadPool pool( threadCount );
+
+ cout << endl << "Doing thread-safe multi-lock test. This test should pass and not deadlock." << endl;
+ ::system( "pause" );
+ for ( unsigned int ii = 0; ii < threadCount; ++ii )
+ {
+ void * p = reinterpret_cast< void * >( ii );
+ pool.Start( MultiLockSafeThread, p );
+ }
+ pool.JoinAll();
+ TestResults::GetIt()->OutputResults();
+
+ cout << endl << "Doing thread-unsafe multi-lock test. This test may fail." << endl;
+ TestResults::GetIt()->Reset( threadCount );
+ ::system( "pause" );
+ for ( unsigned int ii = 0; ii < threadCount; ++ii )
+ {
+ void * p = reinterpret_cast< void * >( ii );
+ pool.Start( MultiLockUnsafeThread, p );
+ }
+ pool.JoinAll();
+ TestResults::GetIt()->OutputResults();
+
+ TestResults::Destroy();
+ Thing::DestroyPool();
+}
+
+// ----------------------------------------------------------------------------
+
+void * MultiLockRandomSafeThread( void * p )
+{
+ const unsigned int value = reinterpret_cast< unsigned int >( p );
+ unsigned int testCount = 0;
+ unsigned int failCount = 0;
+ volatile Thing * thing = nullptr;
+ Thing::ThingPool pool;
+ LevelMutexInfo::MutexContainer mutexes;
+ mutexes.reserve( thingCount );
+
+ unsigned int jj = 0;
+ unsigned int place = 0;
+ try
+ {
+ for ( unsigned int ii = 0; ii < thingCount; ++ii )
+ {
+
+ pool.clear();
+ pool.reserve( thingCount );
+ for ( place = 0; place < thingCount; ++place )
+ {
+ place += ::rand() % 3;
+ if ( thingCount <= place )
+ break;
+ thing = Thing::GetFromPool( place );
+ assert( nullptr != thing );
+ pool.push_back( thing );
+ }
+ const unsigned int poolCount = pool.size();
+
+ mutexes.clear();
+ for ( jj = 0; jj < poolCount; ++jj )
+ {
+ thing = pool[ jj ];
+ assert( nullptr != thing );
+ mutexes.push_back( &thing->GetMutex() );
+ }
+
+ MultiMutexLocker locker( mutexes );
+ (void)locker;
+ for ( jj = 0; jj < poolCount; ++jj )
+ {
+ thing = pool[ jj ];
+ assert( nullptr != thing );
+ thing->SetValue( value );
+ }
+ ::GoToSleep( 3 );
+
+ for ( jj = 0; jj < poolCount; ++jj )
+ {
+ thing = pool[ jj ];
+ assert( nullptr != thing );
+ if ( thing->GetValue() != value )
+ failCount++;
+ testCount++;
+ }
+ }
+ }
+ catch ( ... )
+ {
+ assert( false );
+ }
+
+ TestResults::GetIt()->SetResult( value, testCount, failCount );
+
+ return nullptr;
+}
+
+// ----------------------------------------------------------------------------
+
+void * MultiLockRandomUnsafeThread( void * p )
+{
+ const unsigned int value = reinterpret_cast< unsigned int >( p );
+ unsigned int testCount = 0;
+ unsigned int failCount = 0;
+ Thing * thing = nullptr;
+ UnsafeThingPool pool;
+
+ unsigned int jj = 0;
+ unsigned int place = 0;
+ try
+ {
+ for ( unsigned int ii = 0; ii < thingCount; ++ii )
+ {
+
+ pool.clear();
+ pool.reserve( thingCount );
+ for ( place = 0; place < thingCount; ++place )
+ {
+ place += ::rand() % 3;
+ if ( thingCount <= place )
+ break;
+ thing = thing = const_cast< Thing * >( Thing::GetFromPool( place ) );
+ assert( nullptr != thing );
+ pool.push_back( thing );
+ }
+ const unsigned int poolCount = pool.size();
+
+ for ( jj = 0; jj < poolCount; ++jj )
+ {
+ thing = pool[ jj ];
+ assert( nullptr != thing );
+ thing->SetValue( value );
+ }
+ ::GoToSleep( 3 );
+
+ for ( jj = 0; jj < poolCount; ++jj )
+ {
+ thing = pool[ jj ];
+ assert( nullptr != thing );
+ if ( thing->GetValue() != value )
+ failCount++;
+ testCount++;
+ }
+ }
+ }
+ catch ( ... )
+ {
+ assert( false );
+ }
+
+ TestResults::GetIt()->SetResult( value, testCount, failCount );
+
+ return nullptr;
+}
+
+// ----------------------------------------------------------------------------
+
+void MultiThreadRandomMultiLockTest( void )
+{
+ Thing::MakePool( thingCount );
+ const unsigned int threadCount = 8;
+ TestResults::Create( threadCount );
+ ThreadPool pool( threadCount );
+
+ cout << endl << "Doing thread-safe random multi-lock test. This test should pass and not deadlock." << endl;
+ ::system( "pause" );
+ for ( unsigned int ii = 0; ii < threadCount; ++ii )
+ {
+ void * p = reinterpret_cast< void * >( ii );
+ pool.Start( MultiLockRandomSafeThread, p );
+ }
+ pool.JoinAll();
+ TestResults::GetIt()->OutputResults();
+
+ cout << endl << "Doing thread-unsafe random multi-lock test. This test may fail." << endl;
+ TestResults::GetIt()->Reset( threadCount );
+ ::system( "pause" );
+ for ( unsigned int ii = 0; ii < threadCount; ++ii )
+ {
+ void * p = reinterpret_cast< void * >( ii );
+ pool.Start( MultiLockRandomUnsafeThread, p );
+ }
+ pool.JoinAll();
+ TestResults::GetIt()->OutputResults();
+
+ TestResults::Destroy();
+ Thing::DestroyPool();
+}
+
+// ----------------------------------------------------------------------------
+
+void * SafeHierarchyTest( void * p )
+{
+
+ const unsigned int value = reinterpret_cast< unsigned int >( p );
+ volatile LevelThing * thing = nullptr;
+ unsigned int testCount = 0;
+ unsigned int failCount = 0;
+
+ try
+ {
+ for ( unsigned int ii = 0; ii < thingCount; ++ii )
+ {
+ {
+ thing = LevelThing::GetFromPool( ii );
+ assert( nullptr != thing );
+ LevelThing::Unlocker unlocker( thing->LockHierarchy() );
+ (void)unlocker;
+ thing->SetValue( value );
+ ::GoToSleep( 3 );
+ if ( !thing->DoValuesMatch( value ) )
+ failCount++;
+ testCount++;
+ }
+ {
+ const unsigned int randomIndex = ( ::rand() % thingCount );
+ thing = LevelThing::GetFromPool( randomIndex );
+ assert( nullptr != thing );
+ LevelThing::Unlocker unlocker( thing->LockHierarchy() );
+ (void)unlocker;
+ thing->SetValue( value );
+ ::GoToSleep( 3 );
+ if ( !thing->DoValuesMatch( value ) )
+ failCount++;
+ testCount++;
+ }
+ }
+ }
+ catch ( ... )
+ {
+ assert( false );
+ }
+
+ TestResults::GetIt()->SetResult( value, testCount, failCount );
+
+ return nullptr;
+}
+
+// ----------------------------------------------------------------------------
+
+void * UnsafeHierarchyTest( void * p )
+{
+
+ const unsigned int value = reinterpret_cast< unsigned int >( p );
+ LevelThing * thing = nullptr;
+ unsigned int testCount = 0;
+ unsigned int failCount = 0;
+
+ try
+ {
+ for ( unsigned int ii = 0; ii < thingCount; ++ii )
+ {
+ {
+ thing = const_cast< LevelThing * >( LevelThing::GetFromPool( ii ) );
+ assert( nullptr != thing );
+ thing->SetValue( value );
+ ::GoToSleep( 3 );
+ if ( !thing->DoValuesMatch( value ) )
+ failCount++;
+ testCount++;
+ }
+ {
+ const unsigned int randomIndex = ( ::rand() % thingCount );
+ thing = const_cast< LevelThing * >( LevelThing::GetFromPool( randomIndex ) );
+ assert( nullptr != thing );
+ thing->SetValue( value );
+ ::GoToSleep( 3 );
+ if ( !thing->DoValuesMatch( value ) )
+ failCount++;
+ testCount++;
+ }
+ }
+ }
+ catch ( ... )
+ {
+ assert( false );
+ }
+
+ TestResults::GetIt()->SetResult( value, testCount, failCount );
+
+ return nullptr;
+}
+
+// ----------------------------------------------------------------------------
+
+void MultiThreadHierarchySingleLockTest( void )
+{
+ LevelThing::MakePool( thingCount );
+ const unsigned int threadCount = 8;
+ TestResults::Create( threadCount );
+ ThreadPool pool( threadCount );
+
+ cout << endl << "Doing thread-safe hierarchy test. This test should pass and not deadlock." << endl;
+ ::system( "pause" );
+ for ( unsigned int ii = 0; ii < threadCount; ++ii )
+ {
+ void * p = reinterpret_cast< void * >( ii );
+ pool.Start( SafeHierarchyTest, p );
+ }
+ pool.JoinAll();
+ TestResults::GetIt()->OutputResults();
+
+ cout << endl << "Doing thread-unsafe hierarchy test. This test may fail." << endl;
+ ::system( "pause" );
+ TestResults::GetIt()->Reset( threadCount );
+ for ( unsigned int ii = 0; ii < threadCount; ++ii )
+ {
+ void * p = reinterpret_cast< void * >( ii );
+ pool.Start( UnsafeHierarchyTest, p );
+ }
+ pool.JoinAll();
+ TestResults::GetIt()->OutputResults();
+
+ TestResults::Destroy();
+ LevelThing::DestroyPool();
+}
+
+// ----------------------------------------------------------------------------
+
+void * SafeHierarchyMultiLockTest( void * p )
+{
+
+ const unsigned int value = reinterpret_cast< unsigned int >( p );
+ unsigned int testCount = 0;
+ unsigned int failCount = 0;
+ unsigned int totalTestCount = 0;
+ unsigned int totalFailCount = 0;
+
+ try
+ {
+ for ( unsigned int ii = 0; ii < 20; ++ii )
+ {
+ SomeThingPool pool;
+ MakePool( pool );
+ assert( CountLockedByThisThread( pool ) == 0 );
+ LockThese( pool );
+ assert( CountLockedByThisThread( pool ) == pool.size() );
+
+ for ( SomeThingPoolIter it( pool.begin() );
+ it != pool.end();
+ ++it )
+ {
+ volatile SomeThing * thing = *it;
+ assert( nullptr != thing );
+ thing->SetValue( value );
+ }
+
+ ::GoToSleep( 2 );
+ testCount = 0;
+ failCount = 0;
+ CheckForMatchingValues( failCount, testCount, value, pool, true );
+ totalTestCount += testCount;
+ totalFailCount += failCount;
+
+ UnlockThese( pool );
+ assert( CountLockedByThisThread( pool ) == 0 );
+ }
+ }
+ catch ( ... )
+ {
+ assert( false );
+ }
+
+ TestResults::GetIt()->SetResult( value, totalTestCount, totalFailCount );
+
+ return nullptr;
+}
+
+// ----------------------------------------------------------------------------
+
+void * UnsafeHierarchyMultiLockTest( void * p )
+{
+
+ const unsigned int value = reinterpret_cast< unsigned int >( p );
+ unsigned int testCount = 0;
+ unsigned int failCount = 0;
+ unsigned int totalTestCount = 0;
+ unsigned int totalFailCount = 0;
+
+ try
+ {
+ for ( unsigned int ii = 0; ii < 20; ++ii )
+ {
+ SomeThingPool pool;
+ MakePool( pool );
+ for ( SomeThingPoolIter it( pool.begin() );
+ it != pool.end();
+ ++it )
+ {
+ SomeThing * thing = const_cast< SomeThing * >( *it );
+ assert( nullptr != thing );
+ thing->SetValue( value );
+ }
+ ::GoToSleep( 2 );
+ testCount = 0;
+ failCount = 0;
+ CheckForMatchingValues( failCount, testCount, value, pool );
+ totalTestCount += testCount;
+ totalFailCount += failCount;
+ }
+ }
+ catch ( ... )
+ {
+ assert( false );
+ }
+
+ TestResults::GetIt()->SetResult( value, totalTestCount, totalFailCount );
+
+ return nullptr;
+}
+
+// ----------------------------------------------------------------------------
+
+void MultiThreadHierarchyMultiLockTest( void )
+{
+
+ MultiLevelPool::MakePool( 10, thingCount );
+ const unsigned int threadCount = 8;
+ TestResults::Create( threadCount );
+ ThreadPool pool( threadCount );
+
+ cout << endl << "Doing thread-safe multilock hierarchy test. This test should pass and not deadlock." << endl;
+ ::system( "pause" );
+ for ( unsigned int ii = 0; ii < threadCount; ++ii )
+ {
+ void * p = reinterpret_cast< void * >( ii );
+ pool.Start( SafeHierarchyMultiLockTest, p );
+ }
+ pool.JoinAll();
+ TestResults::GetIt()->OutputResults();
+
+ cout << endl << "Doing thread-unsafe multilock hierarchy test. This test may fail." << endl;
+ ::system( "pause" );
+ TestResults::GetIt()->Reset( threadCount );
+ for ( unsigned int ii = 0; ii < threadCount; ++ii )
+ {
+ void * p = reinterpret_cast< void * >( ii );
+ pool.Start( UnsafeHierarchyMultiLockTest, p );
+ }
+ pool.JoinAll();
+ TestResults::GetIt()->OutputResults();
+
+ TestResults::Destroy();
+ MultiLevelPool::DestroyPool();
+}
+
+// ----------------------------------------------------------------------------
diff --git a/test/LevelMutex/MultiThreadTests.hpp b/test/LevelMutex/MultiThreadTests.hpp
new file mode 100644
index 0000000..42cd621
--- /dev/null
+++ b/test/LevelMutex/MultiThreadTests.hpp
@@ -0,0 +1,55 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+// Part of LevelMutex test program for The Loki Library
+// Copyright (c) 2008 Richard Sposato
+// The copyright on this file is protected under the terms of the MIT license.
+//
+// Permission to use, copy, modify, distribute and sell this software for any
+// purpose is hereby granted without fee, provided that the above copyright
+// notice appear in all copies and that both that copyright notice and this
+// permission notice appear in supporting documentation.
+//
+// The author makes no representations about the suitability of this software
+// for any purpose. It is provided "as is" without express or implied warranty.
+//
+////////////////////////////////////////////////////////////////////////////////
+
+// $Id$
+
+
+#ifndef LOKI_MULTI_THREAD_TESTS_H_INCLUDED
+#define LOKI_MULTI_THREAD_TESTS_H_INCLUDED
+
+// ----------------------------------------------------------------------------
+
+void SetToRedoSingleTests( bool redo );
+
+bool WillRedoSingleTests( void );
+
+void SingleThreadSimpleTest( void );
+
+void SingleThreadReentrantTest( void );
+
+void SingleThreadSimpleMultiLockTest( void );
+
+void SingleThreadComplexMultiLockTest( bool doSetup );
+
+void SingleThreadExceptionTest( void );
+
+void MultiThreadSimpleTest( void );
+
+void MultiThreadTryLockTest( void );
+
+void MultiThreadReentrantTest( void );
+
+void MultiThreadMultiLockTest( void );
+
+void MultiThreadRandomMultiLockTest( void );
+
+void MultiThreadHierarchySingleLockTest( void );
+
+void MultiThreadHierarchyMultiLockTest( void );
+
+// ----------------------------------------------------------------------------
+
+#endif
diff --git a/test/LevelMutex/Thing.cpp b/test/LevelMutex/Thing.cpp
new file mode 100644
index 0000000..7c34cfd
--- /dev/null
+++ b/test/LevelMutex/Thing.cpp
@@ -0,0 +1,922 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+// Part of LevelMutex test program for The Loki Library
+// Copyright (c) 2008 Richard Sposato
+// The copyright on this file is protected under the terms of the MIT license.
+//
+// Permission to use, copy, modify, distribute and sell this software for any
+// purpose is hereby granted without fee, provided that the above copyright
+// notice appear in all copies and that both that copyright notice and this
+// permission notice appear in supporting documentation.
+//
+// The author makes no representations about the suitability of this software
+// for any purpose. It is provided "as is" without express or implied warranty.
+//
+////////////////////////////////////////////////////////////////////////////////
+
+// $Id$
+
+
+// ----------------------------------------------------------------------------
+
+#include "Thing.hpp"
+
+#include
+#include
+#include
+
+#include
+#include
+
+using namespace ::Loki;
+
+
+// ----------------------------------------------------------------------------
+
+typedef ::Loki::LockingPtr< Thing, volatile SleepMutex > SingleThingLocker;
+
+typedef ::Loki::LockingPtr< const Thing, volatile SleepMutex > ConstSingleThingLocker;
+
+typedef ::Loki::LockingPtr< LevelThing, volatile SleepMutex > LevelThingLocker;
+
+typedef ::Loki::LockingPtr< const LevelThing, volatile SleepMutex > ConstLevelThingLocker;
+
+typedef ::Loki::LockingPtr< SomeThing, volatile SleepMutex > SomeThingLocker;
+
+typedef ::Loki::LockingPtr< const SomeThing, volatile SleepMutex > ConstSomeThingLocker;
+
+volatile Thing * Thing::s_thing = NULL;
+
+Thing::ThingPool Thing::s_pool;
+
+LevelThing::LevelThingPool LevelThing::s_pool;
+
+TestResults * TestResults::s_instance = NULL;
+
+MultiLevelPool::MultiThingPool MultiLevelPool::s_pool;
+
+
+// ----------------------------------------------------------------------------
+
+ExceptionTossingMutex::ExceptionTossingMutex( unsigned int level ) :
+#if defined( _MSC_VER )
+ SleepLevelMutex( level ),
+#else
+ SleepLevelMutex( level ),
+#endif
+ m_policy( ExceptionTossingMutex::Never )
+{
+ assert( NULL != this );
+#if defined( _MSC_VER )
+ SetSleepTime( 5 );
+ SetWakable( false );
+#else
+ SetSleepTime( 1 );
+#endif
+}
+
+// ----------------------------------------------------------------------------
+
+ExceptionTossingMutex::~ExceptionTossingMutex( void )
+{
+ assert( NULL != this );
+}
+
+// ----------------------------------------------------------------------------
+
+MutexErrors::Type ExceptionTossingMutex::Lock( void ) volatile
+{
+ if ( m_policy != ExceptionTossingMutex::Never )
+ {
+ bool toss = ( m_policy == ExceptionTossingMutex::Always );
+ if ( !toss )
+ {
+ const unsigned int number = ( ::rand() % 16 );
+ toss = ( 0 == number );
+ }
+ if ( toss )
+ {
+ throw MutexException( "Just a test exception from DoLock.",
+ GetLevel(), ::Loki::MutexErrors::OtherError );
+ }
+ }
+ return SleepLevelMutex::Lock();
+}
+
+// ----------------------------------------------------------------------------
+
+MutexErrors::Type ExceptionTossingMutex::TryLock( void ) volatile
+{
+ if ( m_policy != ExceptionTossingMutex::Never )
+ {
+ bool toss = ( m_policy == ExceptionTossingMutex::Always );
+ if ( !toss )
+ {
+ const unsigned int number = ( ::rand() % 16 );
+ toss = ( 0 == number );
+ }
+ if ( toss )
+ {
+ throw MutexException( "Just a test exception from DoLock.",
+ GetLevel(), ::Loki::MutexErrors::OtherError );
+ }
+ }
+ return SleepLevelMutex::TryLock();
+}
+
+// ----------------------------------------------------------------------------
+
+MutexErrors::Type ExceptionTossingMutex::Unlock( void ) volatile
+{
+ if ( m_policy != ExceptionTossingMutex::Never )
+ {
+ bool toss = ( m_policy == ExceptionTossingMutex::Always );
+ if ( !toss )
+ {
+ const unsigned int number = ( ::rand() % 16 );
+ toss = ( 0 == number );
+ }
+ if ( toss )
+ {
+ throw MutexException( "Just a test exception from DoLock.",
+ GetLevel(), ::Loki::MutexErrors::OtherError );
+ }
+ }
+ return SleepLevelMutex::Unlock();
+}
+
+// ----------------------------------------------------------------------------
+
+bool TestResults::Create( unsigned int threadCount )
+{
+ if ( NULL != s_instance )
+ return true;
+ s_instance = new TestResults( threadCount );
+ return ( NULL != s_instance );
+}
+
+// ----------------------------------------------------------------------------
+
+void TestResults::Destroy( void )
+{
+ delete s_instance;
+ s_instance = NULL;
+}
+
+// ----------------------------------------------------------------------------
+
+TestResults::TestResults( unsigned int threadCount ) :
+ m_results()
+{
+ assert( NULL != this );
+ Reset( threadCount );
+}
+
+// ----------------------------------------------------------------------------
+
+TestResults::~TestResults( void )
+{
+ assert( NULL != this );
+ assert( this == s_instance );
+}
+
+// ----------------------------------------------------------------------------
+
+void TestResults::Reset( unsigned int threadCount )
+{
+ assert( NULL != this );
+ TestResult result;
+ result.m_total = 0;
+ result.m_fails = 0;
+ m_results.clear();
+ m_results.resize( threadCount, result );
+}
+
+// ----------------------------------------------------------------------------
+
+void TestResults::SetResult( unsigned int threadIndex, unsigned int total,
+ unsigned int fails )
+{
+ assert( NULL != this );
+ assert( this == s_instance );
+
+ if ( threadIndex <= m_results.size() )
+ {
+ m_results[ threadIndex ].m_total = total;
+ m_results[ threadIndex ].m_fails = fails;
+ }
+}
+
+// ----------------------------------------------------------------------------
+
+void TestResults::OutputResults( void )
+{
+ assert( NULL != this );
+ assert( this == s_instance );
+
+ bool passed = true;
+ const unsigned int count = m_results.size();
+ for ( unsigned int ii = 0; ii < count; ++ii )
+ {
+ Printf( "Thread: %d \t total: %d \t fails: %d\n" )( ii )
+ ( m_results[ ii ].m_total )
+ ( m_results[ ii ].m_fails );
+ if ( m_results[ ii ].m_fails != 0 )
+ passed = false;
+ m_results[ ii ].m_total = 0;
+ m_results[ ii ].m_fails = 0;
+ }
+ const char * message = ( passed ) ? "Passed!" : "FAILED!";
+ Printf( "Value Test Result: %s\n" )( message );
+}
+
+// ----------------------------------------------------------------------------
+
+TestResults::TestResult::TestResult( void ) :
+ m_total( 0 ),
+ m_fails( 0 )
+{
+ assert( NULL != this );
+}
+
+// ----------------------------------------------------------------------------
+
+TestResults::TestResult::TestResult( const TestResult & that ) :
+ m_total( that.m_total ),
+ m_fails( that.m_fails )
+{
+ assert( NULL != this );
+}
+
+// ----------------------------------------------------------------------------
+
+TestResults::TestResult::~TestResult( void )
+{
+ assert( NULL != this );
+}
+
+// ----------------------------------------------------------------------------
+
+void GoToSleep( unsigned int milliSeconds )
+{
+#if defined( _MSC_VER )
+ ::SleepEx( milliSeconds, true );
+#elif ( __GNUC__ )
+ unsigned int seconds = milliSeconds / 1000;
+ if ( 0 == seconds )
+ seconds = 1;
+ ::_sleep( seconds );
+#else
+ #error "Find out if your compiler supports a sleep command and add it here."
+#endif
+}
+
+// ----------------------------------------------------------------------------
+
+void Thing::Init( unsigned int value )
+{
+ if ( NULL != s_thing )
+ return;
+ s_thing = new Thing( value );
+}
+
+// ----------------------------------------------------------------------------
+
+void Thing::Destroy( void )
+{
+ if ( NULL == s_thing )
+ return;
+ delete s_thing;
+ s_thing = NULL;
+}
+
+// ----------------------------------------------------------------------------
+
+volatile Thing * Thing::GetFromPool( unsigned int index )
+{
+ if ( s_pool.size() <= index )
+ return NULL;
+ volatile Thing * thing = s_pool[ index ];
+ return thing;
+}
+
+// ----------------------------------------------------------------------------
+
+unsigned int Thing::GetPoolSize( void )
+{
+ return s_pool.size();
+}
+
+// ----------------------------------------------------------------------------
+
+bool Thing::MakePool( unsigned int count )
+{
+ if ( ( 0 == count ) || ( 100 < count ) )
+ return false;
+
+ DestroyPool();
+ s_pool.reserve( count );
+ for ( unsigned int ii = 0; ii < count; ++ii )
+ {
+ volatile Thing * thing = new Thing( ii );
+ s_pool.push_back( thing );
+ }
+
+ return true;
+}
+
+// ----------------------------------------------------------------------------
+
+void Thing::DestroyPool( void )
+{
+ const unsigned int count = s_pool.size();
+ for ( unsigned int ii = 0; ii < count; ++ii )
+ {
+ volatile Thing * thing = s_pool[ ii ];
+ delete thing;
+ }
+ s_pool.clear();
+}
+
+// ----------------------------------------------------------------------------
+
+Thing::Thing( unsigned int value ) :
+ m_mutex( 10 ),
+ m_value( value )
+{
+ assert( NULL != this );
+#if defined( _MSC_VER )
+ m_mutex.GetMutexPolicy().SetSleepTime( 5 );
+ m_mutex.GetMutexPolicy().SetWakable( false );
+#else
+ m_mutex.GetMutexPolicy().SetSleepTime( 1 );
+#endif
+}
+
+// ----------------------------------------------------------------------------
+
+Thing::~Thing( void )
+{
+ assert( NULL != this );
+}
+
+// ----------------------------------------------------------------------------
+
+void Thing::Print( unsigned int value, unsigned int index, unsigned int startSize ) const volatile
+{
+ assert( NULL != this );
+ volatile SleepMutex & mutex = const_cast< volatile SleepMutex & >( m_mutex );
+ ConstSingleThingLocker pSafeThis( *this, mutex );
+ pSafeThis->Print( value, index, startSize );
+}
+
+// ----------------------------------------------------------------------------
+
+void Thing::Print( unsigned int value, unsigned int index, unsigned int startSize ) const
+{
+ assert( NULL != this );
+ switch ( startSize )
+ {
+ default:
+ case 16: ::GoToSleep( 3 ); Printf("%d: %d: ----------------\n")( value )( index );
+ case 15: ::GoToSleep( 3 ); Printf("%d: %d: ---------------\n")( value )( index );
+ case 14: ::GoToSleep( 3 ); Printf("%d: %d: --------------\n")( value )( index );
+ case 13: ::GoToSleep( 3 ); Printf("%d: %d: -------------\n")( value )( index );
+ case 12: ::GoToSleep( 3 ); Printf("%d: %d: ------------\n")( value )( index );
+ case 11: ::GoToSleep( 3 ); Printf("%d: %d: -----------\n")( value )( index );
+ case 10: ::GoToSleep( 3 ); Printf("%d: %d: ----------\n")( value )( index );
+ case 9: ::GoToSleep( 3 ); Printf("%d: %d: ---------\n")( value )( index );
+ case 8: ::GoToSleep( 3 ); Printf("%d: %d: --------\n")( value )( index );
+ case 7: ::GoToSleep( 3 ); Printf("%d: %d: -------\n")( value )( index );
+ case 6: ::GoToSleep( 3 ); Printf("%d: %d: ------\n")( value )( index );
+ case 5: ::GoToSleep( 3 ); Printf("%d: %d: -----\n")( value )( index );
+ case 4: ::GoToSleep( 3 ); Printf("%d: %d: ----\n")( value )( index );
+ case 3: ::GoToSleep( 3 ); Printf("%d: %d: ---\n")( value )( index );
+ case 2: ::GoToSleep( 3 ); Printf("%d: %d: --\n")( value )( index );
+ case 1: ::GoToSleep( 3 ); Printf("%d: %d: -\n")( value )( index );
+ case 0: ::GoToSleep( 3 ); Printf("%d: %d: \n")( value )( index );
+ }
+}
+
+// ----------------------------------------------------------------------------
+
+void Thing::SetValue( unsigned int value ) volatile
+{
+ assert( NULL != this );
+ SingleThingLocker pSafeThis( *this, m_mutex );
+ pSafeThis->m_value = value;
+}
+
+// ----------------------------------------------------------------------------
+
+volatile LevelThing * LevelThing::GetFromPool( unsigned int index )
+{
+ if ( s_pool.size() <= index )
+ return NULL;
+ volatile LevelThing * thing = s_pool[ index ];
+ return thing;
+}
+
+// ----------------------------------------------------------------------------
+
+bool LevelThing::MakePool( unsigned int count )
+{
+ s_pool.clear();
+ s_pool.reserve( count );
+ for ( unsigned int ii = 0; ii < count; ++ii )
+ {
+ volatile LevelThing * thing = new LevelThing( ii * 10, ii );
+ s_pool.push_back( thing );
+ }
+ return true;
+}
+
+// ----------------------------------------------------------------------------
+
+void LevelThing::DestroyPool( void )
+{
+ const unsigned int count = s_pool.size();
+ for ( unsigned int ii = 0; ii < count; ++ii )
+ {
+ volatile LevelThing * thing = s_pool[ ii ];
+ delete thing;
+ }
+ s_pool.clear();
+}
+
+// ----------------------------------------------------------------------------
+
+LevelThing::LevelThing( unsigned int level, unsigned int place ) :
+ m_mutex( level ),
+ m_place( place ),
+ m_value( 0 )
+{
+ assert( NULL != this );
+#if defined( _MSC_VER )
+ m_mutex.GetMutexPolicy().SetSleepTime( 5 );
+ m_mutex.GetMutexPolicy().SetWakable( false );
+#else
+ m_mutex.GetMutexPolicy().SetSleepTime( 1 );
+#endif
+}
+
+// ----------------------------------------------------------------------------
+
+LevelThing::~LevelThing( void )
+{
+ assert( NULL != this );
+}
+
+// ----------------------------------------------------------------------------
+
+LevelThing::Unlocker LevelThing::LockHierarchy( void ) volatile
+{
+ assert( NULL != this );
+ for ( signed ii = m_place; 0 <= ii; --ii )
+ {
+ volatile LevelThing * thing = s_pool[ ii ];
+ assert( NULL != thing );
+ const MutexErrors::Type result = thing->m_mutex.Lock();
+ if ( MutexErrors::Success != result )
+ break;
+ }
+
+ Unlocker unlocker( this );
+ return unlocker;
+}
+
+// ----------------------------------------------------------------------------
+
+void LevelThing::UnlockHierarchy( void ) volatile
+{
+ assert( NULL != this );
+ try
+ {
+ for ( unsigned ii = 0; ii <= m_place; ++ii )
+ {
+ volatile LevelThing * thing = s_pool[ ii ];
+ assert( NULL != thing );
+ thing->m_mutex.Unlock();
+ }
+ }
+ catch ( ... )
+ {
+ }
+}
+
+// ----------------------------------------------------------------------------
+
+void LevelThing::SetValue( unsigned int value ) volatile
+{
+ assert( NULL != this );
+ MutexLocker locker( m_mutex, !m_mutex.IsLockedByCurrentThread() );
+ (void)locker;
+ m_value = value;
+ if ( 0 == m_place )
+ return;
+
+ volatile LevelThing * thing = s_pool[ m_place - 1 ];
+ assert( NULL != thing );
+ thing->SetValue( value );
+}
+
+// ----------------------------------------------------------------------------
+
+void LevelThing::SetValue( unsigned int value )
+{
+ assert( NULL != this );
+ m_value = value;
+ if ( 0 == m_place )
+ return;
+
+ LevelThing * thing = const_cast< LevelThing * >( s_pool[ m_place - 1 ] );
+ assert( NULL != thing );
+ thing->SetValue( value );
+}
+
+// ----------------------------------------------------------------------------
+
+bool LevelThing::DoValuesMatch( unsigned int value ) const volatile
+{
+ assert( NULL != this );
+ {
+ MutexLocker locker( m_mutex, !m_mutex.IsLockedByCurrentThread() );
+ (void)locker;
+ if ( m_value != value )
+ return false;
+ }
+ if ( 0 == m_place )
+ return true;
+
+ const volatile LevelThing * thing = s_pool[ m_place - 1 ];
+ assert( NULL != thing );
+ return thing->DoValuesMatch( value );
+}
+
+// ----------------------------------------------------------------------------
+
+bool LevelThing::DoValuesMatch( unsigned int value ) const
+{
+ assert( NULL != this );
+ if ( m_value != value )
+ return false;
+ if ( 0 == m_place )
+ return true;
+
+ const LevelThing * thing = const_cast< LevelThing * >( s_pool[ m_place - 1 ] );
+ assert( NULL != thing );
+ return thing->DoValuesMatch( value );
+}
+
+// ----------------------------------------------------------------------------
+
+SomeThing::SomeThing( unsigned int level, unsigned int place ) :
+ m_mutex( level ),
+ m_place( place ),
+ m_level( level ),
+ m_value( 0 )
+{
+ assert( NULL != this );
+#if defined( _MSC_VER )
+ m_mutex.GetMutexPolicy().SetSleepTime( 5 );
+ m_mutex.GetMutexPolicy().SetWakable( false );
+#else
+ m_mutex.GetMutexPolicy().SetSleepTime( 1 );
+#endif
+}
+
+// ----------------------------------------------------------------------------
+
+SomeThing::~SomeThing( void )
+{
+ assert( NULL != this );
+}
+
+// ----------------------------------------------------------------------------
+
+void SomeThing::SetValue( unsigned int value ) volatile
+{
+ assert( NULL != this );
+ SomeThingLocker pSafeThis( *this, m_mutex );
+ pSafeThis->m_value = value;
+}
+
+// ----------------------------------------------------------------------------
+
+void SomeThing::SetValue( unsigned int value )
+{
+ assert( NULL != this );
+ m_value = value;
+}
+
+// ----------------------------------------------------------------------------
+
+ManyThingsPool::ManyThingsPool( unsigned int level, unsigned int count ) :
+ m_pool()
+{
+ assert( NULL != this );
+ m_pool.reserve( count );
+ for ( unsigned int ii = 0; ii < count; ++ii )
+ {
+ volatile SomeThing * thing = new SomeThing( level, ii );
+ m_pool.push_back( thing );
+ }
+}
+
+// ----------------------------------------------------------------------------
+
+ManyThingsPool::~ManyThingsPool( void )
+{
+ assert( NULL != this );
+ const unsigned int count = m_pool.size();
+ for ( unsigned int ii = 0; ii < count; ++ii )
+ {
+ volatile SomeThing * thing = m_pool[ ii ];
+ delete thing;
+ }
+ m_pool.clear();
+}
+
+// ----------------------------------------------------------------------------
+
+unsigned int ManyThingsPool::GetCount( void ) const
+{
+ assert( NULL != this );
+ const unsigned int count = m_pool.size();
+ return count;
+}
+
+// ----------------------------------------------------------------------------
+
+volatile SomeThing * ManyThingsPool::GetFromPool( unsigned int index )
+{
+ assert( NULL != this );
+ ManyThingsPool * pThis = const_cast< ManyThingsPool * >( this );
+ if ( pThis->m_pool.size() <= index )
+ return NULL;
+ volatile SomeThing * thing = pThis->m_pool[ index ];
+ assert( NULL != thing );
+ return thing;
+}
+
+// ----------------------------------------------------------------------------
+
+void MultiLevelPool::MakePool( unsigned int count, unsigned int thingCount )
+{
+ s_pool.clear();
+ s_pool.reserve( count );
+ unsigned int level = 100;
+ for ( unsigned int ii = 0; ii < count; ++ii )
+ {
+ const unsigned int newCount = ( ::rand() % thingCount ) + 4;
+ ManyThingsPool * pool = new ManyThingsPool( ii + level, newCount );
+ s_pool.push_back( pool );
+ }
+}
+
+// ----------------------------------------------------------------------------
+
+void MultiLevelPool::DestroyPool( void )
+{
+ const unsigned int count = s_pool.size();
+ for ( unsigned int ii = 0; ii < count; ++ii )
+ {
+ ManyThingsPool * pool = s_pool[ ii ];
+ delete pool;
+ }
+ s_pool.clear();
+}
+
+// ----------------------------------------------------------------------------
+
+unsigned int MultiLevelPool::GetCount( void )
+{
+ const unsigned int count = s_pool.size();
+ return count;
+}
+
+// ----------------------------------------------------------------------------
+
+ManyThingsPool * MultiLevelPool::GetFromPool( unsigned int index )
+{
+ if ( s_pool.size() <= index )
+ return NULL;
+ ManyThingsPool * pool = s_pool[ index ];
+ assert( NULL != pool );
+ return pool;
+}
+
+// ----------------------------------------------------------------------------
+
+void CheckForMatchingValues( unsigned int & failCount, unsigned int & testCount,
+ unsigned int value, const SomeThingPool & pool )
+{
+ const unsigned int count = pool.size();
+ for ( unsigned int ii = 0; ii < count; ++ii )
+ {
+ SomeThing * thing = const_cast< SomeThing * >( pool[ ii ] );
+ assert( NULL != thing );
+ testCount++;
+ if ( value != thing->GetValue() )
+ failCount++;
+ }
+}
+
+// ----------------------------------------------------------------------------
+
+void CheckForMatchingValues( unsigned int & failCount, unsigned int & testCount,
+ unsigned int value, const SomeThingPool & pool, bool locked )
+{
+ if ( !locked )
+ {
+ CheckForMatchingValues( failCount, testCount, value, pool );
+ return;
+ }
+
+ const unsigned int count = pool.size();
+ for ( unsigned int ii = 0; ii < count; ++ii )
+ {
+ volatile SomeThing * thing = pool[ ii ];
+ assert( NULL != thing );
+ assert( thing->GetMutex().IsLockedByCurrentThread() );
+ testCount++;
+ if ( value != thing->GetValue() )
+ failCount++;
+ }
+}
+
+// ----------------------------------------------------------------------------
+
+void MakePool( SomeThingPool & target )
+{
+ SomeThingPool temp;
+ bool useThis = false;
+ unsigned int count2 = 0;
+ unsigned int r = 0;
+ bool first = true;
+ const unsigned int count1 = MultiLevelPool::GetCount();
+
+ for ( unsigned int index1 = 0; index1 < count1; ++index1 )
+ {
+ r = ( ::rand() % 4 );
+ useThis = ( 0 == index1 ) || ( r != 0 );
+ if ( !useThis )
+ continue;
+
+ ManyThingsPool * pool = MultiLevelPool::GetFromPool( index1 );
+ assert( NULL != pool );
+ count2 = pool->GetCount();
+ first = true;
+
+ for ( unsigned int index2 = 0; index2 < count2; ++index2 )
+ {
+ r = ( ::rand() % 4 );
+ useThis = ( 0 == index1 ) || ( r != 0 );
+ if ( !useThis )
+ continue;
+ volatile SomeThing * thing = pool->GetFromPool( index2 );
+ assert( NULL != thing );
+ temp.push_back( thing );
+
+ if ( first )
+ {
+ // The containers at most levels will have multiple mutexes,
+ // but some levels will have just a single mutex.
+ r = ( ::rand() % 4 );
+ if ( r != 0 )
+ break;
+ }
+ first = false;
+ }
+ }
+
+ temp.swap( target );
+}
+
+// ----------------------------------------------------------------------------
+
+/// @struct SomeThingComparator Binary-functor for comparing 2 SomeThing's.
+struct SomeThingComparator : std::binary_function< volatile SomeThing *, volatile SomeThing *, bool >
+{
+ /// Returns true if left string < right string.
+ inline bool operator ()( volatile SomeThing * lhs, volatile SomeThing * rhs ) const
+ {
+ return ( lhs->GetLevel() < rhs->GetLevel() );
+ }
+};
+
+// ----------------------------------------------------------------------------
+
+void SortByLevel( SomeThingPool & pool )
+{
+ const unsigned int count = pool.size();
+ assert( 0 != count );
+ SomeThingComparator comparator;
+ ::std::sort( pool.begin(), pool.end(), comparator );
+}
+
+// ----------------------------------------------------------------------------
+
+void LockThese( SomeThingPool & pool )
+{
+
+ const unsigned int count = pool.size();
+ assert( 0 != count );
+ SortByLevel( pool );
+
+ volatile SomeThing * thing = pool[ count - 1 ];
+ assert( NULL != thing );
+ unsigned int level = thing->GetLevel();
+ LevelMutexInfo::MutexContainer mutexes;
+ MutexErrors::Type result = MutexErrors::Success;
+ signed int ii = count - 1;
+
+ for ( ; 0 <= ii; --ii )
+ {
+ thing = pool[ ii ];
+ assert( NULL != thing );
+ if ( level != thing->GetLevel() )
+ {
+ assert( mutexes.size() != 0 );
+ result = LevelMutexInfo::MultiLock( mutexes );
+ assert( result == MutexErrors::Success );
+ mutexes.clear();
+ level = thing->GetLevel();
+ }
+ mutexes.push_back( &thing->GetMutex() );
+ }
+
+ assert( mutexes.size() != 0 );
+ result = LevelMutexInfo::MultiLock( mutexes );
+ assert( result == MutexErrors::Success );
+
+ for ( ii = count - 1; 0 <= ii; --ii )
+ {
+ thing = pool[ ii ];
+ assert( NULL != thing );
+ assert( thing->GetMutex().IsLockedByCurrentThread() );
+ }
+}
+
+// ----------------------------------------------------------------------------
+
+void UnlockThese( SomeThingPool & pool )
+{
+
+ const unsigned int count = pool.size();
+ assert( 0 != count );
+ SortByLevel( pool );
+
+ volatile SomeThing * thing = pool[ 0 ];
+ assert( NULL != thing );
+ unsigned int level = thing->GetLevel();
+ LevelMutexInfo::MutexContainer mutexes;
+ MutexErrors::Type result = MutexErrors::Success;
+ unsigned int ii = 0;
+
+ for ( ii = 0; ii < count; ++ii )
+ {
+ thing = pool[ ii ];
+ assert( NULL != thing );
+ if ( level != thing->GetLevel() )
+ {
+ assert( mutexes.size() != 0 );
+ result = LevelMutexInfo::MultiUnlock( mutexes );
+ assert( result == MutexErrors::Success );
+ mutexes.clear();
+ level = thing->GetLevel();
+ }
+ mutexes.push_back( &thing->GetMutex() );
+ }
+
+ assert( mutexes.size() != 0 );
+ result = LevelMutexInfo::MultiUnlock( mutexes );
+ assert( result == MutexErrors::Success );
+
+ for ( ii = 0; ii < count; ++ii )
+ {
+ thing = pool[ ii ];
+ assert( NULL != thing );
+ assert( !thing->GetMutex().IsLockedByCurrentThread() );
+ }
+}
+
+// ----------------------------------------------------------------------------
+
+unsigned int CountLockedByThisThread( const SomeThingPool & pool )
+{
+ const unsigned int count = pool.size();
+ unsigned int locked = 0;
+
+ for ( unsigned int ii = 0; ii < count; ++ii )
+ {
+ const volatile SomeThing * thing = pool[ ii ];
+ assert( NULL != thing );
+ if ( thing->GetMutex().IsLockedByCurrentThread() )
+ locked++;
+ }
+
+ return locked;
+}
+
+// ----------------------------------------------------------------------------
diff --git a/test/LevelMutex/Thing.hpp b/test/LevelMutex/Thing.hpp
new file mode 100644
index 0000000..38ecfb9
--- /dev/null
+++ b/test/LevelMutex/Thing.hpp
@@ -0,0 +1,364 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+// Part of LevelMutex test program for The Loki Library
+// Copyright (c) 2008 Richard Sposato
+// The copyright on this file is protected under the terms of the MIT license.
+//
+// Permission to use, copy, modify, distribute and sell this software for any
+// purpose is hereby granted without fee, provided that the above copyright
+// notice appear in all copies and that both that copyright notice and this
+// permission notice appear in supporting documentation.
+//
+// The author makes no representations about the suitability of this software
+// for any purpose. It is provided "as is" without express or implied warranty.
+//
+////////////////////////////////////////////////////////////////////////////////
+
+// $Id$
+
+
+// ----------------------------------------------------------------------------
+
+#ifndef LOKI_TEST_LEVEL_MUTEX_THING_H
+#define LOKI_TEST_LEVEL_MUTEX_THING_H
+
+#include
+#include
+
+#include
+
+
+// ----------------------------------------------------------------------------
+
+void GoToSleep( unsigned int milliSeconds );
+
+// ----------------------------------------------------------------------------
+
+class ExceptionTossingMutex : public ::Loki::SleepLevelMutex
+{
+public:
+
+ enum ThrowingPolicy
+ {
+ Never,
+ Always,
+ Randomly
+ };
+
+ ExceptionTossingMutex( unsigned int level );
+
+ virtual ~ExceptionTossingMutex( void );
+
+ virtual ::Loki::MutexErrors::Type Lock( void ) volatile;
+
+ virtual ::Loki::MutexErrors::Type TryLock( void ) volatile;
+
+ virtual ::Loki::MutexErrors::Type Unlock( void ) volatile;
+
+ inline ThrowingPolicy GetTossPolicy( void ) const { return m_policy; }
+
+ inline void SetTossPolicy( ThrowingPolicy policy ) { m_policy = policy; }
+
+private:
+
+ ExceptionTossingMutex( void );
+ ExceptionTossingMutex( const ExceptionTossingMutex & );
+ ExceptionTossingMutex & operator = ( const ExceptionTossingMutex & );
+
+ ThrowingPolicy m_policy;
+};
+
+typedef ::Loki::LevelMutex< ExceptionTossingMutex, 1,
+ ::Loki::JustReturnMutexError, ::Loki::NoMutexWait > ExceptionMutex;
+
+typedef ::Loki::LevelMutex< ::Loki::SleepLevelMutex, 1,
+ ::Loki::JustReturnMutexError, ::Loki::MutexSleepWaits > SleepMutex;
+
+
+// ----------------------------------------------------------------------------
+
+class TestResults
+{
+public:
+
+ static bool Create( unsigned int threadCount );
+
+ static void Destroy( void );
+
+ inline static TestResults * GetIt( void ) { return s_instance; }
+
+ void Reset( unsigned int threadCount );
+
+ void SetResult( unsigned int threadIndex, unsigned int total,
+ unsigned int fails );
+
+ void OutputResults( void );
+
+private:
+
+ struct TestResult
+ {
+ TestResult( void );
+ TestResult( const TestResult & that );
+ ~TestResult( void );
+ unsigned int m_total;
+ unsigned int m_fails;
+ };
+
+ typedef ::std::vector< TestResult, ::Loki::LokiAllocator< TestResult > > Results;
+
+ explicit TestResults( unsigned int threadCount );
+
+ ~TestResults( void );
+
+ TestResults( void );
+ TestResults( const TestResults & );
+ TestResults & operator = ( const TestResults & );
+
+ static TestResults * s_instance;
+
+ Results m_results;
+};
+
+// ----------------------------------------------------------------------------
+
+class Thing
+{
+public:
+
+ typedef ::std::vector< volatile Thing * > ThingPool;
+
+ static volatile Thing & GetIt( void ) { return *s_thing; }
+
+ static void Init( unsigned int value );
+
+ static void Destroy( void );
+
+ static volatile Thing * GetFromPool( unsigned int index );
+
+ static bool MakePool( unsigned int count );
+
+ static unsigned int GetPoolSize( void );
+
+ static void DestroyPool( void );
+
+ void Print( unsigned int value, unsigned int index, unsigned int startSize ) const volatile;
+
+ void Print( unsigned int value, unsigned int index, unsigned int startSize ) const;
+
+ unsigned int GetValue( void ) const volatile { return m_value; }
+
+ unsigned int GetValue( void ) const { return m_value; }
+
+ void SetValue( unsigned int value ) volatile;
+
+ void SetValue( unsigned int value ) { m_value = value; }
+
+ inline volatile SleepMutex & GetMutex( void ) volatile { return m_mutex; }
+
+ inline const volatile SleepMutex & GetMutex( void ) const volatile { return m_mutex; }
+
+private:
+
+ explicit Thing( unsigned int value );
+
+ ~Thing( void );
+
+ Thing( void );
+ Thing( const Thing & );
+ Thing & operator = ( const Thing & );
+
+ static volatile Thing * s_thing;
+
+ static ThingPool s_pool;
+
+ static TestResults s_results;
+
+ mutable volatile SleepMutex m_mutex;
+ unsigned int m_value;
+};
+
+typedef ::std::vector< Thing * > UnsafeThingPool;
+
+// ----------------------------------------------------------------------------
+
+template < class Thingy >
+class Unlocker
+{
+public:
+ explicit inline Unlocker( volatile Thingy * thing ) : m_thing( thing ) {}
+ inline Unlocker( const Unlocker & that ) : m_thing( that.m_thing )
+ {
+ const_cast< Unlocker & >( that ).m_thing = NULL;
+ }
+ inline ~Unlocker( void ) { Unlock(); }
+ inline void Unlock( void )
+ {
+ if ( NULL != m_thing )
+ m_thing->UnlockHierarchy();
+ }
+private:
+ volatile Thingy * m_thing;
+};
+
+// ----------------------------------------------------------------------------
+
+class LevelThing
+{
+public:
+
+ typedef Unlocker< LevelThing > Unlocker;
+
+ static volatile LevelThing * GetFromPool( unsigned int index );
+
+ static bool MakePool( unsigned int count );
+
+ static void DestroyPool( void );
+
+ Unlocker LockHierarchy( void ) volatile;
+
+ void UnlockHierarchy( void ) volatile;
+
+ void SetValue( unsigned int value ) volatile;
+
+ void SetValue( unsigned int value );
+
+ inline unsigned int GetValue( void ) const volatile { return m_value; }
+
+ inline unsigned int GetValue( void ) const { return m_value; }
+
+ bool DoValuesMatch( unsigned int value ) const volatile;
+
+ bool DoValuesMatch( unsigned int value ) const;
+
+ inline volatile ::Loki::LevelMutexInfo & GetMutex( void ) volatile { return m_mutex; }
+
+ inline const volatile ::Loki::LevelMutexInfo & GetMutex( void ) const volatile { return m_mutex; }
+
+private:
+
+ typedef ::std::vector< volatile LevelThing * > LevelThingPool;
+
+ LevelThing( unsigned int level, unsigned int place );
+
+ ~LevelThing( void );
+
+ LevelThing( void );
+ LevelThing( const LevelThing & );
+ LevelThing & operator = ( const LevelThing & );
+
+ static LevelThingPool s_pool;
+
+ mutable volatile SleepMutex m_mutex;
+ const unsigned int m_place;
+ unsigned int m_value;
+};
+
+// ----------------------------------------------------------------------------
+
+class SomeThing
+{
+public:
+
+ SomeThing( unsigned int level, unsigned int place );
+
+ ~SomeThing( void );
+
+ inline unsigned int GetLevel( void ) const volatile { return m_level; }
+
+ void SetValue( unsigned int value ) volatile;
+
+ void SetValue( unsigned int value );
+
+ inline unsigned int GetValue( void ) const volatile { return m_value; }
+
+ inline unsigned int GetValue( void ) const { return m_value; }
+
+ inline volatile ::Loki::LevelMutexInfo & GetMutex( void ) volatile { return m_mutex; }
+
+ inline const volatile ::Loki::LevelMutexInfo & GetMutex( void ) const volatile { return m_mutex; }
+
+private:
+
+ SomeThing( void );
+ SomeThing( const SomeThing & );
+ SomeThing & operator = ( const SomeThing & );
+
+ mutable volatile SleepMutex m_mutex;
+ const unsigned int m_place;
+ const unsigned int m_level;
+ unsigned int m_value;
+};
+
+typedef ::std::vector< volatile SomeThing * > SomeThingPool;
+
+typedef SomeThingPool::iterator SomeThingPoolIter;
+
+// ----------------------------------------------------------------------------
+
+class ManyThingsPool
+{
+public:
+
+ explicit ManyThingsPool( unsigned int level, unsigned int count );
+
+ ~ManyThingsPool( void );
+
+ volatile SomeThing * GetFromPool( unsigned int index );
+
+ unsigned int GetCount( void ) const;
+
+private:
+
+ ManyThingsPool( void );
+ ManyThingsPool( const ManyThingsPool & );
+ ManyThingsPool & operator = ( const ManyThingsPool & );
+
+ SomeThingPool m_pool;
+};
+
+// ----------------------------------------------------------------------------
+
+class MultiLevelPool
+{
+public:
+
+ static void MakePool( unsigned int count, unsigned int thingCount );
+
+ static void DestroyPool( void );
+
+ static ManyThingsPool * GetFromPool( unsigned int index );
+
+ static unsigned int GetCount( void );
+
+private:
+
+ typedef ::std::vector< ManyThingsPool * > MultiThingPool;
+
+ ~MultiLevelPool( void );
+ MultiLevelPool( void );
+ MultiLevelPool( const MultiLevelPool & );
+ MultiLevelPool & operator = ( const MultiLevelPool & );
+
+ static MultiThingPool s_pool;
+};
+
+// ----------------------------------------------------------------------------
+
+void CheckForMatchingValues( unsigned int & failCount, unsigned int & testCount,
+ unsigned int value, const SomeThingPool & pool );
+
+void CheckForMatchingValues( unsigned int & failCount, unsigned int & testCount,
+ unsigned int value, const SomeThingPool & pool, bool locked );
+
+void MakePool( SomeThingPool & pool );
+
+void LockThese( SomeThingPool & pool );
+
+void UnlockThese( SomeThingPool & pool );
+
+unsigned int CountLockedByThisThread( const SomeThingPool & pool );
+
+// ----------------------------------------------------------------------------
+
+#endif
diff --git a/test/LevelMutex/ThreadPool.hpp b/test/LevelMutex/ThreadPool.hpp
new file mode 100644
index 0000000..655b552
--- /dev/null
+++ b/test/LevelMutex/ThreadPool.hpp
@@ -0,0 +1,226 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+// Part of LevelMutex test program for The Loki Library
+// Copyright (c) 2008 Richard Sposato
+// The copyright on this file is protected under the terms of the MIT license.
+//
+// Permission to use, copy, modify, distribute and sell this software for any
+// purpose is hereby granted without fee, provided that the above copyright
+// notice appear in all copies and that both that copyright notice and this
+// permission notice appear in supporting documentation.
+//
+// The author makes no representations about the suitability of this software
+// for any purpose. It is provided "as is" without express or implied warranty.
+//
+////////////////////////////////////////////////////////////////////////////////
+
+
+// ----------------------------------------------------------------------------
+
+#include
+
+// ----------------------------------------------------------------------------
+
+typedef void * ( * CallFunction )( void * );
+
+#if defined( _MSC_VER )
+ #include
+
+ #define LokiThread HANDLE
+
+ #define LokiThreadCreate( handle, attr, func, arg ) \
+ (int)(( * handle = ( HANDLE ) _beginthreadex ( nullptr, 0, func, arg, 0, nullptr ) ) == nullptr )
+
+ #define LokiThreadJoin( thread ) \
+ ((::WaitForSingleObject((thread),INFINITE)!=WAIT_OBJECT_0) || !CloseHandle(thread))
+
+#else
+ #include
+
+ typedef void * ( * CallFunction )( void * );
+
+ #define LokiThread pthread_t
+ #define LokiThreadCreate( handle, attr, func, arg ) \
+ pthread_create( handle, attr, func, arg )
+ #define LokiThreadJoin( thread ) pthread_join( thread, NULL )
+
+#endif
+
+
+/** @par thread_local Keyword
+ The mutexes require compilers to provide thread local storage - meaning each
+ thread gets its own copy of the data. The next version of C++ will have a
+ new keyword, thread_local for that purpose. Some existing compilers already
+ provide thread local storage using different syntax, so these lines use
+ thread_local to mimic that syntax. If your compiler provides thread local
+ storage but using different syntax besides "thread_local", you may want to
+ modify these lines. If your compiler does not support thread local storage,
+ you can't use LevelMutex.
+ */
+#ifndef LOKI_THREAD_LOCAL
+ #if defined( _MSC_VER )
+ #if ( _MSC_VER >= 1300 )
+ #define LOKI_THREAD_LOCAL __declspec( thread )
+ #else
+ #error "Only Visual Studio versions 7.0 and after supported."
+ #endif
+
+ #elif ( __GNUC__ )
+ #define LOKI_THREAD_LOCAL __thread
+
+ #else
+ #warning "Check if your compiler provides thread local storage."
+ #define LOKI_THREAD_LOCAL thread_local
+ #endif
+#endif
+
+
+class ThreadPool;
+
+class Thread
+{
+public:
+
+ enum Status
+ {
+ Dead = 0,
+ Idle,
+ Starting,
+ Active
+ };
+
+ inline static volatile Thread * GetCurrentThread( void )
+ {
+ return s_thread;
+ }
+
+ bool WaitForThread( void ) volatile;
+
+ inline CallFunction GetFunction( void ) const volatile { return m_func; }
+
+ inline void * GetParameter( void ) const volatile { return m_parm; }
+
+ inline Status GetStatus( void ) const volatile { return m_status; }
+
+ inline const volatile ThreadPool * GetThreadPool( void ) const volatile
+ {
+ return m_owner;
+ }
+
+ inline volatile ThreadPool * GetThreadPool( void ) volatile
+ {
+ return m_owner;
+ }
+
+private:
+
+ friend class ThreadPool;
+
+ inline static void SetCurrentThread( volatile Thread * thread )
+ {
+ s_thread = thread;
+ }
+
+ explicit Thread( volatile ThreadPool * owner );
+
+ Thread( volatile ThreadPool * owner, CallFunction function, void * parm );
+
+ ~Thread( void );
+
+ /// Default constructor not implemented.
+ Thread( void );
+
+ /// not implemented.
+ Thread( const Thread & );
+
+ /// not implemented.
+ Thread & operator = ( const Thread & );
+
+ bool IsValid( const volatile ThreadPool * owner ) const volatile;
+
+ static LOKI_THREAD_LOCAL volatile Thread * s_thread;
+
+ LokiThread m_thread;
+ CallFunction m_func;
+ void * m_parm;
+ Status m_status;
+ bool m_stop;
+ volatile ThreadPool * const m_owner;
+};
+
+class ThreadPool
+{
+public:
+
+ explicit ThreadPool( unsigned int threadCount = 0 );
+
+ ~ThreadPool( void );
+
+ unsigned int Create( unsigned int threadCount ) volatile;
+
+ volatile Thread * Start( CallFunction function, void * parm ) volatile;
+
+ void Join( unsigned int index ) const volatile;
+
+ void JoinAll( void ) const volatile;
+
+ unsigned int GetCount( void ) const volatile;
+
+ unsigned int GetCount( Thread::Status status ) const volatile;
+
+ volatile const Thread * GetThread( unsigned int index ) const volatile;
+
+ inline volatile Thread * GetThread( unsigned int index ) volatile
+ {
+ return const_cast< volatile Thread * >(
+ const_cast< const volatile ThreadPool * >( this )->
+ GetThread( index ) );
+ }
+
+private:
+
+ /** @class Checker
+ Performs validity check on ThreadPool to insure no class invariants were
+ violated inside any member function. This class only gets used in debug
+ builds, and any instance of it gets optimized away in a release build. A
+ checker is created inside many of member functions so that it's destructor
+ gets called when the function exits.
+ */
+ class Checker
+ {
+ public:
+ inline explicit Checker( const volatile ThreadPool * mutex ) :
+ m_pool( mutex ) {}
+ inline ~Checker( void ) { m_pool->IsValid(); }
+ private:
+ Checker( void );
+ Checker( const Checker & );
+ Checker & operator = ( const Checker & );
+ const volatile ThreadPool * m_pool;
+ };
+
+ /// Copy-constructor is not implemented.
+ ThreadPool( const ThreadPool & );
+
+ /// Copy-assignment operator is not implemented.
+ ThreadPool & operator = ( const ThreadPool & );
+
+#if defined( _MSC_VER )
+ static unsigned int __stdcall TopFunction( void * p );
+#else
+ static void * TopFunction( void * p );
+#endif
+
+ /** Returns true if no class invariant broken, otherwise asserts. This function
+ only gets called in debug builds.
+ */
+ bool IsValid( void ) const volatile;
+
+ typedef ::std::vector< volatile Thread * > Threads;
+ typedef Threads::iterator ThreadsIter;
+ typedef Threads::const_iterator ThreadsCIter;
+
+ Threads m_threads;
+};
+
+// ----------------------------------------------------------------------------
diff --git a/test/LevelMutex/main.cpp b/test/LevelMutex/main.cpp
new file mode 100644
index 0000000..08450d5
--- /dev/null
+++ b/test/LevelMutex/main.cpp
@@ -0,0 +1,681 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+// Part of LevelMutex test program for The Loki Library
+// Copyright (c) 2008 Richard Sposato
+// The copyright on this file is protected under the terms of the MIT license.
+//
+// Permission to use, copy, modify, distribute and sell this software for any
+// purpose is hereby granted without fee, provided that the above copyright
+// notice appear in all copies and that both that copyright notice and this
+// permission notice appear in supporting documentation.
+//
+// The author makes no representations about the suitability of this software
+// for any purpose. It is provided "as is" without express or implied warranty.
+//
+////////////////////////////////////////////////////////////////////////////////
+
+// $Id$
+
+
+// ----------------------------------------------------------------------------
+
+
+#include "LevelMutex.h"
+
+#include "MultiThreadTests.hpp"
+#include "Thing.hpp"
+
+#include
+#include
+#include
+
+
+// define nullptr even though new compilers will have this keyword just so we
+// have a consistent and easy way of identifying which uses of 0 mean null.
+#define nullptr 0
+
+
+using namespace ::std;
+using namespace ::Loki;
+
+
+typedef ::Loki::LevelMutex< ::Loki::SpinLevelMutex, 1,
+ ::Loki::JustReturnMutexError, ::Loki::NoMutexWait > SpinMutex;
+
+
+// ----------------------------------------------------------------------------
+
+void SingleThreadSimpleTest( void )
+{
+
+ const unsigned int priorLevel = GetCurrentThreadsLevel();
+ const unsigned int priorLockCount = CountLocksInCurrentThread();
+ const unsigned int priorMutexCount = CountMutexesInCurrentThread();
+ const unsigned int priorLevelMutexCount = CountMutexesAtCurrentLevel();
+ if ( LevelMutexInfo::UnlockedLevel == priorLevel )
+ {
+ assert( priorLockCount == 0 );
+ assert( priorMutexCount == 0 );
+ assert( priorLevelMutexCount == 0 );
+ }
+ else
+ {
+ assert( priorLockCount != 0 );
+ assert( priorMutexCount != 0 );
+ assert( priorLevelMutexCount != 0 );
+ assert( priorLevelMutexCount <= priorMutexCount );
+ assert( priorMutexCount <= priorLockCount );
+ }
+
+ SpinMutex mutex1( 1 );
+ SpinMutex mutex2( 2 );
+ SpinMutex mutex1a( 1 );
+
+ bool okay = ( mutex1.GetLevel() == 1 );
+ assert( okay );
+ okay = ( mutex2.GetLevel() == 2 );
+ assert( okay );
+ okay = ( !mutex1.IsLocked() );
+ assert( okay );
+ okay = ( !mutex2.IsLocked() );
+ assert( okay );
+ okay = ( mutex1.GetLockCount() == 0 );
+ assert( okay );
+ okay = ( mutex2.GetLockCount() == 0 );
+ assert( okay );
+ MutexErrors::Type result = mutex1.Unlock();
+ assert( MutexErrors::WasntLocked == result );
+ result = mutex2.Unlock();
+ assert( MutexErrors::WasntLocked == result );
+ okay = ( GetCurrentThreadsLevel() == priorLevel );
+ assert( okay );
+ okay = ( CountMutexesAtCurrentLevel() == priorLevelMutexCount );
+ assert( okay );
+
+ // Check for attempt to lock mutex with level higher than current level.
+ result = mutex1.Lock();
+ assert( result == MutexErrors::Success );
+ okay = ( GetCurrentThreadsLevel() == mutex1.GetLevel() );
+ assert( okay );
+ try
+ {
+ result = mutex2.Lock();
+ }
+ catch ( const MutexException & ex1 )
+ {
+ assert( MutexErrors::LevelTooHigh == ex1.GetReason() );
+ }
+ catch ( ... )
+ {
+ assert( false );
+ }
+ result = mutex2.Unlock();
+ assert( result == MutexErrors::WasntLocked );
+ okay = ( GetCurrentThreadsLevel() == mutex1.GetLevel() );
+ assert( okay );
+ try
+ {
+ // Now check for attempt to lock mutex at same level as current one.
+ result = mutex1a.Lock();
+ }
+ catch ( const MutexException & ex2 )
+ {
+ assert( MutexErrors::LevelTooHigh == ex2.GetReason() );
+ }
+ catch ( ... )
+ {
+ assert( false );
+ }
+ result = mutex1.Unlock();
+ assert( result == MutexErrors::Success );
+ okay = ( GetCurrentThreadsLevel() == priorLevel );
+ assert( okay );
+ okay = ( CountMutexesAtCurrentLevel() == priorLevelMutexCount );
+ assert( okay );
+
+ // Check for unlocking mutex that is not current lock.
+ result = mutex2.Lock();
+ assert( result == MutexErrors::Success );
+ okay = ( GetCurrentThreadsLevel() == mutex2.GetLevel() );
+ assert( okay );
+ result = mutex1.Lock();
+ assert( result == MutexErrors::Success );
+ try
+ {
+ result = mutex2.Unlock();
+ }
+ catch ( const MutexException & ex1 )
+ {
+ assert( MutexErrors::LevelTooHigh == ex1.GetReason() );
+ }
+ catch ( ... )
+ {
+ assert( false );
+ }
+ okay = ( GetCurrentThreadsLevel() == mutex1.GetLevel() );
+ assert( okay );
+ result = mutex1.Unlock();
+ assert( result == MutexErrors::Success );
+ okay = ( GetCurrentThreadsLevel() == mutex2.GetLevel() );
+ assert( okay );
+ result = mutex1.Unlock();
+ assert( result == MutexErrors::WasntLocked );
+ result = mutex2.Unlock();
+ assert( result == MutexErrors::Success );
+
+ okay = ( GetCurrentThreadsLevel() == priorLevel );
+ assert( okay );
+ okay = ( CountLocksInCurrentThread() == priorLockCount );
+ assert( okay );
+ okay = ( CountMutexesInCurrentThread() == priorMutexCount );
+ assert( okay );
+ okay = ( CountMutexesAtCurrentLevel() == priorLevelMutexCount );
+ assert( okay );
+}
+
+// ----------------------------------------------------------------------------
+
+void SingleThreadReentrantTest( void )
+{
+
+ const unsigned int priorLevel = GetCurrentThreadsLevel();
+ const unsigned int priorLockCount = CountLocksInCurrentThread();
+ const unsigned int priorMutexCount = CountMutexesInCurrentThread();
+ const unsigned int priorLevelMutexCount = CountMutexesAtCurrentLevel();
+ if ( LevelMutexInfo::UnlockedLevel == priorLevel )
+ {
+ assert( priorLockCount == 0 );
+ assert( priorMutexCount == 0 );
+ assert( priorLevelMutexCount == 0 );
+ }
+ else
+ {
+ assert( priorLockCount != 0 );
+ assert( priorMutexCount != 0 );
+ assert( priorLevelMutexCount != 0 );
+ assert( priorLevelMutexCount <= priorMutexCount );
+ assert( priorMutexCount <= priorLockCount );
+ }
+
+ SpinMutex mutex1( 1 );
+ SpinMutex mutex2( 2 );
+ bool okay = ( mutex2.GetLevel() != mutex1.GetLevel() );
+ assert( okay );
+
+ okay = ( mutex2.GetLockCount() == 0 );
+ assert( okay );
+ okay = ( mutex1.GetLockCount() == 0 );
+ assert( okay );
+ okay = ( GetCurrentThreadsLevel() == priorLevel );
+ assert( okay );
+ okay = ( CountLocksInCurrentThread() == priorLockCount );
+ assert( okay );
+ okay = ( CountMutexesInCurrentThread() == priorMutexCount );
+ assert( okay );
+ okay = ( CountMutexesAtCurrentLevel() == priorLevelMutexCount );
+ assert( okay );
+
+ MutexErrors::Type result = mutex2.Lock();
+ assert( result == MutexErrors::Success );
+ okay = ( GetCurrentThreadsLevel() == mutex2.GetLevel() );
+ assert( okay );
+ okay = ( mutex2.GetLockCount() == 1 );
+ assert( okay );
+ result = mutex2.Lock();
+ assert( MutexErrors::Success == result );
+ okay = ( mutex2.GetLockCount() == 2 );
+ assert( okay );
+ result = mutex2.TryLock();
+ assert( result == MutexErrors::Success );
+ okay = ( mutex2.GetLockCount() == 3 );
+ assert( okay );
+ result = mutex2.Unlock();
+ okay = ( mutex2.GetLockCount() == 2 );
+ assert( okay );
+ result = mutex1.Lock();
+ assert( MutexErrors::Success == result );
+ okay = ( GetCurrentThreadsLevel() == mutex1.GetLevel() );
+ assert( okay );
+ okay = ( mutex1.GetLockCount() == 1 );
+ assert( okay );
+ result = mutex1.Lock();
+ assert( MutexErrors::Success == result );
+ okay = ( CountLocksInCurrentThread() == priorLockCount + 4 );
+ assert( okay );
+ okay = ( CountMutexesInCurrentThread() == priorMutexCount + 2 );
+ assert( okay );
+ okay = ( mutex1.GetLockCount() == 2 );
+ assert( okay );
+ result = mutex1.Unlock();
+ assert( MutexErrors::Success == result );
+ okay = ( mutex1.GetLockCount() == 1 );
+ assert( okay );
+ result = mutex1.Unlock();
+ assert( MutexErrors::Success == result );
+ okay = ( mutex1.GetLockCount() == 0 );
+ assert( okay );
+ okay = ( mutex2.GetLockCount() == 2 );
+ assert( okay );
+ result = mutex2.Unlock();
+ assert( MutexErrors::Success == result );
+ okay = ( mutex2.GetLockCount() == 1 );
+ assert( okay );
+ result = mutex2.Unlock();
+ assert( MutexErrors::Success == result );
+
+ // Now check if current thread has no locks.
+ okay = ( CountLocksInCurrentThread() == priorLockCount );
+ assert( okay );
+ okay = ( CountMutexesInCurrentThread() == priorMutexCount );
+ assert( okay );
+ okay = ( CountMutexesAtCurrentLevel() == priorLevelMutexCount );
+ assert( okay );
+ okay = ( mutex2.GetLockCount() == 0 );
+ assert( okay );
+ okay = ( mutex1.GetLockCount() == 0 );
+ assert( okay );
+
+ okay = ( GetCurrentThreadsLevel() == priorLevel );
+ assert( okay );
+ okay = ( CountLocksInCurrentThread() == priorLockCount );
+ assert( okay );
+ okay = ( CountMutexesInCurrentThread() == priorMutexCount );
+ assert( okay );
+ okay = ( CountMutexesAtCurrentLevel() == priorLevelMutexCount );
+ assert( okay );
+}
+
+// ----------------------------------------------------------------------------
+
+void SingleThreadSimpleMultiLockTest( void )
+{
+
+ const unsigned int priorLevel = GetCurrentThreadsLevel();
+ const unsigned int priorLockCount = CountLocksInCurrentThread();
+ const unsigned int priorMutexCount = CountMutexesInCurrentThread();
+ const unsigned int priorLevelMutexCount = CountMutexesAtCurrentLevel();
+ if ( LevelMutexInfo::UnlockedLevel == priorLevel )
+ {
+ assert( priorLockCount == 0 );
+ assert( priorMutexCount == 0 );
+ assert( priorLevelMutexCount == 0 );
+ }
+ else
+ {
+ assert( priorLockCount != 0 );
+ assert( priorMutexCount != 0 );
+ assert( priorLevelMutexCount != 0 );
+ assert( priorLevelMutexCount <= priorMutexCount );
+ assert( priorMutexCount <= priorLockCount );
+ }
+
+ SpinMutex mutex1( 1 );
+ SpinMutex mutex2( 1 );
+ bool okay = ( mutex2.GetLevel() == mutex1.GetLevel() );
+ assert( okay );
+
+ okay = ( GetCurrentThreadsLevel() == priorLevel );
+ assert( okay );
+ LevelMutexInfo::MutexContainer mutexes;
+ mutexes.push_back( &mutex1 );
+ mutexes.push_back( &mutex2 );
+
+ MutexErrors::Type result = LevelMutexInfo::MultiLock( mutexes ); // locks: 1, 2
+ assert( MutexErrors::Success == result );
+ okay = ( GetCurrentThreadsLevel() == mutex1.GetLevel() );
+ assert( okay );
+ result = LevelMutexInfo::MultiUnlock( mutexes ); // locks: none
+ assert( MutexErrors::Success == result );
+ okay = ( CountMutexesAtCurrentLevel() == priorLevelMutexCount );
+ assert( okay );
+
+ mutexes.clear();
+ mutexes.push_back( &mutex1 );
+ mutexes.push_back( &mutex2 );
+ mutexes.push_back( &mutex1 );
+ result = LevelMutexInfo::MultiLock( mutexes ); // locks: none
+ assert( MutexErrors::DuplicateMutex == result );
+ okay = ( CountMutexesAtCurrentLevel() == priorLevelMutexCount );
+ assert( okay );
+ okay = ( GetCurrentThreadsLevel() == priorLevel );
+ assert( okay );
+
+ mutexes.clear();
+ mutexes.push_back( &mutex1 );
+ mutexes.push_back( nullptr );
+ mutexes.push_back( &mutex2 );
+ result = LevelMutexInfo::MultiLock( mutexes ); // locks: none
+ assert( MutexErrors::NullMutexPointer == result );
+ okay = ( CountMutexesAtCurrentLevel() == priorLevelMutexCount );
+ assert( okay );
+ okay = ( GetCurrentThreadsLevel() == priorLevel );
+ assert( okay );
+
+ SpinMutex mutex3( 3 );
+ mutexes.clear();
+ mutexes.push_back( &mutex1 );
+ mutexes.push_back( &mutex2 );
+ mutexes.push_back( &mutex3 );
+ result = LevelMutexInfo::MultiLock( mutexes ); // locks: none
+ assert( MutexErrors::WrongLevel == result );
+ okay = ( CountMutexesAtCurrentLevel() == priorLevelMutexCount );
+ assert( okay );
+ okay = ( GetCurrentThreadsLevel() == priorLevel );
+ assert( okay );
+
+ result = mutex1.Lock(); // locks: 1
+ assert( MutexErrors::Success == result );
+ mutexes.clear();
+ mutexes.push_back( &mutex1 );
+ mutexes.push_back( &mutex2 );
+ try
+ {
+ result = LevelMutexInfo::MultiLock( mutexes ); // locks: 1
+ }
+ catch ( const MutexException & ex1 )
+ {
+ assert( MutexErrors::LevelTooHigh == ex1.GetReason() );
+ }
+ catch ( ... )
+ {
+ assert( false );
+ }
+ result = mutex1.Unlock(); // locks: none
+ assert( MutexErrors::Success == result );
+
+ result = LevelMutexInfo::MultiLock( mutexes ); // locks: 1, 2
+ assert( MutexErrors::Success == result );
+ const bool m1IsFirst = ( &mutex1 > &mutex2 );
+ LevelMutexInfo * first = ( m1IsFirst ) ? &mutex1 : &mutex2;
+ LevelMutexInfo * second = ( m1IsFirst ) ? &mutex2 : &mutex1;
+ result = first->Unlock(); // locks: 2
+ assert( MutexErrors::Success == result );
+ result = LevelMutexInfo::MultiUnlock( mutexes ); // locks: 2
+ assert( MutexErrors::NotRecentLock == result );
+ result = second->Unlock(); // locks: none
+ assert( MutexErrors::Success == result );
+
+ // Now combine some calls to TryLock with a call to MultiLock.
+ mutexes.clear();
+ mutexes.push_back( &mutex1 );
+ mutexes.push_back( &mutex2 );
+ result = LevelMutexInfo::MultiLock( mutexes ); // locks: 1, 2
+ assert( MutexErrors::Success == result );
+ result = mutex2.TryLock(); // locks: 1, 2+
+ assert( MutexErrors::Success == result );
+ okay = ( mutex2.GetLockCount() == 2 );
+ assert( okay );
+ result = mutex2.Unlock(); // locks: 1, 2
+ assert( MutexErrors::Success == result );
+ result = mutex1.TryLock(); // locks: 1+, 2
+ assert( result == MutexErrors::Success );
+ okay = ( mutex1.GetLockCount() == 2 );
+ assert( okay );
+ result = mutex2.TryLock(); // locks: 1+, 2+
+ assert( result == MutexErrors::Success );
+ okay = ( mutex2.GetLockCount() == 2 );
+ assert( okay );
+ result = mutex2.Unlock(); // locks: 1+, 2
+ assert( MutexErrors::Success == result );
+ result = mutex1.Unlock(); // locks: 1, 2
+ assert( MutexErrors::Success == result );
+ result = LevelMutexInfo::MultiUnlock( mutexes ); // locks: 1, 2
+ assert( MutexErrors::Success == result );
+
+ okay = ( GetCurrentThreadsLevel() == priorLevel );
+ assert( okay );
+ okay = ( CountMutexesAtCurrentLevel() == priorLevelMutexCount );
+ assert( okay );
+
+ SpinMutex mutex4( 1 );
+ result = mutex4.Lock();
+ assert( MutexErrors::Success == result );
+ mutexes.clear();
+ mutexes.push_back( &mutex1 );
+ mutexes.push_back( &mutex2 );
+
+ try
+ {
+ result = LevelMutexInfo::MultiLock( mutexes );
+ }
+ catch ( const MutexException & ex1 )
+ {
+ assert( MutexErrors::LevelTooHigh == ex1.GetReason() );
+ }
+ catch ( ... )
+ {
+ assert( false );
+ }
+ mutexes.clear();
+ mutexes.push_back( &mutex1 );
+ mutexes.push_back( &mutex4 );
+ result = LevelMutexInfo::MultiUnlock( mutexes );
+ assert( MutexErrors::NotRecentLock == result );
+
+ mutexes.clear();
+ mutexes.push_back( &mutex1 );
+ mutexes.push_back( &mutex2 );
+ result = LevelMutexInfo::MultiUnlock( mutexes );
+ assert( MutexErrors::NotRecentLock == result );
+ result = mutex4.Unlock();
+ assert( MutexErrors::Success == result );
+
+ // Make sure current thread has released locks acquired in this function.
+ okay = ( GetCurrentThreadsLevel() == priorLevel );
+ assert( okay );
+ okay = ( CountMutexesAtCurrentLevel() == priorLevelMutexCount );
+ assert( okay );
+
+ // What if this tries to unlock multiple mutexes when nothing is locked?
+ mutexes.clear();
+ mutexes.push_back( &mutex1 );
+ mutexes.push_back( &mutex2 );
+ result = LevelMutexInfo::MultiUnlock( mutexes );
+ assert( MutexErrors::NotRecentLock == result );
+
+ okay = ( GetCurrentThreadsLevel() == priorLevel );
+ assert( okay );
+ okay = ( CountLocksInCurrentThread() == priorLockCount );
+ assert( okay );
+ okay = ( CountMutexesInCurrentThread() == priorMutexCount );
+ assert( okay );
+ okay = ( CountMutexesAtCurrentLevel() == priorLevelMutexCount );
+ assert( okay );
+}
+
+// ----------------------------------------------------------------------------
+
+void SingleThreadExceptionTest( void )
+{
+
+ const unsigned int priorLevel = GetCurrentThreadsLevel();
+ const unsigned int priorLockCount = CountLocksInCurrentThread();
+ const unsigned int priorMutexCount = CountMutexesInCurrentThread();
+ const unsigned int priorLevelMutexCount = CountMutexesAtCurrentLevel();
+ if ( LevelMutexInfo::UnlockedLevel == priorLevel )
+ {
+ assert( priorLockCount == 0 );
+ assert( priorMutexCount == 0 );
+ assert( priorLevelMutexCount == 0 );
+ }
+ else
+ {
+ assert( priorLockCount != 0 );
+ assert( priorMutexCount != 0 );
+ assert( priorLevelMutexCount != 0 );
+ assert( priorLevelMutexCount <= priorMutexCount );
+ assert( priorMutexCount <= priorLockCount );
+ }
+
+ ExceptionMutex mutex1a( 1 );
+ mutex1a.GetMutexPolicy().SetTossPolicy( ExceptionTossingMutex::Always );
+ ExceptionMutex mutex2a( 2 );
+ mutex2a.GetMutexPolicy().SetTossPolicy( ExceptionTossingMutex::Never );
+ MutexErrors::Type result = MutexErrors::Success;
+
+ try
+ {
+ mutex1a.Lock();
+ assert( false );
+ }
+ catch ( ... )
+ {
+ assert( true );
+ }
+
+ try
+ {
+ result = mutex2a.Lock();
+ assert( MutexErrors::Success == result );
+ mutex1a.GetMutexPolicy().SetTossPolicy( ExceptionTossingMutex::Never );
+ result = mutex1a.Lock();
+ assert( MutexErrors::Success == result );
+ result = mutex1a.Unlock();
+ assert( MutexErrors::Success == result );
+ result = mutex2a.Unlock();
+ assert( MutexErrors::Success == result );
+ }
+ catch ( ... )
+ {
+ assert( false );
+ }
+
+ ExceptionMutex mutex2b( 2 );
+ mutex2b.GetMutexPolicy().SetTossPolicy( ExceptionTossingMutex::Always );
+ ExceptionMutex mutex2c( 2 );
+ mutex2c.GetMutexPolicy().SetTossPolicy( ExceptionTossingMutex::Never );
+ ExceptionMutex mutex2d( 2 );
+ mutex2d.GetMutexPolicy().SetTossPolicy( ExceptionTossingMutex::Never );
+ LevelMutexInfo::MutexContainer mutexes;
+ try
+ {
+ mutexes.push_back( &mutex2a );
+ mutexes.push_back( &mutex2b );
+ mutexes.push_back( &mutex2c );
+ mutexes.push_back( &mutex2d );
+ result = LevelMutexInfo::MultiLock( mutexes );
+ assert( false );
+ }
+ catch ( ... )
+ {
+ assert( true );
+ }
+
+ bool okay = ( !mutex2a.IsLockedByCurrentThread() );
+ assert( okay );
+ okay = ( !mutex2b.IsLockedByCurrentThread() );
+ assert( okay );
+ okay = ( !mutex2c.IsLockedByCurrentThread() );
+ assert( okay );
+ okay = ( !mutex2d.IsLockedByCurrentThread() );
+ assert( okay );
+
+ mutex2b.GetMutexPolicy().SetTossPolicy( ExceptionTossingMutex::Never );
+
+ ExceptionMutex mutex3a( 3 );
+ mutex3a.GetMutexPolicy().SetTossPolicy( ExceptionTossingMutex::Never );
+ ExceptionMutex mutex3b( 3 );
+ mutex3b.GetMutexPolicy().SetTossPolicy( ExceptionTossingMutex::Never );
+ mutexes.clear();
+ try
+ {
+ mutexes.push_back( &mutex3a );
+ mutexes.push_back( &mutex3b );
+ result = LevelMutexInfo::MultiLock( mutexes ); // 3a, 3b
+ assert( true );
+ assert( MutexErrors::Success == result );
+ okay = ( mutex3a.IsLockedByCurrentThread() );
+ assert( okay );
+ okay = ( mutex3b.IsLockedByCurrentThread() );
+ assert( okay );
+ }
+ catch ( ... )
+ {
+ assert( false );
+ }
+
+ try
+ {
+ mutex2c.GetMutexPolicy().SetTossPolicy( ExceptionTossingMutex::Always );
+ mutexes.clear();
+ mutexes.push_back( &mutex2a );
+ mutexes.push_back( &mutex2b );
+ mutexes.push_back( &mutex2c );
+ mutexes.push_back( &mutex2d );
+ result = LevelMutexInfo::MultiLock( mutexes );
+ assert( false );
+ }
+ catch ( ... )
+ {
+ assert( true );
+ okay = ( !mutex2a.IsLockedByCurrentThread() );
+ assert( okay );
+ okay = ( !mutex2b.IsLockedByCurrentThread() );
+ assert( okay );
+ okay = ( !mutex2c.IsLockedByCurrentThread() );
+ assert( okay );
+ okay = ( !mutex2d.IsLockedByCurrentThread() );
+ assert( okay );
+ okay = ( mutex3a.IsLockedByCurrentThread() );
+ assert( okay );
+ okay = ( mutex3b.IsLockedByCurrentThread() );
+ assert( okay );
+ }
+
+ mutexes.clear();
+ mutexes.push_back( &mutex3a );
+ mutexes.push_back( &mutex3b );
+ result = LevelMutexInfo::MultiUnlock( mutexes );
+ assert( MutexErrors::Success == result );
+
+ okay = ( GetCurrentThreadsLevel() == priorLevel );
+ assert( okay );
+ okay = ( CountLocksInCurrentThread() == priorLockCount );
+ assert( okay );
+ okay = ( CountMutexesInCurrentThread() == priorMutexCount );
+ assert( okay );
+ okay = ( CountMutexesAtCurrentLevel() == priorLevelMutexCount );
+ assert( okay );
+}
+
+// ----------------------------------------------------------------------------
+
+int main( unsigned int argc, const char * const argv[] )
+{
+
+ cout << "Starting LevelMutex tests." << endl;
+ if ( 1 < argc )
+ {
+ if ( ::strcmp( argv[1], "-r" ) == 0 )
+ SetToRedoSingleTests( true );
+ }
+
+ try
+ {
+ SingleThreadSimpleTest();
+ SingleThreadReentrantTest();
+ SingleThreadSimpleMultiLockTest();
+ SingleThreadComplexMultiLockTest( true );
+ SingleThreadExceptionTest();
+
+ MultiThreadSimpleTest();
+ MultiThreadTryLockTest();
+ MultiThreadReentrantTest();
+ MultiThreadMultiLockTest();
+ MultiThreadRandomMultiLockTest();
+
+ // This tests for single locking in a multi-threaded environment.
+ MultiThreadHierarchySingleLockTest();
+ // This tests for multi-locking and single locking in a multi-threaded environment.
+ MultiThreadHierarchyMultiLockTest();
+ }
+ catch ( ... )
+ {
+ cout << "Caught unknown exception!" << endl;
+ }
+
+ ::system( "pause" );
+ return 0;
+}
+
+// ----------------------------------------------------------------------------