diff options
Diffstat (limited to 'tier0/tslist.cpp')
| -rw-r--r-- | tier0/tslist.cpp | 538 |
1 files changed, 538 insertions, 0 deletions
diff --git a/tier0/tslist.cpp b/tier0/tslist.cpp new file mode 100644 index 0000000..9cd8191 --- /dev/null +++ b/tier0/tslist.cpp @@ -0,0 +1,538 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//============================================================================= + +#include "pch_tier0.h" +#include "tier0/tslist.h" +#include <list> +#include <stdlib.h> +#if defined( _X360 ) +#include "xbox/xbox_win32stubs.h" +#endif + +namespace TSListTests +{ +int NUM_TEST = 10000; +int NUM_THREADS; +int MAX_THREADS = 8; +int NUM_PROCESSORS = 1; + +CInterlockedInt g_nTested; +CInterlockedInt g_nThreads; +CInterlockedInt g_nPushThreads; +CInterlockedInt g_nPopThreads; +CInterlockedInt g_nPushes; +CInterlockedInt g_nPops; +CTSQueue<int, true> g_TestQueue; +CTSList<int> g_TestList; +volatile bool g_bStart; +std::list<ThreadHandle_t> g_ThreadHandles; + +int *g_pTestBuckets; + +CTSListBase g_Test; +TSLNodeBase_t **g_nodes; +int idx = 0; + +const char *g_pListType; + +class CTestOps +{ +public: + virtual void Push( int item ) = 0; + virtual bool Pop( int *pResult ) = 0; + virtual bool Validate() { return true; } + virtual bool IsEmpty() = 0; +}; + +class CQueueOps : public CTestOps +{ + void Push( int item ) + { + g_TestQueue.PushItem( item ); + g_nPushes++; + } + bool Pop( int *pResult ) + { + if ( g_TestQueue.PopItem( pResult ) ) + { + g_nPops++; + return true; + } + return false; + } + bool Validate() + { + return g_TestQueue.ValidateQueue(); + } + bool IsEmpty() + { + return ( g_TestQueue.Count() == 0 ); + } +} g_QueueOps; + +class CListOps : public CTestOps +{ + void Push( int item ) + { + g_TestList.PushItem( item ); + g_nPushes++; + } + bool Pop( int *pResult ) + { + if ( g_TestList.PopItem( pResult ) ) + { + g_nPops++; + return true; + } + return false; + } + bool Validate() + { + return true; + } + bool IsEmpty() + { + return ( g_TestList.Count() == 0 ); + } +} g_ListOps; + +CTestOps *g_pTestOps; + +void ClearBuckets() +{ + memset( g_pTestBuckets, 0, sizeof(int) * NUM_TEST ); +} + +void IncBucket( int i ) +{ + if ( i < NUM_TEST ) // tests can slop over a bit + { + ThreadInterlockedIncrement( &g_pTestBuckets[i] ); + } +} + +void DecBucket( int i ) +{ + if ( i < NUM_TEST ) // tests can slop over a bit + { + ThreadInterlockedDecrement( &g_pTestBuckets[i] ); + } +} + +void ValidateBuckets() +{ + for ( int i = 0; i < NUM_TEST; i++ ) + { + if ( g_pTestBuckets[i] != 0 ) + { + Msg( "Test bucket %d has an invalid value %d\n", i, g_pTestBuckets[i] ); + DebuggerBreakIfDebugging(); + return; + } + } +} + +unsigned PopThreadFunc( void *) +{ + ThreadSetDebugName( "PopThread" ); + g_nPopThreads++; + g_nThreads++; + while ( !g_bStart ) + { + ThreadSleep( 0 ); + } + int ignored; + for (;;) + { + if ( !g_pTestOps->Pop( &ignored ) ) + { + if ( g_nPushThreads == 0 ) + { + // Pop the rest + while ( g_pTestOps->Pop( &ignored ) ) + { + ThreadSleep( 0 ); + } + break; + } + } + } + g_nThreads--; + g_nPopThreads--; + return 0; +} + +unsigned PushThreadFunc( void * ) +{ + ThreadSetDebugName( "PushThread" ); + g_nPushThreads++; + g_nThreads++; + while ( !g_bStart ) + { + ThreadSleep( 0 ); + } + + while ( g_nTested < NUM_TEST ) + { + g_pTestOps->Push( g_nTested ); + g_nTested++; + } + g_nThreads--; + g_nPushThreads--; + return 0; +} + +void TestStart() +{ + g_nTested = 0; + g_nThreads = 0; + g_nPushThreads = 0; + g_nPopThreads = 0; + g_bStart = false; + g_nPops = g_nPushes = 0; + ClearBuckets(); +} + +void TestWait() +{ + while ( g_nThreads < NUM_THREADS ) + { + ThreadSleep( 0 ); + } + g_bStart = true; + while ( g_nThreads > 0 ) + { + ThreadSleep( 50 ); + } +} + +void TestEnd( bool bExpectEmpty = true ) +{ + ValidateBuckets(); + + if ( g_nPops != g_nPushes ) + { + Msg( "FAIL: Not all items popped\n" ); + return; + } + + if ( g_pTestOps->Validate() ) + { + if ( !bExpectEmpty || g_pTestOps->IsEmpty() ) + { + Msg("pass\n"); + } + else + { + Msg("FAIL: !IsEmpty()\n"); + } + } + else + { + Msg("FAIL: !Validate()\n"); + } + while ( g_ThreadHandles.size() ) + { + ThreadJoin( g_ThreadHandles.front(), 0 ); + + ReleaseThreadHandle( g_ThreadHandles.front() ); + g_ThreadHandles.pop_front(); + } +} + + +//-------------------------------------------------- +// +// Shared Tests for CTSQueue and CTSList +// +//-------------------------------------------------- +void PushPopTest() +{ + Msg( "%s test: single thread push/pop, in order... ", g_pListType ); + ClearBuckets(); + g_nTested = 0; + int value; + while ( g_nTested < NUM_TEST ) + { + value = g_nTested++; + g_pTestOps->Push( value ); + IncBucket( value ); + } + + g_pTestOps->Validate(); + + while ( g_pTestOps->Pop( &value ) ) + { + DecBucket( value ); + } + TestEnd(); +} + +void PushPopInterleavedTestGuts() +{ + int value; + for (;;) + { + bool bPush = ( rand() % 2 == 0 ); + if ( bPush && ( value = g_nTested++ ) < NUM_TEST ) + { + g_pTestOps->Push( value ); + IncBucket( value ); + } + else if ( g_pTestOps->Pop( &value ) ) + { + DecBucket( value ); + } + else + { + if ( g_nTested >= NUM_TEST ) + { + break; + } + } + } +} + +void PushPopInterleavedTest() +{ + Msg( "%s test: single thread push/pop, interleaved... ", g_pListType ); + srand( Plat_MSTime() ); + g_nTested = 0; + ClearBuckets(); + PushPopInterleavedTestGuts(); + TestEnd(); +} + +unsigned PushPopInterleavedTestThreadFunc( void * ) +{ + ThreadSetDebugName( "PushPopThread" ); + g_nThreads++; + while ( !g_bStart ) + { + ThreadSleep( 0 ); + } + PushPopInterleavedTestGuts(); + g_nThreads--; + return 0; +} + +void STPushMTPop( bool bDistribute ) +{ + Msg( "%s test: single thread push, multithread pop, %s", g_pListType, bDistribute ? "distributed..." : "no affinity..." ); + TestStart(); + g_ThreadHandles.push_back( CreateSimpleThread( &PushThreadFunc, NULL ) ); + for ( int i = 0; i < NUM_THREADS - 1; i++ ) + { + ThreadHandle_t hThread = CreateSimpleThread( &PopThreadFunc, NULL ); + g_ThreadHandles.push_back( hThread ); + if ( bDistribute ) + { + int32 mask = 1 << (i % NUM_PROCESSORS); + ThreadSetAffinity( hThread, mask ); + } + } + + TestWait(); + TestEnd(); +} + +void MTPushSTPop( bool bDistribute ) +{ + Msg( "%s test: multithread push, single thread pop, %s", g_pListType, bDistribute ? "distributed..." : "no affinity..." ); + TestStart(); + g_ThreadHandles.push_back( CreateSimpleThread( &PopThreadFunc, NULL ) ); + for ( int i = 0; i < NUM_THREADS - 1; i++ ) + { + ThreadHandle_t hThread = CreateSimpleThread( &PushThreadFunc, NULL ); + g_ThreadHandles.push_back( hThread ); + if ( bDistribute ) + { + int32 mask = 1 << (i % NUM_PROCESSORS); + ThreadSetAffinity( hThread, mask ); + } + } + + TestWait(); + TestEnd(); +} + +void MTPushMTPop( bool bDistribute ) +{ + Msg( "%s test: multithread push, multithread pop, %s", g_pListType, bDistribute ? "distributed..." : "no affinity..." ); + TestStart(); + int ct = 0; + for ( int i = 0; i < NUM_THREADS / 2 ; i++ ) + { + ThreadHandle_t hThread = CreateSimpleThread( &PopThreadFunc, NULL ); + g_ThreadHandles.push_back( hThread ); + if ( bDistribute ) + { + int32 mask = 1 << (ct++ % NUM_PROCESSORS); + ThreadSetAffinity( hThread, mask ); + } + } + for ( int i = 0; i < NUM_THREADS / 2 ; i++ ) + { + ThreadHandle_t hThread = CreateSimpleThread( &PushThreadFunc, NULL ); + g_ThreadHandles.push_back( hThread ); + if ( bDistribute ) + { + int32 mask = 1 << (ct++ % NUM_PROCESSORS); + ThreadSetAffinity( hThread, mask ); + } + } + + TestWait(); + TestEnd(); +} + +void MTPushPopPopInterleaved( bool bDistribute ) +{ + Msg( "%s test: multithread interleaved push/pop, %s", g_pListType, bDistribute ? "distributed..." : "no affinity..." ); + srand( Plat_MSTime() ); + TestStart(); + for ( int i = 0; i < NUM_THREADS; i++ ) + { + ThreadHandle_t hThread = CreateSimpleThread( &PushPopInterleavedTestThreadFunc, NULL ); + g_ThreadHandles.push_back( hThread ); + if ( bDistribute ) + { + int32 mask = 1 << (i % NUM_PROCESSORS); + ThreadSetAffinity( hThread, mask ); + } + } + TestWait(); + TestEnd(); +} + +void MTPushSeqPop( bool bDistribute ) +{ + Msg( "%s test: multithread push, sequential pop, %s", g_pListType, bDistribute ? "distributed..." : "no affinity..." ); + TestStart(); + for ( int i = 0; i < NUM_THREADS; i++ ) + { + ThreadHandle_t hThread = CreateSimpleThread( &PushThreadFunc, NULL ); + g_ThreadHandles.push_back( hThread ); + if ( bDistribute ) + { + int32 mask = 1 << (i % NUM_PROCESSORS); + ThreadSetAffinity( hThread, mask ); + } + } + + TestWait(); + int ignored; + g_pTestOps->Validate(); + while ( g_pTestOps->Pop( &ignored ) ) + { + } + TestEnd(); +} + +void SeqPushMTPop( bool bDistribute ) +{ + Msg( "%s test: sequential push, multithread pop, %s", g_pListType, bDistribute ? "distributed..." : "no affinity..." ); + TestStart(); + while ( g_nTested++ < NUM_TEST ) + { + g_pTestOps->Push( g_nTested ); + } + for ( int i = 0; i < NUM_THREADS; i++ ) + { + ThreadHandle_t hThread = CreateSimpleThread( &PopThreadFunc, NULL ); + g_ThreadHandles.push_back( hThread ); + if ( bDistribute ) + { + int32 mask = 1 << (i % NUM_PROCESSORS); + ThreadSetAffinity( hThread, mask ); + } + } + + TestWait(); + TestEnd(); +} + +} +void RunSharedTests( int nTests ) +{ + using namespace TSListTests; + + const CPUInformation &pi = *GetCPUInformation(); + NUM_PROCESSORS = pi.m_nLogicalProcessors; + MAX_THREADS = NUM_PROCESSORS * 2; + g_pTestBuckets = new int[NUM_TEST]; + while ( nTests-- ) + { + for ( NUM_THREADS = 2; NUM_THREADS <= MAX_THREADS; NUM_THREADS *= 2) + { + Msg( "\nTesting %d threads:\n", NUM_THREADS ); + PushPopTest(); + PushPopInterleavedTest(); + SeqPushMTPop( false ); + STPushMTPop( false ); + MTPushSeqPop( false ); + MTPushSTPop( false ); + MTPushMTPop( false ); + MTPushPopPopInterleaved( false ); + if ( NUM_PROCESSORS > 1 ) + { + SeqPushMTPop( true ); + STPushMTPop( true ); + MTPushSeqPop( true ); + MTPushSTPop( true ); + MTPushMTPop( true ); + MTPushPopPopInterleaved( true ); + } + } + } + delete[] g_pTestBuckets; +} + +bool RunTSListTests( int nListSize, int nTests ) +{ + using namespace TSListTests; + NUM_TEST = nListSize; + + TSLHead_t foo; + (void)foo; // Avoid warning about unused variable. +#ifdef USE_NATIVE_SLIST + int maxSize = ( 1 << (sizeof( foo.Depth ) * 8) ) - 1; +#else + int maxSize = ( 1 << (sizeof( foo.value.Depth ) * 8) ) - 1; +#endif + if ( NUM_TEST > maxSize ) + { + Msg( "TSList cannot hold more that %d nodes\n", maxSize ); + return false; + } + + + g_pTestOps = &g_ListOps; + g_pListType = "CTSList"; + + RunSharedTests( nTests ); + + Msg("Tests done, purging test memory..." ); + g_TestList.Purge(); + Msg( "done\n"); + return true; +} + +bool RunTSQueueTests( int nListSize, int nTests ) +{ + using namespace TSListTests; + NUM_TEST = nListSize; + + g_pTestOps = &g_QueueOps; + g_pListType = "CTSQueue"; + + RunSharedTests( nTests ); + + Msg("Tests done, purging test memory..." ); + g_TestQueue.Purge(); + Msg( "done\n"); + return true; +} |