diff --git a/test/Lockable/Lockable.cbp b/test/Lockable/Lockable.cbp new file mode 100644 index 0000000..cad043e --- /dev/null +++ b/test/Lockable/Lockable.cbp @@ -0,0 +1,52 @@ + + + + + + diff --git a/test/Lockable/ThreadPool.cpp b/test/Lockable/ThreadPool.cpp new file mode 100644 index 0000000..ec18dba --- /dev/null +++ b/test/Lockable/ThreadPool.cpp @@ -0,0 +1,116 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// ThreadLocal test program for The Loki Library +// Copyright (c) 2009 by Richard Sposato +// The copyright on this file is protected under the terms of the MIT license. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// +//////////////////////////////////////////////////////////////////////////////// + +// ---------------------------------------------------------------------------- + +#include "ThreadPool.hpp" + +#include +#include + +// ---------------------------------------------------------------------------- + +Thread::Thread( CallFunction func, void * parm ) + : pthread_() + , func_( func ) + , parm_( parm ) +{ +} + +// ---------------------------------------------------------------------------- + +void Thread::AssignTask( CallFunction func, void * parm ) +{ + func_ = func; + parm_ = parm; +} + +// ---------------------------------------------------------------------------- + +int Thread::Start( void ) +{ + return LOKI_pthread_create( &pthread_, NULL, func_, parm_ ); +} + +// ---------------------------------------------------------------------------- + +int Thread::WaitForThread( void ) const +{ + return LOKI_pthread_join( pthread_ ); +} + +// ---------------------------------------------------------------------------- + +ThreadPool::ThreadPool( void ) : m_threads() +{ +} + +// ---------------------------------------------------------------------------- + +ThreadPool::~ThreadPool( void ) +{ + for ( size_t ii = 0; ii < m_threads.size(); ++ii ) + { + delete m_threads.at( ii ); + } +} + +// ---------------------------------------------------------------------------- + +void ThreadPool::Create( size_t threadCount, Thread::CallFunction function ) +{ + for( size_t ii = 0; ii < threadCount; ii++ ) + { + ::std::stringstream buffer; + buffer << "Creating thread " << ii << ::std::endl; + ::std::cout << buffer.rdbuf(); + Thread * thread = new Thread( function, reinterpret_cast< void * >( ii ) ); + m_threads.push_back( thread ); + } +} + +// ---------------------------------------------------------------------------- + +void ThreadPool::Start( void ) +{ + for ( size_t ii = 0; ii < m_threads.size(); ii++ ) + { + ::std::stringstream buffer; + buffer << "Starting thread " << ii << ::std::endl; + ::std::cout << buffer.rdbuf(); + m_threads.at( ii )->Start(); + } +} + +// ---------------------------------------------------------------------------- + +void ThreadPool::Join( void ) const +{ + for ( size_t ii = 0; ii < m_threads.size(); ii++ ) + m_threads.at( ii )->WaitForThread(); +} + +// ---------------------------------------------------------------------------- diff --git a/test/Lockable/ThreadPool.hpp b/test/Lockable/ThreadPool.hpp new file mode 100644 index 0000000..f297ee6 --- /dev/null +++ b/test/Lockable/ThreadPool.hpp @@ -0,0 +1,119 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// ThreadLocal test program for The Loki Library +// Copyright (c) 2009 by Richard Sposato +// The copyright on this file is protected under the terms of the MIT license. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// +//////////////////////////////////////////////////////////////////////////////// + + +// ---------------------------------------------------------------------------- + +#include + +#if !defined( NULL ) + #define NULL 0 +#endif + +// 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. +#if !defined( nullptr ) + #define nullptr NULL +#endif + +#if defined(_WIN32) + + #include + #include + + typedef unsigned int ( WINAPI * ThreadFunction_ )( void * ); + + #define LOKI_pthread_t HANDLE + + #define LOKI_pthread_create( handle, attr, func, arg ) \ + ( int )( ( *handle = ( HANDLE ) _beginthreadex ( NULL, 0, ( ThreadFunction_ )func, arg, 0, NULL ) ) == NULL ) + + #define LOKI_pthread_join( thread ) \ + ( ( WaitForSingleObject( ( thread ), INFINITE ) != WAIT_OBJECT_0 ) || !CloseHandle( thread ) ) + +#else + + #include + + #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 + +// ---------------------------------------------------------------------------- + +class Thread +{ +public: + + typedef void * ( * CallFunction )( void * ); + + Thread( CallFunction func, void * parm ); + + void AssignTask( CallFunction func, void * parm ); + + int Start( void ); + + int WaitForThread( void ) const; + +private: + + LOKI_pthread_t pthread_; + + CallFunction func_; + + void * parm_; + +}; + +// ---------------------------------------------------------------------------- + +class ThreadPool +{ +public: + + ThreadPool( void ); + + ~ThreadPool( void ); + + void Create( size_t threadCount, Thread::CallFunction function ); + + void Start( void ); + + void Join( void ) const; + +private: + + typedef ::std::vector< Thread * > Threads; + + Threads m_threads; +}; + +// ---------------------------------------------------------------------------- diff --git a/test/Lockable/main.cpp b/test/Lockable/main.cpp new file mode 100644 index 0000000..277f97a --- /dev/null +++ b/test/Lockable/main.cpp @@ -0,0 +1,302 @@ +//////////////////////////////////////////////////////////////////////////////// +// The Loki Library +// Copyright (c) 2011 by Rich Sposato +// +// This code does not accompany the book: +// Alexandrescu, Andrei. "Modern C++ Design: Generic Programming and Design +// Patterns Applied". Copyright (c) 2001. Addison-Wesley. +// Code covered by the MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//////////////////////////////////////////////////////////////////////////////// + +#define LOKI_OBJECT_LEVEL_THREADING +#include + +#include +#include + +#include +#include "ThreadPool.hpp" + + +using namespace std; + +static unsigned int g = 0; + +#define DO for(int i=0; i<10000000; i++) g++; + +static const unsigned int ThreadCount = 10; + +static const unsigned int ObjectCount = 50; + +static const unsigned int ClassCount = 5; + +static unsigned int FailCounts[ ThreadCount ]; + +// ---------------------------------------------------------------------------- + +class LockableObject : public ::Loki::ObjectLevelLockable< LockableObject > +{ +public: + + typedef ::Loki::ObjectLevelLockable< LockableObject > BaseClass; + + explicit LockableObject( unsigned int index ) : + BaseClass(), m_index( index ), m_value( ObjectCount ) {} + + ~LockableObject( void ) {} + + unsigned int GetIndex( void ) const { return m_index; } + + unsigned int GetValue( void ) const { return m_value; } + + void SetValue( unsigned int value ) { m_value = value; } + + void DoSomething( void ); + + void Print( unsigned int threadIndex ); + +private: + + const unsigned int m_index; + unsigned int m_value; + +}; + +// ---------------------------------------------------------------------------- + +void LockableObject::DoSomething( void) +{ + assert( NULL != this ); + DO; +} + +// ---------------------------------------------------------------------------- + +void LockableObject::Print( unsigned int threadIndex ) +{ + assert( NULL != this ); + const char * message = ( threadIndex != m_value ) ? "Mismatch!" : ""; + ::Loki::Printf( "Object: [%u] Thread: [%u] Value: [%u] %s\n" ) + ( m_index )( threadIndex )( m_value )( message ); +} + +// ---------------------------------------------------------------------------- + +typedef ::std::vector< LockableObject * > LockableObjects; + +LockableObjects & GetLockableObjects( void ) +{ + static LockableObjects objects; + return objects; +} + +// ---------------------------------------------------------------------------- + +LockableObject * GetLockableObject( unsigned int index ) +{ + LockableObjects & objects = GetLockableObjects(); + if ( objects.size() <= index ) + return NULL; + + LockableObject * object = objects[ index ]; + return object; +} + +// ---------------------------------------------------------------------------- + +void * RunObjectTest( void * p ) +{ + const unsigned int threadIndex = reinterpret_cast< unsigned int >( p ); + assert( threadIndex < ThreadCount ); + + unsigned int failCount = 0; + for ( unsigned int ii = 0; ii < ObjectCount; ++ii ) + { + LockableObject * object = GetLockableObject( ii ); + assert( NULL != object ); + LockableObject::Lock lock( *object ); + (void)lock; + object->SetValue( threadIndex ); + object->DoSomething(); + object->Print( threadIndex ); + object->DoSomething(); + const unsigned int value = object->GetValue(); + if ( value != threadIndex ) + ++failCount; + } + + FailCounts[ threadIndex ] = failCount; + + return NULL; +} + +// ---------------------------------------------------------------------------- + +void DoObjectLockTest( void ) +{ + cout << "Starting DoObjectLockTest" << endl; + + LockableObjects & objects = GetLockableObjects(); + objects.reserve( ObjectCount ); + for ( unsigned int ii = 0; ii < ObjectCount; ++ii ) + { + LockableObject * object = new LockableObject( ii ); + objects.push_back( object ); + } + + { + ThreadPool pool; + pool.Create( ThreadCount, &RunObjectTest ); + pool.Start(); + pool.Join(); + } + + unsigned int totalFails = 0; + for ( unsigned int ii = 0; ii < ThreadCount; ++ii ) + { + const unsigned int failCount = FailCounts[ ii ]; + ::Loki::Printf( "Thread: [%u] Failures: [%u]\n" )( ii )( failCount ); + totalFails += failCount; + } + const char * result = ( 0 == totalFails ) ? "Passed" : "FAILED"; + + cout << "Finished DoObjectLockTest. Total Fails: " << totalFails << " Result: " + << result << endl; +} + +// ---------------------------------------------------------------------------- + +class LockableClass : public ::Loki::ClassLevelLockable< LockableClass > +{ +public: + + typedef ::Loki::ClassLevelLockable< LockableClass > BaseClass; + + explicit LockableClass( unsigned int index ) : BaseClass(), m_index( index ) {} + + ~LockableClass( void ) {} + + unsigned int GetIndex( void ) const { return m_index; } + + void Print( unsigned int threadIndex ); + +private: + const unsigned int m_index; +}; + +// ---------------------------------------------------------------------------- + +void LockableClass::Print( unsigned int threadIndex ) +{ + assert( NULL != this ); + DO; ::Loki::Printf( "%u: %u: -----\n" )( m_index )( threadIndex ); + DO; ::Loki::Printf( "%u: %u: ----\n" )( m_index )( threadIndex ); + DO; ::Loki::Printf( "%u: %u: ---\n" )( m_index )( threadIndex ); + DO; ::Loki::Printf( "%u: %u: --\n" )( m_index )( threadIndex ); + DO; ::Loki::Printf( "%u: %u: -\n" )( m_index )( threadIndex ); + DO; ::Loki::Printf( "%u: %u: \n" )( m_index )( threadIndex ); +} + +// ---------------------------------------------------------------------------- + +typedef ::std::vector< LockableClass * > LockableClasses; + +LockableClasses & GetLockableClasses( void ) +{ + static LockableClasses objects; + return objects; +} + +// ---------------------------------------------------------------------------- + +LockableClass * GetLockableClass( unsigned int index ) +{ + LockableClasses & objects = GetLockableClasses(); + if ( objects.size() <= index ) + return NULL; + + LockableClass * object = objects[ index ]; + return object; +} + +// ---------------------------------------------------------------------------- + +void * RunClassTest( void * p ) +{ + const unsigned int threadIndex = reinterpret_cast< unsigned int >( p ); + assert( threadIndex < ThreadCount ); + + for ( unsigned int ii = 0; ii < ClassCount; ++ii ) + { + LockableClass * object = GetLockableClass( ii ); + assert( NULL != object ); + LockableClass::Lock lock( *object ); + (void)lock; + object->Print( threadIndex ); + } + + return NULL; +} + +// ---------------------------------------------------------------------------- + +void DoClassLockTest( void ) +{ + cout << "Starting DoClassLockTest" << endl; + + LockableClasses & objects = GetLockableClasses(); + objects.reserve( ClassCount ); + for ( unsigned int ii = 0; ii < ClassCount; ++ii ) + { + LockableClass * object = new LockableClass( ii ); + objects.push_back( object ); + } + + { + ThreadPool pool; + pool.Create( ThreadCount, &RunClassTest ); + pool.Start(); + pool.Join(); + } + + cout << "Finished DoClassLockTest" << endl; +} + +// ---------------------------------------------------------------------------- + +int main( int argc, const char * const argv[] ) +{ + (void)argc; + (void)argv; + char ender; + + DoObjectLockTest(); + cout << "Press key to continue. "; + cin.get( ender ); + + DoClassLockTest(); + cout << "Press key to finish. "; + cin.get( ender ); + + return 0; +} + +// ----------------------------------------------------------------------------