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() ); } }
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 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 SingleThreadExceptionTest( void ) { cout << "Starting SingleThreadExceptionTest." << endl; 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 ); cout << "Finished SingleThreadExceptionTest." << endl; }
void SingleThreadSimpleMultiLockTest( void ) { cout << "Finished SingleThreadSimpleMultiLockTest." << endl; 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 ); cout << "Finished SingleThreadSimpleMultiLockTest." << endl; }