Adding LevelMutex test program to Loki.

git-svn-id: svn://svn.code.sf.net/p/loki-lib/code/trunk@844 7ec92016-0320-0410-acc4-a06ded1c099a
This commit is contained in:
rich_sposato 2008-05-25 04:05:18 +00:00
parent 42daab3664
commit 336d434934
7 changed files with 3638 additions and 0 deletions

View file

@ -0,0 +1,211 @@
<?xml version="1.0" encoding="Windows-1252"?>
<VisualStudioProject
ProjectType="Visual C++"
Version="9.00"
Name="LevelMutex"
ProjectGUID="{7C09E027-5484-4641-8310-BDDEB1EC8676}"
RootNamespace="LevelMutex"
TargetFrameworkVersion="196613"
>
<Platforms>
<Platform
Name="Win32"
/>
</Platforms>
<ToolFiles>
</ToolFiles>
<Configurations>
<Configuration
Name="Debug|Win32"
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
IntermediateDirectory="$(ConfigurationName)"
ConfigurationType="1"
CharacterSet="0"
>
<Tool
Name="VCPreBuildEventTool"
/>
<Tool
Name="VCCustomBuildTool"
/>
<Tool
Name="VCXMLDataGeneratorTool"
/>
<Tool
Name="VCWebServiceProxyGeneratorTool"
/>
<Tool
Name="VCMIDLTool"
/>
<Tool
Name="VCCLCompilerTool"
Optimization="0"
AdditionalIncludeDirectories="../../include/loki;../../include"
StringPooling="true"
MinimalRebuild="true"
BasicRuntimeChecks="3"
SmallerTypeCheck="true"
RuntimeLibrary="3"
EnableFunctionLevelLinking="true"
WarningLevel="4"
DebugInformationFormat="3"
/>
<Tool
Name="VCManagedResourceCompilerTool"
/>
<Tool
Name="VCResourceCompilerTool"
/>
<Tool
Name="VCPreLinkEventTool"
/>
<Tool
Name="VCLinkerTool"
GenerateDebugInformation="true"
SubSystem="1"
TargetMachine="1"
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCManifestTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
/>
<Tool
Name="VCFxCopTool"
/>
<Tool
Name="VCAppVerifierTool"
/>
<Tool
Name="VCPostBuildEventTool"
/>
</Configuration>
<Configuration
Name="Release|Win32"
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
IntermediateDirectory="$(ConfigurationName)"
ConfigurationType="1"
CharacterSet="0"
WholeProgramOptimization="1"
>
<Tool
Name="VCPreBuildEventTool"
/>
<Tool
Name="VCCustomBuildTool"
/>
<Tool
Name="VCXMLDataGeneratorTool"
/>
<Tool
Name="VCWebServiceProxyGeneratorTool"
/>
<Tool
Name="VCMIDLTool"
/>
<Tool
Name="VCCLCompilerTool"
Optimization="2"
InlineFunctionExpansion="1"
EnableIntrinsicFunctions="true"
AdditionalIncludeDirectories="../../include/loki;../../include"
StringPooling="true"
RuntimeLibrary="2"
StructMemberAlignment="1"
EnableFunctionLevelLinking="true"
WarningLevel="4"
DebugInformationFormat="0"
/>
<Tool
Name="VCManagedResourceCompilerTool"
/>
<Tool
Name="VCResourceCompilerTool"
/>
<Tool
Name="VCPreLinkEventTool"
/>
<Tool
Name="VCLinkerTool"
GenerateDebugInformation="true"
OptimizeReferences="2"
EnableCOMDATFolding="2"
TargetMachine="1"
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCManifestTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
/>
<Tool
Name="VCFxCopTool"
/>
<Tool
Name="VCAppVerifierTool"
/>
<Tool
Name="VCPostBuildEventTool"
/>
</Configuration>
</Configurations>
<References>
</References>
<Files>
<Filter
Name="Source Files"
Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"
UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
>
<File
RelativePath=".\main.cpp"
>
</File>
<File
RelativePath=".\MultiThreadTests.cpp"
>
</File>
<File
RelativePath=".\Thing.cpp"
>
</File>
<File
RelativePath=".\ThreadPool.cpp"
>
</File>
</Filter>
<Filter
Name="Header Files"
Filter="h;hpp;hxx;hm;inl;inc;xsd"
UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
>
<File
RelativePath=".\MultiThreadTests.hpp"
>
</File>
<File
RelativePath=".\Thing.hpp"
>
</File>
<File
RelativePath=".\ThreadPool.hpp"
>
</File>
</Filter>
</Files>
<Globals>
</Globals>
</VisualStudioProject>

File diff suppressed because it is too large Load diff

View file

@ -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

922
test/LevelMutex/Thing.cpp Normal file
View file

@ -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 <assert.h>
#include <algorithm>
#include <functional>
#include <SafeFormat.h>
#include <LockingPtr.h>
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;
}
// ----------------------------------------------------------------------------

364
test/LevelMutex/Thing.hpp Normal file
View file

@ -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 <LevelMutex.h>
#include <Allocator.h>
#include <vector>
// ----------------------------------------------------------------------------
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

View file

@ -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 <vector>
// ----------------------------------------------------------------------------
typedef void * ( * CallFunction )( void * );
#if defined( _MSC_VER )
#include <Windows.h>
#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 <pthread.h>
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;
};
// ----------------------------------------------------------------------------

681
test/LevelMutex/main.cpp Normal file
View file

@ -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 <assert.h>
#include <string.h>
#include <iostream>
// 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;
}
// ----------------------------------------------------------------------------