/////////////////////////////////////////////////////////////////////////////// // Copyright (c) Electronic Arts Inc. All rights reserved. /////////////////////////////////////////////////////////////////////////////// #include "TestThread.h" #include #include #include #include #include using namespace EA::Thread; const int kMaxConcurrentThreadCount = EATHREAD_MAX_CONCURRENT_THREAD_COUNT; struct BT_WorkData { Barrier* mpBarrier; int mnSleepTime; AtomicInt32 mnErrorCount; BT_WorkData(Barrier* pBarrier = NULL, int nSleepTime = 0) : mpBarrier(pBarrier), mnSleepTime(nSleepTime), mnErrorCount(0) {} }; static intptr_t BT_ConsumerFunction(void* pvWorkData) { int nErrorCount = 0; BT_WorkData* pWorkData = (BT_WorkData*)pvWorkData; const ThreadId threadId = GetThreadId(); EA::UnitTest::ReportVerbosity(1, "Barrier consumer test function created: %s\n", EAThreadThreadIdToString(threadId)); // Here we would actually do the job, but printing 'job done' is enough in itself. ThreadSleep((ThreadTime)pWorkData->mnSleepTime); EA::UnitTest::ReportVerbosity(1, "Job done by thread %s.\n", EAThreadThreadIdToString(threadId)); EA::UnitTest::ReportVerbosity(1, "Start synchronizing by thread %s.\n", EAThreadThreadIdToString(threadId)); const Barrier::Result result = pWorkData->mpBarrier->Wait(GetThreadTime() + 10000); if(result == Barrier::kResultPrimary) { // This is the first thread to be released: call producer function EA::UnitTest::ReportVerbosity(1, "Serial execution at Barrier by thread %s\n", EAThreadThreadIdToString(threadId)); } else if(result == Barrier::kResultTimeout) { EA::UnitTest::Report("Barrier time-out by thread %s\n", EAThreadThreadIdToString(threadId)); nErrorCount++; } else if(result == Barrier::kResultError) { EA::UnitTest::Report("Barrier error in thread %s\n", EAThreadThreadIdToString(threadId)); nErrorCount++; } pWorkData->mnErrorCount += nErrorCount; ThreadSleep((ThreadTime)pWorkData->mnSleepTime); EA::UnitTest::ReportVerbosity(1, "Job synchronized by thread %s.\n", EAThreadThreadIdToString(threadId)); return 0; } static intptr_t BT_ConsumerFunction2(void * pvWorkData) { ((Barrier*)pvWorkData)->Wait(); ((Barrier*)pvWorkData)->Wait(); return 0; } int TestThreadBarrier() { int nErrorCount(0); #if EA_THREADS_AVAILABLE { Thread::Status status; const int kThreadCount(kMaxConcurrentThreadCount - 1); BT_WorkData workData[kThreadCount]; Thread threads[kThreadCount]; ThreadId threadId[kThreadCount]; Barrier barrier(kThreadCount); int i; for(i = 0; i < kThreadCount; i++) { workData[i].mpBarrier = &barrier; workData[i].mnSleepTime = (i + 1) * 500; } for(i = 0; i < kThreadCount; i++) { threadId[i] = threads[i].Begin(BT_ConsumerFunction, &workData[i]); EATEST_VERIFY_MSG(threadId[i] != kThreadIdInvalid, "Barrier/Thread failure: Couldn't create thread."); } EA::UnitTest::ThreadSleepRandom(2000, 2000); for(i = 0; i < kThreadCount; i++) { if(threadId[i] != kThreadIdInvalid) { status = threads[i].WaitForEnd(GetThreadTime() + 30000); EATEST_VERIFY_MSG(status != Thread::kStatusRunning, "Barrier/Thread failure."); } nErrorCount += workData[i].mnErrorCount; } } { Thread threads[2]; ThreadId threadId[2]; Barrier barrier(2); Thread::Status status; threadId[0] = threads[0].Begin(BT_ConsumerFunction2, &barrier); EATEST_VERIFY_MSG(threadId[0] != kThreadIdInvalid, "Barrier/Thread failure: Couldn't create thread."); threadId[1] = threads[1].Begin(BT_ConsumerFunction2, &barrier); EATEST_VERIFY_MSG(threadId[1] != kThreadIdInvalid, "Barrier/Thread failure: Couldn't create thread."); if(threadId[0] != kThreadIdInvalid) { status = threads[0].WaitForEnd(); EATEST_VERIFY_MSG(status != Thread::kStatusRunning, "Barrier/Thread failure."); } if(threadId[1] != kThreadIdInvalid) { status = threads[1].WaitForEnd(); EATEST_VERIFY_MSG(status != Thread::kStatusRunning, "Barrier/Thread failure."); } } #endif return nErrorCount; }