TestThreadRWSemaLock.cpp 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259
  1. ///////////////////////////////////////////////////////////////////////////////
  2. // Copyright (c) Electronic Arts Inc. All rights reserved.
  3. ///////////////////////////////////////////////////////////////////////////////
  4. #include "TestThread.h"
  5. #include <EATest/EATest.h>
  6. #include <eathread/eathread_thread.h>
  7. #include <eathread/eathread_rwsemalock.h>
  8. #include <stdlib.h>
  9. const int kThreadCount = EATHREAD_MAX_CONCURRENT_THREAD_COUNT;
  10. ///////////////////////////////////////////////////////////////////////////////
  11. // RWSTestType
  12. //
  13. enum RWSTestType
  14. {
  15. kRWSTestTypeStandard,
  16. kRWSTestTypeAllWriters,
  17. kRWSTestTypeAllReaders,
  18. kRWSTestTypeMostlyWriters,
  19. kRWSTestTypeMostlyReaders,
  20. kRWSTestTypeCount
  21. };
  22. ///////////////////////////////////////////////////////////////////////////////
  23. // RWSemaWorkData
  24. //
  25. struct RWSemaWorkData
  26. {
  27. volatile bool mbShouldQuit;
  28. EA::Thread::RWSemaLock mRWSemaLock;
  29. volatile int mnWriterCount;
  30. EA::Thread::AtomicInt32 mnErrorCount;
  31. EA::Thread::AtomicInt32 mnCurrentTestType;
  32. RWSemaWorkData()
  33. : mbShouldQuit(false)
  34. , mRWSemaLock()
  35. , mnWriterCount(0)
  36. , mnErrorCount(0)
  37. , mnCurrentTestType(kRWSTestTypeStandard) {}
  38. private:
  39. RWSemaWorkData(const RWSemaWorkData& rhs);
  40. RWSemaWorkData& operator=(const RWSemaWorkData& rhs);
  41. };
  42. ///////////////////////////////////////////////////////////////////////////////
  43. // RWSThreadFunction
  44. //
  45. static intptr_t RWSThreadFunction(void* pvWorkData)
  46. {
  47. using namespace EA::Thread;
  48. RWSemaWorkData* const pWorkData = (RWSemaWorkData*)pvWorkData;
  49. ThreadId threadId = GetThreadId();
  50. EA::UnitTest::ReportVerbosity(1, "RWSemaLock test function created: %s\n", EAThreadThreadIdToString(threadId));
  51. int nErrorCount = 0;
  52. while(!pWorkData->mbShouldQuit)
  53. {
  54. int nWriteLockChance = 0;
  55. const RWSTestType testType = (RWSTestType)pWorkData->mnCurrentTestType.GetValue();
  56. switch (testType)
  57. {
  58. default:
  59. case kRWSTestTypeStandard:
  60. nWriteLockChance = 20;
  61. break;
  62. case kRWSTestTypeAllWriters:
  63. nWriteLockChance = 1000;
  64. break;
  65. case kRWSTestTypeAllReaders:
  66. nWriteLockChance = 0;
  67. break;
  68. case kRWSTestTypeMostlyWriters:
  69. nWriteLockChance = 700;
  70. break;
  71. case kRWSTestTypeMostlyReaders:
  72. nWriteLockChance = 5;
  73. break;
  74. }
  75. const bool bShouldWrite = ((rand() % 1000) < nWriteLockChance);
  76. if(bShouldWrite)
  77. {
  78. AutoSemaWriteLock _(pWorkData->mRWSemaLock);
  79. pWorkData->mnWriterCount++;
  80. EA::UnitTest::ThreadSleepRandom(2, 10);
  81. pWorkData->mnWriterCount--;
  82. }
  83. else
  84. {
  85. AutoSemaReadLock _(pWorkData->mRWSemaLock);
  86. EATEST_VERIFY_MSG(pWorkData->mnWriterCount == 0, "ReadLock is held, there should be no active WriteLocks.");
  87. }
  88. }
  89. pWorkData->mnErrorCount.SetValue(nErrorCount);
  90. return nErrorCount;
  91. }
  92. // NOTE(rparolin):
  93. // This exists to introduce test-only functionality for the RWSemaLock. We can add these functions here because we
  94. // guarantee they will not be called in a concurrent context and they simplify validation of assumption of the lock.
  95. struct TestRWSemaLock : public EA::Thread::RWSemaLock
  96. {
  97. TestRWSemaLock() = default;
  98. TestRWSemaLock(const TestRWSemaLock&) = delete;
  99. TestRWSemaLock(TestRWSemaLock&&) = delete;
  100. TestRWSemaLock& operator=(const TestRWSemaLock&) = delete;
  101. TestRWSemaLock& operator=(TestRWSemaLock&&) = delete;
  102. bool IsReadLocked()
  103. {
  104. Status status;
  105. status.data = mStatus.GetValue();
  106. return status.readers > 0;
  107. }
  108. bool IsWriteLocked()
  109. {
  110. Status status;
  111. status.data = mStatus.GetValue();
  112. return status.writers > 0;
  113. }
  114. };
  115. int TestThreadRWSemaLock()
  116. {
  117. using namespace EA::Thread;
  118. int nErrorCount = 0;
  119. { // RWSemaLock -- Basic single-threaded test.
  120. TestRWSemaLock rwSemaLock; // There are no construction parameters.
  121. EATEST_VERIFY_MSG(!rwSemaLock.IsReadLocked(), "RWSemaLock failure");
  122. EATEST_VERIFY_MSG(!rwSemaLock.IsWriteLocked(), "RWSemaLock failure");
  123. rwSemaLock.ReadTryLock();
  124. EATEST_VERIFY_MSG(rwSemaLock.IsReadLocked(), "RWSemaLock failure");
  125. EATEST_VERIFY_MSG(!rwSemaLock.IsWriteLocked(), "RWSemaLock failure");
  126. EATEST_VERIFY_MSG(!rwSemaLock.WriteTryLock(), "RWSemaLock failure");
  127. EATEST_VERIFY_MSG(!rwSemaLock.IsWriteLocked(), "RWSemaLock failure");
  128. rwSemaLock.ReadLock();
  129. EATEST_VERIFY_MSG(rwSemaLock.IsReadLocked(), "RWSemaLock failure");
  130. rwSemaLock.ReadUnlock();
  131. EATEST_VERIFY_MSG(rwSemaLock.IsReadLocked(), "RWSemaLock failure");
  132. rwSemaLock.ReadUnlock();
  133. EATEST_VERIFY_MSG(!rwSemaLock.IsReadLocked(), "RWSemaLock failure");
  134. rwSemaLock.WriteTryLock();
  135. EATEST_VERIFY_MSG(rwSemaLock.IsWriteLocked(), "RWSemaLock failure");
  136. EATEST_VERIFY_MSG(!rwSemaLock.IsReadLocked(), "RWSemaLock failure");
  137. EATEST_VERIFY_MSG(!rwSemaLock.ReadTryLock(), "RWSemaLock failure");
  138. EATEST_VERIFY_MSG(!rwSemaLock.IsReadLocked(), "RWSemaLock failure");
  139. EATEST_VERIFY_MSG(!rwSemaLock.WriteTryLock(), "RWSemaLock failure");
  140. }
  141. { // AutoRWSemaLock -- Basic single-threaded test.
  142. TestRWSemaLock rwSemaLock; // There are no construction parameters.
  143. { //Special scope just for the AutoRWSemaLock
  144. AutoSemaReadLock autoRWSemaLock1(rwSemaLock);
  145. AutoSemaReadLock autoRWSemaLock2(rwSemaLock);
  146. EATEST_VERIFY_MSG(rwSemaLock.IsReadLocked(), "RWSemaLock failure");
  147. EATEST_VERIFY_MSG(!rwSemaLock.IsWriteLocked(), "RWSemaLock failure");
  148. EATEST_VERIFY_MSG(!rwSemaLock.WriteTryLock(), "RWSemaLock failure");
  149. EATEST_VERIFY_MSG(!rwSemaLock.IsWriteLocked(), "RWSemaLock failure");
  150. }
  151. EATEST_VERIFY_MSG(!rwSemaLock.IsReadLocked(), "RWSemaLock failure");
  152. EATEST_VERIFY_MSG(!rwSemaLock.IsWriteLocked(), "RWSemaLock failure");
  153. { //Special scope just for the AutoRWSemaLock
  154. AutoSemaWriteLock autoRWSemaLock(rwSemaLock);
  155. EATEST_VERIFY_MSG(rwSemaLock.IsWriteLocked(), "RWSemaLock failure");
  156. EATEST_VERIFY_MSG(!rwSemaLock.IsReadLocked(), "RWSemaLock failure");
  157. EATEST_VERIFY_MSG(!rwSemaLock.ReadTryLock(), "RWSemaLock failure");
  158. EATEST_VERIFY_MSG(!rwSemaLock.IsReadLocked(), "RWSemaLock failure");
  159. }
  160. EATEST_VERIFY_MSG(!rwSemaLock.IsReadLocked(), "RWSemaLock failure");
  161. EATEST_VERIFY_MSG(!rwSemaLock.IsWriteLocked(), "RWSemaLock failure");
  162. }
  163. #if EA_THREADS_AVAILABLE
  164. { // Multithreaded test
  165. RWSemaWorkData workData;
  166. Thread thread[kThreadCount];
  167. ThreadId threadId[kThreadCount];
  168. Thread::Status status;
  169. for(int i(0); i < kThreadCount; i++)
  170. threadId[i] = thread[i].Begin(RWSThreadFunction, &workData);
  171. for(int e = 0; e < kRWSTestTypeCount; e++)
  172. {
  173. workData.mnCurrentTestType.SetValue(e);
  174. EA::UnitTest::ThreadSleepRandom(gTestLengthSeconds * 500, gTestLengthSeconds * 500);
  175. }
  176. workData.mbShouldQuit = true;
  177. for(int t(0); t < kThreadCount; t++)
  178. {
  179. if(threadId[t] != kThreadIdInvalid)
  180. {
  181. status = thread[t].WaitForEnd(GetThreadTime() + 30000);
  182. EATEST_VERIFY_MSG(status != Thread::kStatusRunning, "RWSemalock/Thread failure: status == kStatusRunning.\n");
  183. }
  184. }
  185. nErrorCount += (int)workData.mnErrorCount;
  186. }
  187. #endif
  188. return nErrorCount;
  189. }