diff --git a/Engine/source/platform/test/testThreadSafeRefCount.cpp b/Engine/source/platform/test/testThreadSafeRefCount.cpp deleted file mode 100644 index 8ad371797..000000000 --- a/Engine/source/platform/test/testThreadSafeRefCount.cpp +++ /dev/null @@ -1,227 +0,0 @@ -//----------------------------------------------------------------------------- -// 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/threadSafeRefCount.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 ) - -CreateUnitTest( TestThreadSafeRefCountSerial, "Platform/ThreadSafeRefCount/Serial" ) -{ - struct TestObject : public ThreadSafeRefCount< TestObject > - { - static bool smDeleted; - - TestObject() - { - smDeleted = false; - } - ~TestObject() - { - smDeleted = true; - } - }; - - typedef ThreadSafeRef< TestObject > TestObjectRef; - - void run() - { - TestObjectRef ref1 = new TestObject; - TEST( !ref1->isShared() ); - TEST( ref1 != NULL ); - - TestObjectRef ref2 = ref1; - TEST( ref1->isShared() ); - TEST( ref2->isShared() ); - TEST( ref1 == ref2 ); - - ref1 = NULL; - TEST( !ref2->isShared() ); - - ref2 = NULL; - TEST( TestObject::smDeleted ); - } -}; - -bool TestThreadSafeRefCountSerial::TestObject::smDeleted; - -CreateUnitTest( TestThreadSafeRefCountConcurrent, "Platform/ThreadSafeRefCount/Concurrent" ) -{ -public: - typedef TestThreadSafeRefCountConcurrent TestType; - enum - { - NUM_ADD_REFS_PER_THREAD = 1000, - NUM_EXTRA_REFS_PER_THREAD = 1000, - NUM_THREADS = 10 - }; - - class TestObject : public ThreadSafeRefCount< TestObject > - { - public: - }; - - ThreadSafeRef< TestObject > mRef; - - class TestThread : public Thread - { - public: - TestType* mTest; - Vector< ThreadSafeRef< TestObject > > mExtraRefs; - - TestThread( TestType* test ) - : mTest( test ) {} - - void run( void* arg ) - { - if( !arg ) - { - for( U32 i = 0; i < NUM_ADD_REFS_PER_THREAD; ++ i ) - mTest->mRef->addRef(); - - mExtraRefs.setSize( NUM_EXTRA_REFS_PER_THREAD ); - for( U32 i = 0; i < NUM_EXTRA_REFS_PER_THREAD; ++ i ) - mExtraRefs[ i ] = mTest->mRef; - } - else - { - mExtraRefs.clear(); - - for( U32 i = 0; i < NUM_ADD_REFS_PER_THREAD; ++ i ) - mTest->mRef->release(); - } - } - }; - - void run() - { - mRef = new TestObject; - TEST( mRef->getRefCount() == 2 ); // increments of 2 - - Vector< TestThread* > threads; - threads.setSize( NUM_THREADS ); - - // Create threads. - for( U32 i = 0; i < NUM_THREADS; ++ i ) - threads[ i ] = new TestThread( this ); - - // Run phase 1: create references. - for( U32 i = 0; i < NUM_THREADS; ++ i ) - threads[ i ]->start( NULL ); - - // Wait for completion. - for( U32 i = 0; i < NUM_THREADS; ++ i ) - threads[ i ]->join(); - - Con::printf( "REF: %i", mRef->getRefCount() ); - TEST( mRef->getRefCount() == 2 + ( ( NUM_ADD_REFS_PER_THREAD + NUM_EXTRA_REFS_PER_THREAD ) * NUM_THREADS * 2 ) ); - - // Run phase 2: release references. - for( U32 i = 0; i < NUM_THREADS; ++ i ) - threads[ i ]->start( ( void* ) 1 ); - - // Wait for completion. - for( U32 i = 0; i < NUM_THREADS; ++ i ) - { - threads[ i ]->join(); - delete threads[ i ]; - } - - TEST( mRef->getRefCount() == 2 ); // increments of two - - mRef = NULL; - } -}; - -CreateUnitTest( TestThreadSafeRefCountTagging, "Platform/ThreadSafeRefCount/Tagging" ) -{ - struct TestObject : public ThreadSafeRefCount< TestObject > {}; - - typedef ThreadSafeRef< TestObject > TestObjectRef; - - void run() - { - TestObjectRef ref; - - TEST( !ref.isTagged() ); - TEST( !ref ); - TEST( !ref.ptr() ); - - TEST( ref.trySetFromTo( ref, NULL ) ); - TEST( !ref.isTagged() ); - - TEST( ref.trySetFromTo( ref, NULL, TestObjectRef::TAG_Set ) ); - TEST( ref.isTagged() ); - TEST( ref.trySetFromTo( ref, NULL, TestObjectRef::TAG_Set ) ); - TEST( ref.isTagged() ); - - TEST( ref.trySetFromTo( ref, NULL, TestObjectRef::TAG_Unset ) ); - TEST( !ref.isTagged() ); - TEST( ref.trySetFromTo( ref, NULL, TestObjectRef::TAG_Unset ) ); - TEST( !ref.isTagged() ); - - TEST( ref.trySetFromTo( ref, NULL, TestObjectRef::TAG_SetOrFail ) ); - TEST( ref.isTagged() ); - TEST( !ref.trySetFromTo( ref, NULL, TestObjectRef::TAG_SetOrFail ) ); - TEST( ref.isTagged() ); - TEST( !ref.trySetFromTo( ref, NULL, TestObjectRef::TAG_FailIfSet ) ); - - TEST( ref.trySetFromTo( ref, NULL, TestObjectRef::TAG_UnsetOrFail ) ); - TEST( !ref.isTagged() ); - TEST( !ref.trySetFromTo( ref, NULL, TestObjectRef::TAG_UnsetOrFail ) ); - TEST( !ref.isTagged() ); - TEST( !ref.trySetFromTo( ref, NULL, TestObjectRef::TAG_FailIfUnset ) ); - - TestObjectRef objectA = new TestObject; - TestObjectRef objectB = new TestObject; - - TEST( !objectA->isShared() ); - TEST( !objectB->isShared() ); - - ref = objectA; - TEST( !ref.isTagged() ); - TEST( ref == objectA ); - TEST( ref == objectA.ptr() ); - TEST( objectA->isShared() ); - - TEST( ref.trySetFromTo( objectA, objectB, TestObjectRef::TAG_Set ) ); - TEST( ref.isTagged() ); - TEST( ref == objectB ); - TEST( ref == objectB.ptr() ); - TEST( objectB->isShared() ); - TEST( !objectA->isShared() ); - - TEST( ref.trySetFromTo( ref, objectA ) ); - TEST( ref.isTagged() ); - TEST( ref == objectA ); - TEST( ref == objectA.ptr() ); - } -}; - -#endif // !TORQUE_SHIPPING diff --git a/Engine/source/platform/test/testThreadPool.cpp b/Engine/source/platform/threads/test/threadPoolTest.cpp similarity index 51% rename from Engine/source/platform/test/testThreadPool.cpp rename to Engine/source/platform/threads/test/threadPoolTest.cpp index 4055ce3fb..95eacd920 100644 --- a/Engine/source/platform/test/testThreadPool.cpp +++ b/Engine/source/platform/threads/test/threadPoolTest.cpp @@ -1,5 +1,5 @@ //----------------------------------------------------------------------------- -// Copyright (c) 2012 GarageGames, LLC +// Copyright (c) 2014 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 @@ -20,66 +20,52 @@ // IN THE SOFTWARE. //----------------------------------------------------------------------------- -#include "unit/test.h" +#ifdef TORQUE_TESTS_ENABLED +#include "testing/unitTesting.h" #include "platform/threads/threadPool.h" #include "console/console.h" #include "core/util/tVector.h" -#ifndef TORQUE_SHIPPING - -using namespace UnitTesting; - -#define TEST( x ) test( ( x ), "FAIL: " #x ) - -// Simple test that creates and verifies an array of numbers using -// thread pool work items. - -CreateUnitTest( TestThreadPool, "Platform/ThreadPool/Simple" ) +TEST(ThreadPool, BasicAPI) { - enum { DEFAULT_NUM_ITEMS = 4000 }; - - static Vector< U32 > results; - + // Represents a single unit of work. In this test we just set an element in + // a result vector. struct TestItem : public ThreadPool::WorkItem { - typedef ThreadPool::WorkItem Parent; - - U32 mIndex; - - TestItem( U32 index ) - : mIndex( index ) {} - - protected: - virtual void execute() - { - results[ mIndex ] = mIndex; - } - }; - - void run() - { - U32 numItems = Con::getIntVariable( "$testThreadPool::numValues", DEFAULT_NUM_ITEMS ); - ThreadPool* pool = &ThreadPool::GLOBAL(); - results.setSize( numItems ); + U32 mIndex; + Vector& mResults; + TestItem(U32 index, Vector& results) + : mIndex(index), mResults(results) {} - for( U32 i = 0; i < numItems; ++ i ) - results[ i ] = U32( -1 ); - - for( U32 i = 0; i < numItems; ++ i ) + protected: + virtual void execute() { - ThreadSafeRef< TestItem > item( new TestItem( i ) ); - pool->queueWorkItem( item ); + mResults[mIndex] = mIndex; } - - pool->flushWorkItems(); - - for( U32 i = 0; i < numItems; ++ i ) - test( results[ i ] == i, "result mismatch" ); - - results.clear(); + }; + + // Construct the vector of results from the work items. + const U32 numItems = 100; + Vector results(__FILE__, __LINE__); + results.setSize(numItems); + for (U32 i = 0; i < numItems; i++) + results[i] = U32(-1); + + // Launch the work items. + ThreadPool* pool = &ThreadPool::GLOBAL(); + for (U32 i = 0; i < numItems; i++) + { + ThreadSafeRef item(new TestItem(i, results)); + pool->queueWorkItem(item); } -}; -Vector< U32 > TestThreadPool::results( __FILE__, __LINE__ ); + // Wait for all items to complete. + pool->flushWorkItems(); -#endif // !TORQUE_SHIPPING + // Verify. + for (U32 i = 0; i < numItems; i++) + EXPECT_EQ(results[i], i) << "result mismatch"; + results.clear(); +} + +#endif \ No newline at end of file diff --git a/Engine/source/platform/threads/test/threadSafeRefCountTest.cpp b/Engine/source/platform/threads/test/threadSafeRefCountTest.cpp new file mode 100644 index 000000000..698637422 --- /dev/null +++ b/Engine/source/platform/threads/test/threadSafeRefCountTest.cpp @@ -0,0 +1,204 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2014 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. +//----------------------------------------------------------------------------- + +#ifdef TORQUE_TESTS_ENABLED +#include "testing/unitTesting.h" +#include "platform/threads/threadSafeRefCount.h" +#include "platform/threads/thread.h" +#include "core/util/tVector.h" +#include "console/console.h" + +TEST(ThreadSafeRefCount, Serial) +{ + struct TestObject : public ThreadSafeRefCount + { + bool &flag; + TestObject(bool &f) : flag(f) + { + flag = false; + } + ~TestObject() + { + flag = true; + } + }; + typedef ThreadSafeRef TestObjectRef; + + bool deleted = false; + TestObjectRef ref1 = new TestObject(deleted); + ASSERT_FALSE(deleted); + EXPECT_FALSE(ref1->isShared()); + EXPECT_TRUE(ref1 != NULL); + + TestObjectRef ref2 = ref1; + EXPECT_TRUE(ref1->isShared()); + EXPECT_TRUE(ref2->isShared()); + EXPECT_EQ(ref1, ref2); + + ref1 = NULL; + EXPECT_FALSE(ref2->isShared()); + + ref2 = NULL; + ASSERT_TRUE(deleted); +} + +TEST(ThreadSafeRefCount, Concurrent) +{ + enum + { + NUM_ADD_REFS_PER_THREAD = 10, + NUM_EXTRA_REFS_PER_THREAD = 10, + NUM_THREADS = 10 + }; + + class TestObject : public ThreadSafeRefCount {}; + typedef ThreadSafeRef TestObjectRef; + TestObjectRef mRef; + + class TestThread : public Thread + { + public: + TestObjectRef mRef; + Vector mExtraRefs; + + TestThread(TestObjectRef ref) : mRef(ref) {} + + void run(void* arg) + { + if (!arg) + { + // Create references. + for (U32 i = 0; i < NUM_ADD_REFS_PER_THREAD; i++) + mRef->addRef(); + + mExtraRefs.setSize(NUM_EXTRA_REFS_PER_THREAD); + for (U32 i = 0; i < NUM_EXTRA_REFS_PER_THREAD; i++) + mExtraRefs[i] = mRef; + } + else + { + // Clear references. + mExtraRefs.clear(); + for (U32 i = 0; i < NUM_ADD_REFS_PER_THREAD; i++) + mRef->release(); + } + } + }; + + mRef = new TestObject; + EXPECT_EQ(mRef->getRefCount(), 2); // increments of 2 + + Vector threads; + threads.setSize(NUM_THREADS); + + // Create threads. + for (U32 i = 0; i < NUM_THREADS; i++) + threads[i] = new TestThread(mRef); + + // Run phase 1: create references. + for (U32 i = 0; i < NUM_THREADS; i++) + threads[i]->start(NULL); + + // Wait for completion. + for (U32 i = 0; i < NUM_THREADS; i++) + threads[i]->join(); + + Con::printf("REF: %i", mRef->getRefCount()); + EXPECT_EQ(mRef->getRefCount(), 2 + ((NUM_ADD_REFS_PER_THREAD + NUM_EXTRA_REFS_PER_THREAD) * NUM_THREADS * 2)); + + // Run phase 2: release references. + for (U32 i = 0; i < NUM_THREADS; i++) + threads[i]->start((void*) 1); + + // Wait for completion. + for (U32 i = 0; i < NUM_THREADS; i++) + { + threads[i]->join(); + delete threads[i]; + } + + EXPECT_EQ(mRef->getRefCount(), 2); // increments of two + + mRef = NULL; +} + +TEST(ThreadSafeRefCount, Tagging) +{ + struct TestObject : public ThreadSafeRefCount {}; + typedef ThreadSafeRef TestObjectRef; + + TestObjectRef ref; + EXPECT_FALSE(ref.isTagged()); + EXPECT_TRUE(bool(ref)); + EXPECT_FALSE(bool(ref.ptr())); + + EXPECT_TRUE(ref.trySetFromTo(ref, NULL)); + EXPECT_FALSE(ref.isTagged()); + + EXPECT_TRUE(ref.trySetFromTo(ref, NULL, TestObjectRef::TAG_Set)); + EXPECT_TRUE(ref.isTagged()); + EXPECT_TRUE(ref.trySetFromTo(ref, NULL, TestObjectRef::TAG_Set)); + EXPECT_TRUE(ref.isTagged()); + + EXPECT_TRUE(ref.trySetFromTo(ref, NULL, TestObjectRef::TAG_Unset)); + EXPECT_FALSE(ref.isTagged()); + EXPECT_TRUE(ref.trySetFromTo(ref, NULL, TestObjectRef::TAG_Unset)); + EXPECT_FALSE(ref.isTagged()); + + EXPECT_TRUE(ref.trySetFromTo(ref, NULL, TestObjectRef::TAG_SetOrFail)); + EXPECT_TRUE(ref.isTagged()); + EXPECT_FALSE(ref.trySetFromTo(ref, NULL, TestObjectRef::TAG_SetOrFail)); + EXPECT_TRUE(ref.isTagged()); + EXPECT_FALSE(ref.trySetFromTo(ref, NULL, TestObjectRef::TAG_FailIfSet)); + + EXPECT_TRUE(ref.trySetFromTo(ref, NULL, TestObjectRef::TAG_UnsetOrFail)); + EXPECT_FALSE(ref.isTagged()); + EXPECT_FALSE(ref.trySetFromTo(ref, NULL, TestObjectRef::TAG_UnsetOrFail)); + EXPECT_FALSE(ref.isTagged()); + EXPECT_FALSE(ref.trySetFromTo(ref, NULL, TestObjectRef::TAG_FailIfUnset)); + + TestObjectRef objectA = new TestObject; + TestObjectRef objectB = new TestObject; + + EXPECT_FALSE(objectA->isShared()); + EXPECT_FALSE(objectB->isShared()); + + ref = objectA; + EXPECT_FALSE(ref.isTagged()); + EXPECT_TRUE(ref == objectA); + EXPECT_TRUE(ref == objectA.ptr()); + EXPECT_TRUE(objectA->isShared()); + + EXPECT_TRUE(ref.trySetFromTo(objectA, objectB, TestObjectRef::TAG_Set)); + EXPECT_TRUE(ref.isTagged()); + EXPECT_EQ(ref, objectB); + EXPECT_EQ(ref, objectB.ptr()); + EXPECT_TRUE(objectB->isShared()); + EXPECT_FALSE(objectA->isShared()); + + EXPECT_TRUE(ref.trySetFromTo(ref, objectA)); + EXPECT_TRUE(ref.isTagged()); + EXPECT_EQ(ref, objectA); + EXPECT_EQ(ref, objectA.ptr()); +} + +#endif \ No newline at end of file