TestThreadMutex.cpp 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233
  1. ///////////////////////////////////////////////////////////////////////////////
  2. // Copyright (c) Electronic Arts Inc. All rights reserved.
  3. ///////////////////////////////////////////////////////////////////////////////
  4. #include "TestThread.h"
  5. #include <EATest/EATest.h>
  6. #include <eathread/eathread_mutex.h>
  7. #include <eathread/eathread_thread.h>
  8. #include <eathread/eathread_atomic.h>
  9. #include <stdlib.h>
  10. using namespace EA::Thread;
  11. const int kMaxConcurrentThreadCount = EATHREAD_MAX_CONCURRENT_THREAD_COUNT;
  12. struct MWorkData
  13. {
  14. volatile bool mbShouldQuit;
  15. Mutex mMutex;
  16. volatile int mnExpectedValue;
  17. volatile int mnCalculatedValue;
  18. AtomicInt32 mnErrorCount;
  19. MWorkData() : mbShouldQuit(false), mMutex(NULL, true),
  20. mnExpectedValue(0), mnCalculatedValue(0),
  21. mnErrorCount(0) {}
  22. // Prevent default generation of these functions by not defining them
  23. private:
  24. MWorkData(const MWorkData& rhs); // copy constructor
  25. MWorkData& operator=(const MWorkData& rhs); // assignment operator
  26. };
  27. static intptr_t MutexTestThreadFunction(void* pvWorkData)
  28. {
  29. int nErrorCount = 0;
  30. MWorkData* pWorkData = (MWorkData*)pvWorkData;
  31. const ThreadId threadId = GetThreadId();
  32. const int currentProcessor = GetThreadProcessor();
  33. EA::UnitTest::ReportVerbosity(1, "Mutex test function created: %s (currently on processor %d).\n", EAThreadThreadIdToString(threadId), currentProcessor);
  34. while(!pWorkData->mbShouldQuit)
  35. {
  36. const int nRecursiveLockCount(rand() % 3);
  37. int i, nLockResult, nLocks = 0;
  38. for(i = 0; i < nRecursiveLockCount; i++)
  39. {
  40. // Do a lock but allow for the possibility of occasional timeout.
  41. ThreadTime expectedTime(GetThreadTime() + 1000);
  42. nLockResult = pWorkData->mMutex.Lock(expectedTime);
  43. // Verify the lock succeeded or timed out.
  44. EATEST_VERIFY_MSG(nLockResult != Mutex::kResultError, "Mutex failure.");
  45. // Verify the timeout of the lock
  46. if (nLockResult == Mutex::kResultTimeout)
  47. {
  48. ThreadTime currentTime(GetThreadTime());
  49. EATEST_VERIFY_MSG(currentTime >= expectedTime, "Mutex timeout failure.");
  50. }
  51. if(nLockResult > 0) // If there was no timeout...
  52. {
  53. nLocks++;
  54. // What we do here is spend some time manipulating mnExpectedValue and mnCalculatedValue
  55. // while we have the lock. We change their values in a predicable way but before we
  56. // are done mnCalculatedValue has been incremented by one and both values are equal.
  57. const uintptr_t x = (uintptr_t)pWorkData;
  58. pWorkData->mnExpectedValue = -1;
  59. EA::UnitTest::ThreadSleepRandom(10, 20);
  60. pWorkData->mnCalculatedValue *= 50;
  61. EA::UnitTest::ThreadSleepRandom(10, 20);
  62. pWorkData->mnCalculatedValue /= (int)(((x + 1) / x) * 50); // This will always be the same as simply '/= 50'.
  63. EA::UnitTest::ThreadSleepRandom(10, 20);
  64. pWorkData->mnCalculatedValue += 1;
  65. EA::UnitTest::ThreadSleepRandom(10, 20);
  66. pWorkData->mnExpectedValue = pWorkData->mnCalculatedValue;
  67. EATEST_VERIFY_MSG(pWorkData->mnCalculatedValue == pWorkData->mnExpectedValue, "Mutex failure.");
  68. }
  69. ThreadCooperativeYield(); // Used by cooperative threading platforms.
  70. }
  71. while(nLocks > 0)
  72. {
  73. // Verify that HasLock returns the expected value.
  74. EATEST_VERIFY_MSG(pWorkData->mMutex.HasLock(), "Mutex failure.");
  75. // Verify that N locks are set.
  76. nLockResult = pWorkData->mMutex.GetLockCount();
  77. EATEST_VERIFY_MSG(nLockResult == nLocks, "Mutex failure.");
  78. // Verify the unlock result.
  79. nLockResult = pWorkData->mMutex.Unlock();
  80. EATEST_VERIFY_MSG(nLockResult >= nLocks-1, "Mutex failure.");
  81. nLocks--;
  82. ThreadCooperativeYield(); // Used by cooperative threading platforms.
  83. }
  84. // If EAT_ASSERT_ENABLED is 1, Mutex::HasLock() tests to see that that not only is the mutex
  85. // locked, but that the lock is owned by the calling thread.
  86. #if EAT_ASSERT_ENABLED
  87. // Verify that HasLock returns the expected value.
  88. EATEST_VERIFY_MSG(!pWorkData->mMutex.HasLock(), "Mutex failure.");
  89. #endif
  90. EA::UnitTest::ThreadSleepRandom(100, 200);
  91. }
  92. pWorkData->mnErrorCount += nErrorCount;
  93. return 0;
  94. }
  95. int TestThreadMutex()
  96. {
  97. int nErrorCount(0);
  98. { // ctor tests
  99. // We test various combinations of Mutex ctor and MutexParameters.
  100. // MutexParameters(bool bIntraProcess = true, const char* pName = NULL);
  101. // Mutex(const MutexParameters* pMutexParameters = NULL, bool bDefaultParameters = true);
  102. MutexParameters mp1(true, NULL);
  103. MutexParameters mp2(true, "mp2");
  104. MutexParameters mp3(false, NULL);
  105. MutexParameters mp4(false, "mp4WithNameThatIsMoreThan32Characters"); // testing kettle mutex name limitations
  106. Mutex mutex1(&mp1, false);
  107. Mutex mutex2(&mp2, false);
  108. Mutex mutex3(&mp3, false);
  109. Mutex mutex4(&mp4, false);
  110. Mutex mutex5(NULL, true);
  111. Mutex mutex6(NULL, false);
  112. mutex6.Init(&mp1);
  113. AutoMutex am1(mutex1);
  114. AutoMutex am2(mutex2);
  115. AutoMutex am3(mutex3);
  116. AutoMutex am4(mutex4);
  117. AutoMutex am5(mutex5);
  118. AutoMutex am6(mutex6);
  119. }
  120. { // Single-threaded tests
  121. Mutex mutex(NULL, true);
  122. int nLockCount;
  123. nLockCount = mutex.GetLockCount();
  124. EATEST_VERIFY_MSG(nLockCount == 0, "Mutex failure.");
  125. nLockCount = mutex.Lock();
  126. EATEST_VERIFY_MSG(nLockCount == 1, "Mutex failure.");
  127. nLockCount = mutex.Lock();
  128. EATEST_VERIFY_MSG(nLockCount == 2, "Mutex failure.");
  129. nLockCount = mutex.Unlock();
  130. EATEST_VERIFY_MSG(nLockCount == 1, "Mutex failure.");
  131. nLockCount = mutex.GetLockCount();
  132. EATEST_VERIFY_MSG(nLockCount == 1, "Mutex failure.");
  133. nLockCount = mutex.Unlock();
  134. EATEST_VERIFY_MSG(nLockCount == 0, "Mutex failure.");
  135. nLockCount = mutex.Lock();
  136. EATEST_VERIFY_MSG(nLockCount == 1, "Mutex failure.");
  137. nLockCount = mutex.Unlock();
  138. EATEST_VERIFY_MSG(nLockCount == 0, "Mutex failure.");
  139. }
  140. #ifdef EA_PLATFORM_PS4
  141. {
  142. // Validate the amount of system resources being consumed by a Sony Mutex without a mutex name. It appears the
  143. // Sony OS allocates 32 bytes per mutex regardless if the mutex name is empty or not. EAThread currently checks
  144. // if the mutex name can be omited in an effort to save memory.
  145. MutexParameters mp(false, nullptr);
  146. Mutex mutexes[4000];
  147. for(auto& m : mutexes)
  148. m.Init(&mp);
  149. }
  150. #endif
  151. #if EA_THREADS_AVAILABLE
  152. { // Multithreaded test
  153. MWorkData workData;
  154. const int kThreadCount(kMaxConcurrentThreadCount);
  155. Thread thread[kThreadCount];
  156. Thread::Status status;
  157. int i;
  158. for(i = 0; i < kThreadCount; i++)
  159. thread[i].Begin(MutexTestThreadFunction, &workData);
  160. EA::UnitTest::ThreadSleepRandom(gTestLengthSeconds*1000, gTestLengthSeconds*1000);
  161. workData.mbShouldQuit = true;
  162. for(i = 0; i < kThreadCount; i++)
  163. {
  164. status = thread[i].WaitForEnd(GetThreadTime() + 30000);
  165. EATEST_VERIFY_MSG(status == EA::Thread::Thread::kStatusEnded, "Mutex/Thread failure: Thread(s) didn't end.");
  166. }
  167. nErrorCount += (int)workData.mnErrorCount;
  168. }
  169. #endif
  170. return nErrorCount;
  171. }