From 683474eff924834277cd81db40d74daa8852ae20 Mon Sep 17 00:00:00 2001 From: syntheticpp Date: Fri, 8 Aug 2008 22:20:05 +0000 Subject: [PATCH] fix line endings git-svn-id: svn://svn.code.sf.net/p/loki-lib/code/trunk@896 7ec92016-0320-0410-acc4-a06ded1c099a --- include/loki/Allocator.h | 306 +-- include/loki/CheckReturn.h | 192 +- include/loki/Checker.h | 906 ++++---- include/loki/SPCachedFactory.h | 408 ++-- test/CheckReturn/CheckReturn.dev | 138 +- test/CheckReturn/CheckReturn.vcproj | 394 ++-- test/CheckReturn/CheckReturn_MSVC_8.vcproj | 396 ++-- test/CheckReturn/main.cpp | 310 +-- test/Checker/Checker_MSVC_9.vcproj | 374 ++-- test/Checker/main.cpp | 400 ++-- test/LevelMutex/LevelMutex_MSVC_9.vcproj | 422 ++-- test/LevelMutex/MultiThreadTests.cpp | 2196 ++++++++++---------- test/LevelMutex/MultiThreadTests.hpp | 110 +- test/LevelMutex/Thing.cpp | 1844 ++++++++-------- test/LevelMutex/Thing.hpp | 728 +++---- test/LevelMutex/ThreadPool.cpp | 780 +++---- test/LevelMutex/ThreadPool.hpp | 452 ++-- test/LevelMutex/main.cpp | 1362 ++++++------ 18 files changed, 5859 insertions(+), 5859 deletions(-) diff --git a/include/loki/Allocator.h b/include/loki/Allocator.h index 3bab337..02ece5e 100644 --- a/include/loki/Allocator.h +++ b/include/loki/Allocator.h @@ -1,153 +1,153 @@ -//////////////////////////////////////////////////////////////////////////////// -// The Loki Library -// Copyright (c) 2008 by Rich Sposato -// -// 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. -//////////////////////////////////////////////////////////////////////////////// - -#ifndef LOKI_ALLOCATOR_HPP_INCLUDED -#define LOKI_ALLOCATOR_HPP_INCLUDED - -// $Id$ - -// Requires project to be compiled with loki/src/SmallObj.cpp and loki/src/Singleton.cpp - -#include - - -namespace Loki -{ - - -//----------------------------------------------------------------------------- - -/** @class LokiAllocator - Adapts Loki's Small-Object Allocator for STL container classes. - This class provides all the functionality required for STL allocators, but - uses Loki's Small-Object Allocator to perform actual memory operations. - Implementation comes from a post in Loki forums (by Rasmus Ekman?). - */ -template -< - typename Type, - typename AllocT = Loki::AllocatorSingleton<> -> -class LokiAllocator -{ -public: - - typedef ::std::size_t size_type; - typedef ::std::ptrdiff_t difference_type; - typedef Type * pointer; - typedef const Type * const_pointer; - typedef Type & reference; - typedef const Type & const_reference; - typedef Type value_type; - - /// Default constructor does nothing. - inline LokiAllocator( void ) throw() { } - - /// Copy constructor does nothing. - inline LokiAllocator( const LokiAllocator & ) throw() { } - - /// Type converting allocator constructor does nothing. - template < typename Type1 > - inline LokiAllocator( const LokiAllocator< Type1 > & ) throw() { } - - /// Destructor does nothing. - inline ~LokiAllocator() throw() { } - - /// Convert an allocator to an allocator . - template < typename Type1 > - struct rebind - { - typedef LokiAllocator< Type1 > other; - }; - - /// Return address of reference to mutable element. - pointer address( reference elem ) const { return &elem; } - - /// Return address of reference to const element. - const_pointer address( const_reference elem ) const { return &elem; } - - /** Allocate an array of count elements. Warning! The true parameter in - the call to Allocate means this function can throw exceptions. This is - better than not throwing, and returning a null pointer in case the caller - assumes the return value is not null. - @param count # of elements in array. - @param hint Place where caller thinks allocation should occur. - @return Pointer to block of memory. - */ - pointer allocate( size_type count, const void * hint = 0 ) - { - (void)hint; // Ignore the hint. - void * p = AllocT::Instance().Allocate( count * sizeof( Type ), true ); - return reinterpret_cast< pointer >( p ); - } - - /// Ask allocator to release memory at pointer with size bytes. - void deallocate( pointer p, size_type size ) - { - AllocT::Instance().Deallocate( p, size * sizeof( Type ) ); - } - - /// Calculate max # of elements allocator can handle. - size_type max_size( void ) const throw() - { - // A good optimizer will see these calculations always produce the same - // value and optimize this function away completely. - const size_type max_bytes = size_type( -1 ); - const size_type bytes = max_bytes / sizeof( Type ); - return bytes; - } - - /// Construct an element at the pointer. - void construct( pointer p, const Type & value ) - { - // A call to global placement new forces a call to copy constructor. - ::new( p ) Type( value ); - } - - /// Destruct the object at pointer. - void destroy( pointer p ) - { - // If the Type has no destructor, then some compilers complain about - // an unreferenced parameter, so use the void cast trick to prevent - // spurious warnings. - (void)p; - p->~Type(); - } - -}; - -//----------------------------------------------------------------------------- - -/** All equality operators return true since LokiAllocator is basically a - monostate design pattern, so all instances of it are identical. - */ -template < typename Type > -inline bool operator == ( const LokiAllocator< Type > &, const LokiAllocator< Type > & ) -{ - return true; -} - -/** All inequality operators return false since LokiAllocator is basically a - monostate design pattern, so all instances of it are identical. - */ -template < typename Type > -inline bool operator != ( const LokiAllocator< Type > & , const LokiAllocator< Type > & ) -{ - return false; -} - -//----------------------------------------------------------------------------- - -} // namespace Loki - -#endif // LOKI_ALLOCATOR_INCLUDED +//////////////////////////////////////////////////////////////////////////////// +// The Loki Library +// Copyright (c) 2008 by Rich Sposato +// +// 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. +//////////////////////////////////////////////////////////////////////////////// + +#ifndef LOKI_ALLOCATOR_HPP_INCLUDED +#define LOKI_ALLOCATOR_HPP_INCLUDED + +// $Id$ + +// Requires project to be compiled with loki/src/SmallObj.cpp and loki/src/Singleton.cpp + +#include + + +namespace Loki +{ + + +//----------------------------------------------------------------------------- + +/** @class LokiAllocator + Adapts Loki's Small-Object Allocator for STL container classes. + This class provides all the functionality required for STL allocators, but + uses Loki's Small-Object Allocator to perform actual memory operations. + Implementation comes from a post in Loki forums (by Rasmus Ekman?). + */ +template +< + typename Type, + typename AllocT = Loki::AllocatorSingleton<> +> +class LokiAllocator +{ +public: + + typedef ::std::size_t size_type; + typedef ::std::ptrdiff_t difference_type; + typedef Type * pointer; + typedef const Type * const_pointer; + typedef Type & reference; + typedef const Type & const_reference; + typedef Type value_type; + + /// Default constructor does nothing. + inline LokiAllocator( void ) throw() { } + + /// Copy constructor does nothing. + inline LokiAllocator( const LokiAllocator & ) throw() { } + + /// Type converting allocator constructor does nothing. + template < typename Type1 > + inline LokiAllocator( const LokiAllocator< Type1 > & ) throw() { } + + /// Destructor does nothing. + inline ~LokiAllocator() throw() { } + + /// Convert an allocator to an allocator . + template < typename Type1 > + struct rebind + { + typedef LokiAllocator< Type1 > other; + }; + + /// Return address of reference to mutable element. + pointer address( reference elem ) const { return &elem; } + + /// Return address of reference to const element. + const_pointer address( const_reference elem ) const { return &elem; } + + /** Allocate an array of count elements. Warning! The true parameter in + the call to Allocate means this function can throw exceptions. This is + better than not throwing, and returning a null pointer in case the caller + assumes the return value is not null. + @param count # of elements in array. + @param hint Place where caller thinks allocation should occur. + @return Pointer to block of memory. + */ + pointer allocate( size_type count, const void * hint = 0 ) + { + (void)hint; // Ignore the hint. + void * p = AllocT::Instance().Allocate( count * sizeof( Type ), true ); + return reinterpret_cast< pointer >( p ); + } + + /// Ask allocator to release memory at pointer with size bytes. + void deallocate( pointer p, size_type size ) + { + AllocT::Instance().Deallocate( p, size * sizeof( Type ) ); + } + + /// Calculate max # of elements allocator can handle. + size_type max_size( void ) const throw() + { + // A good optimizer will see these calculations always produce the same + // value and optimize this function away completely. + const size_type max_bytes = size_type( -1 ); + const size_type bytes = max_bytes / sizeof( Type ); + return bytes; + } + + /// Construct an element at the pointer. + void construct( pointer p, const Type & value ) + { + // A call to global placement new forces a call to copy constructor. + ::new( p ) Type( value ); + } + + /// Destruct the object at pointer. + void destroy( pointer p ) + { + // If the Type has no destructor, then some compilers complain about + // an unreferenced parameter, so use the void cast trick to prevent + // spurious warnings. + (void)p; + p->~Type(); + } + +}; + +//----------------------------------------------------------------------------- + +/** All equality operators return true since LokiAllocator is basically a + monostate design pattern, so all instances of it are identical. + */ +template < typename Type > +inline bool operator == ( const LokiAllocator< Type > &, const LokiAllocator< Type > & ) +{ + return true; +} + +/** All inequality operators return false since LokiAllocator is basically a + monostate design pattern, so all instances of it are identical. + */ +template < typename Type > +inline bool operator != ( const LokiAllocator< Type > & , const LokiAllocator< Type > & ) +{ + return false; +} + +//----------------------------------------------------------------------------- + +} // namespace Loki + +#endif // LOKI_ALLOCATOR_INCLUDED diff --git a/include/loki/CheckReturn.h b/include/loki/CheckReturn.h index b4f7131..74c2c08 100755 --- a/include/loki/CheckReturn.h +++ b/include/loki/CheckReturn.h @@ -1,96 +1,96 @@ -//////////////////////////////////////////////////////////////////////////////// -// The Loki Library -// Copyright (c) 2007 by Rich Sposato -// 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. -//////////////////////////////////////////////////////////////////////////////// - -#ifndef LOKI_CHECK_RETURN_INC_ -#define LOKI_CHECK_RETURN_INC_ - -// $Id$ - - -#include - -namespace Loki -{ - -// ---------------------------------------------------------------------------- - -//////////////////////////////////////////////////////////////////////////////// -/// \class CheckReturn -/// -/// \par Purpose -/// C++ provides no mechanism within the language itself to force code to -/// check the return value from a function call. This simple class provides -/// a mechanism by which programmers can force calling functions to check the -/// return value. Or at least make them consciously choose to disregard the -/// return value. If the calling function fails to use or store the return -/// value, the destructor asserts. -/// -/// \par Return Type -/// The returned value is copied into CheckReturn rather than accessed via a -/// a reference or pointer since return value could be local to a function. -/// CheckReturn works best when the return type is a built-in primitive (bool, -/// int, etc...) a pointer, or an enum (such as an error condition enum). It -/// can work with other types that have cheap copy operations. -//////////////////////////////////////////////////////////////////////////////// - -template < class Value > -class CheckReturn -{ -public: - - /// Conversion constructor changes Value type to CheckReturn type. - inline CheckReturn( Value value ) : - m_value( value ), m_checked( false ) {} - - /// Copy-constructor allows functions to call another function within the - /// return statement. The other CheckReturn's m_checked flag is set since - /// its duty has been passed to the m_checked flag in this one. - inline CheckReturn( const CheckReturn & that ) : - m_value( that.m_value ), m_checked( false ) - { that.m_checked = true; } - - /// Destructor checks if return value was used. - inline ~CheckReturn( void ) - { - // If this assertion fails, then a function failed to check the - // return value from a function call. - assert( m_checked ); - } - - /// Conversion operator changes CheckReturn back to Value type. - inline operator Value ( void ) - { - m_checked = true; - return m_value; - } - -private: - /// Default constructor not implemented. - CheckReturn( void ); - - /// Copy-assignment operator not implemented. - CheckReturn & operator = ( const CheckReturn & that ); - - /// Copy of returned value. - Value m_value; - - /// Flag for whether calling function checked return value yet. - mutable bool m_checked; -}; - -// ---------------------------------------------------------------------------- - -} // namespace Loki - -#endif // end file guardian - -// $Log$ +//////////////////////////////////////////////////////////////////////////////// +// The Loki Library +// Copyright (c) 2007 by Rich Sposato +// 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. +//////////////////////////////////////////////////////////////////////////////// + +#ifndef LOKI_CHECK_RETURN_INC_ +#define LOKI_CHECK_RETURN_INC_ + +// $Id$ + + +#include + +namespace Loki +{ + +// ---------------------------------------------------------------------------- + +//////////////////////////////////////////////////////////////////////////////// +/// \class CheckReturn +/// +/// \par Purpose +/// C++ provides no mechanism within the language itself to force code to +/// check the return value from a function call. This simple class provides +/// a mechanism by which programmers can force calling functions to check the +/// return value. Or at least make them consciously choose to disregard the +/// return value. If the calling function fails to use or store the return +/// value, the destructor asserts. +/// +/// \par Return Type +/// The returned value is copied into CheckReturn rather than accessed via a +/// a reference or pointer since return value could be local to a function. +/// CheckReturn works best when the return type is a built-in primitive (bool, +/// int, etc...) a pointer, or an enum (such as an error condition enum). It +/// can work with other types that have cheap copy operations. +//////////////////////////////////////////////////////////////////////////////// + +template < class Value > +class CheckReturn +{ +public: + + /// Conversion constructor changes Value type to CheckReturn type. + inline CheckReturn( Value value ) : + m_value( value ), m_checked( false ) {} + + /// Copy-constructor allows functions to call another function within the + /// return statement. The other CheckReturn's m_checked flag is set since + /// its duty has been passed to the m_checked flag in this one. + inline CheckReturn( const CheckReturn & that ) : + m_value( that.m_value ), m_checked( false ) + { that.m_checked = true; } + + /// Destructor checks if return value was used. + inline ~CheckReturn( void ) + { + // If this assertion fails, then a function failed to check the + // return value from a function call. + assert( m_checked ); + } + + /// Conversion operator changes CheckReturn back to Value type. + inline operator Value ( void ) + { + m_checked = true; + return m_value; + } + +private: + /// Default constructor not implemented. + CheckReturn( void ); + + /// Copy-assignment operator not implemented. + CheckReturn & operator = ( const CheckReturn & that ); + + /// Copy of returned value. + Value m_value; + + /// Flag for whether calling function checked return value yet. + mutable bool m_checked; +}; + +// ---------------------------------------------------------------------------- + +} // namespace Loki + +#endif // end file guardian + +// $Log$ diff --git a/include/loki/Checker.h b/include/loki/Checker.h index b4f6052..1a2f99c 100644 --- a/include/loki/Checker.h +++ b/include/loki/Checker.h @@ -1,453 +1,453 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// The Loki Library -// Copyright (c) 2008 Rich 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$ - -/// @file Checker.h This file provides Loki's Checker facility. - - -// ---------------------------------------------------------------------------- - -#ifndef LOKI_CHECKER_H_INCLUDED -#define LOKI_CHECKER_H_INCLUDED - -#include // needed for calls to uncaught_exception. -#include - - -namespace Loki -{ - -/** @par Checker and StaticChecker Overview - The Checker and StaticChecker classes have two purposes: - - provide a mechanism by which programmers can determine which functions - violate class/data invariants, - - and determine which exception safety a function provides. - - @par Class & Data Invariants - The Checker and StaticChecker utilities define invariants as "expressions that - are true for particular data". They uses a function which returns true if all - data are valid, and returns false if any datum is invalid. This is called the - validator function, and the host class or function provides a pointer to it. - The validator could also assert for any invariant which fails rather than - return false. If the validator is a static member function, you can use it - with checkers in any function, but especially standalone functions and class - static functions. If the validator is a non-static member function, you can - use it only within non-static member functions. - - @par Exception Safety Levels - Years ago, David Abrahams formalized a framework for assessing the exception - safety level a function provides. His framework describes three levels of - guarantees. Any function which does not provide any of these levels is - considered unsafe. Checker and StaticChecker determine a function's safety - level through the use of policy classes. Checker's policy classes can show if - a function provides any of these three guarantees. (Caveat: Checker can't - detect leaks directly by itself, but it can call a validator which does.) - StaticChecker's policy classes only provide direct checking for the no-throw - and invariant guarantees. With some finesse, a programmer can write a - validator for StaticChecker that checks for the Strong guarantee. - - - No-throw guarantee: A function will not throw any exceptions. - - Strong guarantee: A function will not change data if an exception occurs. - (Also called the no-change guarantee.) - - Basic guarantee: A function will not leak resources and data will remain - in a valid state if an exception occurs. (Also called a no-leak guarantee.) - */ - -// ---------------------------------------------------------------------------- - -/** @class CheckForNoThrow - - @par Exception Safety Level: - This exception-checking policy class for Checker asserts if an exception exists. - Host classes can use this to show that some member functions provide the no-throw - exception safety guarantees. - - @par Requirements For Host Class: - This policy imposes no requirements on a host class. - */ -template < class Host > -class CheckForNoThrow -{ -public: - - inline explicit CheckForNoThrow( const Host * ) {} - - inline void Check( const Host * ) const - { - assert( !::std::uncaught_exception() ); - } -}; - -// ---------------------------------------------------------------------------- - -/** @class CheckForNoChange - - @par Exception Safety Level: - This exception-checking policy class for Checker asserts only if a copy of the - host differs from the host object when an exception occurs. Host classes can - use this policy to show which member functions provide the strong exception - guarantee. - - @par Requirements: - This policy requires hosts to provide both the copy-constructor and the - equality operator, and is intended for classes with value semantics. - equality operator. - */ - -template < class Host > -class CheckForNoChange -{ -public: - - inline explicit CheckForNoChange( const Host * host ) : - m_compare( *host ) {} - - inline void Check( const Host * host ) const - { - if ( ::std::uncaught_exception() ) - { - assert( m_compare == *host ); - } - } - -private: - Host m_compare; -}; - -// ---------------------------------------------------------------------------- - -/** @class CheckForNoChangeOrThrow - - @par Exception Safety Level: - This exception-checking policy class for Checker asserts either if a copy of - the host differs from the original host object, or if an exception occurs. - Host classes can use this policy to show which member functions provide the - no-throw exception guarantee, and would never change data anyway. - - @par Requirements For Host Class: - This policy requires hosts to provide both the copy-constructor and the - equality operator, and is intended for classes with value semantics. - */ - -template < class Host > -class CheckForNoChangeOrThrow -{ -public: - - inline explicit CheckForNoChangeOrThrow( const Host * host ) : - m_compare( *host ) {} - - inline void Check( const Host * host ) const - { - assert( !::std::uncaught_exception() ); - assert( m_compare == *host ); - } - -private: - Host m_compare; -}; - -// ---------------------------------------------------------------------------- - -/** @class CheckForEquality - - @par Exception Safety Level: - This exception-checking policy class for Checker asserts only if a copy of the - host differs from the host object regardless of whether an exception occurs. - Host classes can use this policy to show which member functions never change - data members, and thereby provide the strong exception safety level by default. - - @par Requirements For Host Class: - This policy requires hosts to provide both the copy-constructor and the - equality operator, and is intended for classes with value semantics. - */ - -template < class Host > -class CheckForEquality -{ -public: - - inline explicit CheckForEquality( const Host * host ) : - m_compare( *host ) {} - - inline void Check( const Host * host ) const - { - assert( m_compare == *host ); - } - -private: - Host m_compare; -}; - -// ---------------------------------------------------------------------------- - -/** @class CheckForNothing - - @par Exception Safety Level: - This exception-checking policy class for Checker does nothing when called. - Host classes can use this to show which member functions provide neither the - strong nor no-throw exception guarantees. The best guarantee such functions - can provide is that nothing gets leaked. - - @par Requirements For Host Class: - This policy imposes no requirements on a host class. - */ - -template < class Host > -class CheckForNothing -{ -public: - inline explicit CheckForNothing( const Host * ) {} - inline void Check( const Host * ) const {} -}; - -// ---------------------------------------------------------------------------- - -/** @class Checker - This class checks if a host class violated an invariant. This asserts if any - check for an invariant failed. It can also demonstrate which functions provide - which exception safety level. - - @par Usage - -# Implement a function that checks each class invariant. The function must - have the signature similar to the Validator type. Something like: - "bool Host::IsValid( void ) const;" - - The function should return true if everything is okay, but false if - something is wrong. - - Or it could assert if anything is wrong. - -# Declare some typedef's inside the class declaration like these. Make one - typedef for each exception policy you use. I typedef'ed the CheckForNothing - policy as CheckInvariants because even if a function can't provide either the - no-throw nor the no-change policies, it should still make sure the object - remains in a valid state. - - typedef ::Loki::Checker< Host, ::Loki::CheckForNoThrow > CheckForNoThrow; - - typedef ::Loki::Checker< Host, ::Loki::CheckForNoChange > CheckForNoChange; - - typedef ::Loki::Checker< Host, ::Loki::CheckForEquality > CheckForEquality; - - typedef ::Loki::Checker< Host, ::Loki::CheckForNothing > CheckInvariants; - -# Construct a checker near the top of each member function - except in the - validator member function. Pass the this pointer and the address of your - validator function into the checker's constructor. - - If the function never throws, then use the CheckForNoThrow policy. - - If the function never changes any data members, then use CheckForEquality - policy. - - If the function's normal execution flow changes data, but must make sure - data remains unchanged when any exceptions occur, then use the - CheckForNoChange policy. - - Otherwise use the CheckInvariants policy. - -# Recompile a debug version of your program, run it, and look for which - assertions failed. - */ - -template -< - class Host, - template < class > class ExceptionPolicy -> -class Checker : public ExceptionPolicy< Host > -{ - /// Shorthand for the ExceptionPolicy class. - typedef ExceptionPolicy< Host > Ep; - -public: - - /// Signature for the validation function. - typedef bool ( Host:: * Validator )( void ) const; - - /** The constructor makes sure the host is valid at the time the checker - was created, thus insuring the host object was not corrupt from the start. - */ - inline Checker( const Host * host, Validator validator ) : - Ep( host ), - m_host( host ), - m_validator( validator ) - { - Check(); - } - - /** The destructor checks if any Host invariants failed, and then calls the - ExceptionPolicy's Check function to determine what to do in case of an - exception. - */ - inline ~Checker( void ) - { - Check(); - Ep::Check( m_host ); - } - - /** This first checks the invariants for Checker, and then calls the - validator function for the host to make sure no class invariants - were broken by the host within the Host's member function body. The - host member function can call Check directly to verify the object - remains valid at any time. - */ - inline void Check( void ) const - { - assert( 0 != this ); - assert( 0 != m_host ); - assert( 0 != m_validator ); - // Now that this confirms the pointers to the host and validation - // functions are not null, go ahead and validate the host object. - assert( ( m_host->*( m_validator ) )() ); - } - -private: - - /// Default constructor is not implemented. - Checker( void ); - /// Copy constructor is not implemented. - Checker( const Checker & ); - /// Copy-assignment operator is not implemented. - Checker & operator = ( const Checker & ); - - /// Pointer to the host object. - const Host * m_host; - - /// Pointer to member function that checks Host object's invariants. - Validator m_validator; -}; - -// ---------------------------------------------------------------------------- - -/** @class CheckStaticForNoThrow - - @par Exception Safety Level: - This exception-checking policy class for StaticChecker asserts if an exception - exists. Functions can use this to show they provide the no-throw exception - safety guarantee. - */ -class CheckStaticForNoThrow -{ -public: - static inline void Check( void ) - { - assert( !::std::uncaught_exception() ); - } -}; - -// ---------------------------------------------------------------------------- - -/** @class CheckStaticForNothing - - @par Exception Safety Level: - This exception-checking policy class for StaticChecker does nothing when called. - Functions can use this to show they might provide the weak exception guarantee. - The best guarantee such functions can provide is that nothing gets leaked. - */ -class CheckStaticForNothing -{ -public: - static inline void Check( void ) {} -}; - -// ---------------------------------------------------------------------------- - -/** @class StaticChecker - This class checks if a function provides the no-throw exception safety level - and if the function violated any invariants. Invariants for stand-alone and - static functions act as pre-conditions and post-conditions. - - @par Usage - -# Implement a function that checks the invariants associated with a function, - or with the static data for a class. The function must - have the signature similar to the Validator type. Something like: - "static bool Host::StaticIsValid( void );" or "bool IsOkay( void );" - - The function should return true if everything is okay, but false if - something is wrong. - - Or it could assert if anything is wrong. - -# If the checker is for static functions within a class, declare typedef's - inside the class declaration like these. Make one typedef for each policy - you use. I typedef'ed the CheckForNothing policy as CheckInvariants because - even if a function can't provide the no-throw guarantee, it should still - make sure that static data remains in a valid state. - - typedef ::Loki::StaticChecker< ::Loki::CheckForNoThrow > CheckStaticForNoThrow; - - typedef ::Loki::StaticChecker< ::Loki::CheckForNothing > CheckStaticInvariants; - -# Construct a checker near the top of each member function - except in the - validator member function. Pass the address of your validator function into - the checker's constructor. - - If the function never throws, then use the CheckForNoThrow policy. - - Otherwise use the CheckInvariants policy. - -# Recompile a debug version of your program, run it, and look for which - assertions failed. - */ - -template -< - class ExceptionPolicy -> -class StaticChecker : public ExceptionPolicy -{ - /// Shorthand for the ExceptionPolicy class. - typedef ExceptionPolicy Ep; - -public: - - /// Signature for the validation function. - typedef bool ( * Validator )( void ); - - /** The constructor makes sure the host is valid at the time the checker - was created, thus insuring the host object was not corrupt from the start. - */ - inline explicit StaticChecker( Validator validator ) : - Ep(), - m_validator( validator ) - { - Check(); - } - - /** The destructor checks if any Host invariants failed, and then calls the - ExceptionPolicy's Check function to determine what to do in case of an - exception. - */ - inline ~StaticChecker( void ) - { - Check(); - Ep::Check(); - } - - /** This first checks its own invariants, and then calls the validator - function to make sure no invariants were broken by the function which - created this checker. That function can call Check directly to verify - the data remains valid at any time. - */ - inline void Check( void ) const - { - assert( 0 != this ); - assert( 0 != m_validator ); - // Now that this confirms the pointers to the host and validation - // functions are not null, go ahead and validate the host object. - assert( ( m_validator )() ); - } - -private: - - /// Default constructor is not implemented. - StaticChecker( void ); - /// Copy constructor is not implemented. - StaticChecker( const StaticChecker & ); - /// Copy-assignment operator is not implemented. - StaticChecker & operator = ( const StaticChecker & ); - - /// Pointer to member function that checks Host object's invariants. - Validator m_validator; - -}; - -// ---------------------------------------------------------------------------- - -}; // end namespace Loki - -#endif +//////////////////////////////////////////////////////////////////////////////// +// +// The Loki Library +// Copyright (c) 2008 Rich 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$ + +/// @file Checker.h This file provides Loki's Checker facility. + + +// ---------------------------------------------------------------------------- + +#ifndef LOKI_CHECKER_H_INCLUDED +#define LOKI_CHECKER_H_INCLUDED + +#include // needed for calls to uncaught_exception. +#include + + +namespace Loki +{ + +/** @par Checker and StaticChecker Overview + The Checker and StaticChecker classes have two purposes: + - provide a mechanism by which programmers can determine which functions + violate class/data invariants, + - and determine which exception safety a function provides. + + @par Class & Data Invariants + The Checker and StaticChecker utilities define invariants as "expressions that + are true for particular data". They uses a function which returns true if all + data are valid, and returns false if any datum is invalid. This is called the + validator function, and the host class or function provides a pointer to it. + The validator could also assert for any invariant which fails rather than + return false. If the validator is a static member function, you can use it + with checkers in any function, but especially standalone functions and class + static functions. If the validator is a non-static member function, you can + use it only within non-static member functions. + + @par Exception Safety Levels + Years ago, David Abrahams formalized a framework for assessing the exception + safety level a function provides. His framework describes three levels of + guarantees. Any function which does not provide any of these levels is + considered unsafe. Checker and StaticChecker determine a function's safety + level through the use of policy classes. Checker's policy classes can show if + a function provides any of these three guarantees. (Caveat: Checker can't + detect leaks directly by itself, but it can call a validator which does.) + StaticChecker's policy classes only provide direct checking for the no-throw + and invariant guarantees. With some finesse, a programmer can write a + validator for StaticChecker that checks for the Strong guarantee. + + - No-throw guarantee: A function will not throw any exceptions. + - Strong guarantee: A function will not change data if an exception occurs. + (Also called the no-change guarantee.) + - Basic guarantee: A function will not leak resources and data will remain + in a valid state if an exception occurs. (Also called a no-leak guarantee.) + */ + +// ---------------------------------------------------------------------------- + +/** @class CheckForNoThrow + + @par Exception Safety Level: + This exception-checking policy class for Checker asserts if an exception exists. + Host classes can use this to show that some member functions provide the no-throw + exception safety guarantees. + + @par Requirements For Host Class: + This policy imposes no requirements on a host class. + */ +template < class Host > +class CheckForNoThrow +{ +public: + + inline explicit CheckForNoThrow( const Host * ) {} + + inline void Check( const Host * ) const + { + assert( !::std::uncaught_exception() ); + } +}; + +// ---------------------------------------------------------------------------- + +/** @class CheckForNoChange + + @par Exception Safety Level: + This exception-checking policy class for Checker asserts only if a copy of the + host differs from the host object when an exception occurs. Host classes can + use this policy to show which member functions provide the strong exception + guarantee. + + @par Requirements: + This policy requires hosts to provide both the copy-constructor and the + equality operator, and is intended for classes with value semantics. + equality operator. + */ + +template < class Host > +class CheckForNoChange +{ +public: + + inline explicit CheckForNoChange( const Host * host ) : + m_compare( *host ) {} + + inline void Check( const Host * host ) const + { + if ( ::std::uncaught_exception() ) + { + assert( m_compare == *host ); + } + } + +private: + Host m_compare; +}; + +// ---------------------------------------------------------------------------- + +/** @class CheckForNoChangeOrThrow + + @par Exception Safety Level: + This exception-checking policy class for Checker asserts either if a copy of + the host differs from the original host object, or if an exception occurs. + Host classes can use this policy to show which member functions provide the + no-throw exception guarantee, and would never change data anyway. + + @par Requirements For Host Class: + This policy requires hosts to provide both the copy-constructor and the + equality operator, and is intended for classes with value semantics. + */ + +template < class Host > +class CheckForNoChangeOrThrow +{ +public: + + inline explicit CheckForNoChangeOrThrow( const Host * host ) : + m_compare( *host ) {} + + inline void Check( const Host * host ) const + { + assert( !::std::uncaught_exception() ); + assert( m_compare == *host ); + } + +private: + Host m_compare; +}; + +// ---------------------------------------------------------------------------- + +/** @class CheckForEquality + + @par Exception Safety Level: + This exception-checking policy class for Checker asserts only if a copy of the + host differs from the host object regardless of whether an exception occurs. + Host classes can use this policy to show which member functions never change + data members, and thereby provide the strong exception safety level by default. + + @par Requirements For Host Class: + This policy requires hosts to provide both the copy-constructor and the + equality operator, and is intended for classes with value semantics. + */ + +template < class Host > +class CheckForEquality +{ +public: + + inline explicit CheckForEquality( const Host * host ) : + m_compare( *host ) {} + + inline void Check( const Host * host ) const + { + assert( m_compare == *host ); + } + +private: + Host m_compare; +}; + +// ---------------------------------------------------------------------------- + +/** @class CheckForNothing + + @par Exception Safety Level: + This exception-checking policy class for Checker does nothing when called. + Host classes can use this to show which member functions provide neither the + strong nor no-throw exception guarantees. The best guarantee such functions + can provide is that nothing gets leaked. + + @par Requirements For Host Class: + This policy imposes no requirements on a host class. + */ + +template < class Host > +class CheckForNothing +{ +public: + inline explicit CheckForNothing( const Host * ) {} + inline void Check( const Host * ) const {} +}; + +// ---------------------------------------------------------------------------- + +/** @class Checker + This class checks if a host class violated an invariant. This asserts if any + check for an invariant failed. It can also demonstrate which functions provide + which exception safety level. + + @par Usage + -# Implement a function that checks each class invariant. The function must + have the signature similar to the Validator type. Something like: + "bool Host::IsValid( void ) const;" + - The function should return true if everything is okay, but false if + something is wrong. + - Or it could assert if anything is wrong. + -# Declare some typedef's inside the class declaration like these. Make one + typedef for each exception policy you use. I typedef'ed the CheckForNothing + policy as CheckInvariants because even if a function can't provide either the + no-throw nor the no-change policies, it should still make sure the object + remains in a valid state. + - typedef ::Loki::Checker< Host, ::Loki::CheckForNoThrow > CheckForNoThrow; + - typedef ::Loki::Checker< Host, ::Loki::CheckForNoChange > CheckForNoChange; + - typedef ::Loki::Checker< Host, ::Loki::CheckForEquality > CheckForEquality; + - typedef ::Loki::Checker< Host, ::Loki::CheckForNothing > CheckInvariants; + -# Construct a checker near the top of each member function - except in the + validator member function. Pass the this pointer and the address of your + validator function into the checker's constructor. + - If the function never throws, then use the CheckForNoThrow policy. + - If the function never changes any data members, then use CheckForEquality + policy. + - If the function's normal execution flow changes data, but must make sure + data remains unchanged when any exceptions occur, then use the + CheckForNoChange policy. + - Otherwise use the CheckInvariants policy. + -# Recompile a debug version of your program, run it, and look for which + assertions failed. + */ + +template +< + class Host, + template < class > class ExceptionPolicy +> +class Checker : public ExceptionPolicy< Host > +{ + /// Shorthand for the ExceptionPolicy class. + typedef ExceptionPolicy< Host > Ep; + +public: + + /// Signature for the validation function. + typedef bool ( Host:: * Validator )( void ) const; + + /** The constructor makes sure the host is valid at the time the checker + was created, thus insuring the host object was not corrupt from the start. + */ + inline Checker( const Host * host, Validator validator ) : + Ep( host ), + m_host( host ), + m_validator( validator ) + { + Check(); + } + + /** The destructor checks if any Host invariants failed, and then calls the + ExceptionPolicy's Check function to determine what to do in case of an + exception. + */ + inline ~Checker( void ) + { + Check(); + Ep::Check( m_host ); + } + + /** This first checks the invariants for Checker, and then calls the + validator function for the host to make sure no class invariants + were broken by the host within the Host's member function body. The + host member function can call Check directly to verify the object + remains valid at any time. + */ + inline void Check( void ) const + { + assert( 0 != this ); + assert( 0 != m_host ); + assert( 0 != m_validator ); + // Now that this confirms the pointers to the host and validation + // functions are not null, go ahead and validate the host object. + assert( ( m_host->*( m_validator ) )() ); + } + +private: + + /// Default constructor is not implemented. + Checker( void ); + /// Copy constructor is not implemented. + Checker( const Checker & ); + /// Copy-assignment operator is not implemented. + Checker & operator = ( const Checker & ); + + /// Pointer to the host object. + const Host * m_host; + + /// Pointer to member function that checks Host object's invariants. + Validator m_validator; +}; + +// ---------------------------------------------------------------------------- + +/** @class CheckStaticForNoThrow + + @par Exception Safety Level: + This exception-checking policy class for StaticChecker asserts if an exception + exists. Functions can use this to show they provide the no-throw exception + safety guarantee. + */ +class CheckStaticForNoThrow +{ +public: + static inline void Check( void ) + { + assert( !::std::uncaught_exception() ); + } +}; + +// ---------------------------------------------------------------------------- + +/** @class CheckStaticForNothing + + @par Exception Safety Level: + This exception-checking policy class for StaticChecker does nothing when called. + Functions can use this to show they might provide the weak exception guarantee. + The best guarantee such functions can provide is that nothing gets leaked. + */ +class CheckStaticForNothing +{ +public: + static inline void Check( void ) {} +}; + +// ---------------------------------------------------------------------------- + +/** @class StaticChecker + This class checks if a function provides the no-throw exception safety level + and if the function violated any invariants. Invariants for stand-alone and + static functions act as pre-conditions and post-conditions. + + @par Usage + -# Implement a function that checks the invariants associated with a function, + or with the static data for a class. The function must + have the signature similar to the Validator type. Something like: + "static bool Host::StaticIsValid( void );" or "bool IsOkay( void );" + - The function should return true if everything is okay, but false if + something is wrong. + - Or it could assert if anything is wrong. + -# If the checker is for static functions within a class, declare typedef's + inside the class declaration like these. Make one typedef for each policy + you use. I typedef'ed the CheckForNothing policy as CheckInvariants because + even if a function can't provide the no-throw guarantee, it should still + make sure that static data remains in a valid state. + - typedef ::Loki::StaticChecker< ::Loki::CheckForNoThrow > CheckStaticForNoThrow; + - typedef ::Loki::StaticChecker< ::Loki::CheckForNothing > CheckStaticInvariants; + -# Construct a checker near the top of each member function - except in the + validator member function. Pass the address of your validator function into + the checker's constructor. + - If the function never throws, then use the CheckForNoThrow policy. + - Otherwise use the CheckInvariants policy. + -# Recompile a debug version of your program, run it, and look for which + assertions failed. + */ + +template +< + class ExceptionPolicy +> +class StaticChecker : public ExceptionPolicy +{ + /// Shorthand for the ExceptionPolicy class. + typedef ExceptionPolicy Ep; + +public: + + /// Signature for the validation function. + typedef bool ( * Validator )( void ); + + /** The constructor makes sure the host is valid at the time the checker + was created, thus insuring the host object was not corrupt from the start. + */ + inline explicit StaticChecker( Validator validator ) : + Ep(), + m_validator( validator ) + { + Check(); + } + + /** The destructor checks if any Host invariants failed, and then calls the + ExceptionPolicy's Check function to determine what to do in case of an + exception. + */ + inline ~StaticChecker( void ) + { + Check(); + Ep::Check(); + } + + /** This first checks its own invariants, and then calls the validator + function to make sure no invariants were broken by the function which + created this checker. That function can call Check directly to verify + the data remains valid at any time. + */ + inline void Check( void ) const + { + assert( 0 != this ); + assert( 0 != m_validator ); + // Now that this confirms the pointers to the host and validation + // functions are not null, go ahead and validate the host object. + assert( ( m_validator )() ); + } + +private: + + /// Default constructor is not implemented. + StaticChecker( void ); + /// Copy constructor is not implemented. + StaticChecker( const StaticChecker & ); + /// Copy-assignment operator is not implemented. + StaticChecker & operator = ( const StaticChecker & ); + + /// Pointer to member function that checks Host object's invariants. + Validator m_validator; + +}; + +// ---------------------------------------------------------------------------- + +}; // end namespace Loki + +#endif diff --git a/include/loki/SPCachedFactory.h b/include/loki/SPCachedFactory.h index 724a618..0262177 100644 --- a/include/loki/SPCachedFactory.h +++ b/include/loki/SPCachedFactory.h @@ -1,204 +1,204 @@ -//////////////////////////////////////////////////////////////////////////////// -// The Loki Library -// Copyright (c) 2006 by Guillaume Chatelet -// -// Code covered by 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 authors make no representations about the suitability of this software -// for any purpose. It is provided "as is" without express or implied warranty. -// -// This code DOES NOT accompany the book: -// Alexandrescu, Andrei. "Modern C++ Design: Generic Programming and Design -// Patterns Applied". Copyright (c) 2001. Addison-Wesley. -// -//////////////////////////////////////////////////////////////////////////////// - -// $Id$ - -#ifndef SPCACHEDFACTORY_H_ -#define SPCACHEDFACTORY_H_ - -/** - * This file is intented to be used if you want a CachedFactory with - * a SmartPointer encapsulation policy. - * It as been defined in a separate file because of the many introduced - * dependencies (SmartPtr.h would depend on Functor.h and CachedFactory.h - * would depend on SmartPtr.h). By defining another header you pay for those - * extra dependencies only if you need it. - * - * This file defines FunctionStorage a new SmartPointer storage policy and - * SmartPointer a new CachedFactory encapsulation policy. - */ - -#include -#include -#include - -namespace Loki -{ - -//////////////////////////////////////////////////////////////////////////////// -/// \class FunctionStorage -/// -/// \ingroup SmartPointerStorageGroup -/// \brief Implementation of the StoragePolicy used by SmartPtr. -/// -/// This storage policy is used by SmartPointer CachedFactory's encapsulation -/// policy. It's purpose is to call a Functor instead of deleting the -/// underlying pointee object. You have to set the callback functor by calling -/// SetCallBackFunction(const FunctorType &functor). -/// -/// Unfortunately, the functor argument is not a reference to the SmartPtr but -/// a void *. Making functor argument a reference to the pointer would require -/// the FunctionStorage template to know the full definition of the SmartPtr. -//////////////////////////////////////////////////////////////////////////////// - - template - class FunctionStorage - { - public: - /// the type of the pointee_ object - typedef T* StoredType; - /// type used to declare OwnershipPolicy type. - typedef T* InitPointerType; - /// type returned by operator-> - typedef T* PointerType; - /// type returned by operator* - typedef T& ReferenceType; - /// type of the Functor to set - typedef Functor< void , Seq< void* > > FunctorType; - - FunctionStorage() : pointee_(Default()), functor_() - {} - - // The storage policy doesn't initialize the stored pointer - // which will be initialized by the OwnershipPolicy's Clone fn - FunctionStorage(const FunctionStorage& rsh) : pointee_(0), functor_(rsh.functor_) - {} - - template - FunctionStorage(const FunctionStorage& rsh) : pointee_(0), functor_(rsh.functor_) - {} - - FunctionStorage(const StoredType& p) : pointee_(p), functor_() {} - - PointerType operator->() const { return pointee_; } - - ReferenceType operator*() const { return *pointee_; } - - void Swap(FunctionStorage& rhs) - { - std::swap(pointee_, rhs.pointee_); - std::swap(functor_, rhs.functor_); - } - - /// Sets the callback function to call. You have to specify it or - /// the smartPtr will throw a bad_function_call exception. - void SetCallBackFunction(const FunctorType &functor) - { - functor_ = functor; - } - - // Accessors - template - friend typename FunctionStorage::PointerType GetImpl(const FunctionStorage& sp); - - template - friend const typename FunctionStorage::StoredType& GetImplRef(const FunctionStorage& sp); - - template - friend typename FunctionStorage::StoredType& GetImplRef(FunctionStorage& sp); - - protected: - // Destroys the data stored - // (Destruction might be taken over by the OwnershipPolicy) - void Destroy() - { - functor_(this); - } - - // Default value to initialize the pointer - static StoredType Default() - { return 0; } - - private: - // Data - StoredType pointee_; - FunctorType functor_; - }; - - template - inline typename FunctionStorage::PointerType GetImpl(const FunctionStorage& sp) - { return sp.pointee_; } - - template - inline const typename FunctionStorage::StoredType& GetImplRef(const FunctionStorage& sp) - { return sp.pointee_; } - - template - inline typename FunctionStorage::StoredType& GetImplRef(FunctionStorage& sp) - { return sp.pointee_; } - - /** - * \class SmartPointer - * \ingroup EncapsulationPolicyCachedFactoryGroup - * \brief Encapsulate the object in a SmartPtr with FunctionStorage policy. - * - * The object will come back to the Cache as soon as no more SmartPtr are - * referencing this object. You can customize the SmartPointer with the standard - * SmartPtr policies (OwnershipPolicy, ConversionPolicy, CheckingPolicy, - * ConstnessPolicy) but StoragePolicy is forced to FunctionStorage. - */ - template - < - class AbstractProduct, - template class OwnershipPolicy = RefCounted, - class ConversionPolicy = DisallowConversion, - template class CheckingPolicy = AssertCheck, - template class ConstnessPolicy = LOKI_DEFAULT_CONSTNESS - > - class SmartPointer - { - private: - typedef SmartPtr< AbstractProduct,OwnershipPolicy, - ConversionPolicy, CheckingPolicy, - FunctionStorage, ConstnessPolicy > CallBackSP; - protected: - typedef CallBackSP ProductReturn; - SmartPointer() : fun(this, &SmartPointer::smartPointerCallbackFunction) {} - virtual ~SmartPointer(){} - - ProductReturn encapsulate(AbstractProduct* pProduct) - { - CallBackSP SP(pProduct); - SP.SetCallBackFunction(fun); - return SP; - } - - AbstractProduct* release(ProductReturn &pProduct) - { - return GetImpl(pProduct); - } - - const char* name(){return "smart pointer";} - - private: - SmartPointer& operator=(const SmartPointer&); - SmartPointer(const SmartPointer&); - void smartPointerCallbackFunction(void* pSP) - { - CallBackSP &SP(*reinterpret_cast(pSP)); - ReleaseObject(SP); - } - virtual void ReleaseObject(ProductReturn &object)=0; - const typename CallBackSP::FunctorType fun; - }; - -} // namespace Loki - -#endif /*SPCACHEDFACTORY_H_*/ +//////////////////////////////////////////////////////////////////////////////// +// The Loki Library +// Copyright (c) 2006 by Guillaume Chatelet +// +// Code covered by 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 authors make no representations about the suitability of this software +// for any purpose. It is provided "as is" without express or implied warranty. +// +// This code DOES NOT accompany the book: +// Alexandrescu, Andrei. "Modern C++ Design: Generic Programming and Design +// Patterns Applied". Copyright (c) 2001. Addison-Wesley. +// +//////////////////////////////////////////////////////////////////////////////// + +// $Id$ + +#ifndef SPCACHEDFACTORY_H_ +#define SPCACHEDFACTORY_H_ + +/** + * This file is intented to be used if you want a CachedFactory with + * a SmartPointer encapsulation policy. + * It as been defined in a separate file because of the many introduced + * dependencies (SmartPtr.h would depend on Functor.h and CachedFactory.h + * would depend on SmartPtr.h). By defining another header you pay for those + * extra dependencies only if you need it. + * + * This file defines FunctionStorage a new SmartPointer storage policy and + * SmartPointer a new CachedFactory encapsulation policy. + */ + +#include +#include +#include + +namespace Loki +{ + +//////////////////////////////////////////////////////////////////////////////// +/// \class FunctionStorage +/// +/// \ingroup SmartPointerStorageGroup +/// \brief Implementation of the StoragePolicy used by SmartPtr. +/// +/// This storage policy is used by SmartPointer CachedFactory's encapsulation +/// policy. It's purpose is to call a Functor instead of deleting the +/// underlying pointee object. You have to set the callback functor by calling +/// SetCallBackFunction(const FunctorType &functor). +/// +/// Unfortunately, the functor argument is not a reference to the SmartPtr but +/// a void *. Making functor argument a reference to the pointer would require +/// the FunctionStorage template to know the full definition of the SmartPtr. +//////////////////////////////////////////////////////////////////////////////// + + template + class FunctionStorage + { + public: + /// the type of the pointee_ object + typedef T* StoredType; + /// type used to declare OwnershipPolicy type. + typedef T* InitPointerType; + /// type returned by operator-> + typedef T* PointerType; + /// type returned by operator* + typedef T& ReferenceType; + /// type of the Functor to set + typedef Functor< void , Seq< void* > > FunctorType; + + FunctionStorage() : pointee_(Default()), functor_() + {} + + // The storage policy doesn't initialize the stored pointer + // which will be initialized by the OwnershipPolicy's Clone fn + FunctionStorage(const FunctionStorage& rsh) : pointee_(0), functor_(rsh.functor_) + {} + + template + FunctionStorage(const FunctionStorage& rsh) : pointee_(0), functor_(rsh.functor_) + {} + + FunctionStorage(const StoredType& p) : pointee_(p), functor_() {} + + PointerType operator->() const { return pointee_; } + + ReferenceType operator*() const { return *pointee_; } + + void Swap(FunctionStorage& rhs) + { + std::swap(pointee_, rhs.pointee_); + std::swap(functor_, rhs.functor_); + } + + /// Sets the callback function to call. You have to specify it or + /// the smartPtr will throw a bad_function_call exception. + void SetCallBackFunction(const FunctorType &functor) + { + functor_ = functor; + } + + // Accessors + template + friend typename FunctionStorage::PointerType GetImpl(const FunctionStorage& sp); + + template + friend const typename FunctionStorage::StoredType& GetImplRef(const FunctionStorage& sp); + + template + friend typename FunctionStorage::StoredType& GetImplRef(FunctionStorage& sp); + + protected: + // Destroys the data stored + // (Destruction might be taken over by the OwnershipPolicy) + void Destroy() + { + functor_(this); + } + + // Default value to initialize the pointer + static StoredType Default() + { return 0; } + + private: + // Data + StoredType pointee_; + FunctorType functor_; + }; + + template + inline typename FunctionStorage::PointerType GetImpl(const FunctionStorage& sp) + { return sp.pointee_; } + + template + inline const typename FunctionStorage::StoredType& GetImplRef(const FunctionStorage& sp) + { return sp.pointee_; } + + template + inline typename FunctionStorage::StoredType& GetImplRef(FunctionStorage& sp) + { return sp.pointee_; } + + /** + * \class SmartPointer + * \ingroup EncapsulationPolicyCachedFactoryGroup + * \brief Encapsulate the object in a SmartPtr with FunctionStorage policy. + * + * The object will come back to the Cache as soon as no more SmartPtr are + * referencing this object. You can customize the SmartPointer with the standard + * SmartPtr policies (OwnershipPolicy, ConversionPolicy, CheckingPolicy, + * ConstnessPolicy) but StoragePolicy is forced to FunctionStorage. + */ + template + < + class AbstractProduct, + template class OwnershipPolicy = RefCounted, + class ConversionPolicy = DisallowConversion, + template class CheckingPolicy = AssertCheck, + template class ConstnessPolicy = LOKI_DEFAULT_CONSTNESS + > + class SmartPointer + { + private: + typedef SmartPtr< AbstractProduct,OwnershipPolicy, + ConversionPolicy, CheckingPolicy, + FunctionStorage, ConstnessPolicy > CallBackSP; + protected: + typedef CallBackSP ProductReturn; + SmartPointer() : fun(this, &SmartPointer::smartPointerCallbackFunction) {} + virtual ~SmartPointer(){} + + ProductReturn encapsulate(AbstractProduct* pProduct) + { + CallBackSP SP(pProduct); + SP.SetCallBackFunction(fun); + return SP; + } + + AbstractProduct* release(ProductReturn &pProduct) + { + return GetImpl(pProduct); + } + + const char* name(){return "smart pointer";} + + private: + SmartPointer& operator=(const SmartPointer&); + SmartPointer(const SmartPointer&); + void smartPointerCallbackFunction(void* pSP) + { + CallBackSP &SP(*reinterpret_cast(pSP)); + ReleaseObject(SP); + } + virtual void ReleaseObject(ProductReturn &object)=0; + const typename CallBackSP::FunctorType fun; + }; + +} // namespace Loki + +#endif /*SPCACHEDFACTORY_H_*/ diff --git a/test/CheckReturn/CheckReturn.dev b/test/CheckReturn/CheckReturn.dev index 60c3e0e..eddc815 100755 --- a/test/CheckReturn/CheckReturn.dev +++ b/test/CheckReturn/CheckReturn.dev @@ -1,69 +1,69 @@ -[Project] -FileName=CheckReturn.dev -Name=CheckReturn -UnitCount=2 -Type=1 -Ver=1 -ObjFiles= -Includes=../../include -Libs= -PrivateResource= -ResourceIncludes= -MakeIncludes= -Compiler= -CppCompiler= -Linker= -IsCpp=1 -Icon= -ExeOutput= -ObjectOutput= -OverrideOutput=0 -OverrideOutputName=CheckReturn.exe -HostApplication= -Folders= -CommandLine= -UseCustomMakefile=0 -CustomMakefile= -IncludeVersionInfo=0 -SupportXPThemes=0 -CompilerSet=0 -CompilerSettings=1000001000000001000000 - -[Unit1] -FileName=main.cpp -CompileCpp=1 -Folder=CheckReturn -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit2] -FileName=..\..\include\loki\CheckReturn.h -CompileCpp=1 -Folder=CheckReturn -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[VersionInfo] -Major=0 -Minor=1 -Release=1 -Build=1 -LanguageID=1033 -CharsetID=1252 -CompanyName= -FileVersion= -FileDescription=Developed using the Dev-C++ IDE -InternalName= -LegalCopyright= -LegalTrademarks= -OriginalFilename= -ProductName= -ProductVersion= -AutoIncBuildNr=0 - +[Project] +FileName=CheckReturn.dev +Name=CheckReturn +UnitCount=2 +Type=1 +Ver=1 +ObjFiles= +Includes=../../include +Libs= +PrivateResource= +ResourceIncludes= +MakeIncludes= +Compiler= +CppCompiler= +Linker= +IsCpp=1 +Icon= +ExeOutput= +ObjectOutput= +OverrideOutput=0 +OverrideOutputName=CheckReturn.exe +HostApplication= +Folders= +CommandLine= +UseCustomMakefile=0 +CustomMakefile= +IncludeVersionInfo=0 +SupportXPThemes=0 +CompilerSet=0 +CompilerSettings=1000001000000001000000 + +[Unit1] +FileName=main.cpp +CompileCpp=1 +Folder=CheckReturn +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit2] +FileName=..\..\include\loki\CheckReturn.h +CompileCpp=1 +Folder=CheckReturn +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[VersionInfo] +Major=0 +Minor=1 +Release=1 +Build=1 +LanguageID=1033 +CharsetID=1252 +CompanyName= +FileVersion= +FileDescription=Developed using the Dev-C++ IDE +InternalName= +LegalCopyright= +LegalTrademarks= +OriginalFilename= +ProductName= +ProductVersion= +AutoIncBuildNr=0 + diff --git a/test/CheckReturn/CheckReturn.vcproj b/test/CheckReturn/CheckReturn.vcproj index 9eb8f41..ad203d3 100755 --- a/test/CheckReturn/CheckReturn.vcproj +++ b/test/CheckReturn/CheckReturn.vcproj @@ -1,197 +1,197 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test/CheckReturn/CheckReturn_MSVC_8.vcproj b/test/CheckReturn/CheckReturn_MSVC_8.vcproj index 53d4b47..a3afc4b 100644 --- a/test/CheckReturn/CheckReturn_MSVC_8.vcproj +++ b/test/CheckReturn/CheckReturn_MSVC_8.vcproj @@ -1,198 +1,198 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test/CheckReturn/main.cpp b/test/CheckReturn/main.cpp index 568ca21..f10e095 100755 --- a/test/CheckReturn/main.cpp +++ b/test/CheckReturn/main.cpp @@ -1,155 +1,155 @@ -//////////////////////////////////////////////////////////////////////////////// -// The Loki Library -// Copyright (c) 2007 Rich Sposato -// 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 - -#include -#include - -using namespace std; - - -// ---------------------------------------------------------------------------- - -typedef ::Loki::CheckReturn< bool > BoolReturn; - -typedef ::Loki::CheckReturn< string > StringReturn; - - -// ---------------------------------------------------------------------------- - -bool NoCheckRequired( void ) -{ - return true; -} - -// ---------------------------------------------------------------------------- - -BoolReturn CheckRequired( void ) -{ - return BoolReturn( true ); -} - -// ---------------------------------------------------------------------------- - -BoolReturn CheckRequired( bool value ) -{ - // By passing false into CheckRequired, CheckRequired calls an overloaded - // version of CheckRequired, thus demonstrating that one can use a function - // call within the return statement and not have to create a named - // temporary variable. - if ( !value ) - return CheckRequired(); - - return BoolReturn( value ); -} - -// ---------------------------------------------------------------------------- - -string GetString( void ) -{ - return string( "a" ); -} - -// ---------------------------------------------------------------------------- - -StringReturn MustUseString( const char * s ) -{ - return StringReturn( s ); -} - -// ---------------------------------------------------------------------------- - -void StringEater( const string & s ) -{ - (void)s; -} - -// ---------------------------------------------------------------------------- - -int main( unsigned int argc, const char * argv[] ) -{ - - if ( 2 == argc ) - { - const char * const a = argv[ argc - 1 ]; - - // okay to call without storing or using return value. - GetString(); - cout << "Called GetString without using return value." << endl; - - // Should not assert since caller stores return value. - const string s = MustUseString( a ); - cout << "Called MustUseString and stored return value." << endl; - - { - // Should not assert since caller passes return value into another - // function, thus converting CheckReturn back into actual type. - StringEater( MustUseString( a ) ); - cout << - "Called MustUseString and passed return value into another function." - << endl; - } - - // This should assert since caller does not check return value. - MustUseString( s.c_str() ); - cout << "Should assert before this line! How did we get here?" << endl; - } - - - // okay to call without storing or using return value. - NoCheckRequired(); - cout << "Called NoCheckRequired without using return value." << endl; - - // Should not assert since caller stores return value. - bool okay = CheckRequired(); - cout << "Called CheckRequired and stored return value." << endl; - - if ( CheckRequired( okay ) ) - { - cout << "Called CheckRequired and used return value." << endl; - } - - { - // Should not assert since caller stores return value ... - BoolReturn checkBool = CheckRequired(); - // and then deliberately ignores it before destructor runs. - (bool)checkBool; - cout << "Called CheckRequired, stored return value, and ignored it." - << endl; - } - - { - // This should not assert since caller deliberately chooses to not - // check return value by casting to return value to correct type. - (bool)CheckRequired(); - } - - { - // This should not assert since caller deliberately chooses to not - // check return value by casting to return value to correct type. - (bool)CheckRequired( false ); - cout << "Made a nested call to CheckRequired." << endl; - } - - // This should assert since caller does not check return value. - CheckRequired(); - cout << "Should assert before this line! How did we get here?" << endl; - - return 0; -} - -// ---------------------------------------------------------------------------- - -// $Log$ +//////////////////////////////////////////////////////////////////////////////// +// The Loki Library +// Copyright (c) 2007 Rich Sposato +// 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 + +#include +#include + +using namespace std; + + +// ---------------------------------------------------------------------------- + +typedef ::Loki::CheckReturn< bool > BoolReturn; + +typedef ::Loki::CheckReturn< string > StringReturn; + + +// ---------------------------------------------------------------------------- + +bool NoCheckRequired( void ) +{ + return true; +} + +// ---------------------------------------------------------------------------- + +BoolReturn CheckRequired( void ) +{ + return BoolReturn( true ); +} + +// ---------------------------------------------------------------------------- + +BoolReturn CheckRequired( bool value ) +{ + // By passing false into CheckRequired, CheckRequired calls an overloaded + // version of CheckRequired, thus demonstrating that one can use a function + // call within the return statement and not have to create a named + // temporary variable. + if ( !value ) + return CheckRequired(); + + return BoolReturn( value ); +} + +// ---------------------------------------------------------------------------- + +string GetString( void ) +{ + return string( "a" ); +} + +// ---------------------------------------------------------------------------- + +StringReturn MustUseString( const char * s ) +{ + return StringReturn( s ); +} + +// ---------------------------------------------------------------------------- + +void StringEater( const string & s ) +{ + (void)s; +} + +// ---------------------------------------------------------------------------- + +int main( unsigned int argc, const char * argv[] ) +{ + + if ( 2 == argc ) + { + const char * const a = argv[ argc - 1 ]; + + // okay to call without storing or using return value. + GetString(); + cout << "Called GetString without using return value." << endl; + + // Should not assert since caller stores return value. + const string s = MustUseString( a ); + cout << "Called MustUseString and stored return value." << endl; + + { + // Should not assert since caller passes return value into another + // function, thus converting CheckReturn back into actual type. + StringEater( MustUseString( a ) ); + cout << + "Called MustUseString and passed return value into another function." + << endl; + } + + // This should assert since caller does not check return value. + MustUseString( s.c_str() ); + cout << "Should assert before this line! How did we get here?" << endl; + } + + + // okay to call without storing or using return value. + NoCheckRequired(); + cout << "Called NoCheckRequired without using return value." << endl; + + // Should not assert since caller stores return value. + bool okay = CheckRequired(); + cout << "Called CheckRequired and stored return value." << endl; + + if ( CheckRequired( okay ) ) + { + cout << "Called CheckRequired and used return value." << endl; + } + + { + // Should not assert since caller stores return value ... + BoolReturn checkBool = CheckRequired(); + // and then deliberately ignores it before destructor runs. + (bool)checkBool; + cout << "Called CheckRequired, stored return value, and ignored it." + << endl; + } + + { + // This should not assert since caller deliberately chooses to not + // check return value by casting to return value to correct type. + (bool)CheckRequired(); + } + + { + // This should not assert since caller deliberately chooses to not + // check return value by casting to return value to correct type. + (bool)CheckRequired( false ); + cout << "Made a nested call to CheckRequired." << endl; + } + + // This should assert since caller does not check return value. + CheckRequired(); + cout << "Should assert before this line! How did we get here?" << endl; + + return 0; +} + +// ---------------------------------------------------------------------------- + +// $Log$ diff --git a/test/Checker/Checker_MSVC_9.vcproj b/test/Checker/Checker_MSVC_9.vcproj index 1a5f904..3e7534d 100644 --- a/test/Checker/Checker_MSVC_9.vcproj +++ b/test/Checker/Checker_MSVC_9.vcproj @@ -1,187 +1,187 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test/Checker/main.cpp b/test/Checker/main.cpp index 06ce12c..4ffcb15 100644 --- a/test/Checker/main.cpp +++ b/test/Checker/main.cpp @@ -1,200 +1,200 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// 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$ - -/// @file main.cpp This provides examples on how to use Loki's Checker facility. - - -// ---------------------------------------------------------------------------- - -#include "../../include/loki/Checker.h" - - -#if !defined( nullptr ) - #define nullptr NULL -#endif - -#if !defined( NULL ) - #define NULL 0 -#endif - - -// ---------------------------------------------------------------------------- - -/* This class has 2 invariants. The this pointer may never equal NULL, and the - value may not equal zero. - */ -class Thingy -{ -public: - - // This example shows how static functions can use a no-throw checkers. - static void ChangeThat( void ) - { - CheckStaticForNoThrow checker( &Thingy::StaticIsValid ); - (void)checker; - s_value--; - } - - // This example shows how static functions can use an invariant checkers. - static unsigned int GetThat( void ) - { - CheckStaticInvariants checker( &Thingy::StaticIsValid ); - (void)checker; - return s_value; - } - - // This example shows how ctors can use an invariant checker. - explicit Thingy( unsigned int value ) : m_value( value ) - { - CheckInvariants checker( this, &Thingy::IsValid ); - (void)checker; - checker.Check(); - } - - // A destructor really doesn't need a checker, but does need to confirm - // the object is valid at the start of the destructor. - ~Thingy( void ) - { - assert( IsValid() ); - } - - bool operator == ( const Thingy & that ) const - { - return ( m_value == that.m_value ); - } - - void DoSomethingEvil( void ) - { - m_value = 0; - } - - // This example shows how to use the no-throw checker. - unsigned int GetValue( void ) const - { - CheckForNoThrow checker( this, &Thingy::IsValid ); - (void)checker; - return m_value; - } - - // This example shows how to use the equality checker. - unsigned int DoSomething( bool doThrow ) const - { - CheckForEquality checker( this, &Thingy::IsValid ); - (void)checker; - if ( doThrow ) - throw ::std::exception( "Test Exception." ); - return m_value; - } - - // This example shows how to use the no-change checker. - void DoSomethingElse( void ) const - { - CheckForNoChange checker( this, &Thingy::IsValid ); - (void)checker; - } - -private: - - // This is a static validator. - static bool StaticIsValid( void ) - { - assert( s_value != 0 ); - return true; - } - - // This is a per-instance validator. - bool IsValid( void ) const - { - assert( nullptr != this ); - assert( m_value != 0 ); - return true; - } - - // These lines show how to declare checkers for non-static functions in a host class. - typedef ::Loki::Checker< Thingy, ::Loki::CheckForNoThrow > CheckForNoThrow; - typedef ::Loki::Checker< Thingy, ::Loki::CheckForNoChangeOrThrow > CheckForNoChangeOrThrow; - typedef ::Loki::Checker< Thingy, ::Loki::CheckForNoChange > CheckForNoChange; - typedef ::Loki::Checker< Thingy, ::Loki::CheckForEquality > CheckForEquality; - typedef ::Loki::Checker< Thingy, ::Loki::CheckForNothing > CheckInvariants; - - // These lines show how to declare checkers for static functions of a host class. - typedef ::Loki::StaticChecker< ::Loki::CheckStaticForNoThrow > CheckStaticForNoThrow; - typedef ::Loki::StaticChecker< ::Loki::CheckStaticForNothing > CheckStaticInvariants; - - static unsigned int s_value; - - unsigned int m_value; - -}; - -unsigned int Thingy::s_value = 10; - -// ---------------------------------------------------------------------------- - -// This is a validator function called by checkers inside standalone functions. -bool AllIsValid( void ) -{ - assert( Thingy::GetThat() != 0 ); - return true; -} - -// ---------------------------------------------------------------------------- - -// These lines show how to declare checkers for standalone functions. -typedef ::Loki::StaticChecker< ::Loki::CheckStaticForNoThrow > CheckStaticForNoThrow; -typedef ::Loki::StaticChecker< ::Loki::CheckStaticForNothing > CheckStaticInvariants; - -// ---------------------------------------------------------------------------- - -void DoSomething( void ) -{ - // This example shows how to use a checker in a stand-alone function. - CheckStaticForNoThrow checker( &AllIsValid ); - (void)checker; - Thingy::ChangeThat(); -} - -// ---------------------------------------------------------------------------- - -int main( unsigned int argc, const char * const argv[] ) -{ - - try - { - // First do some tests on class member functions. - Thingy t1( 1 ); - t1.DoSomething( false ); - Thingy t2( 2 ); - t2.DoSomething( true ); - - // Next do some tests with static member functions. - Thingy::ChangeThat(); - const unsigned int value = Thingy::GetThat(); - assert( value != 0 ); - - // Then do a test with a standalone function. - DoSomething(); - } - catch ( ... ) - { - } - - return 0; -} - -// ---------------------------------------------------------------------------- +//////////////////////////////////////////////////////////////////////////////// +// +// 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$ + +/// @file main.cpp This provides examples on how to use Loki's Checker facility. + + +// ---------------------------------------------------------------------------- + +#include "../../include/loki/Checker.h" + + +#if !defined( nullptr ) + #define nullptr NULL +#endif + +#if !defined( NULL ) + #define NULL 0 +#endif + + +// ---------------------------------------------------------------------------- + +/* This class has 2 invariants. The this pointer may never equal NULL, and the + value may not equal zero. + */ +class Thingy +{ +public: + + // This example shows how static functions can use a no-throw checkers. + static void ChangeThat( void ) + { + CheckStaticForNoThrow checker( &Thingy::StaticIsValid ); + (void)checker; + s_value--; + } + + // This example shows how static functions can use an invariant checkers. + static unsigned int GetThat( void ) + { + CheckStaticInvariants checker( &Thingy::StaticIsValid ); + (void)checker; + return s_value; + } + + // This example shows how ctors can use an invariant checker. + explicit Thingy( unsigned int value ) : m_value( value ) + { + CheckInvariants checker( this, &Thingy::IsValid ); + (void)checker; + checker.Check(); + } + + // A destructor really doesn't need a checker, but does need to confirm + // the object is valid at the start of the destructor. + ~Thingy( void ) + { + assert( IsValid() ); + } + + bool operator == ( const Thingy & that ) const + { + return ( m_value == that.m_value ); + } + + void DoSomethingEvil( void ) + { + m_value = 0; + } + + // This example shows how to use the no-throw checker. + unsigned int GetValue( void ) const + { + CheckForNoThrow checker( this, &Thingy::IsValid ); + (void)checker; + return m_value; + } + + // This example shows how to use the equality checker. + unsigned int DoSomething( bool doThrow ) const + { + CheckForEquality checker( this, &Thingy::IsValid ); + (void)checker; + if ( doThrow ) + throw ::std::exception( "Test Exception." ); + return m_value; + } + + // This example shows how to use the no-change checker. + void DoSomethingElse( void ) const + { + CheckForNoChange checker( this, &Thingy::IsValid ); + (void)checker; + } + +private: + + // This is a static validator. + static bool StaticIsValid( void ) + { + assert( s_value != 0 ); + return true; + } + + // This is a per-instance validator. + bool IsValid( void ) const + { + assert( nullptr != this ); + assert( m_value != 0 ); + return true; + } + + // These lines show how to declare checkers for non-static functions in a host class. + typedef ::Loki::Checker< Thingy, ::Loki::CheckForNoThrow > CheckForNoThrow; + typedef ::Loki::Checker< Thingy, ::Loki::CheckForNoChangeOrThrow > CheckForNoChangeOrThrow; + typedef ::Loki::Checker< Thingy, ::Loki::CheckForNoChange > CheckForNoChange; + typedef ::Loki::Checker< Thingy, ::Loki::CheckForEquality > CheckForEquality; + typedef ::Loki::Checker< Thingy, ::Loki::CheckForNothing > CheckInvariants; + + // These lines show how to declare checkers for static functions of a host class. + typedef ::Loki::StaticChecker< ::Loki::CheckStaticForNoThrow > CheckStaticForNoThrow; + typedef ::Loki::StaticChecker< ::Loki::CheckStaticForNothing > CheckStaticInvariants; + + static unsigned int s_value; + + unsigned int m_value; + +}; + +unsigned int Thingy::s_value = 10; + +// ---------------------------------------------------------------------------- + +// This is a validator function called by checkers inside standalone functions. +bool AllIsValid( void ) +{ + assert( Thingy::GetThat() != 0 ); + return true; +} + +// ---------------------------------------------------------------------------- + +// These lines show how to declare checkers for standalone functions. +typedef ::Loki::StaticChecker< ::Loki::CheckStaticForNoThrow > CheckStaticForNoThrow; +typedef ::Loki::StaticChecker< ::Loki::CheckStaticForNothing > CheckStaticInvariants; + +// ---------------------------------------------------------------------------- + +void DoSomething( void ) +{ + // This example shows how to use a checker in a stand-alone function. + CheckStaticForNoThrow checker( &AllIsValid ); + (void)checker; + Thingy::ChangeThat(); +} + +// ---------------------------------------------------------------------------- + +int main( unsigned int argc, const char * const argv[] ) +{ + + try + { + // First do some tests on class member functions. + Thingy t1( 1 ); + t1.DoSomething( false ); + Thingy t2( 2 ); + t2.DoSomething( true ); + + // Next do some tests with static member functions. + Thingy::ChangeThat(); + const unsigned int value = Thingy::GetThat(); + assert( value != 0 ); + + // Then do a test with a standalone function. + DoSomething(); + } + catch ( ... ) + { + } + + return 0; +} + +// ---------------------------------------------------------------------------- diff --git a/test/LevelMutex/LevelMutex_MSVC_9.vcproj b/test/LevelMutex/LevelMutex_MSVC_9.vcproj index 61181f9..21c4812 100644 --- a/test/LevelMutex/LevelMutex_MSVC_9.vcproj +++ b/test/LevelMutex/LevelMutex_MSVC_9.vcproj @@ -1,211 +1,211 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test/LevelMutex/MultiThreadTests.cpp b/test/LevelMutex/MultiThreadTests.cpp index 4ef2ae2..dda2fe2 100644 --- a/test/LevelMutex/MultiThreadTests.cpp +++ b/test/LevelMutex/MultiThreadTests.cpp @@ -1,1098 +1,1098 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// 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; - -// ---------------------------------------------------------------------------- - -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(); -} - -// ---------------------------------------------------------------------------- +//////////////////////////////////////////////////////////////////////////////// +// +// 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; + +// ---------------------------------------------------------------------------- + +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 index 42cd621..e472556 100644 --- a/test/LevelMutex/MultiThreadTests.hpp +++ b/test/LevelMutex/MultiThreadTests.hpp @@ -1,55 +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 +//////////////////////////////////////////////////////////////////////////////// +// +// 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 index 7c34cfd..4d5da4b 100644 --- a/test/LevelMutex/Thing.cpp +++ b/test/LevelMutex/Thing.cpp @@ -1,922 +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; -} - -// ---------------------------------------------------------------------------- +//////////////////////////////////////////////////////////////////////////////// +// +// 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 index 38ecfb9..7d1a54d 100644 --- a/test/LevelMutex/Thing.hpp +++ b/test/LevelMutex/Thing.hpp @@ -1,364 +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 +//////////////////////////////////////////////////////////////////////////////// +// +// 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.cpp b/test/LevelMutex/ThreadPool.cpp index 311634f..77f255e 100644 --- a/test/LevelMutex/ThreadPool.cpp +++ b/test/LevelMutex/ThreadPool.cpp @@ -1,390 +1,390 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// 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 "ThreadPool.hpp" - -#include - -#include - - -using namespace ::std; - -// 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 - -#if defined( DEBUG ) || defined( _DEBUG ) - #define LOKI_DEBUG_CODE( code ) code; -#else - #define LOKI_DEBUG_CODE( code ) ; -#endif - - -volatile Thread * Thread::s_thread = nullptr; - - -// ---------------------------------------------------------------------------- - -Thread::Thread( volatile ThreadPool * owner ) : - m_thread(), - m_func( nullptr ), - m_parm( nullptr ), - m_status( Thread::Idle ), - m_stop( false ), - m_owner( owner ) -{ - assert( IsValid( owner ) ); -} - -// ---------------------------------------------------------------------------- - -Thread::Thread( volatile ThreadPool * owner, CallFunction function, - void * parm ) : - m_thread(), - m_func( function ), - m_parm( parm ), - m_status( Thread::Starting ), - m_stop( false ), - m_owner( owner ) -{ - assert( IsValid( owner ) ); - assert( nullptr != m_func ); -} - -// ---------------------------------------------------------------------------- - -Thread::~Thread( void ) -{ - assert( IsValid( m_owner ) ); - assert( Thread::Dead == m_status ); - assert( nullptr == m_func ); - assert( nullptr == m_parm ); - assert( m_stop ); -} - -// ---------------------------------------------------------------------------- - -bool Thread::WaitForThread( void ) volatile -{ - assert( IsValid( m_owner ) ); - const volatile Thread * current = Thread::GetCurrentThread(); - if ( this == current ) - return false; - if ( m_status == Thread::Dead ) - return false; - while ( this->m_status == Thread::Active ) - { - // Call the wait policy. -#if defined( _MSC_VER ) - ::SleepEx( 1, true ); -#else - ::sleep( 1 ); -#endif - } - return true; -} - -// ---------------------------------------------------------------------------- - -bool Thread::IsValid( const volatile ThreadPool * owner ) const volatile -{ - assert( nullptr != this ); - assert( nullptr != m_owner ); - assert( nullptr != owner ); - assert( m_owner == owner ); - switch ( m_status ) - { - case Thread::Dead: - case Thread::Idle: - assert( nullptr == m_func ); - assert( nullptr == m_parm ); - break; - case Thread::Active: - case Thread::Starting: - assert( nullptr != m_func ); - break; - default: - assert( false ); - break; - } - return true; -} - -// ---------------------------------------------------------------------------- - -ThreadPool::ThreadPool( unsigned int threadCount ) : - m_threads() -{ - assert( IsValid() ); - LOKI_DEBUG_CODE( Checker checker( this ); (void)checker; ) - if ( 0 < threadCount ) - Create( threadCount ); -} - -// ---------------------------------------------------------------------------- - -ThreadPool::~ThreadPool( void ) -{ - assert( IsValid() ); - - ThreadsIter end( m_threads.end() ); - for ( ThreadsIter it( m_threads.begin() ); it != end; ++it ) - { - try - { - volatile Thread * thread = *it; - assert( nullptr != thread ); - thread->m_stop = true; - thread->WaitForThread(); - if ( thread->GetStatus() == Thread::Idle ) - thread->m_status = Thread::Dead; - LokiThreadJoin( thread->m_thread ); - *it = nullptr; - delete thread; - } - catch ( ... ) - { - } - } -} - -// ---------------------------------------------------------------------------- - -unsigned int ThreadPool::Create( unsigned int threadCount ) volatile -{ - assert( IsValid() ); - LOKI_DEBUG_CODE( Checker checker( this ); (void)checker; ) - - ThreadPool * pThis = const_cast< ThreadPool * >( this ); - const unsigned int countNow = GetCount( Thread::Idle ); - if ( threadCount <= countNow ) - return threadCount; - - const unsigned int totalCount = pThis->m_threads.size(); - const unsigned int howManyToAdd = threadCount - countNow; - if ( pThis->m_threads.capacity() <= howManyToAdd ) - pThis->m_threads.reserve( totalCount + howManyToAdd ); - for ( unsigned int ii = 0; ii < howManyToAdd; ++ii ) - { -#if defined( _MSC_VER ) - volatile Thread * thread = new Thread( this ); -#else - Thread * thread = new Thread( this ); -#endif - pThis->m_threads.push_back( thread ); - Thread * pThread = const_cast< Thread * >( thread ); - void * p = reinterpret_cast< void * >( pThread ); - // Call thread creation policy? - LokiThreadCreate( &thread->m_thread, nullptr, TopFunction, p ); - } - - return howManyToAdd; -} - -// ---------------------------------------------------------------------------- - -unsigned int ThreadPool::GetCount( void ) const volatile -{ - assert( IsValid() ); - LOKI_DEBUG_CODE( Checker checker( this ); (void)checker; ) - ThreadPool * pThis = const_cast< ThreadPool * >( this ); - const unsigned int count = pThis->m_threads.size(); - return count; -} - -// ---------------------------------------------------------------------------- - -unsigned int ThreadPool::GetCount( Thread::Status status ) const volatile -{ - assert( IsValid() ); - LOKI_DEBUG_CODE( Checker checker( this ); (void)checker; ) - - ThreadPool * pThis = const_cast< ThreadPool * >( this ); - const unsigned int totalCount = pThis->m_threads.size(); - unsigned int count = 0; - for ( size_t ii = 0; ii < totalCount; ii++ ) - { - const volatile Thread * thread = pThis->m_threads.at( ii ); - assert( nullptr != thread ); - if ( thread->GetStatus() == status ) - ++count; - } - - return count; -} - -// ---------------------------------------------------------------------------- - -volatile const Thread * ThreadPool::GetThread( unsigned int index ) const volatile -{ - assert( IsValid() ); - LOKI_DEBUG_CODE( Checker checker( this ); (void)checker; ) - ThreadPool * pThis = const_cast< ThreadPool * >( this ); - - if ( pThis->m_threads.size() <= index ) - return nullptr; - volatile const Thread * thread = pThis->m_threads[ index ]; - return thread; -} - -// ---------------------------------------------------------------------------- - -void ThreadPool::Join( unsigned int index ) const volatile -{ - assert( IsValid() ); - LOKI_DEBUG_CODE( Checker checker( this ); (void)checker; ) - ThreadPool * pThis = const_cast< ThreadPool * >( this ); - - if ( pThis->m_threads.size() <= index ) - return; - volatile Thread * thread = pThis->m_threads[ index ]; - thread->WaitForThread(); -} - -// ---------------------------------------------------------------------------- - -void ThreadPool::JoinAll( void ) volatile const -{ - assert( IsValid() ); - LOKI_DEBUG_CODE( Checker checker( this ); (void)checker; ) - ThreadPool * pThis = const_cast< ThreadPool * >( this ); - - for ( size_t ii = 0; ii < pThis->m_threads.size(); ii++ ) - { - volatile Thread * thread = pThis->m_threads.at( ii ); - assert( nullptr != thread ); - thread->WaitForThread(); - } -} - -// ---------------------------------------------------------------------------- - -volatile Thread * ThreadPool::Start( CallFunction function, void * parm ) volatile -{ - assert( IsValid() ); - LOKI_DEBUG_CODE( Checker checker( this ); (void)checker; ) - ThreadPool * pThis = const_cast< ThreadPool * >( this ); - - if ( nullptr == function ) - return nullptr; -#if defined( _MSC_VER ) - volatile Thread * thread = nullptr; -#else - Thread * thread = nullptr; -#endif - bool foundOne = false; - - for ( size_t ii = 0; ii < pThis->m_threads.size(); ii++ ) - { -#if defined( _MSC_VER ) - thread = pThis->m_threads.at( ii ); -#else - thread = const_cast< Thread * >( pThis->m_threads.at( ii ) ); -#endif - assert( nullptr != thread ); - if ( Thread::Idle == thread->m_status ) - { - foundOne = true; - break; - } - } - - if ( foundOne ) - { - thread->m_func = function; - thread->m_parm = parm; - thread->m_status = Thread::Starting; - } - else - { - // Did not find an idle thread, so start a new one. - thread = new Thread( this, function, parm ); - pThis->m_threads.push_back( thread ); - Thread * pThread = const_cast< Thread * >( thread ); - void * p = reinterpret_cast< void * >( pThread ); - // Call to thread creation policy? - LokiThreadCreate( &thread->m_thread, nullptr, TopFunction, p ); - } - - return thread; -} - -// ---------------------------------------------------------------------------- - -#if defined( _MSC_VER ) - unsigned int ThreadPool::TopFunction( void * p ) -#else - void * ThreadPool::TopFunction( void * p ) -#endif -{ - assert( nullptr != p ); - - volatile Thread * thread = reinterpret_cast< volatile Thread * >( p ); - Thread::SetCurrentThread( thread ); - while ( ( thread->m_status != Thread::Dead ) && ( !thread->m_stop ) ) - { - // Call the thread's WaitPolicy here? -#if defined( _MSC_VER ) - ::SleepEx( 1, true ); -#else - ::sleep( 1 ); -#endif - if ( thread->m_status == Thread::Starting ) - { - try - { - assert( nullptr != thread->m_func ); - thread->m_status = Thread::Active; - thread->m_func( thread->m_parm ); - } - catch ( ... ) - { - // What to do in case of exception? - // Call an exception policy? - } - thread->m_status = Thread::Idle; - thread->m_func = nullptr; - thread->m_parm = nullptr; - } - } - -#if defined( _MSC_VER ) - return 0; -#else - return nullptr; -#endif -} - -// ---------------------------------------------------------------------------- - -bool ThreadPool::IsValid( void ) const volatile -{ - assert( nullptr != this ); - ThreadPool * pThis = const_cast< ThreadPool * >( this ); - ThreadsCIter end( pThis->m_threads.end() ); - for ( ThreadsCIter it( pThis->m_threads.begin() ); it != end; ++it ) - { - const volatile Thread * thread = *it; - assert( nullptr != thread ); - assert( thread->IsValid( this ) ); - } - return true; -} - -// ---------------------------------------------------------------------------- +//////////////////////////////////////////////////////////////////////////////// +// +// 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 "ThreadPool.hpp" + +#include + +#include + + +using namespace ::std; + +// 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 + +#if defined( DEBUG ) || defined( _DEBUG ) + #define LOKI_DEBUG_CODE( code ) code; +#else + #define LOKI_DEBUG_CODE( code ) ; +#endif + + +volatile Thread * Thread::s_thread = nullptr; + + +// ---------------------------------------------------------------------------- + +Thread::Thread( volatile ThreadPool * owner ) : + m_thread(), + m_func( nullptr ), + m_parm( nullptr ), + m_status( Thread::Idle ), + m_stop( false ), + m_owner( owner ) +{ + assert( IsValid( owner ) ); +} + +// ---------------------------------------------------------------------------- + +Thread::Thread( volatile ThreadPool * owner, CallFunction function, + void * parm ) : + m_thread(), + m_func( function ), + m_parm( parm ), + m_status( Thread::Starting ), + m_stop( false ), + m_owner( owner ) +{ + assert( IsValid( owner ) ); + assert( nullptr != m_func ); +} + +// ---------------------------------------------------------------------------- + +Thread::~Thread( void ) +{ + assert( IsValid( m_owner ) ); + assert( Thread::Dead == m_status ); + assert( nullptr == m_func ); + assert( nullptr == m_parm ); + assert( m_stop ); +} + +// ---------------------------------------------------------------------------- + +bool Thread::WaitForThread( void ) volatile +{ + assert( IsValid( m_owner ) ); + const volatile Thread * current = Thread::GetCurrentThread(); + if ( this == current ) + return false; + if ( m_status == Thread::Dead ) + return false; + while ( this->m_status == Thread::Active ) + { + // Call the wait policy. +#if defined( _MSC_VER ) + ::SleepEx( 1, true ); +#else + ::sleep( 1 ); +#endif + } + return true; +} + +// ---------------------------------------------------------------------------- + +bool Thread::IsValid( const volatile ThreadPool * owner ) const volatile +{ + assert( nullptr != this ); + assert( nullptr != m_owner ); + assert( nullptr != owner ); + assert( m_owner == owner ); + switch ( m_status ) + { + case Thread::Dead: + case Thread::Idle: + assert( nullptr == m_func ); + assert( nullptr == m_parm ); + break; + case Thread::Active: + case Thread::Starting: + assert( nullptr != m_func ); + break; + default: + assert( false ); + break; + } + return true; +} + +// ---------------------------------------------------------------------------- + +ThreadPool::ThreadPool( unsigned int threadCount ) : + m_threads() +{ + assert( IsValid() ); + LOKI_DEBUG_CODE( Checker checker( this ); (void)checker; ) + if ( 0 < threadCount ) + Create( threadCount ); +} + +// ---------------------------------------------------------------------------- + +ThreadPool::~ThreadPool( void ) +{ + assert( IsValid() ); + + ThreadsIter end( m_threads.end() ); + for ( ThreadsIter it( m_threads.begin() ); it != end; ++it ) + { + try + { + volatile Thread * thread = *it; + assert( nullptr != thread ); + thread->m_stop = true; + thread->WaitForThread(); + if ( thread->GetStatus() == Thread::Idle ) + thread->m_status = Thread::Dead; + LokiThreadJoin( thread->m_thread ); + *it = nullptr; + delete thread; + } + catch ( ... ) + { + } + } +} + +// ---------------------------------------------------------------------------- + +unsigned int ThreadPool::Create( unsigned int threadCount ) volatile +{ + assert( IsValid() ); + LOKI_DEBUG_CODE( Checker checker( this ); (void)checker; ) + + ThreadPool * pThis = const_cast< ThreadPool * >( this ); + const unsigned int countNow = GetCount( Thread::Idle ); + if ( threadCount <= countNow ) + return threadCount; + + const unsigned int totalCount = pThis->m_threads.size(); + const unsigned int howManyToAdd = threadCount - countNow; + if ( pThis->m_threads.capacity() <= howManyToAdd ) + pThis->m_threads.reserve( totalCount + howManyToAdd ); + for ( unsigned int ii = 0; ii < howManyToAdd; ++ii ) + { +#if defined( _MSC_VER ) + volatile Thread * thread = new Thread( this ); +#else + Thread * thread = new Thread( this ); +#endif + pThis->m_threads.push_back( thread ); + Thread * pThread = const_cast< Thread * >( thread ); + void * p = reinterpret_cast< void * >( pThread ); + // Call thread creation policy? + LokiThreadCreate( &thread->m_thread, nullptr, TopFunction, p ); + } + + return howManyToAdd; +} + +// ---------------------------------------------------------------------------- + +unsigned int ThreadPool::GetCount( void ) const volatile +{ + assert( IsValid() ); + LOKI_DEBUG_CODE( Checker checker( this ); (void)checker; ) + ThreadPool * pThis = const_cast< ThreadPool * >( this ); + const unsigned int count = pThis->m_threads.size(); + return count; +} + +// ---------------------------------------------------------------------------- + +unsigned int ThreadPool::GetCount( Thread::Status status ) const volatile +{ + assert( IsValid() ); + LOKI_DEBUG_CODE( Checker checker( this ); (void)checker; ) + + ThreadPool * pThis = const_cast< ThreadPool * >( this ); + const unsigned int totalCount = pThis->m_threads.size(); + unsigned int count = 0; + for ( size_t ii = 0; ii < totalCount; ii++ ) + { + const volatile Thread * thread = pThis->m_threads.at( ii ); + assert( nullptr != thread ); + if ( thread->GetStatus() == status ) + ++count; + } + + return count; +} + +// ---------------------------------------------------------------------------- + +volatile const Thread * ThreadPool::GetThread( unsigned int index ) const volatile +{ + assert( IsValid() ); + LOKI_DEBUG_CODE( Checker checker( this ); (void)checker; ) + ThreadPool * pThis = const_cast< ThreadPool * >( this ); + + if ( pThis->m_threads.size() <= index ) + return nullptr; + volatile const Thread * thread = pThis->m_threads[ index ]; + return thread; +} + +// ---------------------------------------------------------------------------- + +void ThreadPool::Join( unsigned int index ) const volatile +{ + assert( IsValid() ); + LOKI_DEBUG_CODE( Checker checker( this ); (void)checker; ) + ThreadPool * pThis = const_cast< ThreadPool * >( this ); + + if ( pThis->m_threads.size() <= index ) + return; + volatile Thread * thread = pThis->m_threads[ index ]; + thread->WaitForThread(); +} + +// ---------------------------------------------------------------------------- + +void ThreadPool::JoinAll( void ) volatile const +{ + assert( IsValid() ); + LOKI_DEBUG_CODE( Checker checker( this ); (void)checker; ) + ThreadPool * pThis = const_cast< ThreadPool * >( this ); + + for ( size_t ii = 0; ii < pThis->m_threads.size(); ii++ ) + { + volatile Thread * thread = pThis->m_threads.at( ii ); + assert( nullptr != thread ); + thread->WaitForThread(); + } +} + +// ---------------------------------------------------------------------------- + +volatile Thread * ThreadPool::Start( CallFunction function, void * parm ) volatile +{ + assert( IsValid() ); + LOKI_DEBUG_CODE( Checker checker( this ); (void)checker; ) + ThreadPool * pThis = const_cast< ThreadPool * >( this ); + + if ( nullptr == function ) + return nullptr; +#if defined( _MSC_VER ) + volatile Thread * thread = nullptr; +#else + Thread * thread = nullptr; +#endif + bool foundOne = false; + + for ( size_t ii = 0; ii < pThis->m_threads.size(); ii++ ) + { +#if defined( _MSC_VER ) + thread = pThis->m_threads.at( ii ); +#else + thread = const_cast< Thread * >( pThis->m_threads.at( ii ) ); +#endif + assert( nullptr != thread ); + if ( Thread::Idle == thread->m_status ) + { + foundOne = true; + break; + } + } + + if ( foundOne ) + { + thread->m_func = function; + thread->m_parm = parm; + thread->m_status = Thread::Starting; + } + else + { + // Did not find an idle thread, so start a new one. + thread = new Thread( this, function, parm ); + pThis->m_threads.push_back( thread ); + Thread * pThread = const_cast< Thread * >( thread ); + void * p = reinterpret_cast< void * >( pThread ); + // Call to thread creation policy? + LokiThreadCreate( &thread->m_thread, nullptr, TopFunction, p ); + } + + return thread; +} + +// ---------------------------------------------------------------------------- + +#if defined( _MSC_VER ) + unsigned int ThreadPool::TopFunction( void * p ) +#else + void * ThreadPool::TopFunction( void * p ) +#endif +{ + assert( nullptr != p ); + + volatile Thread * thread = reinterpret_cast< volatile Thread * >( p ); + Thread::SetCurrentThread( thread ); + while ( ( thread->m_status != Thread::Dead ) && ( !thread->m_stop ) ) + { + // Call the thread's WaitPolicy here? +#if defined( _MSC_VER ) + ::SleepEx( 1, true ); +#else + ::sleep( 1 ); +#endif + if ( thread->m_status == Thread::Starting ) + { + try + { + assert( nullptr != thread->m_func ); + thread->m_status = Thread::Active; + thread->m_func( thread->m_parm ); + } + catch ( ... ) + { + // What to do in case of exception? + // Call an exception policy? + } + thread->m_status = Thread::Idle; + thread->m_func = nullptr; + thread->m_parm = nullptr; + } + } + +#if defined( _MSC_VER ) + return 0; +#else + return nullptr; +#endif +} + +// ---------------------------------------------------------------------------- + +bool ThreadPool::IsValid( void ) const volatile +{ + assert( nullptr != this ); + ThreadPool * pThis = const_cast< ThreadPool * >( this ); + ThreadsCIter end( pThis->m_threads.end() ); + for ( ThreadsCIter it( pThis->m_threads.begin() ); it != end; ++it ) + { + const volatile Thread * thread = *it; + assert( nullptr != thread ); + assert( thread->IsValid( this ) ); + } + return true; +} + +// ---------------------------------------------------------------------------- diff --git a/test/LevelMutex/ThreadPool.hpp b/test/LevelMutex/ThreadPool.hpp index 655b552..7e981e2 100644 --- a/test/LevelMutex/ThreadPool.hpp +++ b/test/LevelMutex/ThreadPool.hpp @@ -1,226 +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; -}; - -// ---------------------------------------------------------------------------- +//////////////////////////////////////////////////////////////////////////////// +// +// 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 index 08450d5..1260935 100644 --- a/test/LevelMutex/main.cpp +++ b/test/LevelMutex/main.cpp @@ -1,681 +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; -} - -// ---------------------------------------------------------------------------- +//////////////////////////////////////////////////////////////////////////////// +// +// 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; +} + +// ----------------------------------------------------------------------------