Counter Strike : Global Offensive Source Code
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

570 lines
11 KiB

  1. //========== Copyright � 2007, Valve Corporation, All rights reserved. ========
  2. //
  3. // Purpose:
  4. //
  5. //=============================================================================
  6. #include "pch_tier0.h"
  7. #include "tier0/tslist.h"
  8. #if defined( _X360 )
  9. #include "xbox/xbox_win32stubs.h"
  10. #endif
  11. #include <stdlib.h>
  12. #include "tier0/threadtools.h" // for rand()
  13. // NOTE: This has to be the last file included!
  14. #include "tier0/memdbgon.h"
  15. extern ThreadHandle_t * CreateTestThreads( ThreadFunc_t fnThread, int numThreads, int nProcessorsToDistribute );
  16. extern void JoinTestThreads( ThreadHandle_t *pHandles );
  17. namespace TSListTests
  18. {
  19. int NUM_TEST = 10000;
  20. int NUM_THREADS;
  21. int MAX_THREADS = 8;
  22. int NUM_PROCESSORS = 1;
  23. CInterlockedInt g_nTested;
  24. CInterlockedInt g_nThreads;
  25. CInterlockedInt g_nPushThreads;
  26. CInterlockedInt g_nPopThreads;
  27. CInterlockedInt g_nPushes;
  28. CInterlockedInt g_nPops;
  29. CTSQueue<int, true> g_TestQueue;
  30. CTSList<int> g_TestList;
  31. volatile bool g_bStart;
  32. int *g_pTestBuckets;
  33. CTSListBase g_Test;
  34. TSLNodeBase_t **g_nodes;
  35. int idx = 0;
  36. const char *g_pListType;
  37. class CTestOps
  38. {
  39. public:
  40. virtual void Push( int item ) = 0;
  41. virtual bool Pop( int *pResult ) = 0;
  42. virtual bool Validate() { return true; }
  43. virtual bool IsEmpty() = 0;
  44. };
  45. bool g_bUseMutex = false;
  46. CThreadConditionalMutex< CThreadFastMutex, &g_bUseMutex > g_TestLock;
  47. class CQueueOps : public CTestOps
  48. {
  49. void Push( int item )
  50. {
  51. g_TestLock.Lock();
  52. g_TestQueue.PushItem( item );
  53. g_TestLock.Unlock();
  54. g_nPushes++;
  55. }
  56. bool Pop( int *pResult )
  57. {
  58. g_TestLock.Lock();
  59. if ( g_TestQueue.PopItem( pResult ) )
  60. {
  61. g_TestLock.Unlock();
  62. g_nPops++;
  63. return true;
  64. }
  65. g_TestLock.Unlock();
  66. return false;
  67. }
  68. bool Validate()
  69. {
  70. return true; //g_TestQueue.Validate();
  71. }
  72. bool IsEmpty()
  73. {
  74. return ( g_TestQueue.Count() == 0 );
  75. }
  76. } g_QueueOps;
  77. class CListOps : public CTestOps
  78. {
  79. void Push( int item )
  80. {
  81. g_TestLock.Lock();
  82. g_TestList.PushItem( item );
  83. g_nPushes++;
  84. }
  85. bool Pop( int *pResult )
  86. {
  87. g_TestLock.Lock();
  88. if ( g_TestList.PopItem( pResult ) )
  89. {
  90. g_TestLock.Unlock();
  91. g_nPops++;
  92. return true;
  93. }
  94. g_TestLock.Unlock();
  95. return false;
  96. }
  97. bool Validate()
  98. {
  99. return true;
  100. }
  101. bool IsEmpty()
  102. {
  103. return ( g_TestList.Count() == 0 );
  104. }
  105. } g_ListOps;
  106. CTestOps *g_pTestOps;
  107. void ClearBuckets()
  108. {
  109. memset( g_pTestBuckets, 0, sizeof(int) * NUM_TEST );
  110. }
  111. void IncBucket( int i )
  112. {
  113. if ( i < NUM_TEST ) // tests can slop over a bit
  114. {
  115. ThreadInterlockedIncrement( &g_pTestBuckets[i] );
  116. }
  117. }
  118. void DecBucket( int i )
  119. {
  120. if ( i < NUM_TEST ) // tests can slop over a bit
  121. {
  122. ThreadInterlockedDecrement( &g_pTestBuckets[i] );
  123. }
  124. }
  125. void ValidateBuckets()
  126. {
  127. for ( int i = 0; i < NUM_TEST; i++ )
  128. {
  129. if ( g_pTestBuckets[i] != 0 )
  130. {
  131. Msg( "Test bucket %d has an invalid value %d\n", i, g_pTestBuckets[i] );
  132. DebuggerBreakIfDebugging();
  133. return;
  134. }
  135. }
  136. }
  137. uintp PopThreadFunc( void *)
  138. {
  139. //ThreadSetDebugName( "PopThread" );
  140. g_nPopThreads++;
  141. g_nThreads++;
  142. while ( !g_bStart )
  143. {
  144. ThreadSleep( 1 );
  145. }
  146. int ignored;
  147. for (;;)
  148. {
  149. if ( !g_pTestOps->Pop( &ignored ) )
  150. {
  151. ThreadPause();
  152. ThreadSleep(0);
  153. if ( g_nPushThreads == 0 )
  154. {
  155. // Pop the rest
  156. while ( g_pTestOps->Pop( &ignored ) )
  157. {
  158. ThreadPause();
  159. ThreadSleep( 0 );
  160. }
  161. break;
  162. }
  163. }
  164. }
  165. g_nThreads--;
  166. g_nPopThreads--;
  167. return 0;
  168. }
  169. uintp PushThreadFunc( void * )
  170. {
  171. //ThreadSetDebugName( "PushThread" );
  172. g_nPushThreads++;
  173. g_nThreads++;
  174. while ( !g_bStart )
  175. {
  176. ThreadPause();
  177. ThreadSleep( 0 );
  178. }
  179. while ( ++g_nTested <= NUM_TEST )
  180. {
  181. g_pTestOps->Push( g_nTested );
  182. }
  183. g_nThreads--;
  184. g_nPushThreads--;
  185. return 0;
  186. }
  187. void TestStart()
  188. {
  189. g_nTested = 0;
  190. g_nThreads = 0;
  191. g_nPushThreads = 0;
  192. g_nPopThreads = 0;
  193. g_bStart = false;
  194. g_nPops = g_nPushes = 0;
  195. ClearBuckets();
  196. }
  197. void TestWait()
  198. {
  199. while ( g_nThreads < NUM_THREADS )
  200. {
  201. ThreadSleep( 0 );
  202. }
  203. g_bStart = true;
  204. while ( g_nThreads > 0 )
  205. {
  206. ThreadSleep( 0 );
  207. }
  208. }
  209. void TestEnd( bool bExpectEmpty = true )
  210. {
  211. ValidateBuckets();
  212. if ( g_nPops != g_nPushes )
  213. {
  214. Msg( "FAIL: Not all items popped\n" );
  215. return;
  216. }
  217. if ( g_pTestOps->Validate() )
  218. {
  219. if ( !bExpectEmpty || g_pTestOps->IsEmpty() )
  220. {
  221. Msg("pass\n");
  222. }
  223. else
  224. {
  225. Msg("FAIL: !IsEmpty()\n");
  226. }
  227. }
  228. else
  229. {
  230. Msg("FAIL: !Validate()\n");
  231. }
  232. }
  233. //--------------------------------------------------
  234. //
  235. // Shared Tests for CTSQueue and CTSList
  236. //
  237. //--------------------------------------------------
  238. void PushPopTest()
  239. {
  240. Msg( "%s test: single thread push/pop, in order... ", g_pListType );
  241. ClearBuckets();
  242. g_nTested = 0;
  243. int value;
  244. while ( g_nTested < NUM_TEST )
  245. {
  246. value = g_nTested++;
  247. g_pTestOps->Push( value );
  248. IncBucket( value );
  249. }
  250. g_pTestOps->Validate();
  251. while ( g_pTestOps->Pop( &value ) )
  252. {
  253. DecBucket( value );
  254. }
  255. TestEnd();
  256. }
  257. void PushPopInterleavedTestGuts()
  258. {
  259. int value;
  260. for (;;)
  261. {
  262. bool bPush = ( rand() % 2 == 0 );
  263. if ( bPush && ( value = g_nTested++ ) < NUM_TEST )
  264. {
  265. g_pTestOps->Push( value );
  266. IncBucket( value );
  267. }
  268. else if ( g_pTestOps->Pop( &value ) )
  269. {
  270. DecBucket( value );
  271. }
  272. else
  273. {
  274. if ( g_nTested >= NUM_TEST )
  275. {
  276. break;
  277. }
  278. }
  279. }
  280. }
  281. void PushPopInterleavedTest()
  282. {
  283. Msg( "%s test: single thread push/pop, interleaved... ", g_pListType );
  284. srand( Plat_MSTime() );
  285. g_nTested = 0;
  286. ClearBuckets();
  287. PushPopInterleavedTestGuts();
  288. TestEnd();
  289. }
  290. uintp PushPopInterleavedTestThreadFunc( void * )
  291. {
  292. ThreadSetDebugName( "PushPopThread" );
  293. g_nThreads++;
  294. while ( !g_bStart )
  295. {
  296. ThreadSleep( 0 );
  297. }
  298. PushPopInterleavedTestGuts();
  299. g_nThreads--;
  300. return 0;
  301. }
  302. void STPushMTPop( bool bDistribute )
  303. {
  304. Msg( "%s test: single thread push, multithread pop, %s", g_pListType, bDistribute ? "distributed..." : "no affinity..." );
  305. TestStart();
  306. ThreadHandle_t hPush = CreateSimpleThread( &PushThreadFunc, NULL );
  307. ThreadHandle_t *arrPops = CreateTestThreads( PopThreadFunc, NUM_THREADS - 1, ( bDistribute ) ? NUM_PROCESSORS : 0 );
  308. TestWait();
  309. TestEnd();
  310. JoinTestThreads( arrPops );
  311. ThreadJoin( hPush );
  312. ReleaseThreadHandle( hPush );
  313. }
  314. void MTPushSTPop( bool bDistribute )
  315. {
  316. Msg( "%s test: multithread push, single thread pop, %s", g_pListType, bDistribute ? "distributed..." : "no affinity..." );
  317. TestStart();
  318. ThreadHandle_t hPop = CreateSimpleThread( &PopThreadFunc, NULL );
  319. ThreadHandle_t* arrPushes = CreateTestThreads( PushThreadFunc, NUM_THREADS - 1, ( bDistribute ) ? NUM_PROCESSORS : 0 );
  320. TestWait();
  321. TestEnd();
  322. JoinTestThreads( arrPushes );
  323. ThreadJoin( hPop );
  324. ReleaseThreadHandle( hPop );
  325. }
  326. void MTPushMTPop( bool bDistribute )
  327. {
  328. Msg( "%s test: multithread push, multithread pop, %s", g_pListType, bDistribute ? "distributed..." : "no affinity..." );
  329. TestStart();
  330. int ct = 0;
  331. ThreadHandle_t *threadHandles = (ThreadHandle_t *)stackalloc( NUM_THREADS * sizeof(ThreadHandle_t) );
  332. int nHandles = 0;
  333. for ( int i = 0; i < NUM_THREADS / 2 ; i++ )
  334. {
  335. ThreadHandle_t hThread = CreateSimpleThread( &PopThreadFunc, NULL );
  336. threadHandles[nHandles++] = hThread;
  337. if ( bDistribute )
  338. {
  339. int32 mask = 1 << (ct++ % NUM_PROCESSORS);
  340. ThreadSetAffinity( hThread, mask );
  341. }
  342. }
  343. for ( int i = 0; i < NUM_THREADS / 2 ; i++ )
  344. {
  345. ThreadHandle_t hThread = CreateSimpleThread( &PushThreadFunc, NULL );
  346. threadHandles[nHandles++] = hThread;
  347. if ( bDistribute )
  348. {
  349. int32 mask = 1 << (ct++ % NUM_PROCESSORS);
  350. ThreadSetAffinity( hThread, mask );
  351. }
  352. }
  353. TestWait();
  354. TestEnd();
  355. for ( int i = 0; i < nHandles; i++ )
  356. {
  357. ReleaseThreadHandle( threadHandles[i] );
  358. }
  359. }
  360. void MTPushPopPopInterleaved( bool bDistribute )
  361. {
  362. Msg( "%s test: multithread interleaved push/pop, %s", g_pListType, bDistribute ? "distributed..." : "no affinity..." );
  363. srand( Plat_MSTime() );
  364. TestStart();
  365. ThreadHandle_t * arrPushPops = CreateTestThreads( &PushPopInterleavedTestThreadFunc, NUM_THREADS, ( bDistribute ) ? NUM_PROCESSORS : 0 );
  366. TestWait();
  367. TestEnd();
  368. JoinTestThreads( arrPushPops );
  369. }
  370. void MTPushSeqPop( bool bDistribute )
  371. {
  372. Msg( "%s test: multithread push, sequential pop, %s", g_pListType, bDistribute ? "distributed..." : "no affinity..." );
  373. TestStart();
  374. ThreadHandle_t * arrPushes = CreateTestThreads( PushThreadFunc, NUM_THREADS, ( bDistribute ) ? NUM_PROCESSORS : 0 );
  375. TestWait();
  376. int ignored;
  377. g_pTestOps->Validate();
  378. int nPopped = 0;
  379. while ( g_pTestOps->Pop( &ignored ) )
  380. {
  381. nPopped++;
  382. }
  383. if ( nPopped != NUM_TEST )
  384. {
  385. Msg( "Pops != pushes?\n" );
  386. DebuggerBreakIfDebugging();
  387. }
  388. TestEnd();
  389. JoinTestThreads( arrPushes );
  390. }
  391. void SeqPushMTPop( bool bDistribute )
  392. {
  393. Msg( "%s test: sequential push, multithread pop, %s", g_pListType, bDistribute ? "distributed..." : "no affinity..." );
  394. TestStart();
  395. while ( g_nTested++ < NUM_TEST )
  396. {
  397. g_pTestOps->Push( g_nTested );
  398. }
  399. ThreadHandle_t * arrPops = CreateTestThreads( PopThreadFunc, NUM_THREADS, ( bDistribute ) ? NUM_PROCESSORS : 0 );
  400. TestWait();
  401. TestEnd();
  402. JoinTestThreads( arrPops );
  403. }
  404. #ifdef _PS3
  405. void TestThreadProc( uint64_t id )
  406. {
  407. printf( "(TS)Hello from PPU thread %lld @%p\n", id, &id );
  408. sys_ppu_thread_exit( id );
  409. }
  410. uintp TestThreadProc2( void *p )
  411. {
  412. printf( "(TS)Hello from PPU thread %lld @%p\n", (int64)p, &p );
  413. return (uintp)p;
  414. }
  415. #endif
  416. void TestThreads()
  417. {
  418. #ifdef _PS3
  419. printf("(TS)testing threads\n");
  420. const int numThreads = 40;
  421. ThreadHandle_t * arrTests = CreateTestThreads( TestThreadProc2, numThreads, false );
  422. JoinTestThreads( arrTests );
  423. #endif
  424. }
  425. }
  426. void RunSharedTests( int nTests )
  427. {
  428. using namespace TSListTests;
  429. TestThreads();
  430. const CPUInformation &pi = GetCPUInformation();
  431. NUM_PROCESSORS = pi.m_nLogicalProcessors;
  432. MAX_THREADS = NUM_PROCESSORS * 2;
  433. g_pTestBuckets = new int[NUM_TEST];
  434. while ( nTests-- )
  435. {
  436. for ( NUM_THREADS = 2; NUM_THREADS <= MAX_THREADS; NUM_THREADS *= 2)
  437. {
  438. Msg( "\nTesting %d threads:\n", NUM_THREADS );
  439. PushPopTest();
  440. PushPopInterleavedTest();
  441. SeqPushMTPop( false );
  442. STPushMTPop( false );
  443. MTPushSeqPop( false );
  444. MTPushSTPop( false );
  445. MTPushMTPop( false );
  446. MTPushPopPopInterleaved( false );
  447. if ( NUM_PROCESSORS > 1 )
  448. {
  449. SeqPushMTPop( true );
  450. STPushMTPop( true );
  451. MTPushSeqPop( true );
  452. MTPushSTPop( true );
  453. MTPushMTPop( true );
  454. MTPushPopPopInterleaved( true );
  455. }
  456. }
  457. }
  458. delete[] g_pTestBuckets;
  459. }
  460. bool RunTSListTests( int nListSize, int nTests )
  461. {
  462. using namespace TSListTests;
  463. NUM_TEST = nListSize;
  464. #ifdef USE_NATIVE_SLIST
  465. #ifdef _WIN64
  466. int maxSize = 65536; // FIXME: How should this be computed?
  467. #else
  468. int maxSize = ( 1 << (sizeof( ((TSLHead_t *)(0))->Depth ) * 8) ) - 1;
  469. #endif
  470. #else
  471. int maxSize = ( 1 << (sizeof( ((TSLHead_t *)(0))->value.Depth ) * 8) ) - 1;
  472. #endif
  473. if ( NUM_TEST > maxSize )
  474. {
  475. Msg( "TSList cannot hold more that %d nodes\n", maxSize );
  476. return false;
  477. }
  478. g_pTestOps = &g_ListOps;
  479. g_pListType = "CTSList";
  480. RunSharedTests( nTests );
  481. Msg("Tests done, purging test memory..." );
  482. g_TestList.Purge();
  483. Msg( "done\n");
  484. return true;
  485. }
  486. bool RunTSQueueTests( int nListSize, int nTests )
  487. {
  488. using namespace TSListTests;
  489. NUM_TEST = nListSize;
  490. g_pTestOps = &g_QueueOps;
  491. g_pListType = "CTSQueue";
  492. RunSharedTests( nTests );
  493. Msg("Tests done, purging test memory..." );
  494. g_TestQueue.Purge();
  495. Msg( "done\n");
  496. return true;
  497. }