| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332 |
- ///////////////////////////////////////////////////////////////////////////////
- // 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*)
- {
- while(sShouldGo == 0)
- ThreadSleep(10);
- 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_XBOXONE)
- {
- 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_PS4)) && !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_PS4)
- 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");
- // Now we exit our scope while our thread in theory still has a second before it completes
- } // NOTE(rparolin): If your test hangs here, its most likely due to a semantic change in the thread object that is waiting for threads to complete.
- // Signal to the thread it is allowed to complete.
- sShouldGo = 1;
- }
- {
- 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;
- }
|