TestThreadInterprocessRWMutex.cpp 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259
  1. ///////////////////////////////////////////////////////////////////////////////
  2. // TestThreadInterprocessRWMutex.cpp
  3. //
  4. // Copyright (c) 2009, Electronic Arts Inc. All rights reserved.
  5. // Created by Paul Pedriana
  6. ///////////////////////////////////////////////////////////////////////////////
  7. #include <EATest/EATest.h>
  8. #include <eathread/eathread.h>
  9. #include <eathread/eathread_thread.h>
  10. #include <eathread/eathread_atomic.h>
  11. #include <eathread/eathread_rwmutex_ip.h>
  12. #include "TestThreadInterprocess.h"
  13. #include <stdlib.h>
  14. struct RWMWorkDataInterProcess
  15. {
  16. volatile int mnExpectedValue; // Intentionally not an atomic variable.
  17. volatile int mnCalculatedValue; // Intentionally not an atomic variable.
  18. volatile int mnWriteLockCount; // How many times the write lock was owned, across all processes.
  19. RWMWorkDataInterProcess()
  20. : mnExpectedValue(0),
  21. mnCalculatedValue(0),
  22. mnWriteLockCount(0)
  23. {
  24. printf("RWMWorkDataInterProcess\n");
  25. }
  26. ~RWMWorkDataInterProcess()
  27. {
  28. printf("~RWMWorkDataInterProcess\n");
  29. }
  30. };
  31. struct RWMWorkDataInterThread
  32. {
  33. volatile bool mbShouldQuit; //
  34. EA::Thread::RWMutexIP mRWMutexIP; //
  35. EA::Thread::AtomicInt32 mnThreadIndex; //
  36. EA::Thread::AtomicInt32 mnErrorCount; //
  37. EA::Thread::AtomicInt32 mnReadLockCount; // How many times the read lock was owned, within this process.
  38. EA::Thread::AtomicInt32 mnWriteLockCount; // How many times the write lock was owned, within this process.
  39. RWMWorkDataInterThread()
  40. : mbShouldQuit(false),
  41. mRWMutexIP(NULL, false),
  42. mnThreadIndex(0),
  43. mnErrorCount(0),
  44. mnReadLockCount(0),
  45. mnWriteLockCount(0)
  46. {
  47. }
  48. protected:
  49. RWMWorkDataInterThread(const RWMWorkDataInterThread& rhs);
  50. RWMWorkDataInterThread& operator=(const RWMWorkDataInterThread& rhs);
  51. };
  52. static intptr_t RWThreadFunction(void* pvWorkData)
  53. {
  54. using namespace EA::Thread;
  55. int nErrorCount = 0;
  56. RWMWorkDataInterThread* pWorkData = (RWMWorkDataInterThread*)pvWorkData;
  57. ThreadId threadId = GetThreadId();
  58. EA::UnitTest::ReportVerbosity(1, "RWMutexIP test function created: %08x\n", (int)(intptr_t)threadId);
  59. // We use the interprocess mutex to control access to an interprocess data struct.
  60. Shared<RWMWorkDataInterProcess> gSharedData("RWMWorkDataIP");
  61. // We track the amount of time we spend waiting for Locks.
  62. //ThreadTime nInitialTime, nFinalTime;
  63. const ThreadTime kMaxExpectedTime = 1000;
  64. while(!pWorkData->mbShouldQuit)
  65. {
  66. const bool bWriteLock((rand() % 10) == 0); // 10% of the time, do a write lock.
  67. if(bWriteLock)
  68. {
  69. //nInitialTime = EA::Thread::GetThreadTime();
  70. int nLockResult = pWorkData->mRWMutexIP.Lock(RWMutexIP::kLockTypeWrite, GetThreadTime() + kMaxExpectedTime);
  71. EATEST_VERIFY_MSG(nLockResult != RWMutexIP::kResultError, "RWMutexIP failure: write lock.");
  72. //nFinalTime = EA::Thread::GetThreadTime();
  73. //EATEST_VERIFY_MSG((nFinalTime - nInitialTime) < kMaxExpectedTime, "RWMutexIP failure: write lock slow.");
  74. if(nLockResult > 0)
  75. {
  76. gSharedData->mnWriteLockCount++;
  77. pWorkData->mnWriteLockCount++;
  78. // Verify exactly one write lock is set.
  79. nLockResult = pWorkData->mRWMutexIP.GetLockCount(RWMutexIP::kLockTypeWrite);
  80. EATEST_VERIFY_MSG(nLockResult == 1, "RWMutexIP failure: write lock verify 1.");
  81. // What we do here is spend some time manipulating mnExpectedValue and mnCalculatedValue
  82. // while we have the write lock. We change their values in a predicable way but before
  83. // we are done mnCalculatedValue has been incremented by one and both values are equal.
  84. const uintptr_t x = (uintptr_t)pWorkData;
  85. gSharedData->mnExpectedValue = -1;
  86. EA::UnitTest::ThreadSleepRandom(10, 20);
  87. gSharedData->mnCalculatedValue *= 50;
  88. EA::UnitTest::ThreadSleepRandom(10, 20);
  89. gSharedData->mnCalculatedValue /= (int)(((x + 1) / x) * 50); // This will always be the same as simply '/= 50'.
  90. EA::UnitTest::ThreadSleepRandom(10, 20);
  91. gSharedData->mnCalculatedValue += 1;
  92. EA::UnitTest::ThreadSleepRandom(10, 20);
  93. gSharedData->mnExpectedValue = gSharedData->mnCalculatedValue;
  94. // Verify no read locks are set.
  95. nLockResult = pWorkData->mRWMutexIP.GetLockCount(RWMutexIP::kLockTypeRead);
  96. EATEST_VERIFY_MSG(nLockResult == 0, "RWMutexIP failure: write lock verify 2.");
  97. // Verify exactly one write lock is set.
  98. nLockResult = pWorkData->mRWMutexIP.GetLockCount(RWMutexIP::kLockTypeWrite);
  99. EATEST_VERIFY_MSG(nLockResult == 1, "RWMutexIP failure: write lock verify 3.");
  100. // Verify there are now zero write locks set.
  101. nLockResult = pWorkData->mRWMutexIP.Unlock();
  102. EATEST_VERIFY_MSG(nLockResult == 0, "RWMutexIP failure: write unlock.");
  103. EA::UnitTest::ThreadSleepRandom(40, 80);
  104. }
  105. }
  106. else
  107. {
  108. const int nRecursiveLockCount(rand() % 2);
  109. int i, nLockResult, nLocks = 0;
  110. for(i = 0; i < nRecursiveLockCount; i++)
  111. {
  112. //nInitialTime = EA::Thread::GetThreadTime();
  113. nLockResult = pWorkData->mRWMutexIP.Lock(RWMutexIP::kLockTypeRead, GetThreadTime() + kMaxExpectedTime);
  114. //nFinalTime = EA::Thread::GetThreadTime();
  115. //EATEST_VERIFY_MSG(nLockResult != RWMutexIP::kResultError, "RWMutexIP failure: read lock.");
  116. if(nLockResult > 0)
  117. {
  118. nLocks++;
  119. pWorkData->mnReadLockCount++;
  120. EA::UnitTest::ReportVerbosity(2, "CValue = %d; EValue = %d\n", gSharedData->mnCalculatedValue, gSharedData->mnExpectedValue);
  121. EATEST_VERIFY_MSG(gSharedData->mnCalculatedValue == gSharedData->mnExpectedValue, "RWMutexIP failure: read lock 2");
  122. }
  123. }
  124. while(nLocks > 0)
  125. {
  126. // Verify no write locks are set.
  127. nLockResult = pWorkData->mRWMutexIP.GetLockCount(RWMutexIP::kLockTypeWrite);
  128. EATEST_VERIFY_MSG(nLockResult == 0, "RWMutexIP failure: read lock verify 1.");
  129. // Verify at least N read locks are set.
  130. nLockResult = pWorkData->mRWMutexIP.GetLockCount(RWMutexIP::kLockTypeRead);
  131. EATEST_VERIFY_MSG(nLockResult >= nLocks, "RWMutexIP failure: read lock verify 2.");
  132. // Verify there is one less read lock set.
  133. nLockResult = pWorkData->mRWMutexIP.Unlock();
  134. EATEST_VERIFY_MSG(nLockResult >= nLocks-1, "RWMutexIP failure: read unlock.");
  135. nLocks--;
  136. }
  137. EA::UnitTest::ThreadSleepRandom(10, 20);
  138. }
  139. }
  140. pWorkData->mnErrorCount += nErrorCount;
  141. return 0;
  142. }
  143. int TestThreadRWMutex()
  144. {
  145. using namespace EA::Thread;
  146. int nErrorCount(0);
  147. EA::UnitTest::Report("Thread Pool Test\n");
  148. /*
  149. { // ctor tests
  150. // We test various combinations of RWMutexIP ctor and RWMutexIPParameters.
  151. // RWMutexIPParameters(bool bIntraProcess = true, const char* pName = NULL);
  152. // RWMutexIP(const RWMutexIPParameters* pRWMutexIPParameters = NULL, bool bDefaultParameters = true);
  153. //RWMutexIPParameters mp1(true, NULL);
  154. //RWMutexIPParameters mp2(true, "mp2");
  155. //RWMutexIPParameters mp3(false, "mp3");
  156. RWMutexIPParameters mp4(false, "mp4");
  157. RWMutexIPParameters mp6(false, "mp6");
  158. //RWMutexIP mutex1(&mp1, false);
  159. //RWMutexIP mutex2(&mp2, false);
  160. //RWMutexIP mutex3(&mp3, false);
  161. RWMutexIP mutex4(&mp4, false);
  162. //RWMutexIP mutex5(NULL, true);
  163. RWMutexIP mutex6(NULL, false);
  164. mutex6.Init(&mp6);
  165. //AutoRWMutexIP am1(mutex1, RWMutexIP::kLockTypeRead);
  166. //AutoRWMutexIP am2(mutex2, RWMutexIP::kLockTypeRead);
  167. //AutoRWMutexIP am3(mutex3, RWMutexIP::kLockTypeRead);
  168. AutoRWMutexIP am4(mutex4, RWMutexIP::kLockTypeRead);
  169. AutoRWMutexIP am6(mutex6, RWMutexIP::kLockTypeRead);
  170. }
  171. */
  172. {
  173. RWMWorkDataInterThread workData;
  174. RWMutexIPParameters rwMutexIPParameters(false, "RWMTest");
  175. // Set up the RWMWorkData
  176. workData.mRWMutexIP.Init(&rwMutexIPParameters);
  177. // Create the threads
  178. Thread* pThreadArray = new Thread[gTestThreadCount];
  179. ThreadId* pThreadIdArray = new ThreadId[gTestThreadCount];
  180. Thread::Status status;
  181. for(unsigned i(0); i < gTestThreadCount; i++)
  182. pThreadIdArray[i] = pThreadArray[i].Begin(RWThreadFunction, &workData);
  183. EA::UnitTest::ThreadSleepRandom(gTestLengthSeconds * 1000, gTestLengthSeconds * 1000);
  184. workData.mbShouldQuit = true;
  185. for(unsigned i(0); i < gTestThreadCount; i++)
  186. {
  187. if(pThreadIdArray[i] != kThreadIdInvalid)
  188. {
  189. status = pThreadArray[i].WaitForEnd(GetThreadTime() + 30000);
  190. EATEST_VERIFY_MSG(status != Thread::kStatusRunning, "RWMutexIP/Thread failure: status == kStatusRunning.");
  191. }
  192. }
  193. delete[] pThreadIdArray;
  194. delete[] pThreadArray;
  195. nErrorCount += (int)workData.mnErrorCount;
  196. }
  197. return nErrorCount;
  198. }