Team Fortress 2 Source Code as on 22/4/2020
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.

538 lines
10 KiB

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