TestThreadRWMutex.cpp 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217
  1. ///////////////////////////////////////////////////////////////////////////////
  2. // Copyright (c) Electronic Arts Inc. All rights reserved.
  3. ///////////////////////////////////////////////////////////////////////////////
  4. #include "TestThread.h"
  5. #include <EATest/EATest.h>
  6. #include <eathread/eathread_rwmutex.h>
  7. #include <eathread/eathread_thread.h>
  8. #include <stdlib.h>
  9. using namespace EA::Thread;
  10. const int kMaxConcurrentThreadCount = EATHREAD_MAX_CONCURRENT_THREAD_COUNT;
  11. struct RWMWorkData
  12. {
  13. volatile bool mbShouldQuit;
  14. RWMutex mRWMutex;
  15. volatile int mnExpectedValue;
  16. volatile int mnCalculatedValue;
  17. AtomicInt32 mnErrorCount;
  18. RWMWorkData() : mbShouldQuit(false), mRWMutex(NULL, true), mnExpectedValue(0),
  19. mnCalculatedValue(0), mnErrorCount(0) {}
  20. // define copy ctor and assignment operator
  21. // so the compiler does define them intrisically
  22. RWMWorkData(const RWMWorkData& rhs); // copy constructor
  23. RWMWorkData& operator=(const RWMWorkData& rhs); // assignment operator
  24. };
  25. static intptr_t ReaderFunction(void* pvWorkData)
  26. {
  27. int nErrorCount = 0;
  28. RWMWorkData* pWorkData = (RWMWorkData*)pvWorkData;
  29. ThreadId threadId = GetThreadId();
  30. EA::UnitTest::ReportVerbosity(1, "RWMutex reader test function created: %s\n", EAThreadThreadIdToString(threadId));
  31. while(!pWorkData->mbShouldQuit)
  32. {
  33. const int nRecursiveLockCount(rand() % 3);
  34. int i, nLockResult, nLocks = 0;
  35. for(i = 0; i < nRecursiveLockCount; i++)
  36. {
  37. // Do a lock but allow for the possibility of occasional timeout.
  38. nLockResult = pWorkData->mRWMutex.Lock(RWMutex::kLockTypeRead, GetThreadTime() + 20);
  39. EATEST_VERIFY_MSG(nLockResult != RWMutex::kResultError, "RWMutex failure");
  40. if(nLockResult > 0)
  41. {
  42. nLocks++;
  43. EA::UnitTest::ReportVerbosity(2, "CValue = %d; EValue = %d\n", pWorkData->mnCalculatedValue, pWorkData->mnExpectedValue);
  44. EATEST_VERIFY_MSG(pWorkData->mnCalculatedValue == pWorkData->mnExpectedValue, "RWMutex failure");
  45. }
  46. ThreadCooperativeYield(); // Used by cooperative threading platforms.
  47. }
  48. while(nLocks > 0)
  49. {
  50. // Verify no write locks are set.
  51. nLockResult = pWorkData->mRWMutex.GetLockCount(RWMutex::kLockTypeWrite);
  52. EATEST_VERIFY_MSG(nLockResult == 0, "RWMutex failure");
  53. // Verify at least N read locks are set.
  54. nLockResult = pWorkData->mRWMutex.GetLockCount(RWMutex::kLockTypeRead);
  55. EATEST_VERIFY_MSG(nLockResult >= nLocks, "RWMutex failure");
  56. // Verify there is one less read lock set.
  57. nLockResult = pWorkData->mRWMutex.Unlock();
  58. EATEST_VERIFY_MSG(nLockResult >= nLocks-1, "RWMutex failure");
  59. nLocks--;
  60. ThreadCooperativeYield(); // Used by cooperative threading platforms.
  61. }
  62. EA::UnitTest::ThreadSleepRandom(100, 200);
  63. }
  64. pWorkData->mnErrorCount += nErrorCount;
  65. return 0;
  66. }
  67. static intptr_t WriterFunction(void* pvWorkData)
  68. {
  69. int nErrorCount = 0;
  70. RWMWorkData* pWorkData = (RWMWorkData*)pvWorkData;
  71. ThreadId threadId = GetThreadId();
  72. EA::UnitTest::ReportVerbosity(1, "RWMutex writer test function created: %s\n", EAThreadThreadIdToString(threadId));
  73. while(!pWorkData->mbShouldQuit)
  74. {
  75. // Do a lock but allow for the possibility of occasional timeout.
  76. int nLockResult = pWorkData->mRWMutex.Lock(RWMutex::kLockTypeWrite, GetThreadTime() + 30);
  77. EATEST_VERIFY_MSG(nLockResult != RWMutex::kResultError, "RWMutex failure");
  78. ThreadCooperativeYield(); // Used by cooperative threading platforms.
  79. if(nLockResult > 0)
  80. {
  81. // Verify exactly one write lock is set.
  82. nLockResult = pWorkData->mRWMutex.GetLockCount(RWMutex::kLockTypeWrite);
  83. EATEST_VERIFY_MSG(nLockResult == 1, "RWMutex failure");
  84. // What we do here is spend some time manipulating mnExpectedValue and mnCalculatedValue
  85. // while we have the write lock. We change their values in a predicable way but before
  86. // we are done mnCalculatedValue has been incremented by one and both values are equal.
  87. const uintptr_t x = (uintptr_t)pWorkData;
  88. pWorkData->mnExpectedValue = -1;
  89. EA::UnitTest::ThreadSleepRandom(10, 20);
  90. pWorkData->mnCalculatedValue *= 50;
  91. EA::UnitTest::ThreadSleepRandom(10, 20);
  92. pWorkData->mnCalculatedValue /= (int)(((x + 1) / x) * 50); // This will always be the same as simply '/= 50'.
  93. EA::UnitTest::ThreadSleepRandom(10, 20);
  94. pWorkData->mnCalculatedValue += 1;
  95. EA::UnitTest::ThreadSleepRandom(10, 20);
  96. pWorkData->mnExpectedValue = pWorkData->mnCalculatedValue;
  97. // Verify no read locks are set.
  98. nLockResult = pWorkData->mRWMutex.GetLockCount(RWMutex::kLockTypeRead);
  99. EATEST_VERIFY_MSG(nLockResult == 0, "RWMutex failure");
  100. // Verify exactly one write lock is set.
  101. nLockResult = pWorkData->mRWMutex.GetLockCount(RWMutex::kLockTypeWrite);
  102. EATEST_VERIFY_MSG(nLockResult == 1, "RWMutex failure");
  103. // Verify there are now zero write locks set.
  104. nLockResult = pWorkData->mRWMutex.Unlock();
  105. EATEST_VERIFY_MSG(nLockResult == 0, "RWMutex failure");
  106. EA::UnitTest::ThreadSleepRandom(400, 800);
  107. }
  108. }
  109. pWorkData->mnErrorCount += nErrorCount;
  110. return 0;
  111. }
  112. int TestThreadRWMutex()
  113. {
  114. int nErrorCount(0);
  115. // Be careful adding when adding more mutexes to this test as the the CTR runs out of
  116. // resources pretty early.
  117. {
  118. RWMutexParameters mp1(true, NULL);
  119. RWMutexParameters mp2(true, "mp2");
  120. {
  121. RWMutex mutex1(&mp1, false);
  122. RWMutex mutex2(&mp2, false);
  123. }
  124. {
  125. RWMutex mutex5(NULL, true);
  126. RWMutex mutex6(NULL, false);
  127. mutex6.Init(&mp1);
  128. }
  129. {
  130. RWMutex mutex1(&mp1, false);
  131. AutoRWMutex am1(mutex1, RWMutex::kLockTypeRead);
  132. }
  133. }
  134. #if EA_THREADS_AVAILABLE
  135. {
  136. RWMWorkData workData;
  137. const int kThreadCount(kMaxConcurrentThreadCount - 1);
  138. Thread thread[kThreadCount];
  139. ThreadId threadId[kThreadCount];
  140. Thread::Status status;
  141. for(int i(0); i < kThreadCount; i++)
  142. {
  143. if(i < (kThreadCount * 3 / 4))
  144. threadId[i] = thread[i].Begin(ReaderFunction, &workData);
  145. else
  146. threadId[i] = thread[i].Begin(WriterFunction, &workData);
  147. }
  148. EA::UnitTest::ThreadSleepRandom(gTestLengthSeconds * 1000, gTestLengthSeconds * 1000);
  149. workData.mbShouldQuit = true;
  150. for(int i(0); i < kThreadCount; i++)
  151. {
  152. if(threadId[i] != kThreadIdInvalid)
  153. {
  154. status = thread[i].WaitForEnd(GetThreadTime() + 30000);
  155. EATEST_VERIFY_MSG(status != Thread::kStatusRunning, "RWMutex/Thread failure: status == kStatusRunning.");
  156. }
  157. }
  158. nErrorCount += (int)workData.mnErrorCount;
  159. }
  160. #endif
  161. return nErrorCount;
  162. }