| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345 |
- ///////////////////////////////////////////////////////////////////////////////
- // Copyright (c) Electronic Arts Inc. All rights reserved.
- ///////////////////////////////////////////////////////////////////////////////
- #include "TestThread.h"
- #include <EATest/EATest.h>
- #include <EAStdC/EAStopwatch.h>
- #include <EAStdC/EASprintf.h>
- #include <EAStdC/EAString.h>
- #include <EASTL/set.h>
- #include <eathread/eathread_thread.h>
- #include <eathread/eathread_sync.h>
- #include <eathread/eathread_condition.h>
- #ifdef EA_PLATFORM_LINUX
- #include <unistd.h>
- #endif
- #ifdef EA_PLATFORM_MICROSOFT
- EA_DISABLE_ALL_VC_WARNINGS()
- #include <Windows.h>
- EA_RESTORE_ALL_VC_WARNINGS()
- #endif
- #include <atomic>
- using namespace EA::Thread;
- const int kMaxConcurrentThreadCount = EATHREAD_MAX_CONCURRENT_THREAD_COUNT;
- static unsigned int sThreadTestTimeMS = 2000; // We potentially change this value below.
- static AtomicInt32 sThreadCount = 0;
- static AtomicInt32 sShouldGo = 0;
- static intptr_t TestFunction1(void*)
- {
- ThreadTime nTimeEnd = EA::Thread::GetThreadTime() + sThreadTestTimeMS;
- while(EA::Thread::GetThreadTime() < nTimeEnd)
- ThreadSleep();
- return 0;
- }
- static intptr_t TestFunction3(void*)
- {
- // This following code should produce NULL pointer access violation exception
- // EA::UnitTest::ReportVerbosity(1, "Throw NULL pointer Exception.\n");
- // char* pTest = NULL;
- // *pTest = 1;
- return 0;
- }
- static intptr_t TestFunction4(void* arg)
- {
- const intptr_t returnValue = (intptr_t)arg;
- EA::UnitTest::ThreadSleepRandom(0, 5);
- return returnValue;
- }
- #if !defined(EA_PLATFORM_MOBILE)
- static intptr_t TestFunction6(void* arg)
- {
- const intptr_t returnValue = (intptr_t)arg;
- sThreadCount++;
- while(sShouldGo == 0)
- ThreadSleep(10);
- EA::UnitTest::ThreadSleepRandom(3, 8);
- sThreadCount--;
- return returnValue;
- }
- #endif
- static intptr_t TestFunction7(void*)
- {
- while(sShouldGo == 0)
- ThreadSleep(10);
- return 0;
- }
- static intptr_t TestFunction8(void*)
- {
- ThreadSleep(2000);
- ++sShouldGo;
- return 0;
- }
- static intptr_t TestFunction9(void* arg)
- {
- return (intptr_t)GetThreadProcessor();
- }
- static intptr_t TestFunction10(void* arg)
- {
- ThreadSleep(10);
- return TestFunction9(arg);
- }
- static intptr_t TestFunction12(void* arg)
- {
- ThreadSleep(10);
- return TestFunction9(arg);
- }
- static intptr_t TestFunction11(void* arg)
- {
- int requestedCore = *(static_cast<int*>(arg));
- int coreIndex = (requestedCore % EA::Thread::GetProcessorCount());
- SetThreadAffinityMask(UINT64_C(1) << coreIndex); // set the highest processor available.
- for (int retryCount = 100; retryCount && (GetThreadProcessor() != coreIndex); retryCount--)
- ThreadSleep(1);
- return (intptr_t)GetThreadProcessor();
- }
- static intptr_t TestFunction13(void* arg)
- {
- ThreadSleep(10);
- ThreadEnd(42); // 42 is a magic number we will verify gets passed through the user.
- return 0;
- }
- static intptr_t TestFunction3ExceptionWrapper(RunnableFunction defaultRunnableFunction, void* pContext)
- {
- return defaultRunnableFunction(pContext);
- }
- class TestRunnable1 : public IRunnable
- {
- intptr_t Run(void*)
- {
- const ThreadTime nTimeEnd = EA::Thread::GetThreadTime() + sThreadTestTimeMS;
- while (EA::Thread::GetThreadTime() < nTimeEnd)
- ThreadSleep();
- return 0;
- }
- } gTestRunnable1;
- class TestRunnable2 : public IRunnable
- {
- intptr_t Run(void*)
- {
- const ThreadTime nTimeEnd = EA::Thread::GetThreadTime() + sThreadTestTimeMS;
- while(EA::Thread::GetThreadTime() < nTimeEnd)
- {
- ThreadSleep();
- }
- return 0;
- }
- } gTestRunnable2;
- static intptr_t TestRunnable3ExceptionWrapper(IRunnable* defaultRunnableFunction, void* pContext)
- {
- return defaultRunnableFunction->Run(pContext);
- }
- class TestRunnable3 : public IRunnable
- {
- intptr_t Run(void*)
- {
- // This following code should produce NULL pointer access violation exception
- // EA::UnitTest::ReportVerbosity(1, "Throw NULL pointer Exception.\n");
- // char* pTest = NULL;
- // *pTest = 1;
- ThreadSleep(500);
- return 0;
- }
- } gTestRunnable3;
- class TestRunnable4 : public IRunnable
- {
- intptr_t Run(void*)
- {
- // IRunnable object that returns the thread id that executed on.
- return TestFunction9(NULL);
- }
- } gTestRunnable4;
- int TestThreadAffinityMask()
- {
- int nErrorCount = 0;
- const int MAX_ITERATIONS = 16;
- const int nAvailableProcessors = EA::Thread::GetProcessorCount();
- auto VERIFY_AFFINITY_RESULT = [&](intptr_t in_result, int count)
- {
- #if EATHREAD_THREAD_AFFINITY_MASK_SUPPORTED
- auto result = static_cast<int>(in_result);
- EATEST_VERIFY_F(result == (count % nAvailableProcessors),
- "Thread failure: SetAffinityMask not working properly. Thread ran on: %d/%d <=> Expected: %d\n",
- result, nAvailableProcessors, (count % nAvailableProcessors));
- #endif
- };
- int count = MAX_ITERATIONS;
- while(--count)
- {
- // Test Thread Affinity Masks (thread parameters)
- Thread thread;
- ThreadParameters params;
- params.mnAffinityMask = INT64_C(1) << (count % nAvailableProcessors);
- params.mnProcessor = kProcessorAny;
- thread.Begin(TestFunction10, NULL, ¶ms);
- intptr_t result = 0;
- thread.WaitForEnd(GetThreadTime() + 30000, &result);
- VERIFY_AFFINITY_RESULT(result, count);
- }
- count = MAX_ITERATIONS;
- while(--count)
- {
- // Test Thread Affinity Masks (thread object)
- ThreadParameters params;
- params.mnProcessor = kProcessorAny;
- Thread thread;
- thread.Begin(TestFunction12, NULL, ¶ms); // sleeps then grabs the current thread id.
- thread.SetAffinityMask(INT64_C(1) << (count % nAvailableProcessors));
- intptr_t result = 0;
- thread.WaitForEnd(GetThreadTime() + 30000, &result);
- VERIFY_AFFINITY_RESULT(result, count);
- }
-
- count = MAX_ITERATIONS;
- while(--count)
- {
- // Test Thread Affinity Masks (global functions)
- ThreadParameters params;
- params.mnProcessor = kProcessorAny;
- Thread thread;
- thread.Begin(TestFunction11, &count, ¶ms);
- intptr_t result = 0;
- thread.WaitForEnd(GetThreadTime() + 30000, &result);
- VERIFY_AFFINITY_RESULT(result, count);
- }
- count = MAX_ITERATIONS;
- while(--count)
- {
- // Test Thread Affinity Masks (thread parameters) - For IRunnable variant of the Thread::Begin function
- ThreadParameters params;
- params.mnProcessor = kProcessorAny;
- params.mnAffinityMask = INT64_C(1) << (count % nAvailableProcessors);
- params.mnProcessor = kProcessorAny;
- Thread thread;
- thread.Begin(&gTestRunnable4, NULL, ¶ms); // sleeps then grabs the current thread id.
- intptr_t result = 0;
- thread.WaitForEnd(GetThreadTime() + 30000, &result);
- VERIFY_AFFINITY_RESULT(result, count);
- }
- return nErrorCount;
- }
- int TestThreadPriorities()
- {
- int nErrorCount = 0;
- if(!IsSuperUser())
- {
- EA::EAMain::Report("Skipping Thread Priority test because we don't have sufficient system priviliages.\n");
- return nErrorCount;
- }
- // Verify that thread priorities act as expected.
- // Threads with higher priorities should execute instead of or before threads of lower priorities.
- // On some platforms (e.g. Windows), lower priority threads do get some execution time, so we have to recognize that.
- // Create 20 threads of very high priority, 20 threads of high priority, and 20 threads of regular priority.
- // Start the 20 very high priority threads first.
- // Wait a bit then start the other 40 threads.
- // Quit all the very high priority threads.
- // Wait a bit, while having the 40 threads measure how much time they execute.
- // Quit the remaining 40 threads.
- // Verify that the 20 high priority threads executed much more than the regular threads.
- struct PriorityTestThread : public EA::Thread::IRunnable
- {
- EA::Thread::Thread mThread;
- EA::Thread::ThreadParameters mParameters;
- EA::Thread::Semaphore mSemaphore;
- char mThreadName[16];
- volatile uint64_t mCounter;
- volatile bool mbShouldRun;
- char mPadd[EA_CACHE_LINE_SIZE];// make sure that these structures end up on different cachelines
- PriorityTestThread() : mThread(), mParameters(), mSemaphore(0), mCounter(0), mbShouldRun(true) {}
- PriorityTestThread(const PriorityTestThread&){}
- void operator=(const PriorityTestThread&){}
- intptr_t Run(void*)
- {
- mSemaphore.Wait();
- while(mbShouldRun)
- {
- //char buffer[64];
- //EA::StdC::Sprintf(buffer, "%f", (double)(mCounter*1.23)); // Just waste time.
- mCounter++;
- EAReadBarrier();
- }
- return 0;
- }
- };
- if((EA::Thread::GetProcessorCount() >= 4))
- {
- const int kThreadCount = 4;
- PriorityTestThread threadHighestPriority[kThreadCount];
- PriorityTestThread threadRegularPriority[kThreadCount];
- PriorityTestThread threadHighPriority[kThreadCount];
- EA::StdC::LimitStopwatch limitStopwatch(EA::StdC::Stopwatch::kUnitsSeconds);
- const EA::Thread::ThreadAffinityMask kCommonAffinityMask = 0xf; // first 4 cores only
- EA::Thread::ThreadParameters commonParams;
- commonParams.mbDisablePriorityBoost = true; // we can disable boosting if we want a better simulation of console-like behavior
- commonParams.mnAffinityMask = kCommonAffinityMask;
- commonParams.mnProcessor = kProcessorAny;
- #if defined(EA_PLATFORM_MICROSOFT)
- // Due to how windows thread priorities work we need to further increase the thread priority of the high threads
- // If this is not done the test will randomly have the regular priority have a higher count than the high threads.
- static const int kHigherPriorityDelta = kThreadPriorityMax - 2;
- #else
- static const int kHigherPriorityDelta = 0;
- #endif
- EA::Thread::SetThreadPriority(EA::Thread::kThreadPriorityDefault + 3);
- for (int i = 0; i < kThreadCount; i++)
- {
- {
- PriorityTestThread& highestThread = threadHighestPriority[i];
- highestThread.mParameters = commonParams;
- highestThread.mParameters.mnPriority = (EA::Thread::kThreadPriorityDefault + 2 + kHigherPriorityDelta);
- EA::StdC::Sprintf(highestThread.mThreadName, "Highest%d", i);
- highestThread.mParameters.mpName = highestThread.mThreadName;
- highestThread.mThread.Begin(&highestThread, NULL, &highestThread.mParameters);
- }
- {
- PriorityTestThread& regularThread = threadRegularPriority[i];
- regularThread.mParameters = commonParams;
- regularThread.mParameters.mnPriority = (EA::Thread::kThreadPriorityDefault);
- EA::StdC::Sprintf(regularThread.mThreadName, "Reg%d", i);
- regularThread.mParameters.mpName = regularThread.mThreadName;
- regularThread.mThread.Begin(®ularThread, NULL, ®ularThread.mParameters);
- }
- {
- PriorityTestThread& highThread = threadHighPriority[i];
- highThread.mParameters = commonParams;
- highThread.mParameters.mnPriority = (EA::Thread::kThreadPriorityDefault + 1 + kHigherPriorityDelta);
- EA::StdC::Sprintf(highThread.mThreadName, "High%d", i);
- highThread.mParameters.mpName = highThread.mThreadName;
- highThread.mThread.Begin(&highThread, NULL, &highThread.mParameters);
- }
- }
- limitStopwatch.SetTimeLimit(1, true);
- while(!limitStopwatch.IsTimeUp())
- { /* Do nothing. Don't even sleep, as some platform might not give us the CPU back. */ }
- for(int i = 0; i < kThreadCount; i++)
- {
- threadHighestPriority[i].mSemaphore.Post(1);
- threadHighPriority[i].mSemaphore.Post(1);
- threadRegularPriority[i].mSemaphore.Post(1);
- }
- limitStopwatch.SetTimeLimit(3, true);
- while(!limitStopwatch.IsTimeUp())
- { /* Do nothing. Don't even sleep, as some platform might not give us the CPU back. */ }
- for(int i = 0; i < kThreadCount; i++)
- threadHighestPriority[i].mbShouldRun = false;
- EAWriteBarrier();
- limitStopwatch.SetTimeLimit(3, true);
- while(!limitStopwatch.IsTimeUp())
- { /* Do nothing. Don't even sleep, as some platform might not give us the CPU back. */ }
- for(int i = 0; i < kThreadCount; i++)
- {
- threadHighPriority[i].mbShouldRun = false;
- threadRegularPriority[i].mbShouldRun = false;
- }
- EAWriteBarrier();
- limitStopwatch.SetTimeLimit(3, true);
- while(!limitStopwatch.IsTimeUp())
- { /* Do nothing. Don't even sleep, as some platform might not give us the CPU back. */ }
- EA::Thread::SetThreadPriority(EA::Thread::kThreadPriorityDefault);
- uint64_t highPriorityCount = 0;
- uint64_t regularPriorityCount = 0;
- for(int i = 0; i < kThreadCount; i++)
- highPriorityCount += threadHighPriority[i].mCounter;
- for(int i = 0; i < kThreadCount; i++)
- regularPriorityCount += threadRegularPriority[i].mCounter;
- // Verify that the higher priority threads always got priority over the regular priority threads.
- EATEST_VERIFY_F(highPriorityCount > regularPriorityCount, "Priority execution failure: highPriorityCount: %I64u, regularPriorityCount: %I64u", highPriorityCount, regularPriorityCount);
- // Wait for the threads to end before continuing.
- for(int i = 0; i < kThreadCount; i++)
- {
- threadHighestPriority[i].mThread.WaitForEnd();
- threadHighPriority[i].mThread.WaitForEnd();
- threadRegularPriority[i].mThread.WaitForEnd();
- }
- }
-
- return nErrorCount;
- }
- int TestSetThreadProcessConstants()
- {
- int nErrorCount = 0;
- // testing a user reported regression of negative value shifts
- for(auto k : { kProcessorDefault, kProcessorAny })
- {
- Thread t;
- t.Begin([](void* param) -> intptr_t
- {
- int kConstant = *(int*)param;
- SetThreadProcessor(kConstant);
- return 0;
- }, &k);
- t.WaitForEnd();
- }
- return nErrorCount;
- }
- int TestNullThreadNames()
- {
- int nErrorCount = 0;
- {
- ThreadParameters threadParams;
- threadParams.mpName = nullptr;
- Thread t;
- t.Begin([](void*) -> intptr_t { return 0; }, NULL, &threadParams);
- t.WaitForEnd();
- }
- return nErrorCount;
- }
- int TestLambdaThreads()
- {
- int nErrorCount = 0;
- { // test rvalue
- int foo = 0;
- MakeThread([&]
- {
- EATEST_VERIFY(foo == 0);
- foo = 42;
- })
- .WaitForEnd();
- EATEST_VERIFY(foo == 42);
- }
- { // test lvalue
- int foo = 0;
- auto callme = [&]
- {
- EATEST_VERIFY(foo == 0);
- foo = 42;
- };
- MakeThread(callme).WaitForEnd();
- EATEST_VERIFY(foo == 42);
- }
- { // test thread parameters
- const char* MY_THREAD_NAME = "my thread name";
- ThreadParameters params;
- params.mpName = MY_THREAD_NAME;
- MakeThread(
- [&] { EATEST_VERIFY(strncmp(MY_THREAD_NAME, GetThreadName(), EATHREAD_NAME_SIZE) == 0); }, params)
- .WaitForEnd();
- }
- return nErrorCount;
- }
- int TestThreadDynamicData()
- {
- int nErrorCount = 0;
- const int kOverflowDynamicDataCount = 256; // Must be greater than EA::Thread::kMaxThreadDynamicDataCount.
- for(int i = 0; i < kOverflowDynamicDataCount; i++)
- {
- EA::Thread::ThreadId id = 0;
- EA::Thread::SysThreadId sysId = 0;
- MakeThread([&]
- {
- id = EA::Thread::GetThreadId();
- sysId = EA::Thread::GetSysThreadId();
- })
- .WaitForEnd();
- EATEST_VERIFY(FindThreadDynamicData(id) == nullptr);
- EATEST_VERIFY(FindThreadDynamicData(sysId) == nullptr);
- }
- return nErrorCount;
- }
- int TestSetThreadProcessor()
- {
- // Exercise EA::Thread::GetThreadId, EA::Thread::GetSysThreadId, EAThreadGetUniqueId, SetThreadProcessor, GetThreadProcessor.
- // Create and start N threads paused.
- // Release all threads to run at once.
- // Have each of the threads record its EAThreadGetUniqueId value and exit.
- // Verify that there were no collisions in the recorded id values.
- int nErrorCount = 0;
- struct IdTestThread : public EA::Thread::IRunnable
- {
- EA::Thread::Thread mThread; // The Thread object.
- EA::Thread::Semaphore mSemaphore; // Used to pause the thread after it starts.
- EA::Thread::ThreadUniqueId mUniqueId; // The EAThreadUniqueId that this thread gets assigned by the OS.
- EA::Thread::ThreadId mThreadId; // The EAThreadUniqueId that this thread gets assigned by the OS.
- EA::Thread::SysThreadId mSysThreadId; // The EAThreadUniqueId that this thread gets assigned by the OS.
- int mAssignedProcessorId; // The processor id that we ask the OS to run this thread on.
- int mProcessorId; // The processor id that this thread gets assigned by the OS. Should equal mAssignedProcessorId.
- IdTestThread() : mThread(), mSemaphore(0), mUniqueId(), mThreadId(), mSysThreadId(), mAssignedProcessorId(), mProcessorId() {}
- IdTestThread(const IdTestThread&){} // Avoid compiler warnings.
- void operator=(const IdTestThread&){} // Avoid compiler warnings.
- intptr_t Run(void*)
- {
- mSemaphore.Wait();
- EAThreadGetUniqueId(mUniqueId);
- mThreadId = EA::Thread::GetThreadId();
- mSysThreadId = EA::Thread::GetSysThreadId();
- mProcessorId = EA::Thread::GetThreadProcessor();
- EAWriteBarrier();
- return 0;
- }
- };
- #if defined(EA_PLATFORM_DESKTOP)
- const int kThreadCount = 100;
- #elif (EA_PLATFORM_WORD_SIZE == 8)
- const int kThreadCount = 50;
- #else
- const int kThreadCount = 16;
- #endif
- IdTestThread thread[kThreadCount];
- ThreadParameters threadParams;
- EA::UnitTest::RandGenT<int> random(EA::UnitTest::GetRandSeed());
- const int processorCount = EA::Thread::GetProcessorCount();
- for(int i = 0; i < kThreadCount; i++)
- {
- threadParams.mnProcessor = random(processorCount);
- threadParams.mpName = "IdTest";
- thread[i].mAssignedProcessorId = threadParams.mnProcessor;
- thread[i].mThread.Begin(&thread[i], NULL, &threadParams);
- }
- EA::UnitTest::ThreadSleep(1000);
- for(int i = 0; i < kThreadCount; i++)
- thread[i].mSemaphore.Post(1);
- EA::UnitTest::ThreadSleep(1000);
- for(int i = 0; i < kThreadCount; i++)
- thread[i].mThread.WaitForEnd();
- EAReadBarrier();
- EA::Thread::ThreadUniqueId uniqueIdArray[kThreadCount];
- EA::Thread::ThreadId idArray[kThreadCount];
- EA::Thread::SysThreadId sysIdArray[kThreadCount];
- // Problem: We don't have an EAThreadEqual(const ThreadId&, const ThreadId&) function, but could use one.
- // If we had such a thing, then we wouldn't need the odd code below and could probably use an eastl::set.
- for(int i = 0; i < kThreadCount; i++)
- {
- memset(&uniqueIdArray[i], 0, sizeof(EA::Thread::ThreadUniqueId));
- memset(&idArray[i], 0, sizeof(EA::Thread::ThreadId));
- memset(&sysIdArray[i], 0, sizeof(EA::Thread::SysThreadId));
- }
- for(int i = 0; i < kThreadCount; i++)
- {
- for(int j = 0; j < i; j++)
- EATEST_VERIFY(memcmp(&uniqueIdArray[j], &thread[i].mUniqueId, sizeof(EA::Thread::ThreadUniqueId)) != 0);
- uniqueIdArray[i] = thread[i].mUniqueId;
- for(int j = 0; j < i; j++)
- EATEST_VERIFY(memcmp(&idArray[j], &thread[i].mThreadId, sizeof(EA::Thread::ThreadId)) != 0);
- idArray[i] = thread[i].mThreadId;
- for(int j = 0; j < i; j++)
- EATEST_VERIFY(memcmp(&sysIdArray[j], &thread[i].mSysThreadId, sizeof(EA::Thread::SysThreadId)) != 0);
- sysIdArray[i] = thread[i].mSysThreadId;
- // The following will fail on some platforms, as they don't support assigning
- // thread affinity (e.g. PS3) or don't respect the assigned thread affinity (e.g. Windows).
- // To consider: make a define which identifies which platforms rigidly follow thread processor assignments.
- // On Windows, EAThread doesn't use SetThreadAffinityMask but rather uses SetThreadIdealProcessor,
- // which doesn't guarantee which processor the thread will run on and rather is a hint.
- #if defined(EA_PLATFORM_CONSOLE) && defined(EA_PLATFORM_MICROSOFT) // To do: add platforms to this list appropriately.
- EATEST_VERIFY_F(thread[i].mProcessorId == thread[i].mAssignedProcessorId,
- " Error: Thread assigned to run on processor %d, found to be running on processor %d.",
- thread[i].mAssignedProcessorId, thread[i].mProcessorId);
- #endif
- }
-
- return nErrorCount;
- }
- int TestThreadDisablePriorityBoost()
- {
- int nErrorCount = 0;
- #if defined(EA_PLATFORM_WINDOWS) || defined(EA_PLATFORM_CAPILANO)
- {
- Thread thread;
- ThreadParameters params;
- auto priorityBoostTester = [&](BOOL expectedDisablePriorityBoost)
- {
- thread.Begin(
- [](void* pInExpectedDisablePriorityBoost) -> intptr_t
- {
- BOOL* pExpectedDisablePriorityBoost = (BOOL*)pInExpectedDisablePriorityBoost;
- int nErrorCount = 0;
- PBOOL pDisablePriorityBoost = nullptr;
- auto result = GetThreadPriorityBoost(GetCurrentThread(), pDisablePriorityBoost);
- EATEST_VERIFY_MSG(result != 0, "GetThreadPriorityBoost failed\n");
- EATEST_VERIFY_MSG((result != 0) && *pDisablePriorityBoost == *pExpectedDisablePriorityBoost,
- "Thread Priority Boost was not disabled\n");
- return nErrorCount;
- },
- &expectedDisablePriorityBoost, ¶ms);
- intptr_t threadErrorCount = 0;
- thread.WaitForEnd(GetThreadTime() + 30000, &threadErrorCount);
- nErrorCount += (int)threadErrorCount;
- };
- params.mbDisablePriorityBoost = true;
- priorityBoostTester(TRUE);
- params.mbDisablePriorityBoost = false;
- priorityBoostTester(FALSE);
- }
- #endif
- return nErrorCount;
- }
- int TestThreadParameters()
- { // Test ThreadParameters
- int nErrorCount = 0;
- Thread::Status status;
- const int kThreadCount(kMaxConcurrentThreadCount - 1);
- ThreadId threadId[kThreadCount];
- Thread thread[kThreadCount];
- int i;
- ThreadParameters threadParameters;
- const int nOriginalPriority = GetThreadPriority();
- // Set our thread priority to match that of the threads we will be creating below.
- SetThreadPriority(kThreadPriorityDefault + 1);
- for(i = 0; i < kThreadCount; i++)
- {
- status = thread[i].GetStatus();
- EATEST_VERIFY_MSG(status == Thread::kStatusNone, "Thread failure: Thread should have kStatusNone (2).\n");
- // ThreadParameters
- threadParameters.mnStackSize = 32768;
- threadParameters.mnPriority = kThreadPriorityDefault + 1;
- threadParameters.mpName = "abcdefghijklmnopqrstuvwxyz"; // Make an overly large name.
- threadId[i] = thread[i].Begin(TestFunction1, NULL, &threadParameters);
- EATEST_VERIFY_MSG(threadId[i] != (ThreadId)kThreadIdInvalid, "Thread failure: ThreadBegin failed.\n");
- // 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.
- #if ((!defined(EA_PLATFORM_UNIX) && !defined(__APPLE__)) || defined(__CYGWIN__)) && !EA_USE_CPP11_CONCURRENCY
- EATEST_VERIFY_MSG(thread[i].GetPriority() == threadParameters.mnPriority, "Thread failure: Thread Priority not set correctly (2).\n");
-
- #endif
- if(i > 0)
- EATEST_VERIFY_MSG(threadId[i] != threadId[i-1], "Thread failure: Thread id collision (2).\n");
- }
- ThreadSleep(200);
- for(i = 0; i < kThreadCount; i++)
- {
- if(threadId[i] != kThreadIdInvalid)
- thread[i].SetName("0123456789012345678901234567890"); // Make an overly large name.
- }
- ThreadSleep(200);
- // It turns out that you can't really do such a thing as set lower priority in native Linux.
- #if ((!defined(EA_PLATFORM_UNIX) || defined(__CYGWIN__)) && !defined(__APPLE__) && !EA_USE_CPP11_CONCURRENCY)
- int nPriority;
- if(threadId[0] != kThreadIdInvalid)
- {
- nPriority = thread[0].GetPriority();
- thread[0].SetPriority(nPriority - 1);
- EATEST_VERIFY_MSG(thread[0].GetPriority() == nPriority - 1, "Thread failure: Thread Priority not set correctly (3.1).\n");
- thread[0].SetPriority(nPriority);
- EATEST_VERIFY_MSG(thread[0].GetPriority() == nPriority, "Thread failure: Thread Priority not set correctly (3.2).\n");
-
- }
- #endif
- for(i = 0; i < kThreadCount; i++)
- {
- if(threadId[i] != kThreadIdInvalid)
- thread[i].WaitForEnd(GetThreadTime() + 30000);
- }
- SetThreadPriority(kThreadPriorityDefault);
- for(i = 0; i < kThreadCount; i++)
- {
- status = thread[i].GetStatus();
- if(threadId[i] != kThreadIdInvalid)
- EATEST_VERIFY_MSG(status == Thread::kStatusEnded, "Thread failure: Thread should have kStatusEnded (2).\n");
- else
- EATEST_VERIFY_MSG(status == Thread::kStatusNone, "Thread failure: Thread should have kStatusNone (2).\n");
- }
-
- SetThreadPriority(nOriginalPriority);
- return nErrorCount;
- }
- int TestThreadEnd()
- {
- int nErrorCount(0);
- EA::Thread::Thread thread;
- Thread::Status status;
- thread.Begin(TestFunction13);
- ThreadSleep(20);
-
- intptr_t returncode;
- thread.WaitForEnd();
- status = thread.GetStatus(&returncode);
-
- EATEST_VERIFY_MSG(returncode == 42, "Thread return code failure: Expected return code 42.");
- EATEST_VERIFY_MSG(status == Thread::kStatusEnded, "Thread failure: Thread should have kStatusEnded.\n");
-
- return nErrorCount;
- }
- int TestThreadThread()
- {
- int nErrorCount(0);
- sThreadTestTimeMS = (gTestLengthSeconds * 1000) / 2; // '/2' because this test doesn't need so much time.
- nErrorCount += TestThreadAffinityMask();
- nErrorCount += TestThreadEnd();
- {
- ThreadId threadId = GetThreadId();
- EATEST_VERIFY_MSG(threadId != kThreadIdInvalid, "GetThreadId failure.\n");
- SysThreadId sysThreadId = GetSysThreadId(threadId);
- EATEST_VERIFY_MSG(sysThreadId != kSysThreadIdInvalid, "GetSysThreadId failure.\n");
- #if (defined(EA_PLATFORM_MICROSOFT) || defined(EA_PLATFORM_UNIX) || defined(EA_PLATFORM_KETTLE)) && !EA_USE_CPP11_CONCURRENCY
- const void* pStackBase = GetThreadStackBase();
- const void* pStackTop = &pStackBase;
- const intptr_t stackSize = ((char*)pStackBase - (char*)pStackTop);
- // Verify that pStackBase is non-NULL and that the stack size is less than N MB.
- EATEST_VERIFY_MSG(pStackBase && (stackSize < 10485760), "GetThreadStackBase failure.\n");
-
- #endif
- // We disable this test for now on Kettle because although we have 7 cores available
- // there is no guaranty the their ID are 0..6 and the system takes core ID 7
- #if !defined(EA_PLATFORM_KETTLE)
- int processorCount = GetProcessorCount();
- int processor = GetThreadProcessor();
-
- // This isn't much of a test, but it at least exercizes the function.
- EATEST_VERIFY_F(processor < processorCount, " Error: GetThreadProcessor [%d] >= GetProcessorCount [%d].\n", processor, processorCount);
- #endif
- // To do: Test this:
- // void SetThreadProcessor(int nProcessor);
- }
- { // Test Current thread functionality
- const int nOriginalPriority = GetThreadPriority();
- int nPriority = kThreadPriorityDefault;
- EATEST_VERIFY_MSG(nPriority >= kThreadPriorityMin, "Thread priority failure (1).\n");
- EATEST_VERIFY_MSG(nPriority <= kThreadPriorityMax, "Thread priority failure (2).\n");
- // It turns out that you can't really do such a thing as set lower priority with most Unix threading subsystems.
- // You can do so with Cygwin because it is just a pthreads API running on Windows OS/threading.
- // C++11 thread libraries also provide no means to set or query thread priority.
- #if (!defined(EA_PLATFORM_UNIX) || defined(__CYGWIN__)) && !EA_USE_CPP11_CONCURRENCY
- int nPriority1;
- bool bResult;
- bResult = SetThreadPriority(nPriority);
- EATEST_VERIFY_MSG(bResult, "Thread priority failure (3).\n");
- bResult = SetThreadPriority(nPriority - 1);
- EATEST_VERIFY_MSG(bResult, "Thread priority failure (4).\n");
- nPriority1 = GetThreadPriority();
- EATEST_VERIFY_MSG(nPriority1 == nPriority - 1, "Thread priority failure (5).\n");
- bResult = SetThreadPriority(nPriority + 1);
- EATEST_VERIFY_MSG(bResult, "Thread priority failure (6).\n");
- nPriority1 = GetThreadPriority();
- EATEST_VERIFY_MSG(nPriority1 == nPriority + 1, "Thread priority failure (7).\n");
- bResult = SetThreadPriority(kThreadPriorityDefault);
- EATEST_VERIFY_MSG(bResult, "Thread priority failure (8).\n");
- nPriority1 = GetThreadPriority();
- EATEST_VERIFY_MSG(nPriority1 == kThreadPriorityDefault, "Thread priority failure (9).\n");
-
- #endif
- SetThreadPriority(nOriginalPriority);
- ThreadSleep(kTimeoutImmediate);
- ThreadSleep(500);
- }
- #if defined(EA_PLATFORM_WINDOWS) && !EA_USE_CPP11_CONCURRENCY
- { // Try to reproduce Windows problem with Thread::GetStatus returning kStatusEnded when it should return kStatusRunning.
- // On my current work machine (WinXP32, Single P4 CPU) this problem doesn't occur. But it might occur with others.
- Thread::Status status;
- Thread threadBackground[8];
- Thread thread;
- for(int i = 0; i < 8; i++)
- threadBackground[i].Begin(TestFunction1);
- EA::Thread::SetThreadPriority(kThreadPriorityDefault + 2);
- thread.Begin(TestFunction1);
- status = thread.GetStatus();
- EA::Thread::SetThreadPriority(kThreadPriorityDefault);
- EATEST_VERIFY_MSG(status == Thread::kStatusRunning, "Thread failure: Thread should have kStatusRunning.\n");
- thread.WaitForEnd();
- }
- #endif
- EA::Thread::Thread::SetGlobalRunnableFunctionUserWrapper(TestFunction3ExceptionWrapper);
- EA::Thread::Thread::SetGlobalRunnableClassUserWrapper(TestRunnable3ExceptionWrapper);
- { // Test thread creation functionality.
- Thread::Status status;
- const int kThreadCount(kMaxConcurrentThreadCount - 1);
- Thread thread[kThreadCount];
- ThreadId threadId[kThreadCount];
- int i;
- for(i = 0; i < kThreadCount; i++)
- {
- status = thread[i].GetStatus();
- EATEST_VERIFY_MSG(status == Thread::kStatusNone, "Thread failure: Thread should have kStatusNone (1).\n");
- threadId[i] = thread[i].Begin(TestFunction1);
- EATEST_VERIFY_MSG(threadId[i] != kThreadIdInvalid, "Thread failure: ThreadBegin(TestFunction1) failed.\n");
- threadId[i] = thread[i].Begin(TestFunction3);
- EATEST_VERIFY_MSG(threadId[i] != kThreadIdInvalid, "Thread failure: ThreadBegin(TestFunction3) failed.\n");
- threadId[i] = thread[i].Begin(&gTestRunnable3);
- EATEST_VERIFY_MSG(threadId[i] != kThreadIdInvalid, "Thread failure: ThreadBegin(&gTestRunnable3) failed.\n");
- if(i > 0)
- EATEST_VERIFY_MSG(threadId[i] != threadId[i-1], "Thread failure: Thread id collision (1).\n");
- }
- // It turns out that you can't really do such a thing as set lower priority in native Linux.
- // C++11 threads also have no support for priorities
- #if (!defined(EA_PLATFORM_UNIX) || defined(__CYGWIN__)) && !EA_USE_CPP11_CONCURRENCY
- int nPriority;
- nPriority = thread[0].GetPriority();
- thread[0].SetPriority(nPriority - 1);
- nPriority = thread[0].GetPriority();
- thread[0].SetPriority(nPriority + 1);
- #endif
- ThreadSleep(200);
- for(i = 0; i < kThreadCount; i++)
- {
- if(threadId[i] != kThreadIdInvalid)
- thread[i].WaitForEnd(GetThreadTime() + 30000);
- }
- for(i = 0; i < kThreadCount; i++)
- {
- if(threadId[i] != kThreadIdInvalid)
- {
- status = thread[i].GetStatus();
- EATEST_VERIFY_MSG(status == Thread::kStatusEnded, "Thread failure: Thread should have kStatusEnded (1).\n");
- }
- }
- }
- nErrorCount += TestThreadParameters();
- {
- // Test if we can set and retrieve a custom thread name
- Thread::Status status;
- Thread thread;
- ThreadId threadId;
- const char* threadName = "Test_Thread";
- const char* defaultName = "DEFAULT";
- ThreadParameters threadParameters;
- threadParameters.mpName = defaultName;
- sThreadTestTimeMS = 10000;
- threadId = thread.Begin(TestFunction1, NULL, &threadParameters);
- EATEST_VERIFY_MSG(threadId != kThreadIdInvalid, "Thread failure: ThreadBegin(TestFunction1) failed.\n");
- EATEST_VERIFY_MSG(strncmp(defaultName, thread.GetName(), EATHREAD_NAME_SIZE) == 0, "Thread failure: GetName should return the name used when initializing the thread.");
- thread.SetName(threadName);
- EATEST_VERIFY_MSG(strncmp(threadName, thread.GetName(), EATHREAD_NAME_SIZE) == 0, "Thread failure: GetName should return the name set in SetName.");
- ThreadSleep(sThreadTestTimeMS + 1000);
- if(threadId != kThreadIdInvalid)
- {
- thread.WaitForEnd(GetThreadTime() + 30000);
- status = thread.GetStatus();
- EATEST_VERIFY_MSG(status == Thread::kStatusEnded, "Thread failure: Thread should have kStatusEnded (3).\n");
- }
- }
- // Test the free standing function variant of the GetName/SetName API
- {
- // Test if we can set and retrieve a custom thread name
- Thread::Status status;
- Thread thread;
- ThreadId threadId;
- const char* threadName = "Test_Thread";
- const char* defaultName = "DEFAULT";
- ThreadParameters threadParameters;
- threadParameters.mpName = defaultName;
- threadParameters.mnProcessor = EA::Thread::kProcessorAny;
- threadParameters.mnAffinityMask = EA::Thread::kThreadAffinityMaskAny;
- static volatile std::atomic<bool> sbThreadStarted;
- static volatile std::atomic<bool> sbThreadTestDone;
- sbThreadStarted = false;
- sbThreadTestDone = false;
- threadId = thread.Begin( [](void*) -> intptr_t
- {
- sbThreadStarted = true;
- while (!sbThreadTestDone)
- ThreadSleep();
- return 0;
- },
- NULL, &threadParameters);
- while(!sbThreadStarted) // Wait for thread to start up
- ThreadSleep();
- EATEST_VERIFY_MSG(threadId != kThreadIdInvalid, "Thread failure: ThreadBegin(TestFunction1) failed.\n");
- EATEST_VERIFY_MSG(strncmp(defaultName, GetThreadName(threadId), EATHREAD_NAME_SIZE) == 0, "Thread failure: GetName should return the name used when initializing the thread.");
- SetThreadName(threadId, threadName);
- EATEST_VERIFY_MSG(strncmp(threadName, GetThreadName(threadId), EATHREAD_NAME_SIZE) == 0, "Thread failure: GetName should return the name set in SetName.");
- sbThreadTestDone = true; // signal that test is completed and the thread can shutdown
- if(threadId != kThreadIdInvalid)
- {
- thread.WaitForEnd(GetThreadTime() + 30000);
- status = thread.GetStatus();
- EATEST_VERIFY_MSG(status == Thread::kStatusEnded, "Thread failure: Thread should have kStatusEnded (3).\n");
- }
- }
- {
- // Test the creation+destruction of many threads to make sure resources are recycled properly
- #if defined(EA_PLATFORM_DESKTOP)
- const int kThreadCount(200);
- #else
- const int kThreadCount(20);
- #endif
- Thread thread;
- ThreadId threadId;
- intptr_t returnValue;
- EA::UnitTest::ReportVerbosity(1, "Creating many threads and then WaitForEnd()\n");
- for(int i = 0; i < kThreadCount; i++)
- {
- threadId = thread.Begin(TestFunction4, reinterpret_cast<void*>((uintptr_t)i));
- Thread::Status status = thread.GetStatus(&returnValue);
- if(status != Thread::kStatusEnded)
- thread.WaitForEnd(GetThreadTime() + 30000, &returnValue);
- EA::UnitTest::ReportVerbosity(1, "Thread ended.\n");
- 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);
- if(returnValue != i)
- EA::UnitTest::ReportVerbosity(1, " Expected: %u, actual: %" PRId64 ".\n", (unsigned)i, (int64_t)returnValue);
- // Get the status again to make sure it returns the correct status.
- thread.GetStatus(&returnValue);
- 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);
- if(returnValue != i)
- EA::UnitTest::ReportVerbosity(1, " Expected: %u, actual: %" PRId64 ".\n", (unsigned)i, (int64_t)returnValue);
- }
-
- EA::UnitTest::ReportVerbosity(1, "Creating many threads and then repeat GetStatus() until they finish\n");
- for(int i = 0; i < kThreadCount + 1; i++)
- {
- // Windows will get stuck in infinite loop if return value is 259 (STILL_ACTIVE)
- if(i == 259)
- ++i;
- threadId = thread.Begin(TestFunction4, reinterpret_cast<void*>((uintptr_t)i));
- const ThreadTime nGiveUpTime = EA::Thread::GetThreadTime() + 20000; // Give up after N milliseconds.
- // Test to see if GetStatus() will recycle system resource properly
- while(thread.GetStatus(&returnValue) != Thread::kStatusEnded)
- {
- ThreadSleep(100);
- if(EA::Thread::GetThreadTime() > nGiveUpTime)
- {
- EA::UnitTest::ReportVerbosity(1, "Thread failure: GetStatus failed.\n");
- break;
- }
- }
- EATEST_VERIFY_MSG(returnValue == i, "Thread failure: Thread return code is wrong (3).\n");
- if(returnValue != i)
- EA::UnitTest::ReportVerbosity(1, " Expected: %u, actual: %" PRId64 ".\n", (unsigned)i, (int64_t)returnValue);
- // Get the status again to make sure it returns the correct status.
- thread.GetStatus(&returnValue);
- EATEST_VERIFY_MSG(returnValue == i, "Thread failure: Thread return code is wrong (4).\n");
- if(returnValue != i)
- EA::UnitTest::ReportVerbosity(1, " Expected: %u, actual: %" PRId64 ".\n", (unsigned)i, (int64_t)returnValue);
- // See if calling WaitForEnd() now will result in a crash
- thread.WaitForEnd(GetThreadTime() + 30000, &returnValue);
- EATEST_VERIFY_MSG(returnValue == i, "Thread failure: Thread return code is wrong (5).\n");
- }
- }
- {
- // Test the creation of many threads
- #if defined(EA_PLATFORM_DESKTOP)
- Thread::Status status;
- const int kThreadCount(96);
- Thread thread[kThreadCount];
- ThreadId threadId[kThreadCount];
- int i;
- sThreadTestTimeMS = 10000;
- for(i = 0; i < kThreadCount; i++)
- {
- threadId[i] = thread[i].Begin(TestFunction1);
- EATEST_VERIFY_MSG(threadId[i] != kThreadIdInvalid, "Thread failure: ThreadBegin(TestFunction1) failed.\n");
- }
- ThreadSleep(sThreadTestTimeMS + 1000);
- for(i = 0; i < kThreadCount; i++)
- {
- if(threadId[i] != kThreadIdInvalid)
- thread[i].WaitForEnd(GetThreadTime() + 30000);
- }
- for(i = 0; i < kThreadCount; i++)
- {
- if(threadId[i] != kThreadIdInvalid)
- {
- status = thread[i].GetStatus();
- EATEST_VERIFY_MSG(status == Thread::kStatusEnded, "Thread failure: Thread should have kStatusEnded (3).\n");
- }
- }
- #endif
- }
- {
- // Regression of Thread dtor behaviour - the dtor should not wait for created threads
- // We reuse the atomic shouldgo to figure out what is really happening setting it to 0
- // initially and then incrementing it when the thread callback completes, allowing
- // us to detect whether the dtor completed before the thread callback.
- sShouldGo = 0;
- {
- // Create a scope for our thread object
- Thread thread;
-
- // Get our thread going. It will sleep immediately for 2 seconds and then increment sShouldGo
- ThreadId threadId = thread.Begin(TestFunction8);
- EATEST_VERIFY_MSG(threadId != kThreadIdInvalid, "Thread failure: thread.Begin(TestFunction8) failed.\n");
- // Give the thread a nominal second to get going and ensure we get a decent overlap of a second.
- ThreadSleep(1000);
- // Now we exit our scope while our thread in theory still has a second before it completes
- }
-
- // We either get here before the thread function completes in which case sShouldGo is 0 or
- // we get here after it completes which would be incorrect and would give us a sShouldGo of 1
- const bool threadScopeSemanticsVerified = sShouldGo == 0;
- // Rather than try to wait on threadid in a cross platform way we simply sleep for 2 seconds which
- // we know should be sufficient for the thread to complete. We can make this part of the test
- // as shouldgo should be 1 when the thread has completed.
- ThreadSleep(2000);
- EATEST_VERIFY_MSG(threadScopeSemanticsVerified, "Thread failure: Thread should not have ended before Thread object dtor completed. Behaviour is inconsistent with intent.\n");
- 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");
- }
- {
- int nOriginalPriority = EA::Thread::GetThreadPriority();
- EA::Thread::SetThreadPriority(kThreadPriorityDefault);
- // Tests setting and getting thread priority while the thread is active.
- Thread::Status status;
- Thread thread;
- ThreadId threadId;
- sShouldGo = 0;
- threadId = thread.Begin(TestFunction7);
- EATEST_VERIFY_MSG(threadId != kThreadIdInvalid, "Thread failure: ThreadBegin(TestFunction7) failed.\n");
-
- // It turns out that you can't really do such a thing as set lower priority in native Linux.
- #if ((!defined(EA_PLATFORM_LINUX) && !defined(__APPLE__) && !defined(EA_PLATFORM_BSD)) || defined(__CYGWIN__)) && !EA_USE_CPP11_CONCURRENCY
- int nPriority = thread.GetPriority();
- thread.SetPriority(nPriority - 1);
- if(EATEST_VERIFY_MSG(thread.GetPriority() == nPriority - 1, "Thread failure: Thread Priority not set correctly (2).\n"))
- nErrorCount++;
- thread.SetPriority(nPriority);
- EATEST_VERIFY_MSG(thread.GetPriority() == nPriority, "Thread failure: Thread Priority not set correctly (3).\n");
- #endif
- sShouldGo = 1;
- thread.WaitForEnd(GetThreadTime() + 30000);
- status = thread.GetStatus();
- EATEST_VERIFY_MSG(status == Thread::kStatusEnded, "Thread failure: Thread should have kStatusEnded.\n");
- EA::Thread::SetThreadPriority(nOriginalPriority);
- }
- nErrorCount += TestThreadDynamicData();
- nErrorCount += TestThreadPriorities();
- nErrorCount += TestSetThreadProcessor();
- nErrorCount += TestSetThreadProcessConstants();
- nErrorCount += TestNullThreadNames();
- nErrorCount += TestLambdaThreads();
- {
- // Test SetDefaultProcessor
- Thread::SetDefaultProcessor(kProcessorAny);
- }
- #if !defined(EA_PLATFORM_MOBILE)
- // This test does not execute correctly on Android. The 'while(sThreadCount
- // < kThreadCount)' loop sometimes hangs indefinitely because the spawned
- // threads exit often before the loop continues, so the spawned
- // thread count never gets up to kThreadCount. I guess the inner loop
- // should be testing addedThreadCount too. (?) But, just disable for now,
- // since I guess it's working on other platforms.
- {
- // Test very many threads starting and quitting, recycling
- // The PS3 code had problems with leaking threads in this case.
- // Create a bunch of threads.
- // Every N ms quit one of them and create a new one.
- // This test tends to be quite taxing on system resources, so cut it back for
- // certain platforms. To do: use EATest's platform speed metrics.
- const int kThreadCount(48); // This needs to be greater than the eathread_thread.cpp kMaxThreadDynamicDataCount value.
- const int kTotalThreadsToRun(500);
- Thread thread;
- ThreadId threadId;
- int addedThreadCount = 0;
- int i = 0;
- sThreadCount = 0;
- sShouldGo = 0;
- // Create threads.
- for(i = 0; i < kThreadCount; i++)
- {
- threadId = thread.Begin(TestFunction6, reinterpret_cast<void*>((uintptr_t)i));
- EATEST_VERIFY(threadId != kThreadIdInvalid);
- }
- // Wait until they are all created and started.
- while(sThreadCount < kThreadCount)
- ThreadSleep(500);
- // Let them run and exit as they go.
- sShouldGo = 1;
- // Add new threads as existing ones leave.
- while(addedThreadCount < kTotalThreadsToRun)
- {
- if(sThreadCount < kThreadCount)
- {
- threadId = thread.Begin(TestFunction6, reinterpret_cast<void*>((uintptr_t)i++));
- EATEST_VERIFY(threadId != kThreadIdInvalid);
- addedThreadCount++;
-
- #if defined(EA_PLATFORM_DESKTOP)
- // The created threads will not get any time slices on weaker platforms.
- // thus making the test create threads until the system is out of resources
- ThreadSleep(kTimeoutYield);
- // Sometimes it will never exit this loop.. because it will leave faster then it is made
- if(addedThreadCount >= kTotalThreadsToRun)
- break;
- #endif
- }
- else
- ThreadSleep(10);
- }
- // Wait until they have all exited.
- while(sThreadCount != 0) // While there are threads...
- ThreadSleep(500);
- // On some platforms it seems that thread entry/exit is so slow that the thread count might not necessarily
- // reflect the actual number of threads running. This was causing an issue where the function was
- // exiting before all threads completed, so I added this wait. It may be worth considering
- // keeping track of all created threads in an array, and then using a thread-join operation here
- // instead
- ThreadSleep(1000);
- }
- #endif
- return nErrorCount;
- }
|