TestThreadThread.cpp 42 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332
  1. ///////////////////////////////////////////////////////////////////////////////
  2. // Copyright (c) Electronic Arts Inc. All rights reserved.
  3. ///////////////////////////////////////////////////////////////////////////////
  4. #include "TestThread.h"
  5. #include <EATest/EATest.h>
  6. #include <EAStdC/EAStopwatch.h>
  7. #include <EAStdC/EASprintf.h>
  8. #include <EAStdC/EAString.h>
  9. #include <EASTL/set.h>
  10. #include <eathread/eathread_thread.h>
  11. #include <eathread/eathread_sync.h>
  12. #include <eathread/eathread_condition.h>
  13. #ifdef EA_PLATFORM_LINUX
  14. #include <unistd.h>
  15. #endif
  16. #ifdef EA_PLATFORM_MICROSOFT
  17. EA_DISABLE_ALL_VC_WARNINGS()
  18. #include <Windows.h>
  19. EA_RESTORE_ALL_VC_WARNINGS()
  20. #endif
  21. #include <atomic>
  22. using namespace EA::Thread;
  23. const int kMaxConcurrentThreadCount = EATHREAD_MAX_CONCURRENT_THREAD_COUNT;
  24. static unsigned int sThreadTestTimeMS = 2000; // We potentially change this value below.
  25. static AtomicInt32 sThreadCount = 0;
  26. static AtomicInt32 sShouldGo = 0;
  27. static intptr_t TestFunction1(void*)
  28. {
  29. ThreadTime nTimeEnd = EA::Thread::GetThreadTime() + sThreadTestTimeMS;
  30. while(EA::Thread::GetThreadTime() < nTimeEnd)
  31. ThreadSleep();
  32. return 0;
  33. }
  34. static intptr_t TestFunction3(void*)
  35. {
  36. // This following code should produce NULL pointer access violation exception
  37. // EA::UnitTest::ReportVerbosity(1, "Throw NULL pointer Exception.\n");
  38. // char* pTest = NULL;
  39. // *pTest = 1;
  40. return 0;
  41. }
  42. static intptr_t TestFunction4(void* arg)
  43. {
  44. const intptr_t returnValue = (intptr_t)arg;
  45. EA::UnitTest::ThreadSleepRandom(0, 5);
  46. return returnValue;
  47. }
  48. #if !defined(EA_PLATFORM_MOBILE)
  49. static intptr_t TestFunction6(void* arg)
  50. {
  51. const intptr_t returnValue = (intptr_t)arg;
  52. sThreadCount++;
  53. while(sShouldGo == 0)
  54. ThreadSleep(10);
  55. EA::UnitTest::ThreadSleepRandom(3, 8);
  56. sThreadCount--;
  57. return returnValue;
  58. }
  59. #endif
  60. static intptr_t TestFunction7(void*)
  61. {
  62. while(sShouldGo == 0)
  63. ThreadSleep(10);
  64. return 0;
  65. }
  66. static intptr_t TestFunction8(void*)
  67. {
  68. while(sShouldGo == 0)
  69. ThreadSleep(10);
  70. return 0;
  71. }
  72. static intptr_t TestFunction9(void* arg)
  73. {
  74. return (intptr_t)GetThreadProcessor();
  75. }
  76. static intptr_t TestFunction10(void* arg)
  77. {
  78. ThreadSleep(10);
  79. return TestFunction9(arg);
  80. }
  81. static intptr_t TestFunction12(void* arg)
  82. {
  83. ThreadSleep(10);
  84. return TestFunction9(arg);
  85. }
  86. static intptr_t TestFunction11(void* arg)
  87. {
  88. int requestedCore = *(static_cast<int*>(arg));
  89. int coreIndex = (requestedCore % EA::Thread::GetProcessorCount());
  90. SetThreadAffinityMask(UINT64_C(1) << coreIndex); // set the highest processor available.
  91. for (int retryCount = 100; retryCount && (GetThreadProcessor() != coreIndex); retryCount--)
  92. ThreadSleep(1);
  93. return (intptr_t)GetThreadProcessor();
  94. }
  95. static intptr_t TestFunction13(void* arg)
  96. {
  97. ThreadSleep(10);
  98. ThreadEnd(42); // 42 is a magic number we will verify gets passed through the user.
  99. return 0;
  100. }
  101. static intptr_t TestFunction3ExceptionWrapper(RunnableFunction defaultRunnableFunction, void* pContext)
  102. {
  103. return defaultRunnableFunction(pContext);
  104. }
  105. class TestRunnable1 : public IRunnable
  106. {
  107. intptr_t Run(void*)
  108. {
  109. const ThreadTime nTimeEnd = EA::Thread::GetThreadTime() + sThreadTestTimeMS;
  110. while (EA::Thread::GetThreadTime() < nTimeEnd)
  111. ThreadSleep();
  112. return 0;
  113. }
  114. } gTestRunnable1;
  115. class TestRunnable2 : public IRunnable
  116. {
  117. intptr_t Run(void*)
  118. {
  119. const ThreadTime nTimeEnd = EA::Thread::GetThreadTime() + sThreadTestTimeMS;
  120. while(EA::Thread::GetThreadTime() < nTimeEnd)
  121. {
  122. ThreadSleep();
  123. }
  124. return 0;
  125. }
  126. } gTestRunnable2;
  127. static intptr_t TestRunnable3ExceptionWrapper(IRunnable* defaultRunnableFunction, void* pContext)
  128. {
  129. return defaultRunnableFunction->Run(pContext);
  130. }
  131. class TestRunnable3 : public IRunnable
  132. {
  133. intptr_t Run(void*)
  134. {
  135. // This following code should produce NULL pointer access violation exception
  136. // EA::UnitTest::ReportVerbosity(1, "Throw NULL pointer Exception.\n");
  137. // char* pTest = NULL;
  138. // *pTest = 1;
  139. ThreadSleep(500);
  140. return 0;
  141. }
  142. } gTestRunnable3;
  143. class TestRunnable4 : public IRunnable
  144. {
  145. intptr_t Run(void*)
  146. {
  147. // IRunnable object that returns the thread id that executed on.
  148. return TestFunction9(NULL);
  149. }
  150. } gTestRunnable4;
  151. int TestThreadAffinityMask()
  152. {
  153. int nErrorCount = 0;
  154. const int MAX_ITERATIONS = 16;
  155. const int nAvailableProcessors = EA::Thread::GetProcessorCount();
  156. auto VERIFY_AFFINITY_RESULT = [&](intptr_t in_result, int count)
  157. {
  158. #if EATHREAD_THREAD_AFFINITY_MASK_SUPPORTED
  159. auto result = static_cast<int>(in_result);
  160. EATEST_VERIFY_F(result == (count % nAvailableProcessors),
  161. "Thread failure: SetAffinityMask not working properly. Thread ran on: %d/%d <=> Expected: %d\n",
  162. result, nAvailableProcessors, (count % nAvailableProcessors));
  163. #endif
  164. };
  165. int count = MAX_ITERATIONS;
  166. while(--count)
  167. {
  168. // Test Thread Affinity Masks (thread parameters)
  169. Thread thread;
  170. ThreadParameters params;
  171. params.mnAffinityMask = INT64_C(1) << (count % nAvailableProcessors);
  172. params.mnProcessor = kProcessorAny;
  173. thread.Begin(TestFunction10, NULL, &params);
  174. intptr_t result = 0;
  175. thread.WaitForEnd(GetThreadTime() + 30000, &result);
  176. VERIFY_AFFINITY_RESULT(result, count);
  177. }
  178. count = MAX_ITERATIONS;
  179. while(--count)
  180. {
  181. // Test Thread Affinity Masks (thread object)
  182. ThreadParameters params;
  183. params.mnProcessor = kProcessorAny;
  184. Thread thread;
  185. thread.Begin(TestFunction12, NULL, &params); // sleeps then grabs the current thread id.
  186. thread.SetAffinityMask(INT64_C(1) << (count % nAvailableProcessors));
  187. intptr_t result = 0;
  188. thread.WaitForEnd(GetThreadTime() + 30000, &result);
  189. VERIFY_AFFINITY_RESULT(result, count);
  190. }
  191. count = MAX_ITERATIONS;
  192. while(--count)
  193. {
  194. // Test Thread Affinity Masks (global functions)
  195. ThreadParameters params;
  196. params.mnProcessor = kProcessorAny;
  197. Thread thread;
  198. thread.Begin(TestFunction11, &count, &params);
  199. intptr_t result = 0;
  200. thread.WaitForEnd(GetThreadTime() + 30000, &result);
  201. VERIFY_AFFINITY_RESULT(result, count);
  202. }
  203. count = MAX_ITERATIONS;
  204. while(--count)
  205. {
  206. // Test Thread Affinity Masks (thread parameters) - For IRunnable variant of the Thread::Begin function
  207. ThreadParameters params;
  208. params.mnProcessor = kProcessorAny;
  209. params.mnAffinityMask = INT64_C(1) << (count % nAvailableProcessors);
  210. params.mnProcessor = kProcessorAny;
  211. Thread thread;
  212. thread.Begin(&gTestRunnable4, NULL, &params); // sleeps then grabs the current thread id.
  213. intptr_t result = 0;
  214. thread.WaitForEnd(GetThreadTime() + 30000, &result);
  215. VERIFY_AFFINITY_RESULT(result, count);
  216. }
  217. return nErrorCount;
  218. }
  219. int TestThreadPriorities()
  220. {
  221. int nErrorCount = 0;
  222. if(!IsSuperUser())
  223. {
  224. EA::EAMain::Report("Skipping Thread Priority test because we don't have sufficient system priviliages.\n");
  225. return nErrorCount;
  226. }
  227. // Verify that thread priorities act as expected.
  228. // Threads with higher priorities should execute instead of or before threads of lower priorities.
  229. // On some platforms (e.g. Windows), lower priority threads do get some execution time, so we have to recognize that.
  230. // Create 20 threads of very high priority, 20 threads of high priority, and 20 threads of regular priority.
  231. // Start the 20 very high priority threads first.
  232. // Wait a bit then start the other 40 threads.
  233. // Quit all the very high priority threads.
  234. // Wait a bit, while having the 40 threads measure how much time they execute.
  235. // Quit the remaining 40 threads.
  236. // Verify that the 20 high priority threads executed much more than the regular threads.
  237. struct PriorityTestThread : public EA::Thread::IRunnable
  238. {
  239. EA::Thread::Thread mThread;
  240. EA::Thread::ThreadParameters mParameters;
  241. EA::Thread::Semaphore mSemaphore;
  242. char mThreadName[16];
  243. volatile uint64_t mCounter;
  244. volatile bool mbShouldRun;
  245. char mPadd[EA_CACHE_LINE_SIZE];// make sure that these structures end up on different cachelines
  246. PriorityTestThread() : mThread(), mParameters(), mSemaphore(0), mCounter(0), mbShouldRun(true) {}
  247. PriorityTestThread(const PriorityTestThread&){}
  248. void operator=(const PriorityTestThread&){}
  249. intptr_t Run(void*)
  250. {
  251. mSemaphore.Wait();
  252. while(mbShouldRun)
  253. {
  254. //char buffer[64];
  255. //EA::StdC::Sprintf(buffer, "%f", (double)(mCounter*1.23)); // Just waste time.
  256. mCounter++;
  257. EAReadBarrier();
  258. }
  259. return 0;
  260. }
  261. };
  262. if((EA::Thread::GetProcessorCount() >= 4))
  263. {
  264. const int kThreadCount = 4;
  265. PriorityTestThread threadHighestPriority[kThreadCount];
  266. PriorityTestThread threadRegularPriority[kThreadCount];
  267. PriorityTestThread threadHighPriority[kThreadCount];
  268. EA::StdC::LimitStopwatch limitStopwatch(EA::StdC::Stopwatch::kUnitsSeconds);
  269. const EA::Thread::ThreadAffinityMask kCommonAffinityMask = 0xf; // first 4 cores only
  270. EA::Thread::ThreadParameters commonParams;
  271. commonParams.mbDisablePriorityBoost = true; // we can disable boosting if we want a better simulation of console-like behavior
  272. commonParams.mnAffinityMask = kCommonAffinityMask;
  273. commonParams.mnProcessor = kProcessorAny;
  274. #if defined(EA_PLATFORM_MICROSOFT)
  275. // Due to how windows thread priorities work we need to further increase the thread priority of the high threads
  276. // If this is not done the test will randomly have the regular priority have a higher count than the high threads.
  277. static const int kHigherPriorityDelta = kThreadPriorityMax - 2;
  278. #else
  279. static const int kHigherPriorityDelta = 0;
  280. #endif
  281. EA::Thread::SetThreadPriority(EA::Thread::kThreadPriorityDefault + 3);
  282. for (int i = 0; i < kThreadCount; i++)
  283. {
  284. {
  285. PriorityTestThread& highestThread = threadHighestPriority[i];
  286. highestThread.mParameters = commonParams;
  287. highestThread.mParameters.mnPriority = (EA::Thread::kThreadPriorityDefault + 2 + kHigherPriorityDelta);
  288. EA::StdC::Sprintf(highestThread.mThreadName, "Highest%d", i);
  289. highestThread.mParameters.mpName = highestThread.mThreadName;
  290. highestThread.mThread.Begin(&highestThread, NULL, &highestThread.mParameters);
  291. }
  292. {
  293. PriorityTestThread& regularThread = threadRegularPriority[i];
  294. regularThread.mParameters = commonParams;
  295. regularThread.mParameters.mnPriority = (EA::Thread::kThreadPriorityDefault);
  296. EA::StdC::Sprintf(regularThread.mThreadName, "Reg%d", i);
  297. regularThread.mParameters.mpName = regularThread.mThreadName;
  298. regularThread.mThread.Begin(&regularThread, NULL, &regularThread.mParameters);
  299. }
  300. {
  301. PriorityTestThread& highThread = threadHighPriority[i];
  302. highThread.mParameters = commonParams;
  303. highThread.mParameters.mnPriority = (EA::Thread::kThreadPriorityDefault + 1 + kHigherPriorityDelta);
  304. EA::StdC::Sprintf(highThread.mThreadName, "High%d", i);
  305. highThread.mParameters.mpName = highThread.mThreadName;
  306. highThread.mThread.Begin(&highThread, NULL, &highThread.mParameters);
  307. }
  308. }
  309. limitStopwatch.SetTimeLimit(1, true);
  310. while(!limitStopwatch.IsTimeUp())
  311. { /* Do nothing. Don't even sleep, as some platform might not give us the CPU back. */ }
  312. for(int i = 0; i < kThreadCount; i++)
  313. {
  314. threadHighestPriority[i].mSemaphore.Post(1);
  315. threadHighPriority[i].mSemaphore.Post(1);
  316. threadRegularPriority[i].mSemaphore.Post(1);
  317. }
  318. limitStopwatch.SetTimeLimit(3, true);
  319. while(!limitStopwatch.IsTimeUp())
  320. { /* Do nothing. Don't even sleep, as some platform might not give us the CPU back. */ }
  321. for(int i = 0; i < kThreadCount; i++)
  322. threadHighestPriority[i].mbShouldRun = false;
  323. EAWriteBarrier();
  324. limitStopwatch.SetTimeLimit(3, true);
  325. while(!limitStopwatch.IsTimeUp())
  326. { /* Do nothing. Don't even sleep, as some platform might not give us the CPU back. */ }
  327. for(int i = 0; i < kThreadCount; i++)
  328. {
  329. threadHighPriority[i].mbShouldRun = false;
  330. threadRegularPriority[i].mbShouldRun = false;
  331. }
  332. EAWriteBarrier();
  333. limitStopwatch.SetTimeLimit(3, true);
  334. while(!limitStopwatch.IsTimeUp())
  335. { /* Do nothing. Don't even sleep, as some platform might not give us the CPU back. */ }
  336. EA::Thread::SetThreadPriority(EA::Thread::kThreadPriorityDefault);
  337. uint64_t highPriorityCount = 0;
  338. uint64_t regularPriorityCount = 0;
  339. for(int i = 0; i < kThreadCount; i++)
  340. highPriorityCount += threadHighPriority[i].mCounter;
  341. for(int i = 0; i < kThreadCount; i++)
  342. regularPriorityCount += threadRegularPriority[i].mCounter;
  343. // Verify that the higher priority threads always got priority over the regular priority threads.
  344. EATEST_VERIFY_F(highPriorityCount > regularPriorityCount, "Priority execution failure: highPriorityCount: %I64u, regularPriorityCount: %I64u", highPriorityCount, regularPriorityCount);
  345. // Wait for the threads to end before continuing.
  346. for(int i = 0; i < kThreadCount; i++)
  347. {
  348. threadHighestPriority[i].mThread.WaitForEnd();
  349. threadHighPriority[i].mThread.WaitForEnd();
  350. threadRegularPriority[i].mThread.WaitForEnd();
  351. }
  352. }
  353. return nErrorCount;
  354. }
  355. int TestSetThreadProcessConstants()
  356. {
  357. int nErrorCount = 0;
  358. // testing a user reported regression of negative value shifts
  359. for(auto k : { kProcessorDefault, kProcessorAny })
  360. {
  361. Thread t;
  362. t.Begin([](void* param) -> intptr_t
  363. {
  364. int kConstant = *(int*)param;
  365. SetThreadProcessor(kConstant);
  366. return 0;
  367. }, &k);
  368. t.WaitForEnd();
  369. }
  370. return nErrorCount;
  371. }
  372. int TestNullThreadNames()
  373. {
  374. int nErrorCount = 0;
  375. {
  376. ThreadParameters threadParams;
  377. threadParams.mpName = nullptr;
  378. Thread t;
  379. t.Begin([](void*) -> intptr_t { return 0; }, NULL, &threadParams);
  380. t.WaitForEnd();
  381. }
  382. return nErrorCount;
  383. }
  384. int TestLambdaThreads()
  385. {
  386. int nErrorCount = 0;
  387. { // test rvalue
  388. int foo = 0;
  389. MakeThread([&]
  390. {
  391. EATEST_VERIFY(foo == 0);
  392. foo = 42;
  393. })
  394. .WaitForEnd();
  395. EATEST_VERIFY(foo == 42);
  396. }
  397. { // test lvalue
  398. int foo = 0;
  399. auto callme = [&]
  400. {
  401. EATEST_VERIFY(foo == 0);
  402. foo = 42;
  403. };
  404. MakeThread(callme).WaitForEnd();
  405. EATEST_VERIFY(foo == 42);
  406. }
  407. { // test thread parameters
  408. const char* MY_THREAD_NAME = "my thread name";
  409. ThreadParameters params;
  410. params.mpName = MY_THREAD_NAME;
  411. MakeThread(
  412. [&] { EATEST_VERIFY(strncmp(MY_THREAD_NAME, GetThreadName(), EATHREAD_NAME_SIZE) == 0); }, params)
  413. .WaitForEnd();
  414. }
  415. return nErrorCount;
  416. }
  417. int TestThreadDynamicData()
  418. {
  419. int nErrorCount = 0;
  420. const int kOverflowDynamicDataCount = 256; // Must be greater than EA::Thread::kMaxThreadDynamicDataCount.
  421. for(int i = 0; i < kOverflowDynamicDataCount; i++)
  422. {
  423. EA::Thread::ThreadId id = 0;
  424. EA::Thread::SysThreadId sysId = 0;
  425. MakeThread([&]
  426. {
  427. id = EA::Thread::GetThreadId();
  428. sysId = EA::Thread::GetSysThreadId();
  429. })
  430. .WaitForEnd();
  431. EATEST_VERIFY(FindThreadDynamicData(id) == nullptr);
  432. EATEST_VERIFY(FindThreadDynamicData(sysId) == nullptr);
  433. }
  434. return nErrorCount;
  435. }
  436. int TestSetThreadProcessor()
  437. {
  438. // Exercise EA::Thread::GetThreadId, EA::Thread::GetSysThreadId, EAThreadGetUniqueId, SetThreadProcessor, GetThreadProcessor.
  439. // Create and start N threads paused.
  440. // Release all threads to run at once.
  441. // Have each of the threads record its EAThreadGetUniqueId value and exit.
  442. // Verify that there were no collisions in the recorded id values.
  443. int nErrorCount = 0;
  444. struct IdTestThread : public EA::Thread::IRunnable
  445. {
  446. EA::Thread::Thread mThread; // The Thread object.
  447. EA::Thread::Semaphore mSemaphore; // Used to pause the thread after it starts.
  448. EA::Thread::ThreadUniqueId mUniqueId; // The EAThreadUniqueId that this thread gets assigned by the OS.
  449. EA::Thread::ThreadId mThreadId; // The EAThreadUniqueId that this thread gets assigned by the OS.
  450. EA::Thread::SysThreadId mSysThreadId; // The EAThreadUniqueId that this thread gets assigned by the OS.
  451. int mAssignedProcessorId; // The processor id that we ask the OS to run this thread on.
  452. int mProcessorId; // The processor id that this thread gets assigned by the OS. Should equal mAssignedProcessorId.
  453. IdTestThread() : mThread(), mSemaphore(0), mUniqueId(), mThreadId(), mSysThreadId(), mAssignedProcessorId(), mProcessorId() {}
  454. IdTestThread(const IdTestThread&){} // Avoid compiler warnings.
  455. void operator=(const IdTestThread&){} // Avoid compiler warnings.
  456. intptr_t Run(void*)
  457. {
  458. mSemaphore.Wait();
  459. EAThreadGetUniqueId(mUniqueId);
  460. mThreadId = EA::Thread::GetThreadId();
  461. mSysThreadId = EA::Thread::GetSysThreadId();
  462. mProcessorId = EA::Thread::GetThreadProcessor();
  463. EAWriteBarrier();
  464. return 0;
  465. }
  466. };
  467. #if defined(EA_PLATFORM_DESKTOP)
  468. const int kThreadCount = 100;
  469. #elif (EA_PLATFORM_WORD_SIZE == 8)
  470. const int kThreadCount = 50;
  471. #else
  472. const int kThreadCount = 16;
  473. #endif
  474. IdTestThread thread[kThreadCount];
  475. ThreadParameters threadParams;
  476. EA::UnitTest::RandGenT<int> random(EA::UnitTest::GetRandSeed());
  477. const int processorCount = EA::Thread::GetProcessorCount();
  478. for(int i = 0; i < kThreadCount; i++)
  479. {
  480. threadParams.mnProcessor = random(processorCount);
  481. threadParams.mpName = "IdTest";
  482. thread[i].mAssignedProcessorId = threadParams.mnProcessor;
  483. thread[i].mThread.Begin(&thread[i], NULL, &threadParams);
  484. }
  485. EA::UnitTest::ThreadSleep(1000);
  486. for(int i = 0; i < kThreadCount; i++)
  487. thread[i].mSemaphore.Post(1);
  488. EA::UnitTest::ThreadSleep(1000);
  489. for(int i = 0; i < kThreadCount; i++)
  490. thread[i].mThread.WaitForEnd();
  491. EAReadBarrier();
  492. EA::Thread::ThreadUniqueId uniqueIdArray[kThreadCount];
  493. EA::Thread::ThreadId idArray[kThreadCount];
  494. EA::Thread::SysThreadId sysIdArray[kThreadCount];
  495. // Problem: We don't have an EAThreadEqual(const ThreadId&, const ThreadId&) function, but could use one.
  496. // If we had such a thing, then we wouldn't need the odd code below and could probably use an eastl::set.
  497. for(int i = 0; i < kThreadCount; i++)
  498. {
  499. memset(&uniqueIdArray[i], 0, sizeof(EA::Thread::ThreadUniqueId));
  500. memset(&idArray[i], 0, sizeof(EA::Thread::ThreadId));
  501. memset(&sysIdArray[i], 0, sizeof(EA::Thread::SysThreadId));
  502. }
  503. for(int i = 0; i < kThreadCount; i++)
  504. {
  505. for(int j = 0; j < i; j++)
  506. EATEST_VERIFY(memcmp(&uniqueIdArray[j], &thread[i].mUniqueId, sizeof(EA::Thread::ThreadUniqueId)) != 0);
  507. uniqueIdArray[i] = thread[i].mUniqueId;
  508. for(int j = 0; j < i; j++)
  509. EATEST_VERIFY(memcmp(&idArray[j], &thread[i].mThreadId, sizeof(EA::Thread::ThreadId)) != 0);
  510. idArray[i] = thread[i].mThreadId;
  511. for(int j = 0; j < i; j++)
  512. EATEST_VERIFY(memcmp(&sysIdArray[j], &thread[i].mSysThreadId, sizeof(EA::Thread::SysThreadId)) != 0);
  513. sysIdArray[i] = thread[i].mSysThreadId;
  514. // The following will fail on some platforms, as they don't support assigning
  515. // thread affinity (e.g. PS3) or don't respect the assigned thread affinity (e.g. Windows).
  516. // To consider: make a define which identifies which platforms rigidly follow thread processor assignments.
  517. // On Windows, EAThread doesn't use SetThreadAffinityMask but rather uses SetThreadIdealProcessor,
  518. // which doesn't guarantee which processor the thread will run on and rather is a hint.
  519. #if defined(EA_PLATFORM_CONSOLE) && defined(EA_PLATFORM_MICROSOFT) // To do: add platforms to this list appropriately.
  520. EATEST_VERIFY_F(thread[i].mProcessorId == thread[i].mAssignedProcessorId,
  521. " Error: Thread assigned to run on processor %d, found to be running on processor %d.",
  522. thread[i].mAssignedProcessorId, thread[i].mProcessorId);
  523. #endif
  524. }
  525. return nErrorCount;
  526. }
  527. int TestThreadDisablePriorityBoost()
  528. {
  529. int nErrorCount = 0;
  530. #if defined(EA_PLATFORM_WINDOWS) || defined(EA_PLATFORM_XBOXONE)
  531. {
  532. Thread thread;
  533. ThreadParameters params;
  534. auto priorityBoostTester = [&](BOOL expectedDisablePriorityBoost)
  535. {
  536. thread.Begin(
  537. [](void* pInExpectedDisablePriorityBoost) -> intptr_t
  538. {
  539. BOOL* pExpectedDisablePriorityBoost = (BOOL*)pInExpectedDisablePriorityBoost;
  540. int nErrorCount = 0;
  541. PBOOL pDisablePriorityBoost = nullptr;
  542. auto result = GetThreadPriorityBoost(GetCurrentThread(), pDisablePriorityBoost);
  543. EATEST_VERIFY_MSG(result != 0, "GetThreadPriorityBoost failed\n");
  544. EATEST_VERIFY_MSG((result != 0) && *pDisablePriorityBoost == *pExpectedDisablePriorityBoost,
  545. "Thread Priority Boost was not disabled\n");
  546. return nErrorCount;
  547. },
  548. &expectedDisablePriorityBoost, &params);
  549. intptr_t threadErrorCount = 0;
  550. thread.WaitForEnd(GetThreadTime() + 30000, &threadErrorCount);
  551. nErrorCount += (int)threadErrorCount;
  552. };
  553. params.mbDisablePriorityBoost = true;
  554. priorityBoostTester(TRUE);
  555. params.mbDisablePriorityBoost = false;
  556. priorityBoostTester(FALSE);
  557. }
  558. #endif
  559. return nErrorCount;
  560. }
  561. int TestThreadParameters()
  562. { // Test ThreadParameters
  563. int nErrorCount = 0;
  564. Thread::Status status;
  565. const int kThreadCount(kMaxConcurrentThreadCount - 1);
  566. ThreadId threadId[kThreadCount];
  567. Thread thread[kThreadCount];
  568. int i;
  569. ThreadParameters threadParameters;
  570. const int nOriginalPriority = GetThreadPriority();
  571. // Set our thread priority to match that of the threads we will be creating below.
  572. SetThreadPriority(kThreadPriorityDefault + 1);
  573. for(i = 0; i < kThreadCount; i++)
  574. {
  575. status = thread[i].GetStatus();
  576. EATEST_VERIFY_MSG(status == Thread::kStatusNone, "Thread failure: Thread should have kStatusNone (2).\n");
  577. // ThreadParameters
  578. threadParameters.mnStackSize = 32768;
  579. threadParameters.mnPriority = kThreadPriorityDefault + 1;
  580. threadParameters.mpName = "abcdefghijklmnopqrstuvwxyz"; // Make an overly large name.
  581. threadId[i] = thread[i].Begin(TestFunction1, NULL, &threadParameters);
  582. EATEST_VERIFY_MSG(threadId[i] != (ThreadId)kThreadIdInvalid, "Thread failure: ThreadBegin failed.\n");
  583. // It turns out that you can't really do such a thing as set lower priority in native Linux, not with SCHED_OTHER, at least.
  584. #if ((!defined(EA_PLATFORM_UNIX) && !defined(__APPLE__)) || defined(__CYGWIN__)) && !EA_USE_CPP11_CONCURRENCY
  585. EATEST_VERIFY_MSG(thread[i].GetPriority() == threadParameters.mnPriority, "Thread failure: Thread Priority not set correctly (2).\n");
  586. #endif
  587. if(i > 0)
  588. EATEST_VERIFY_MSG(threadId[i] != threadId[i-1], "Thread failure: Thread id collision (2).\n");
  589. }
  590. ThreadSleep(200);
  591. for(i = 0; i < kThreadCount; i++)
  592. {
  593. if(threadId[i] != kThreadIdInvalid)
  594. thread[i].SetName("0123456789012345678901234567890"); // Make an overly large name.
  595. }
  596. ThreadSleep(200);
  597. // It turns out that you can't really do such a thing as set lower priority in native Linux.
  598. #if ((!defined(EA_PLATFORM_UNIX) || defined(__CYGWIN__)) && !defined(__APPLE__) && !EA_USE_CPP11_CONCURRENCY)
  599. int nPriority;
  600. if(threadId[0] != kThreadIdInvalid)
  601. {
  602. nPriority = thread[0].GetPriority();
  603. thread[0].SetPriority(nPriority - 1);
  604. EATEST_VERIFY_MSG(thread[0].GetPriority() == nPriority - 1, "Thread failure: Thread Priority not set correctly (3.1).\n");
  605. thread[0].SetPriority(nPriority);
  606. EATEST_VERIFY_MSG(thread[0].GetPriority() == nPriority, "Thread failure: Thread Priority not set correctly (3.2).\n");
  607. }
  608. #endif
  609. for(i = 0; i < kThreadCount; i++)
  610. {
  611. if(threadId[i] != kThreadIdInvalid)
  612. thread[i].WaitForEnd(GetThreadTime() + 30000);
  613. }
  614. SetThreadPriority(kThreadPriorityDefault);
  615. for(i = 0; i < kThreadCount; i++)
  616. {
  617. status = thread[i].GetStatus();
  618. if(threadId[i] != kThreadIdInvalid)
  619. EATEST_VERIFY_MSG(status == Thread::kStatusEnded, "Thread failure: Thread should have kStatusEnded (2).\n");
  620. else
  621. EATEST_VERIFY_MSG(status == Thread::kStatusNone, "Thread failure: Thread should have kStatusNone (2).\n");
  622. }
  623. SetThreadPriority(nOriginalPriority);
  624. return nErrorCount;
  625. }
  626. int TestThreadEnd()
  627. {
  628. int nErrorCount(0);
  629. EA::Thread::Thread thread;
  630. Thread::Status status;
  631. thread.Begin(TestFunction13);
  632. ThreadSleep(20);
  633. intptr_t returncode;
  634. thread.WaitForEnd();
  635. status = thread.GetStatus(&returncode);
  636. EATEST_VERIFY_MSG(returncode == 42, "Thread return code failure: Expected return code 42.");
  637. EATEST_VERIFY_MSG(status == Thread::kStatusEnded, "Thread failure: Thread should have kStatusEnded.\n");
  638. return nErrorCount;
  639. }
  640. int TestThreadThread()
  641. {
  642. int nErrorCount(0);
  643. sThreadTestTimeMS = (gTestLengthSeconds * 1000) / 2; // '/2' because this test doesn't need so much time.
  644. nErrorCount += TestThreadAffinityMask();
  645. nErrorCount += TestThreadEnd();
  646. {
  647. ThreadId threadId = GetThreadId();
  648. EATEST_VERIFY_MSG(threadId != kThreadIdInvalid, "GetThreadId failure.\n");
  649. SysThreadId sysThreadId = GetSysThreadId(threadId);
  650. EATEST_VERIFY_MSG(sysThreadId != kSysThreadIdInvalid, "GetSysThreadId failure.\n");
  651. #if (defined(EA_PLATFORM_MICROSOFT) || defined(EA_PLATFORM_UNIX) || defined(EA_PLATFORM_PS4)) && !EA_USE_CPP11_CONCURRENCY
  652. const void* pStackBase = GetThreadStackBase();
  653. const void* pStackTop = &pStackBase;
  654. const intptr_t stackSize = ((char*)pStackBase - (char*)pStackTop);
  655. // Verify that pStackBase is non-NULL and that the stack size is less than N MB.
  656. EATEST_VERIFY_MSG(pStackBase && (stackSize < 10485760), "GetThreadStackBase failure.\n");
  657. #endif
  658. // We disable this test for now on Kettle because although we have 7 cores available
  659. // there is no guaranty the their ID are 0..6 and the system takes core ID 7
  660. #if !defined(EA_PLATFORM_PS4)
  661. int processorCount = GetProcessorCount();
  662. int processor = GetThreadProcessor();
  663. // This isn't much of a test, but it at least exercizes the function.
  664. EATEST_VERIFY_F(processor < processorCount, " Error: GetThreadProcessor [%d] >= GetProcessorCount [%d].\n", processor, processorCount);
  665. #endif
  666. // To do: Test this:
  667. // void SetThreadProcessor(int nProcessor);
  668. }
  669. { // Test Current thread functionality
  670. const int nOriginalPriority = GetThreadPriority();
  671. int nPriority = kThreadPriorityDefault;
  672. EATEST_VERIFY_MSG(nPriority >= kThreadPriorityMin, "Thread priority failure (1).\n");
  673. EATEST_VERIFY_MSG(nPriority <= kThreadPriorityMax, "Thread priority failure (2).\n");
  674. // It turns out that you can't really do such a thing as set lower priority with most Unix threading subsystems.
  675. // You can do so with Cygwin because it is just a pthreads API running on Windows OS/threading.
  676. // C++11 thread libraries also provide no means to set or query thread priority.
  677. #if (!defined(EA_PLATFORM_UNIX) || defined(__CYGWIN__)) && !EA_USE_CPP11_CONCURRENCY
  678. int nPriority1;
  679. bool bResult;
  680. bResult = SetThreadPriority(nPriority);
  681. EATEST_VERIFY_MSG(bResult, "Thread priority failure (3).\n");
  682. bResult = SetThreadPriority(nPriority - 1);
  683. EATEST_VERIFY_MSG(bResult, "Thread priority failure (4).\n");
  684. nPriority1 = GetThreadPriority();
  685. EATEST_VERIFY_MSG(nPriority1 == nPriority - 1, "Thread priority failure (5).\n");
  686. bResult = SetThreadPriority(nPriority + 1);
  687. EATEST_VERIFY_MSG(bResult, "Thread priority failure (6).\n");
  688. nPriority1 = GetThreadPriority();
  689. EATEST_VERIFY_MSG(nPriority1 == nPriority + 1, "Thread priority failure (7).\n");
  690. bResult = SetThreadPriority(kThreadPriorityDefault);
  691. EATEST_VERIFY_MSG(bResult, "Thread priority failure (8).\n");
  692. nPriority1 = GetThreadPriority();
  693. EATEST_VERIFY_MSG(nPriority1 == kThreadPriorityDefault, "Thread priority failure (9).\n");
  694. #endif
  695. SetThreadPriority(nOriginalPriority);
  696. ThreadSleep(kTimeoutImmediate);
  697. ThreadSleep(500);
  698. }
  699. #if defined(EA_PLATFORM_WINDOWS) && !EA_USE_CPP11_CONCURRENCY
  700. { // Try to reproduce Windows problem with Thread::GetStatus returning kStatusEnded when it should return kStatusRunning.
  701. // On my current work machine (WinXP32, Single P4 CPU) this problem doesn't occur. But it might occur with others.
  702. Thread::Status status;
  703. Thread threadBackground[8];
  704. Thread thread;
  705. for(int i = 0; i < 8; i++)
  706. threadBackground[i].Begin(TestFunction1);
  707. EA::Thread::SetThreadPriority(kThreadPriorityDefault + 2);
  708. thread.Begin(TestFunction1);
  709. status = thread.GetStatus();
  710. EA::Thread::SetThreadPriority(kThreadPriorityDefault);
  711. EATEST_VERIFY_MSG(status == Thread::kStatusRunning, "Thread failure: Thread should have kStatusRunning.\n");
  712. thread.WaitForEnd();
  713. }
  714. #endif
  715. EA::Thread::Thread::SetGlobalRunnableFunctionUserWrapper(TestFunction3ExceptionWrapper);
  716. EA::Thread::Thread::SetGlobalRunnableClassUserWrapper(TestRunnable3ExceptionWrapper);
  717. { // Test thread creation functionality.
  718. Thread::Status status;
  719. const int kThreadCount(kMaxConcurrentThreadCount - 1);
  720. Thread thread[kThreadCount];
  721. ThreadId threadId[kThreadCount];
  722. int i;
  723. for(i = 0; i < kThreadCount; i++)
  724. {
  725. status = thread[i].GetStatus();
  726. EATEST_VERIFY_MSG(status == Thread::kStatusNone, "Thread failure: Thread should have kStatusNone (1).\n");
  727. threadId[i] = thread[i].Begin(TestFunction1);
  728. EATEST_VERIFY_MSG(threadId[i] != kThreadIdInvalid, "Thread failure: ThreadBegin(TestFunction1) failed.\n");
  729. threadId[i] = thread[i].Begin(TestFunction3);
  730. EATEST_VERIFY_MSG(threadId[i] != kThreadIdInvalid, "Thread failure: ThreadBegin(TestFunction3) failed.\n");
  731. threadId[i] = thread[i].Begin(&gTestRunnable3);
  732. EATEST_VERIFY_MSG(threadId[i] != kThreadIdInvalid, "Thread failure: ThreadBegin(&gTestRunnable3) failed.\n");
  733. if(i > 0)
  734. EATEST_VERIFY_MSG(threadId[i] != threadId[i-1], "Thread failure: Thread id collision (1).\n");
  735. }
  736. // It turns out that you can't really do such a thing as set lower priority in native Linux.
  737. // C++11 threads also have no support for priorities
  738. #if (!defined(EA_PLATFORM_UNIX) || defined(__CYGWIN__)) && !EA_USE_CPP11_CONCURRENCY
  739. int nPriority;
  740. nPriority = thread[0].GetPriority();
  741. thread[0].SetPriority(nPriority - 1);
  742. nPriority = thread[0].GetPriority();
  743. thread[0].SetPriority(nPriority + 1);
  744. #endif
  745. ThreadSleep(200);
  746. for(i = 0; i < kThreadCount; i++)
  747. {
  748. if(threadId[i] != kThreadIdInvalid)
  749. thread[i].WaitForEnd(GetThreadTime() + 30000);
  750. }
  751. for(i = 0; i < kThreadCount; i++)
  752. {
  753. if(threadId[i] != kThreadIdInvalid)
  754. {
  755. status = thread[i].GetStatus();
  756. EATEST_VERIFY_MSG(status == Thread::kStatusEnded, "Thread failure: Thread should have kStatusEnded (1).\n");
  757. }
  758. }
  759. }
  760. nErrorCount += TestThreadParameters();
  761. {
  762. // Test if we can set and retrieve a custom thread name
  763. Thread::Status status;
  764. Thread thread;
  765. ThreadId threadId;
  766. const char* threadName = "Test_Thread";
  767. const char* defaultName = "DEFAULT";
  768. ThreadParameters threadParameters;
  769. threadParameters.mpName = defaultName;
  770. sThreadTestTimeMS = 10000;
  771. threadId = thread.Begin(TestFunction1, NULL, &threadParameters);
  772. EATEST_VERIFY_MSG(threadId != kThreadIdInvalid, "Thread failure: ThreadBegin(TestFunction1) failed.\n");
  773. EATEST_VERIFY_MSG(strncmp(defaultName, thread.GetName(), EATHREAD_NAME_SIZE) == 0, "Thread failure: GetName should return the name used when initializing the thread.");
  774. thread.SetName(threadName);
  775. EATEST_VERIFY_MSG(strncmp(threadName, thread.GetName(), EATHREAD_NAME_SIZE) == 0, "Thread failure: GetName should return the name set in SetName.");
  776. ThreadSleep(sThreadTestTimeMS + 1000);
  777. if(threadId != kThreadIdInvalid)
  778. {
  779. thread.WaitForEnd(GetThreadTime() + 30000);
  780. status = thread.GetStatus();
  781. EATEST_VERIFY_MSG(status == Thread::kStatusEnded, "Thread failure: Thread should have kStatusEnded (3).\n");
  782. }
  783. }
  784. // Test the free standing function variant of the GetName/SetName API
  785. {
  786. // Test if we can set and retrieve a custom thread name
  787. Thread::Status status;
  788. Thread thread;
  789. ThreadId threadId;
  790. const char* threadName = "Test_Thread";
  791. const char* defaultName = "DEFAULT";
  792. ThreadParameters threadParameters;
  793. threadParameters.mpName = defaultName;
  794. threadParameters.mnProcessor = EA::Thread::kProcessorAny;
  795. threadParameters.mnAffinityMask = EA::Thread::kThreadAffinityMaskAny;
  796. static volatile std::atomic<bool> sbThreadStarted;
  797. static volatile std::atomic<bool> sbThreadTestDone;
  798. sbThreadStarted = false;
  799. sbThreadTestDone = false;
  800. threadId = thread.Begin( [](void*) -> intptr_t
  801. {
  802. sbThreadStarted = true;
  803. while (!sbThreadTestDone)
  804. ThreadSleep();
  805. return 0;
  806. },
  807. NULL, &threadParameters);
  808. while(!sbThreadStarted) // Wait for thread to start up
  809. ThreadSleep();
  810. EATEST_VERIFY_MSG(threadId != kThreadIdInvalid, "Thread failure: ThreadBegin(TestFunction1) failed.\n");
  811. EATEST_VERIFY_MSG(strncmp(defaultName, GetThreadName(threadId), EATHREAD_NAME_SIZE) == 0, "Thread failure: GetName should return the name used when initializing the thread.");
  812. SetThreadName(threadId, threadName);
  813. EATEST_VERIFY_MSG(strncmp(threadName, GetThreadName(threadId), EATHREAD_NAME_SIZE) == 0, "Thread failure: GetName should return the name set in SetName.");
  814. sbThreadTestDone = true; // signal that test is completed and the thread can shutdown
  815. if(threadId != kThreadIdInvalid)
  816. {
  817. thread.WaitForEnd(GetThreadTime() + 30000);
  818. status = thread.GetStatus();
  819. EATEST_VERIFY_MSG(status == Thread::kStatusEnded, "Thread failure: Thread should have kStatusEnded (3).\n");
  820. }
  821. }
  822. {
  823. // Test the creation+destruction of many threads to make sure resources are recycled properly
  824. #if defined(EA_PLATFORM_DESKTOP)
  825. const int kThreadCount(200);
  826. #else
  827. const int kThreadCount(20);
  828. #endif
  829. Thread thread;
  830. ThreadId threadId;
  831. intptr_t returnValue;
  832. EA::UnitTest::ReportVerbosity(1, "Creating many threads and then WaitForEnd()\n");
  833. for(int i = 0; i < kThreadCount; i++)
  834. {
  835. threadId = thread.Begin(TestFunction4, reinterpret_cast<void*>((uintptr_t)i));
  836. Thread::Status status = thread.GetStatus(&returnValue);
  837. if(status != Thread::kStatusEnded)
  838. thread.WaitForEnd(GetThreadTime() + 30000, &returnValue);
  839. EA::UnitTest::ReportVerbosity(1, "Thread ended.\n");
  840. EATEST_VERIFY_F(returnValue == i, "Thread failure: Thread return code is wrong (1). threadId: %s, status: %d, returnValue: %d\n", EAThreadThreadIdToString(threadId), (int)status, (int)returnValue);
  841. if(returnValue != i)
  842. EA::UnitTest::ReportVerbosity(1, " Expected: %u, actual: %" PRId64 ".\n", (unsigned)i, (int64_t)returnValue);
  843. // Get the status again to make sure it returns the correct status.
  844. thread.GetStatus(&returnValue);
  845. EATEST_VERIFY_F(returnValue == i, "Thread failure: Thread return code is wrong (2). threadId: %s, status: %d, returnValue: %d\n", EAThreadThreadIdToString(threadId), (int)status, (int)returnValue);
  846. if(returnValue != i)
  847. EA::UnitTest::ReportVerbosity(1, " Expected: %u, actual: %" PRId64 ".\n", (unsigned)i, (int64_t)returnValue);
  848. }
  849. EA::UnitTest::ReportVerbosity(1, "Creating many threads and then repeat GetStatus() until they finish\n");
  850. for(int i = 0; i < kThreadCount + 1; i++)
  851. {
  852. // Windows will get stuck in infinite loop if return value is 259 (STILL_ACTIVE)
  853. if(i == 259)
  854. ++i;
  855. threadId = thread.Begin(TestFunction4, reinterpret_cast<void*>((uintptr_t)i));
  856. const ThreadTime nGiveUpTime = EA::Thread::GetThreadTime() + 20000; // Give up after N milliseconds.
  857. // Test to see if GetStatus() will recycle system resource properly
  858. while(thread.GetStatus(&returnValue) != Thread::kStatusEnded)
  859. {
  860. ThreadSleep(100);
  861. if(EA::Thread::GetThreadTime() > nGiveUpTime)
  862. {
  863. EA::UnitTest::ReportVerbosity(1, "Thread failure: GetStatus failed.\n");
  864. break;
  865. }
  866. }
  867. EATEST_VERIFY_MSG(returnValue == i, "Thread failure: Thread return code is wrong (3).\n");
  868. if(returnValue != i)
  869. EA::UnitTest::ReportVerbosity(1, " Expected: %u, actual: %" PRId64 ".\n", (unsigned)i, (int64_t)returnValue);
  870. // Get the status again to make sure it returns the correct status.
  871. thread.GetStatus(&returnValue);
  872. EATEST_VERIFY_MSG(returnValue == i, "Thread failure: Thread return code is wrong (4).\n");
  873. if(returnValue != i)
  874. EA::UnitTest::ReportVerbosity(1, " Expected: %u, actual: %" PRId64 ".\n", (unsigned)i, (int64_t)returnValue);
  875. // See if calling WaitForEnd() now will result in a crash
  876. thread.WaitForEnd(GetThreadTime() + 30000, &returnValue);
  877. EATEST_VERIFY_MSG(returnValue == i, "Thread failure: Thread return code is wrong (5).\n");
  878. }
  879. }
  880. {
  881. // Test the creation of many threads
  882. #if defined(EA_PLATFORM_DESKTOP)
  883. Thread::Status status;
  884. const int kThreadCount(96);
  885. Thread thread[kThreadCount];
  886. ThreadId threadId[kThreadCount];
  887. int i;
  888. sThreadTestTimeMS = 10000;
  889. for(i = 0; i < kThreadCount; i++)
  890. {
  891. threadId[i] = thread[i].Begin(TestFunction1);
  892. EATEST_VERIFY_MSG(threadId[i] != kThreadIdInvalid, "Thread failure: ThreadBegin(TestFunction1) failed.\n");
  893. }
  894. ThreadSleep(sThreadTestTimeMS + 1000);
  895. for(i = 0; i < kThreadCount; i++)
  896. {
  897. if(threadId[i] != kThreadIdInvalid)
  898. thread[i].WaitForEnd(GetThreadTime() + 30000);
  899. }
  900. for(i = 0; i < kThreadCount; i++)
  901. {
  902. if(threadId[i] != kThreadIdInvalid)
  903. {
  904. status = thread[i].GetStatus();
  905. EATEST_VERIFY_MSG(status == Thread::kStatusEnded, "Thread failure: Thread should have kStatusEnded (3).\n");
  906. }
  907. }
  908. #endif
  909. }
  910. {
  911. // Regression of Thread dtor behaviour - the dtor should not wait for created threads
  912. // We reuse the atomic shouldgo to figure out what is really happening setting it to 0
  913. // initially and then incrementing it when the thread callback completes, allowing
  914. // us to detect whether the dtor completed before the thread callback.
  915. sShouldGo = 0;
  916. {
  917. // Create a scope for our thread object
  918. Thread thread;
  919. // Get our thread going. It will sleep immediately for 2 seconds and then increment sShouldGo
  920. ThreadId threadId = thread.Begin(TestFunction8);
  921. EATEST_VERIFY_MSG(threadId != kThreadIdInvalid, "Thread failure: thread.Begin(TestFunction8) failed.\n");
  922. // Now we exit our scope while our thread in theory still has a second before it completes
  923. } // NOTE(rparolin): If your test hangs here, its most likely due to a semantic change in the thread object that is waiting for threads to complete.
  924. // Signal to the thread it is allowed to complete.
  925. sShouldGo = 1;
  926. }
  927. {
  928. int nOriginalPriority = EA::Thread::GetThreadPriority();
  929. EA::Thread::SetThreadPriority(kThreadPriorityDefault);
  930. // Tests setting and getting thread priority while the thread is active.
  931. Thread::Status status;
  932. Thread thread;
  933. ThreadId threadId;
  934. sShouldGo = 0;
  935. threadId = thread.Begin(TestFunction7);
  936. EATEST_VERIFY_MSG(threadId != kThreadIdInvalid, "Thread failure: ThreadBegin(TestFunction7) failed.\n");
  937. // It turns out that you can't really do such a thing as set lower priority in native Linux.
  938. #if ((!defined(EA_PLATFORM_LINUX) && !defined(__APPLE__) && !defined(EA_PLATFORM_BSD)) || defined(__CYGWIN__)) && !EA_USE_CPP11_CONCURRENCY
  939. int nPriority = thread.GetPriority();
  940. thread.SetPriority(nPriority - 1);
  941. if(EATEST_VERIFY_MSG(thread.GetPriority() == nPriority - 1, "Thread failure: Thread Priority not set correctly (2).\n"))
  942. nErrorCount++;
  943. thread.SetPriority(nPriority);
  944. EATEST_VERIFY_MSG(thread.GetPriority() == nPriority, "Thread failure: Thread Priority not set correctly (3).\n");
  945. #endif
  946. sShouldGo = 1;
  947. thread.WaitForEnd(GetThreadTime() + 30000);
  948. status = thread.GetStatus();
  949. EATEST_VERIFY_MSG(status == Thread::kStatusEnded, "Thread failure: Thread should have kStatusEnded.\n");
  950. EA::Thread::SetThreadPriority(nOriginalPriority);
  951. }
  952. nErrorCount += TestThreadDynamicData();
  953. nErrorCount += TestThreadPriorities();
  954. nErrorCount += TestSetThreadProcessor();
  955. nErrorCount += TestSetThreadProcessConstants();
  956. nErrorCount += TestNullThreadNames();
  957. nErrorCount += TestLambdaThreads();
  958. {
  959. // Test SetDefaultProcessor
  960. Thread::SetDefaultProcessor(kProcessorAny);
  961. }
  962. #if !defined(EA_PLATFORM_MOBILE)
  963. // This test does not execute correctly on Android. The 'while(sThreadCount
  964. // < kThreadCount)' loop sometimes hangs indefinitely because the spawned
  965. // threads exit often before the loop continues, so the spawned
  966. // thread count never gets up to kThreadCount. I guess the inner loop
  967. // should be testing addedThreadCount too. (?) But, just disable for now,
  968. // since I guess it's working on other platforms.
  969. {
  970. // Test very many threads starting and quitting, recycling
  971. // The PS3 code had problems with leaking threads in this case.
  972. // Create a bunch of threads.
  973. // Every N ms quit one of them and create a new one.
  974. // This test tends to be quite taxing on system resources, so cut it back for
  975. // certain platforms. To do: use EATest's platform speed metrics.
  976. const int kThreadCount(48); // This needs to be greater than the eathread_thread.cpp kMaxThreadDynamicDataCount value.
  977. const int kTotalThreadsToRun(500);
  978. Thread thread;
  979. ThreadId threadId;
  980. int addedThreadCount = 0;
  981. int i = 0;
  982. sThreadCount = 0;
  983. sShouldGo = 0;
  984. // Create threads.
  985. for(i = 0; i < kThreadCount; i++)
  986. {
  987. threadId = thread.Begin(TestFunction6, reinterpret_cast<void*>((uintptr_t)i));
  988. EATEST_VERIFY(threadId != kThreadIdInvalid);
  989. }
  990. // Wait until they are all created and started.
  991. while(sThreadCount < kThreadCount)
  992. ThreadSleep(500);
  993. // Let them run and exit as they go.
  994. sShouldGo = 1;
  995. // Add new threads as existing ones leave.
  996. while(addedThreadCount < kTotalThreadsToRun)
  997. {
  998. if(sThreadCount < kThreadCount)
  999. {
  1000. threadId = thread.Begin(TestFunction6, reinterpret_cast<void*>((uintptr_t)i++));
  1001. EATEST_VERIFY(threadId != kThreadIdInvalid);
  1002. addedThreadCount++;
  1003. #if defined(EA_PLATFORM_DESKTOP)
  1004. // The created threads will not get any time slices on weaker platforms.
  1005. // thus making the test create threads until the system is out of resources
  1006. ThreadSleep(kTimeoutYield);
  1007. // Sometimes it will never exit this loop.. because it will leave faster then it is made
  1008. if(addedThreadCount >= kTotalThreadsToRun)
  1009. break;
  1010. #endif
  1011. }
  1012. else
  1013. ThreadSleep(10);
  1014. }
  1015. // Wait until they have all exited.
  1016. while(sThreadCount != 0) // While there are threads...
  1017. ThreadSleep(500);
  1018. // On some platforms it seems that thread entry/exit is so slow that the thread count might not necessarily
  1019. // reflect the actual number of threads running. This was causing an issue where the function was
  1020. // exiting before all threads completed, so I added this wait. It may be worth considering
  1021. // keeping track of all created threads in an array, and then using a thread-join operation here
  1022. // instead
  1023. ThreadSleep(1000);
  1024. }
  1025. #endif
  1026. return nErrorCount;
  1027. }