TestThreadRWSpinLock.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477
  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_rwspinlock.h>
  8. #include <eathread/eathread_rwspinlockw.h>
  9. #include <stdlib.h>
  10. #include <atomic>
  11. const int kThreadCount = EATHREAD_MAX_CONCURRENT_THREAD_COUNT;
  12. ///////////////////////////////////////////////////////////////////////////////
  13. // RWSTestType
  14. //
  15. enum RWSTestType
  16. {
  17. kRWSTestTypeStandard,
  18. kRWSTestTypeAllWriters,
  19. kRWSTestTypeAllReaders,
  20. kRWSTestTypeMostlyWriters,
  21. kRWSTestTypeMostlyReaders,
  22. kRWSTestTypeCount
  23. };
  24. ///////////////////////////////////////////////////////////////////////////////
  25. // RWSWorkData
  26. //
  27. struct RWSWorkData
  28. {
  29. std::atomic<bool> mbShouldQuit;
  30. EA::Thread::RWSpinLock mRWSpinLock;
  31. EA::Thread::RWSpinLockW mRWSpinLockW;
  32. volatile int mnExpectedValue;
  33. volatile int mnCalculatedValue;
  34. EA::Thread::AtomicInt32 mnErrorCount;
  35. EA::Thread::AtomicInt32 mnCurrentTestType;
  36. RWSWorkData() : mbShouldQuit(false), mRWSpinLock(), mRWSpinLockW(), mnExpectedValue(0), mnCalculatedValue(0),
  37. mnErrorCount(0), mnCurrentTestType(kRWSTestTypeStandard) {}
  38. private:
  39. RWSWorkData(const RWSWorkData& rhs);
  40. RWSWorkData& operator=(const RWSWorkData& rhs);
  41. };
  42. ///////////////////////////////////////////////////////////////////////////////
  43. // RWSWThreadFunction
  44. //
  45. static intptr_t RWSWThreadFunction(void* pvWorkData)
  46. {
  47. using namespace EA::Thread;
  48. RWSWorkData* const pWorkData = (RWSWorkData*)pvWorkData;
  49. ThreadId threadId = GetThreadId();
  50. EA::UnitTest::ReportVerbosity(1, "RWSpinLockW 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. pWorkData->mRWSpinLockW.WriteLock();
  79. EATEST_VERIFY_MSG(!pWorkData->mRWSpinLockW.IsReadLocked(), "RWSpinlock failure: IsReadLocked\n");
  80. EATEST_VERIFY_MSG(pWorkData->mRWSpinLockW.IsWriteLocked(), "RWSpinlock failure: IsWriteLocked\n");
  81. pWorkData->mRWSpinLockW.WriteUnlock();
  82. ThreadCooperativeYield();
  83. }
  84. else
  85. {
  86. const int nRecursiveLockCount = 1; // Disabled because we are not recursive: (rand() % 10) ? 1 : 2;
  87. int nLocks = 0;
  88. for(int i = 0; i < nRecursiveLockCount; i++)
  89. {
  90. pWorkData->mRWSpinLockW.ReadLock();
  91. nLocks++;
  92. ThreadCooperativeYield();
  93. }
  94. // Disabled because we are not recursive:
  95. // if((rand() % 10) == 0)
  96. // {
  97. // if(pWorkData->mRWSpinLockW.ReadTryLock())
  98. // nLocks++;
  99. // }
  100. while(nLocks > 0)
  101. {
  102. EATEST_VERIFY_MSG(pWorkData->mRWSpinLockW.IsReadLocked(), "RWSpinlock failure: IsReadLocked\n");
  103. EATEST_VERIFY_MSG(!pWorkData->mRWSpinLockW.IsWriteLocked(), "RWSpinlock failure: IsWriteLocked\n");
  104. pWorkData->mRWSpinLockW.ReadUnlock();
  105. nLocks--;
  106. ThreadCooperativeYield();
  107. }
  108. }
  109. //if((rand() % 1000) < 3)
  110. // EA::UnitTest::ThreadSleepRandom(50, 100);
  111. }
  112. pWorkData->mnErrorCount.SetValue(nErrorCount);
  113. return nErrorCount;
  114. }
  115. static int TestThreadRWSpinLockW()
  116. {
  117. using namespace EA::Thread;
  118. int nErrorCount = 0;
  119. { // RWSpinLockW -- Basic single-threaded test.
  120. RWSpinLockW rwSpinLock; // There are no construction parameters.
  121. EATEST_VERIFY_MSG(!rwSpinLock.IsReadLocked(), "RWSpinLockW failure");
  122. EATEST_VERIFY_MSG(!rwSpinLock.IsWriteLocked(), "RWSpinLockW failure");
  123. rwSpinLock.ReadTryLock();
  124. EATEST_VERIFY_MSG( rwSpinLock.IsReadLocked(), "RWSpinLockW failure");
  125. EATEST_VERIFY_MSG(!rwSpinLock.IsWriteLocked(), "RWSpinLockW failure");
  126. EATEST_VERIFY_MSG(!rwSpinLock.WriteTryLock(), "RWSpinLockW failure");
  127. EATEST_VERIFY_MSG(!rwSpinLock.IsWriteLocked(), "RWSpinLockW failure");
  128. // Disabled because we don't support read lock recursion.
  129. // rwSpinLock.ReadLock();
  130. // EATEST_VERIFY_MSG(rwSpinLock.IsReadLocked(), "RWSpinLockW failure");
  131. // rwSpinLock.ReadUnlock();
  132. // EATEST_VERIFY_MSG(rwSpinLock.IsReadLocked(), "RWSpinLockW failure");
  133. rwSpinLock.ReadUnlock();
  134. EATEST_VERIFY_MSG(!rwSpinLock.IsReadLocked(), "RWSpinLockW failure");
  135. rwSpinLock.WriteTryLock();
  136. EATEST_VERIFY_MSG( rwSpinLock.IsWriteLocked(),"RWSpinLockW failure");
  137. EATEST_VERIFY_MSG(!rwSpinLock.IsReadLocked(), "RWSpinLockW failure");
  138. EATEST_VERIFY_MSG(!rwSpinLock.ReadTryLock(), "RWSpinLockW failure");
  139. EATEST_VERIFY_MSG(!rwSpinLock.IsReadLocked(), "RWSpinLockW failure");
  140. EATEST_VERIFY_MSG(!rwSpinLock.WriteTryLock(), "RWSpinLockW failure");
  141. }
  142. { // AutoRWSpinLockW -- Basic single-threaded test.
  143. RWSpinLockW rwSpinLock; // There are no construction parameters.
  144. { //Special scope just for the AutoRWSpinLockW
  145. AutoRWSpinLockW autoRWSpinLockW1(rwSpinLock, AutoRWSpinLockW::kLockTypeRead);
  146. AutoRWSpinLockW autoRWSpinLockW2(rwSpinLock, AutoRWSpinLockW::kLockTypeRead);
  147. EATEST_VERIFY_MSG( rwSpinLock.IsReadLocked(), "RWSpinLockW failure");
  148. EATEST_VERIFY_MSG(!rwSpinLock.IsWriteLocked(), "RWSpinLockW failure");
  149. EATEST_VERIFY_MSG(!rwSpinLock.WriteTryLock(), "RWSpinLockW failure");
  150. EATEST_VERIFY_MSG(!rwSpinLock.IsWriteLocked(), "RWSpinLockW failure");
  151. }
  152. EATEST_VERIFY_MSG(!rwSpinLock.IsReadLocked(), "RWSpinLockW failure");
  153. EATEST_VERIFY_MSG(!rwSpinLock.IsWriteLocked(), "RWSpinLockW failure");
  154. { //Special scope just for the AutoRWSpinLockW
  155. AutoRWSpinLockW autoRWSpinLockW(rwSpinLock, AutoRWSpinLockW::kLockTypeWrite);
  156. EATEST_VERIFY_MSG( rwSpinLock.IsWriteLocked(),"RWSpinLockW failure");
  157. EATEST_VERIFY_MSG(!rwSpinLock.IsReadLocked(), "RWSpinLockW failure");
  158. EATEST_VERIFY_MSG(!rwSpinLock.ReadTryLock(), "RWSpinLockW failure");
  159. EATEST_VERIFY_MSG(!rwSpinLock.IsReadLocked(), "RWSpinLockW failure");
  160. }
  161. EATEST_VERIFY_MSG(!rwSpinLock.IsReadLocked(), "RWSpinLockW failure");
  162. EATEST_VERIFY_MSG(!rwSpinLock.IsWriteLocked(), "RWSpinLockW failure");
  163. }
  164. #if EA_THREADS_AVAILABLE
  165. { // Multithreaded test
  166. RWSWorkData workData;
  167. Thread thread[kThreadCount];
  168. ThreadId threadId[kThreadCount];
  169. Thread::Status status;
  170. for(int i(0); i < kThreadCount; i++)
  171. threadId[i] = thread[i].Begin(RWSWThreadFunction, &workData);
  172. for(int e = 0; e < kRWSTestTypeCount; e++)
  173. {
  174. workData.mnCurrentTestType.SetValue(e);
  175. EA::UnitTest::ThreadSleepRandom(gTestLengthSeconds * 500, gTestLengthSeconds * 500);
  176. }
  177. workData.mbShouldQuit = true;
  178. for(int t(0); t < kThreadCount; t++)
  179. {
  180. if(threadId[t] != kThreadIdInvalid)
  181. {
  182. status = thread[t].WaitForEnd(GetThreadTime() + 30000);
  183. EATEST_VERIFY_MSG(status != Thread::kStatusRunning, "RWSpinlock/Thread failure: status == kStatusRunning.\n");
  184. }
  185. }
  186. nErrorCount += (int)workData.mnErrorCount;
  187. }
  188. #endif
  189. return nErrorCount;
  190. }
  191. ///////////////////////////////////////////////////////////////////////////////
  192. // RWSThreadFunction
  193. //
  194. static intptr_t RWSThreadFunction(void* pvWorkData)
  195. {
  196. using namespace EA::Thread;
  197. RWSWorkData* const pWorkData = (RWSWorkData*)pvWorkData;
  198. ThreadId threadId = GetThreadId();
  199. EA::UnitTest::ReportVerbosity(1, "RWSpinLock test function created: %s\n", EAThreadThreadIdToString(threadId));
  200. int nErrorCount = 0;
  201. while(!pWorkData->mbShouldQuit)
  202. {
  203. int nWriteLockChance = 0;
  204. const RWSTestType testType = (RWSTestType)pWorkData->mnCurrentTestType.GetValue();
  205. switch (testType)
  206. {
  207. default:
  208. case kRWSTestTypeStandard:
  209. nWriteLockChance = 20;
  210. break;
  211. case kRWSTestTypeAllWriters:
  212. nWriteLockChance = 1000;
  213. break;
  214. case kRWSTestTypeAllReaders:
  215. nWriteLockChance = 0;
  216. break;
  217. case kRWSTestTypeMostlyWriters:
  218. nWriteLockChance = 700;
  219. break;
  220. case kRWSTestTypeMostlyReaders:
  221. nWriteLockChance = 5;
  222. break;
  223. }
  224. const bool bShouldWrite = ((rand() % 1000) < nWriteLockChance);
  225. if(bShouldWrite)
  226. {
  227. pWorkData->mRWSpinLock.WriteLock();
  228. EATEST_VERIFY_MSG(!pWorkData->mRWSpinLock.IsReadLocked(), "RWSpinlock failure: IsReadLocked\n");
  229. EATEST_VERIFY_MSG(pWorkData->mRWSpinLock.IsWriteLocked(), "RWSpinlock failure: IsWriteLocked\n");
  230. pWorkData->mRWSpinLock.WriteUnlock();
  231. ThreadCooperativeYield();
  232. }
  233. else
  234. {
  235. const int nRecursiveLockCount = (rand() % 10) ? 1 : 2;
  236. int nLocks = 0;
  237. for(int i = 0; i < nRecursiveLockCount; i++)
  238. {
  239. pWorkData->mRWSpinLock.ReadLock();
  240. nLocks++;
  241. ThreadCooperativeYield();
  242. }
  243. if((rand() % 10) == 0)
  244. {
  245. if(pWorkData->mRWSpinLock.ReadTryLock())
  246. nLocks++;
  247. }
  248. while(nLocks > 0)
  249. {
  250. const int32_t n = pWorkData->mRWSpinLock.mValue; (void)n;
  251. // It turns out IsReadLocked has a bug and can return a false negative.
  252. // EATEST_VERIFY_MSG(pWorkData->mRWSpinLock.IsReadLocked(), "RWSpinlock failure: IsReadLocked\n");
  253. // EATEST_VERIFY_MSG(!pWorkData->mRWSpinLock.IsWriteLocked(), "RWSpinlock failure: IsWriteLocked\n");
  254. pWorkData->mRWSpinLock.ReadUnlock();
  255. nLocks--;
  256. ThreadCooperativeYield();
  257. }
  258. }
  259. }
  260. pWorkData->mnErrorCount.SetValue(nErrorCount);
  261. return nErrorCount;
  262. }
  263. int TestThreadRWSpinLock()
  264. {
  265. using namespace EA::Thread;
  266. int nErrorCount = 0;
  267. { // RWSpinLock -- Basic single-threaded test.
  268. RWSpinLock rwSpinLock; // There are no construction parameters.
  269. EATEST_VERIFY_MSG(!rwSpinLock.IsReadLocked(), "RWSpinLock failure");
  270. EATEST_VERIFY_MSG(!rwSpinLock.IsWriteLocked(), "RWSpinLock failure");
  271. rwSpinLock.ReadTryLock();
  272. EATEST_VERIFY_MSG(rwSpinLock.IsReadLocked(), "RWSpinLock failure");
  273. EATEST_VERIFY_MSG(!rwSpinLock.IsWriteLocked(), "RWSpinLock failure");
  274. EATEST_VERIFY_MSG(!rwSpinLock.WriteTryLock(), "RWSpinLock failure");
  275. EATEST_VERIFY_MSG(!rwSpinLock.IsWriteLocked(), "RWSpinLock failure");
  276. rwSpinLock.ReadLock();
  277. EATEST_VERIFY_MSG(rwSpinLock.IsReadLocked(), "RWSpinLock failure");
  278. rwSpinLock.ReadUnlock();
  279. EATEST_VERIFY_MSG(rwSpinLock.IsReadLocked(), "RWSpinLock failure");
  280. rwSpinLock.ReadUnlock();
  281. EATEST_VERIFY_MSG(!rwSpinLock.IsReadLocked(), "RWSpinLock failure");
  282. rwSpinLock.WriteTryLock();
  283. EATEST_VERIFY_MSG(rwSpinLock.IsWriteLocked(), "RWSpinLock failure");
  284. EATEST_VERIFY_MSG(!rwSpinLock.IsReadLocked(), "RWSpinLock failure");
  285. EATEST_VERIFY_MSG(!rwSpinLock.ReadTryLock(), "RWSpinLock failure");
  286. EATEST_VERIFY_MSG(!rwSpinLock.IsReadLocked(), "RWSpinLock failure");
  287. EATEST_VERIFY_MSG(!rwSpinLock.WriteTryLock(), "RWSpinLock failure");
  288. }
  289. { // AutoRWSpinLock -- Basic single-threaded test.
  290. RWSpinLock rwSpinLock; // There are no construction parameters.
  291. { //Special scope just for the AutoRWSpinLock
  292. AutoRWSpinLock autoRWSpinLock1(rwSpinLock, AutoRWSpinLock::kLockTypeRead);
  293. AutoRWSpinLock autoRWSpinLock2(rwSpinLock, AutoRWSpinLock::kLockTypeRead);
  294. EATEST_VERIFY_MSG(rwSpinLock.IsReadLocked(), "RWSpinLock failure");
  295. EATEST_VERIFY_MSG(!rwSpinLock.IsWriteLocked(), "RWSpinLock failure");
  296. EATEST_VERIFY_MSG(!rwSpinLock.WriteTryLock(), "RWSpinLock failure");
  297. EATEST_VERIFY_MSG(!rwSpinLock.IsWriteLocked(), "RWSpinLock failure");
  298. }
  299. EATEST_VERIFY_MSG(!rwSpinLock.IsReadLocked(), "RWSpinLock failure");
  300. EATEST_VERIFY_MSG(!rwSpinLock.IsWriteLocked(), "RWSpinLock failure");
  301. { //Special scope just for the AutoRWSpinLock
  302. AutoRWSpinLock autoRWSpinLock(rwSpinLock, AutoRWSpinLock::kLockTypeWrite);
  303. EATEST_VERIFY_MSG(rwSpinLock.IsWriteLocked(), "RWSpinLock failure");
  304. EATEST_VERIFY_MSG(!rwSpinLock.IsReadLocked(), "RWSpinLock failure");
  305. EATEST_VERIFY_MSG(!rwSpinLock.ReadTryLock(), "RWSpinLock failure");
  306. EATEST_VERIFY_MSG(!rwSpinLock.IsReadLocked(), "RWSpinLock failure");
  307. }
  308. EATEST_VERIFY_MSG(!rwSpinLock.IsReadLocked(), "RWSpinLock failure");
  309. EATEST_VERIFY_MSG(!rwSpinLock.IsWriteLocked(), "RWSpinLock failure");
  310. }
  311. #if EA_THREADS_AVAILABLE
  312. { // Multithreaded test
  313. RWSWorkData workData;
  314. Thread thread[kThreadCount];
  315. ThreadId threadId[kThreadCount];
  316. Thread::Status status;
  317. for(int i(0); i < kThreadCount; i++)
  318. threadId[i] = thread[i].Begin(RWSThreadFunction, &workData);
  319. for(int e = 0; e < kRWSTestTypeCount; e++)
  320. {
  321. workData.mnCurrentTestType.SetValue(e);
  322. EA::UnitTest::ThreadSleepRandom(gTestLengthSeconds * 500, gTestLengthSeconds * 500);
  323. }
  324. workData.mbShouldQuit = true;
  325. for(int t(0); t < kThreadCount; t++)
  326. {
  327. if(threadId[t] != kThreadIdInvalid)
  328. {
  329. status = thread[t].WaitForEnd(GetThreadTime() + 30000);
  330. EATEST_VERIFY_MSG(status != Thread::kStatusRunning, "RWSpinlock/Thread failure: status == kStatusRunning.\n");
  331. }
  332. }
  333. nErrorCount += (int)workData.mnErrorCount;
  334. }
  335. #endif
  336. // TestThreadRWSpinLockW
  337. nErrorCount += TestThreadRWSpinLockW();
  338. return nErrorCount;
  339. }