| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477 |
- ///////////////////////////////////////////////////////////////////////////////
- // Copyright (c) Electronic Arts Inc. All rights reserved.
- ///////////////////////////////////////////////////////////////////////////////
- #include "TestThread.h"
- #include <EATest/EATest.h>
- #include <eathread/eathread_thread.h>
- #include <eathread/eathread_rwspinlock.h>
- #include <eathread/eathread_rwspinlockw.h>
- #include <stdlib.h>
- #include <atomic>
- const int kThreadCount = EATHREAD_MAX_CONCURRENT_THREAD_COUNT;
- ///////////////////////////////////////////////////////////////////////////////
- // RWSTestType
- //
- enum RWSTestType
- {
- kRWSTestTypeStandard,
- kRWSTestTypeAllWriters,
- kRWSTestTypeAllReaders,
- kRWSTestTypeMostlyWriters,
- kRWSTestTypeMostlyReaders,
- kRWSTestTypeCount
- };
- ///////////////////////////////////////////////////////////////////////////////
- // RWSWorkData
- //
- struct RWSWorkData
- {
- std::atomic<bool> mbShouldQuit;
- EA::Thread::RWSpinLock mRWSpinLock;
- EA::Thread::RWSpinLockW mRWSpinLockW;
- volatile int mnExpectedValue;
- volatile int mnCalculatedValue;
- EA::Thread::AtomicInt32 mnErrorCount;
- EA::Thread::AtomicInt32 mnCurrentTestType;
- RWSWorkData() : mbShouldQuit(false), mRWSpinLock(), mRWSpinLockW(), mnExpectedValue(0), mnCalculatedValue(0),
- mnErrorCount(0), mnCurrentTestType(kRWSTestTypeStandard) {}
- private:
- RWSWorkData(const RWSWorkData& rhs);
- RWSWorkData& operator=(const RWSWorkData& rhs);
- };
- ///////////////////////////////////////////////////////////////////////////////
- // RWSWThreadFunction
- //
- static intptr_t RWSWThreadFunction(void* pvWorkData)
- {
- using namespace EA::Thread;
- RWSWorkData* const pWorkData = (RWSWorkData*)pvWorkData;
- ThreadId threadId = GetThreadId();
- EA::UnitTest::ReportVerbosity(1, "RWSpinLockW test function created: %s\n", EAThreadThreadIdToString(threadId));
- int nErrorCount = 0;
- while(!pWorkData->mbShouldQuit)
- {
- int nWriteLockChance = 0;
- const RWSTestType testType = (RWSTestType)pWorkData->mnCurrentTestType.GetValue();
- switch (testType)
- {
- default:
- case kRWSTestTypeStandard:
- nWriteLockChance = 20;
- break;
- case kRWSTestTypeAllWriters:
- nWriteLockChance = 1000;
- break;
- case kRWSTestTypeAllReaders:
- nWriteLockChance = 0;
- break;
- case kRWSTestTypeMostlyWriters:
- nWriteLockChance = 700;
- break;
- case kRWSTestTypeMostlyReaders:
- nWriteLockChance = 5;
- break;
- }
- const bool bShouldWrite = ((rand() % 1000) < nWriteLockChance);
- if(bShouldWrite)
- {
- pWorkData->mRWSpinLockW.WriteLock();
- EATEST_VERIFY_MSG(!pWorkData->mRWSpinLockW.IsReadLocked(), "RWSpinlock failure: IsReadLocked\n");
- EATEST_VERIFY_MSG(pWorkData->mRWSpinLockW.IsWriteLocked(), "RWSpinlock failure: IsWriteLocked\n");
- pWorkData->mRWSpinLockW.WriteUnlock();
- ThreadCooperativeYield();
- }
- else
- {
- const int nRecursiveLockCount = 1; // Disabled because we are not recursive: (rand() % 10) ? 1 : 2;
- int nLocks = 0;
- for(int i = 0; i < nRecursiveLockCount; i++)
- {
- pWorkData->mRWSpinLockW.ReadLock();
- nLocks++;
- ThreadCooperativeYield();
- }
- // Disabled because we are not recursive:
- // if((rand() % 10) == 0)
- // {
- // if(pWorkData->mRWSpinLockW.ReadTryLock())
- // nLocks++;
- // }
- while(nLocks > 0)
- {
- EATEST_VERIFY_MSG(pWorkData->mRWSpinLockW.IsReadLocked(), "RWSpinlock failure: IsReadLocked\n");
- EATEST_VERIFY_MSG(!pWorkData->mRWSpinLockW.IsWriteLocked(), "RWSpinlock failure: IsWriteLocked\n");
- pWorkData->mRWSpinLockW.ReadUnlock();
- nLocks--;
- ThreadCooperativeYield();
- }
- }
- //if((rand() % 1000) < 3)
- // EA::UnitTest::ThreadSleepRandom(50, 100);
- }
- pWorkData->mnErrorCount.SetValue(nErrorCount);
- return nErrorCount;
- }
- static int TestThreadRWSpinLockW()
- {
- using namespace EA::Thread;
- int nErrorCount = 0;
- { // RWSpinLockW -- Basic single-threaded test.
- RWSpinLockW rwSpinLock; // There are no construction parameters.
- EATEST_VERIFY_MSG(!rwSpinLock.IsReadLocked(), "RWSpinLockW failure");
- EATEST_VERIFY_MSG(!rwSpinLock.IsWriteLocked(), "RWSpinLockW failure");
- rwSpinLock.ReadTryLock();
- EATEST_VERIFY_MSG( rwSpinLock.IsReadLocked(), "RWSpinLockW failure");
- EATEST_VERIFY_MSG(!rwSpinLock.IsWriteLocked(), "RWSpinLockW failure");
- EATEST_VERIFY_MSG(!rwSpinLock.WriteTryLock(), "RWSpinLockW failure");
- EATEST_VERIFY_MSG(!rwSpinLock.IsWriteLocked(), "RWSpinLockW failure");
- // Disabled because we don't support read lock recursion.
- // rwSpinLock.ReadLock();
- // EATEST_VERIFY_MSG(rwSpinLock.IsReadLocked(), "RWSpinLockW failure");
- // rwSpinLock.ReadUnlock();
- // EATEST_VERIFY_MSG(rwSpinLock.IsReadLocked(), "RWSpinLockW failure");
- rwSpinLock.ReadUnlock();
- EATEST_VERIFY_MSG(!rwSpinLock.IsReadLocked(), "RWSpinLockW failure");
- rwSpinLock.WriteTryLock();
- EATEST_VERIFY_MSG( rwSpinLock.IsWriteLocked(),"RWSpinLockW failure");
- EATEST_VERIFY_MSG(!rwSpinLock.IsReadLocked(), "RWSpinLockW failure");
- EATEST_VERIFY_MSG(!rwSpinLock.ReadTryLock(), "RWSpinLockW failure");
- EATEST_VERIFY_MSG(!rwSpinLock.IsReadLocked(), "RWSpinLockW failure");
- EATEST_VERIFY_MSG(!rwSpinLock.WriteTryLock(), "RWSpinLockW failure");
- }
- { // AutoRWSpinLockW -- Basic single-threaded test.
- RWSpinLockW rwSpinLock; // There are no construction parameters.
- { //Special scope just for the AutoRWSpinLockW
- AutoRWSpinLockW autoRWSpinLockW1(rwSpinLock, AutoRWSpinLockW::kLockTypeRead);
- AutoRWSpinLockW autoRWSpinLockW2(rwSpinLock, AutoRWSpinLockW::kLockTypeRead);
- EATEST_VERIFY_MSG( rwSpinLock.IsReadLocked(), "RWSpinLockW failure");
- EATEST_VERIFY_MSG(!rwSpinLock.IsWriteLocked(), "RWSpinLockW failure");
- EATEST_VERIFY_MSG(!rwSpinLock.WriteTryLock(), "RWSpinLockW failure");
- EATEST_VERIFY_MSG(!rwSpinLock.IsWriteLocked(), "RWSpinLockW failure");
- }
- EATEST_VERIFY_MSG(!rwSpinLock.IsReadLocked(), "RWSpinLockW failure");
- EATEST_VERIFY_MSG(!rwSpinLock.IsWriteLocked(), "RWSpinLockW failure");
- { //Special scope just for the AutoRWSpinLockW
- AutoRWSpinLockW autoRWSpinLockW(rwSpinLock, AutoRWSpinLockW::kLockTypeWrite);
- EATEST_VERIFY_MSG( rwSpinLock.IsWriteLocked(),"RWSpinLockW failure");
- EATEST_VERIFY_MSG(!rwSpinLock.IsReadLocked(), "RWSpinLockW failure");
- EATEST_VERIFY_MSG(!rwSpinLock.ReadTryLock(), "RWSpinLockW failure");
- EATEST_VERIFY_MSG(!rwSpinLock.IsReadLocked(), "RWSpinLockW failure");
- }
- EATEST_VERIFY_MSG(!rwSpinLock.IsReadLocked(), "RWSpinLockW failure");
- EATEST_VERIFY_MSG(!rwSpinLock.IsWriteLocked(), "RWSpinLockW failure");
- }
- #if EA_THREADS_AVAILABLE
- { // Multithreaded test
-
- RWSWorkData workData;
- Thread thread[kThreadCount];
- ThreadId threadId[kThreadCount];
- Thread::Status status;
- for(int i(0); i < kThreadCount; i++)
- threadId[i] = thread[i].Begin(RWSWThreadFunction, &workData);
- for(int e = 0; e < kRWSTestTypeCount; e++)
- {
- workData.mnCurrentTestType.SetValue(e);
- EA::UnitTest::ThreadSleepRandom(gTestLengthSeconds * 500, gTestLengthSeconds * 500);
- }
- workData.mbShouldQuit = true;
- for(int t(0); t < kThreadCount; t++)
- {
- if(threadId[t] != kThreadIdInvalid)
- {
- status = thread[t].WaitForEnd(GetThreadTime() + 30000);
- EATEST_VERIFY_MSG(status != Thread::kStatusRunning, "RWSpinlock/Thread failure: status == kStatusRunning.\n");
- }
- }
- nErrorCount += (int)workData.mnErrorCount;
- }
- #endif
- return nErrorCount;
- }
- ///////////////////////////////////////////////////////////////////////////////
- // RWSThreadFunction
- //
- static intptr_t RWSThreadFunction(void* pvWorkData)
- {
- using namespace EA::Thread;
- RWSWorkData* const pWorkData = (RWSWorkData*)pvWorkData;
- ThreadId threadId = GetThreadId();
- EA::UnitTest::ReportVerbosity(1, "RWSpinLock test function created: %s\n", EAThreadThreadIdToString(threadId));
- int nErrorCount = 0;
- while(!pWorkData->mbShouldQuit)
- {
- int nWriteLockChance = 0;
- const RWSTestType testType = (RWSTestType)pWorkData->mnCurrentTestType.GetValue();
- switch (testType)
- {
- default:
- case kRWSTestTypeStandard:
- nWriteLockChance = 20;
- break;
- case kRWSTestTypeAllWriters:
- nWriteLockChance = 1000;
- break;
- case kRWSTestTypeAllReaders:
- nWriteLockChance = 0;
- break;
- case kRWSTestTypeMostlyWriters:
- nWriteLockChance = 700;
- break;
- case kRWSTestTypeMostlyReaders:
- nWriteLockChance = 5;
- break;
- }
- const bool bShouldWrite = ((rand() % 1000) < nWriteLockChance);
- if(bShouldWrite)
- {
- pWorkData->mRWSpinLock.WriteLock();
- EATEST_VERIFY_MSG(!pWorkData->mRWSpinLock.IsReadLocked(), "RWSpinlock failure: IsReadLocked\n");
- EATEST_VERIFY_MSG(pWorkData->mRWSpinLock.IsWriteLocked(), "RWSpinlock failure: IsWriteLocked\n");
- pWorkData->mRWSpinLock.WriteUnlock();
- ThreadCooperativeYield();
- }
- else
- {
- const int nRecursiveLockCount = (rand() % 10) ? 1 : 2;
- int nLocks = 0;
- for(int i = 0; i < nRecursiveLockCount; i++)
- {
- pWorkData->mRWSpinLock.ReadLock();
- nLocks++;
- ThreadCooperativeYield();
- }
- if((rand() % 10) == 0)
- {
- if(pWorkData->mRWSpinLock.ReadTryLock())
- nLocks++;
- }
- while(nLocks > 0)
- {
- const int32_t n = pWorkData->mRWSpinLock.mValue; (void)n;
- // It turns out IsReadLocked has a bug and can return a false negative.
- // EATEST_VERIFY_MSG(pWorkData->mRWSpinLock.IsReadLocked(), "RWSpinlock failure: IsReadLocked\n");
- // EATEST_VERIFY_MSG(!pWorkData->mRWSpinLock.IsWriteLocked(), "RWSpinlock failure: IsWriteLocked\n");
- pWorkData->mRWSpinLock.ReadUnlock();
- nLocks--;
- ThreadCooperativeYield();
- }
- }
- }
- pWorkData->mnErrorCount.SetValue(nErrorCount);
- return nErrorCount;
- }
- int TestThreadRWSpinLock()
- {
- using namespace EA::Thread;
- int nErrorCount = 0;
- { // RWSpinLock -- Basic single-threaded test.
- RWSpinLock rwSpinLock; // There are no construction parameters.
- EATEST_VERIFY_MSG(!rwSpinLock.IsReadLocked(), "RWSpinLock failure");
- EATEST_VERIFY_MSG(!rwSpinLock.IsWriteLocked(), "RWSpinLock failure");
- rwSpinLock.ReadTryLock();
- EATEST_VERIFY_MSG(rwSpinLock.IsReadLocked(), "RWSpinLock failure");
- EATEST_VERIFY_MSG(!rwSpinLock.IsWriteLocked(), "RWSpinLock failure");
- EATEST_VERIFY_MSG(!rwSpinLock.WriteTryLock(), "RWSpinLock failure");
- EATEST_VERIFY_MSG(!rwSpinLock.IsWriteLocked(), "RWSpinLock failure");
- rwSpinLock.ReadLock();
- EATEST_VERIFY_MSG(rwSpinLock.IsReadLocked(), "RWSpinLock failure");
- rwSpinLock.ReadUnlock();
- EATEST_VERIFY_MSG(rwSpinLock.IsReadLocked(), "RWSpinLock failure");
- rwSpinLock.ReadUnlock();
- EATEST_VERIFY_MSG(!rwSpinLock.IsReadLocked(), "RWSpinLock failure");
- rwSpinLock.WriteTryLock();
- EATEST_VERIFY_MSG(rwSpinLock.IsWriteLocked(), "RWSpinLock failure");
- EATEST_VERIFY_MSG(!rwSpinLock.IsReadLocked(), "RWSpinLock failure");
- EATEST_VERIFY_MSG(!rwSpinLock.ReadTryLock(), "RWSpinLock failure");
- EATEST_VERIFY_MSG(!rwSpinLock.IsReadLocked(), "RWSpinLock failure");
- EATEST_VERIFY_MSG(!rwSpinLock.WriteTryLock(), "RWSpinLock failure");
- }
- { // AutoRWSpinLock -- Basic single-threaded test.
- RWSpinLock rwSpinLock; // There are no construction parameters.
- { //Special scope just for the AutoRWSpinLock
- AutoRWSpinLock autoRWSpinLock1(rwSpinLock, AutoRWSpinLock::kLockTypeRead);
- AutoRWSpinLock autoRWSpinLock2(rwSpinLock, AutoRWSpinLock::kLockTypeRead);
- EATEST_VERIFY_MSG(rwSpinLock.IsReadLocked(), "RWSpinLock failure");
- EATEST_VERIFY_MSG(!rwSpinLock.IsWriteLocked(), "RWSpinLock failure");
- EATEST_VERIFY_MSG(!rwSpinLock.WriteTryLock(), "RWSpinLock failure");
- EATEST_VERIFY_MSG(!rwSpinLock.IsWriteLocked(), "RWSpinLock failure");
- }
- EATEST_VERIFY_MSG(!rwSpinLock.IsReadLocked(), "RWSpinLock failure");
- EATEST_VERIFY_MSG(!rwSpinLock.IsWriteLocked(), "RWSpinLock failure");
- { //Special scope just for the AutoRWSpinLock
- AutoRWSpinLock autoRWSpinLock(rwSpinLock, AutoRWSpinLock::kLockTypeWrite);
- EATEST_VERIFY_MSG(rwSpinLock.IsWriteLocked(), "RWSpinLock failure");
- EATEST_VERIFY_MSG(!rwSpinLock.IsReadLocked(), "RWSpinLock failure");
- EATEST_VERIFY_MSG(!rwSpinLock.ReadTryLock(), "RWSpinLock failure");
- EATEST_VERIFY_MSG(!rwSpinLock.IsReadLocked(), "RWSpinLock failure");
- }
- EATEST_VERIFY_MSG(!rwSpinLock.IsReadLocked(), "RWSpinLock failure");
- EATEST_VERIFY_MSG(!rwSpinLock.IsWriteLocked(), "RWSpinLock failure");
- }
- #if EA_THREADS_AVAILABLE
- { // Multithreaded test
-
- RWSWorkData workData;
- Thread thread[kThreadCount];
- ThreadId threadId[kThreadCount];
- Thread::Status status;
- for(int i(0); i < kThreadCount; i++)
- threadId[i] = thread[i].Begin(RWSThreadFunction, &workData);
- for(int e = 0; e < kRWSTestTypeCount; e++)
- {
- workData.mnCurrentTestType.SetValue(e);
- EA::UnitTest::ThreadSleepRandom(gTestLengthSeconds * 500, gTestLengthSeconds * 500);
- }
- workData.mbShouldQuit = true;
- for(int t(0); t < kThreadCount; t++)
- {
- if(threadId[t] != kThreadIdInvalid)
- {
- status = thread[t].WaitForEnd(GetThreadTime() + 30000);
- EATEST_VERIFY_MSG(status != Thread::kStatusRunning, "RWSpinlock/Thread failure: status == kStatusRunning.\n");
- }
- }
- nErrorCount += (int)workData.mnErrorCount;
- }
- #endif
- // TestThreadRWSpinLockW
- nErrorCount += TestThreadRWSpinLockW();
- return nErrorCount;
- }
|