123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403 |
- //-----------------------------------------------------------------------------
- // Copyright (c) 2012 GarageGames, LLC
- //
- // 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 "unit/test.h"
- #include "platform/threads/threadSafeDeque.h"
- #include "platform/threads/thread.h"
- #include "core/util/tVector.h"
- #include "console/console.h"
- #ifndef TORQUE_SHIPPING
- using namespace UnitTesting;
- #define TEST( x ) test( ( x ), "FAIL: " #x )
- #define XTEST( t, x ) t->test( ( x ), "FAIL: " #x )
- // Test deque without concurrency.
- CreateUnitTest( TestThreadSafeDequeSerial, "Platform/ThreadSafeDeque/Serial" )
- {
- void test1()
- {
- ThreadSafeDeque< char > deque;
- String str = "teststring";
- for( U32 i = 0; i < str.length(); ++ i )
- deque.pushBack( str[ i ] );
- TEST( !deque.isEmpty() );
- for( U32 i = 0; i < str.length(); ++ i )
- {
- char ch;
- TEST( deque.tryPopFront( ch ) && ch == str[ i ] );
- }
- }
- void test2()
- {
- ThreadSafeDeque< char > deque;
- String str = "teststring";
- const char* p1 = str.c_str() + 4;
- const char* p2 = p1 + 1;
- while( *p2 )
- {
- deque.pushFront( *p1 );
- deque.pushBack( *p2 );
- -- p1;
- ++ p2;
- }
- #ifdef TORQUE_DEBUG
- deque.dumpDebug();
- #endif
- for( U32 i = 0; i < str.length(); ++ i )
- {
- char ch;
- TEST( deque.tryPopFront( ch ) && ch == str[ i ] );
- }
- }
- void test3()
- {
- ThreadSafeDeque< char > deque;
- String str = "teststring";
- const char* p1 = str.c_str() + 4;
- const char* p2 = p1 + 1;
- while( *p2 )
- {
- deque.pushFront( *p1 );
- deque.pushBack( *p2 );
- -- p1;
- ++ p2;
- }
- #ifdef TORQUE_DEBUG
- deque.dumpDebug();
- #endif
- for( S32 i = ( str.length() - 1 ); i >= 0; -- i )
- {
- char ch;
- TEST( deque.tryPopBack( ch ) && ch == str[ i ] );
- }
- }
- void test4()
- {
- ThreadSafeDeque< char > deque;
- char ch;
- TEST( deque.isEmpty() );
-
- deque.pushFront( 'a' );
- TEST( !deque.isEmpty() );
- TEST( deque.tryPopFront( ch ) );
- TEST( ch == 'a' );
-
- deque.pushBack( 'a' );
- TEST( !deque.isEmpty() );
- TEST( deque.tryPopFront( ch ) );
- TEST( ch == 'a' );
- deque.pushBack( 'a' );
- TEST( !deque.isEmpty() );
- TEST( deque.tryPopBack( ch ) );
- TEST( ch == 'a' );
- deque.pushFront( 'a' );
- TEST( !deque.isEmpty() );
- TEST( deque.tryPopBack( ch ) );
- TEST( ch == 'a' );
- }
- void run()
- {
- test1();
- test2();
- test3();
- test4();
- }
- };
- // Test deque in a concurrent setting.
- CreateUnitTest( TestThreadSafeDequeConcurrentSimple, "Platform/ThreadSafeDeque/ConcurrentSimple" )
- {
- public:
- typedef TestThreadSafeDequeConcurrentSimple TestType;
- enum
- {
- DEFAULT_NUM_VALUES = 100000,
- };
- struct Value : public ThreadSafeRefCount< Value >
- {
- U32 mIndex;
- U32 mTick;
- Value() {}
- Value( U32 index, U32 tick )
- : mIndex( index ), mTick( tick ) {}
- };
- typedef ThreadSafeRef< Value > ValueRef;
- struct Deque : public ThreadSafeDeque< ValueRef >
- {
- typedef ThreadSafeDeque<ValueRef> Parent;
- U32 mPushIndex;
- U32 mPopIndex;
- Deque()
- : mPushIndex( 0 ), mPopIndex( 0 ) {}
- void pushBack( const ValueRef& value )
- {
- AssertFatal( value->mIndex == mPushIndex, "index out of line" );
- mPushIndex ++;
- Parent::pushBack( value );
- }
- bool tryPopFront( ValueRef& outValue )
- {
- if( Parent::tryPopFront( outValue ) )
- {
- AssertFatal( outValue->mIndex == mPopIndex, "index out of line" );
- mPopIndex ++;
- return true;
- }
- else
- return false;
- }
- };
- Deque mDeque;
- Vector< U32 > mValues;
- struct ProducerThread : public Thread
- {
- ProducerThread( TestType* test )
- : Thread( 0, test ) {}
- virtual void run( void* arg )
- {
- _setName( "ProducerThread" );
- Platform::outputDebugString( "Starting ProducerThread" );
-
- TestType* test = ( TestType* ) arg;
- for( U32 i = 0; i < test->mValues.size(); ++ i )
- {
- U32 tick = Platform::getRealMilliseconds();
- test->mValues[ i ] = tick;
- ValueRef val = new Value( i, tick );
- test->mDeque.pushBack( val );
- }
- Platform::outputDebugString( "Stopping ProducerThread" );
- }
- };
- struct ConsumerThread : public Thread
- {
- ConsumerThread( TestType* test )
- : Thread( 0, test ) {}
- virtual void run( void* arg )
- {
- _setName( "ConsumerThread" );
- Platform::outputDebugString( "Starting CosumerThread" );
- TestType* t = ( TestType* ) arg;
- for( U32 i = 0; i < t->mValues.size(); ++ i )
- {
- ValueRef value;
- while( !t->mDeque.tryPopFront( value ) );
- XTEST( t, value->mIndex == i );
- XTEST( t, t->mValues[ i ] == value->mTick );
- }
- Platform::outputDebugString( "Stopping ConsumerThread" );
- }
- };
- void run()
- {
- U32 numValues = Con::getIntVariable( "$testThreadSafeDeque::numValues", DEFAULT_NUM_VALUES );
- mValues.setSize( numValues );
- ProducerThread pThread( this );
- ConsumerThread cThread( this );
-
- pThread.start();
- cThread.start();
-
- pThread.join();
- cThread.join();
- mValues.clear();
- }
- };
- CreateUnitTest( TestThreadSafeDequeConcurrent, "Platform/ThreadSafeDeque/Concurrent" )
- {
- public:
- typedef TestThreadSafeDequeConcurrent TestType;
- enum
- {
- DEFAULT_NUM_VALUES = 100000,
- DEFAULT_NUM_CONSUMERS = 10,
- DEFAULT_NUM_PRODUCERS = 10
- };
- struct Value : public ThreadSafeRefCount< Value >
- {
- U32 mIndex;
- U32 mTick;
- Value() {}
- Value( U32 index, U32 tick )
- : mIndex( index ), mTick( tick ) {}
- };
- typedef ThreadSafeRef< Value > ValueRef;
- U32 mProducerIndex;
- U32 mConsumerIndex;
- ThreadSafeDeque< ValueRef > mDeque;
- Vector< U32 > mValues;
- struct ProducerThread : public Thread
- {
- ProducerThread( TestType* test )
- : Thread( 0, test ) {}
- virtual void run( void* arg )
- {
- _setName( "ProducerThread" );
- Platform::outputDebugString( "Starting ProducerThread" );
- TestType* test = ( TestType* ) arg;
- while( 1 )
- {
- U32 index = test->mProducerIndex;
- if( index == test->mValues.size() )
- break;
-
- if( dCompareAndSwap( test->mProducerIndex, index, index + 1 ) )
- {
- U32 tick = Platform::getRealMilliseconds();
- test->mValues[ index ] = tick;
- ValueRef val = new Value( index, tick );
- test->mDeque.pushBack( val );
- }
- }
- Platform::outputDebugString( "Stopping ProducerThread" );
- }
- };
- struct ConsumerThread : public Thread
- {
- ConsumerThread( TestType* test )
- : Thread( 0, test ) {}
- virtual void run( void* arg )
- {
- _setName( "ConsumerThread" );
- Platform::outputDebugString( "Starting ConsumerThread" );
- TestType* t = ( TestType* ) arg;
- while( t->mConsumerIndex < t->mValues.size() )
- {
- ValueRef value;
- if( t->mDeque.tryPopFront( value ) )
- {
- dFetchAndAdd( t->mConsumerIndex, 1 );
- XTEST( t, t->mValues[ value->mIndex ] == value->mTick );
- t->mValues[ value->mIndex ] = 0;
- }
- }
-
- Platform::outputDebugString( "Stopping ConsumerThread" );
- }
- };
- void run()
- {
- U32 numValues = Con::getIntVariable( "$testThreadSafeDeque::numValues", DEFAULT_NUM_VALUES );
- U32 numConsumers = Con::getIntVariable( "$testThreadSafeDeque::numConsumers", DEFAULT_NUM_CONSUMERS );
- U32 numProducers = Con::getIntVariable( "$testThreadSafeDeque::numProducers", DEFAULT_NUM_PRODUCERS );
- mProducerIndex = 0;
- mConsumerIndex = 0;
- mValues.setSize( numValues );
- U32 tick = Platform::getRealMilliseconds();
- for( U32 i = 0; i < numValues; ++ i )
- mValues[ i ] = tick;
- Vector< ProducerThread* > producers;
- Vector< ConsumerThread* > consumers;
- producers.setSize( numProducers );
- consumers.setSize( numConsumers );
- for( U32 i = 0; i < numProducers; ++ i )
- {
- producers[ i ] = new ProducerThread( this );
- producers[ i ]->start();
- }
- for( U32 i = 0; i < numConsumers; ++ i )
- {
- consumers[ i ] = new ConsumerThread( this );
- consumers[ i ]->start();
- }
- for( U32 i = 0; i < numProducers; ++ i )
- {
- producers[ i ]->join();
- delete producers[ i ];
- }
- for( U32 i = 0; i < numConsumers; ++ i )
- {
- consumers[ i ]->join();
- delete consumers[ i ];
- }
- for( U32 i = 0; i < mValues.size(); ++ i )
- TEST( mValues[ i ] == 0 );
- mValues.clear();
- }
- };
- #endif // !TORQUE_SHIPPING
|