TestThreadThread.cpp 43 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345
  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. ThreadSleep(2000);
  69. ++sShouldGo;
  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_CAPILANO)
  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_KETTLE)) && !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_KETTLE)
  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. // Give the thread a nominal second to get going and ensure we get a decent overlap of a second.
  923. ThreadSleep(1000);
  924. // Now we exit our scope while our thread in theory still has a second before it completes
  925. }
  926. // We either get here before the thread function completes in which case sShouldGo is 0 or
  927. // we get here after it completes which would be incorrect and would give us a sShouldGo of 1
  928. const bool threadScopeSemanticsVerified = sShouldGo == 0;
  929. // Rather than try to wait on threadid in a cross platform way we simply sleep for 2 seconds which
  930. // we know should be sufficient for the thread to complete. We can make this part of the test
  931. // as shouldgo should be 1 when the thread has completed.
  932. ThreadSleep(2000);
  933. EATEST_VERIFY_MSG(threadScopeSemanticsVerified, "Thread failure: Thread should not have ended before Thread object dtor completed. Behaviour is inconsistent with intent.\n");
  934. EATEST_VERIFY_MSG(sShouldGo == 1, "Thread failure: Thread set to run for 2 seconds at least 3 seconds ago. Should have ended by now.\n");
  935. }
  936. {
  937. int nOriginalPriority = EA::Thread::GetThreadPriority();
  938. EA::Thread::SetThreadPriority(kThreadPriorityDefault);
  939. // Tests setting and getting thread priority while the thread is active.
  940. Thread::Status status;
  941. Thread thread;
  942. ThreadId threadId;
  943. sShouldGo = 0;
  944. threadId = thread.Begin(TestFunction7);
  945. EATEST_VERIFY_MSG(threadId != kThreadIdInvalid, "Thread failure: ThreadBegin(TestFunction7) failed.\n");
  946. // It turns out that you can't really do such a thing as set lower priority in native Linux.
  947. #if ((!defined(EA_PLATFORM_LINUX) && !defined(__APPLE__) && !defined(EA_PLATFORM_BSD)) || defined(__CYGWIN__)) && !EA_USE_CPP11_CONCURRENCY
  948. int nPriority = thread.GetPriority();
  949. thread.SetPriority(nPriority - 1);
  950. if(EATEST_VERIFY_MSG(thread.GetPriority() == nPriority - 1, "Thread failure: Thread Priority not set correctly (2).\n"))
  951. nErrorCount++;
  952. thread.SetPriority(nPriority);
  953. EATEST_VERIFY_MSG(thread.GetPriority() == nPriority, "Thread failure: Thread Priority not set correctly (3).\n");
  954. #endif
  955. sShouldGo = 1;
  956. thread.WaitForEnd(GetThreadTime() + 30000);
  957. status = thread.GetStatus();
  958. EATEST_VERIFY_MSG(status == Thread::kStatusEnded, "Thread failure: Thread should have kStatusEnded.\n");
  959. EA::Thread::SetThreadPriority(nOriginalPriority);
  960. }
  961. nErrorCount += TestThreadDynamicData();
  962. nErrorCount += TestThreadPriorities();
  963. nErrorCount += TestSetThreadProcessor();
  964. nErrorCount += TestSetThreadProcessConstants();
  965. nErrorCount += TestNullThreadNames();
  966. nErrorCount += TestLambdaThreads();
  967. {
  968. // Test SetDefaultProcessor
  969. Thread::SetDefaultProcessor(kProcessorAny);
  970. }
  971. #if !defined(EA_PLATFORM_MOBILE)
  972. // This test does not execute correctly on Android. The 'while(sThreadCount
  973. // < kThreadCount)' loop sometimes hangs indefinitely because the spawned
  974. // threads exit often before the loop continues, so the spawned
  975. // thread count never gets up to kThreadCount. I guess the inner loop
  976. // should be testing addedThreadCount too. (?) But, just disable for now,
  977. // since I guess it's working on other platforms.
  978. {
  979. // Test very many threads starting and quitting, recycling
  980. // The PS3 code had problems with leaking threads in this case.
  981. // Create a bunch of threads.
  982. // Every N ms quit one of them and create a new one.
  983. // This test tends to be quite taxing on system resources, so cut it back for
  984. // certain platforms. To do: use EATest's platform speed metrics.
  985. const int kThreadCount(48); // This needs to be greater than the eathread_thread.cpp kMaxThreadDynamicDataCount value.
  986. const int kTotalThreadsToRun(500);
  987. Thread thread;
  988. ThreadId threadId;
  989. int addedThreadCount = 0;
  990. int i = 0;
  991. sThreadCount = 0;
  992. sShouldGo = 0;
  993. // Create threads.
  994. for(i = 0; i < kThreadCount; i++)
  995. {
  996. threadId = thread.Begin(TestFunction6, reinterpret_cast<void*>((uintptr_t)i));
  997. EATEST_VERIFY(threadId != kThreadIdInvalid);
  998. }
  999. // Wait until they are all created and started.
  1000. while(sThreadCount < kThreadCount)
  1001. ThreadSleep(500);
  1002. // Let them run and exit as they go.
  1003. sShouldGo = 1;
  1004. // Add new threads as existing ones leave.
  1005. while(addedThreadCount < kTotalThreadsToRun)
  1006. {
  1007. if(sThreadCount < kThreadCount)
  1008. {
  1009. threadId = thread.Begin(TestFunction6, reinterpret_cast<void*>((uintptr_t)i++));
  1010. EATEST_VERIFY(threadId != kThreadIdInvalid);
  1011. addedThreadCount++;
  1012. #if defined(EA_PLATFORM_DESKTOP)
  1013. // The created threads will not get any time slices on weaker platforms.
  1014. // thus making the test create threads until the system is out of resources
  1015. ThreadSleep(kTimeoutYield);
  1016. // Sometimes it will never exit this loop.. because it will leave faster then it is made
  1017. if(addedThreadCount >= kTotalThreadsToRun)
  1018. break;
  1019. #endif
  1020. }
  1021. else
  1022. ThreadSleep(10);
  1023. }
  1024. // Wait until they have all exited.
  1025. while(sThreadCount != 0) // While there are threads...
  1026. ThreadSleep(500);
  1027. // On some platforms it seems that thread entry/exit is so slow that the thread count might not necessarily
  1028. // reflect the actual number of threads running. This was causing an issue where the function was
  1029. // exiting before all threads completed, so I added this wait. It may be worth considering
  1030. // keeping track of all created threads in an array, and then using a thread-join operation here
  1031. // instead
  1032. ThreadSleep(1000);
  1033. }
  1034. #endif
  1035. return nErrorCount;
  1036. }