TestThreadAtomic.cpp 33 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007
  1. ///////////////////////////////////////////////////////////////////////////////
  2. // Copyright (c) Electronic Arts Inc. All rights reserved.
  3. ///////////////////////////////////////////////////////////////////////////////
  4. #include "TestThread.h"
  5. #include <EATest/EATest.h>
  6. #include <eathread/eathread_atomic.h>
  7. #include <eathread/eathread_thread.h>
  8. EA_DISABLE_VC_WARNING(4265 4365 4836 4571 4625 4626 4628 4193 4127 4548)
  9. #include <string.h>
  10. EA_RESTORE_VC_WARNING()
  11. #if defined(_MSC_VER)
  12. #pragma warning(disable: 4996) // This function or variable may be unsafe / deprecated.
  13. #endif
  14. #include <EASTL/numeric_limits.h>
  15. using namespace EA::Thread;
  16. #if EA_THREADS_AVAILABLE
  17. const int kMaxConcurrentThreadCount = EATHREAD_MAX_CONCURRENT_THREAD_COUNT;
  18. struct AWorkData32
  19. {
  20. volatile bool mbShouldQuit;
  21. AtomicInt32 mnAtomicInteger1;
  22. AtomicInt32 mnAtomicInteger2;
  23. AtomicInt32 mnErrorCount;
  24. AWorkData32() : mbShouldQuit(false),
  25. mnAtomicInteger1(0), mnAtomicInteger2(0),
  26. mnErrorCount(0) {}
  27. };
  28. static intptr_t Atomic32TestThreadFunction1(void* pvWorkData)
  29. {
  30. int nErrorCount = 0;
  31. AWorkData32* pWorkData = (AWorkData32*)pvWorkData;
  32. const ThreadId threadId = GetThreadId();
  33. EA::UnitTest::ReportVerbosity(1, "Atomic test function 1 created, thread id %s\n", EAThreadThreadIdToString(threadId));
  34. // Do a series of operations, the final result of which is zero.
  35. while(!pWorkData->mbShouldQuit)
  36. {
  37. ++pWorkData->mnAtomicInteger1;
  38. ++pWorkData->mnAtomicInteger2;
  39. --pWorkData->mnAtomicInteger1;
  40. --pWorkData->mnAtomicInteger2;
  41. pWorkData->mnAtomicInteger1 += 5;
  42. pWorkData->mnAtomicInteger2 += 5;
  43. pWorkData->mnAtomicInteger1 -= 5;
  44. pWorkData->mnAtomicInteger2 -= 5;
  45. pWorkData->mnAtomicInteger1++;
  46. pWorkData->mnAtomicInteger2++;
  47. pWorkData->mnAtomicInteger1--;
  48. pWorkData->mnAtomicInteger2--;
  49. ThreadCooperativeYield();
  50. }
  51. pWorkData->mnErrorCount += nErrorCount;
  52. EA::UnitTest::ReportVerbosity(1, "Atomic test function 1 exiting, thread id %s\n", EAThreadThreadIdToString(threadId));
  53. return 0;
  54. }
  55. static intptr_t Atomic32TestThreadFunction2(void* pvWorkData)
  56. {
  57. int nErrorCount = 0;
  58. AWorkData32* pWorkData = (AWorkData32*)pvWorkData;
  59. const ThreadId threadId = GetThreadId();
  60. ThreadUniqueId threadUniqueId;
  61. EAThreadGetUniqueId(threadUniqueId);
  62. int32_t threadUniqueId32 = (int32_t)threadUniqueId;
  63. EA::UnitTest::ReportVerbosity(1, "Atomic test function 2 created, thread id %s\n", EAThreadThreadIdToString(threadId));
  64. // Test the SetValueConditional function. We basically create a spinlock here.
  65. while(!pWorkData->mbShouldQuit)
  66. {
  67. if(pWorkData->mnAtomicInteger1.SetValueConditional(threadUniqueId32, 0x11223344))
  68. {
  69. EATEST_VERIFY_MSG(pWorkData->mnAtomicInteger1 == threadUniqueId32, "AtomicInt SetValueConditional failure.");
  70. pWorkData->mnAtomicInteger1 = 0x11223344;
  71. }
  72. ThreadCooperativeYield();
  73. }
  74. pWorkData->mnErrorCount += nErrorCount;
  75. EA::UnitTest::ReportVerbosity(1, "Atomic test function 2 exiting, thread id %s\n", EAThreadThreadIdToString(threadId));
  76. return 0;
  77. }
  78. struct AWorkData64
  79. {
  80. volatile bool mbShouldQuit;
  81. AtomicInt64 mnAtomicInteger1;
  82. AtomicInt64 mnAtomicInteger2;
  83. AtomicInt64 mnErrorCount;
  84. AWorkData64() : mbShouldQuit(false),
  85. mnAtomicInteger1(0), mnAtomicInteger2(0),
  86. mnErrorCount(0) {}
  87. };
  88. static intptr_t Atomic64TestThreadFunction1(void* pvWorkData)
  89. {
  90. int nErrorCount = 0;
  91. AWorkData64* pWorkData = (AWorkData64*)pvWorkData;
  92. const ThreadId threadId = GetThreadId();
  93. EA::UnitTest::ReportVerbosity(1, "Atomic64 test function 1 created, thread id %s\n", EAThreadThreadIdToString(threadId));
  94. // Do a series of operations, the final result of which is zero.
  95. while(!pWorkData->mbShouldQuit)
  96. {
  97. ++pWorkData->mnAtomicInteger1;
  98. ++pWorkData->mnAtomicInteger2;
  99. --pWorkData->mnAtomicInteger1;
  100. --pWorkData->mnAtomicInteger2;
  101. pWorkData->mnAtomicInteger1 += UINT64_C(0x0000000fffffffff);
  102. pWorkData->mnAtomicInteger2 += UINT64_C(0x0000000ffffffffe);
  103. pWorkData->mnAtomicInteger1 -= UINT64_C(0x0000000fffffffff);
  104. pWorkData->mnAtomicInteger2 -= UINT64_C(0x0000000ffffffffe);
  105. pWorkData->mnAtomicInteger1++;
  106. pWorkData->mnAtomicInteger2++;
  107. pWorkData->mnAtomicInteger1--;
  108. pWorkData->mnAtomicInteger2--;
  109. ThreadCooperativeYield();
  110. }
  111. pWorkData->mnErrorCount += nErrorCount;
  112. EA::UnitTest::ReportVerbosity(1, "Atomic64 test function 1 exiting, thread id %s\n", EAThreadThreadIdToString(threadId));
  113. return 0;
  114. }
  115. static intptr_t Atomic64TestThreadFunction2(void* pvWorkData)
  116. {
  117. int nErrorCount = 0;
  118. AWorkData64* pWorkData = (AWorkData64*)pvWorkData;
  119. const ThreadId threadId = GetThreadId();
  120. ThreadUniqueId threadUniqueId;
  121. EAThreadGetUniqueId(threadUniqueId);
  122. uint64_t threadUnqueId64 = (uint64_t)threadUniqueId | UINT64_C(0xeeeeddddffffffff);
  123. EA::UnitTest::ReportVerbosity(1, "Atomic64 test function 2 created, thread id %s\n", EAThreadThreadIdToString(threadId));
  124. // Test the SetValueConditional function. We basically create a spinlock here.
  125. while(!pWorkData->mbShouldQuit)
  126. {
  127. if(pWorkData->mnAtomicInteger1.SetValueConditional(threadUnqueId64, 0x1122334455667788))
  128. {
  129. EATEST_VERIFY_MSG(pWorkData->mnAtomicInteger1 == static_cast<int64_t>(threadUnqueId64), "AtomicInt64 SetValueConditional failure.");
  130. pWorkData->mnAtomicInteger1.SetValue(0x1122334455667788);
  131. }
  132. ThreadCooperativeYield();
  133. }
  134. pWorkData->mnErrorCount += nErrorCount;
  135. EA::UnitTest::ReportVerbosity(1, "Atomic64 test function 2 exiting, thread id %s\n", EAThreadThreadIdToString(threadId));
  136. return 0;
  137. }
  138. static intptr_t Atomic64TestThreadFunction3(void* pvWorkData)
  139. {
  140. int nErrorCount = 0;
  141. AWorkData64* pWorkData = (AWorkData64*)pvWorkData;
  142. const ThreadId threadId = GetThreadId();
  143. const uint64_t value0 = UINT64_C(0x0000000000000000);
  144. const uint64_t value1 = UINT64_C(0xffffffffffffffff);
  145. EA::UnitTest::ReportVerbosity(1, "Atomic64 test function 2 created, thread id %s\n", EAThreadThreadIdToString(threadId));
  146. // Test the SetValueConditional function.
  147. while(!pWorkData->mbShouldQuit)
  148. {
  149. pWorkData->mnAtomicInteger1.SetValueConditional(value0, value1);
  150. uint64_t currentValue = pWorkData->mnAtomicInteger1.GetValue();
  151. EATEST_VERIFY_MSG((currentValue == value0) || (currentValue == value1), "AtomicInt64 SetValueConditional failure.");
  152. pWorkData->mnAtomicInteger1.SetValueConditional(value1, value0);
  153. currentValue = pWorkData->mnAtomicInteger1.GetValue();
  154. EATEST_VERIFY_MSG((currentValue == value0) || (currentValue == value1), "AtomicInt64 SetValueConditional failure.");
  155. ThreadCooperativeYield();
  156. }
  157. pWorkData->mnErrorCount += nErrorCount;
  158. EA::UnitTest::ReportVerbosity(1, "Atomic64 test function 2 exiting, thread id %s\n", EAThreadThreadIdToString(threadId));
  159. return 0;
  160. }
  161. template<typename T>
  162. int TestSimpleAtomicOps()
  163. {
  164. int nErrorCount = 0;
  165. bool result = false;
  166. alignas(16) T value = 0;
  167. alignas(16) T dest = 0;
  168. alignas(16) T conditionFail = 4;
  169. alignas(16) T conditionSucceed = 0;
  170. // AtomicGetValue
  171. dest = 3;
  172. value = AtomicGetValue(&dest);
  173. EATEST_VERIFY_MSG(value == 3, "AtomicGetValue failure\n");
  174. // AtomicSetValue
  175. value = AtomicSetValue(&dest, 4);
  176. EATEST_VERIFY_MSG(value == 3, "AtomicSetValue failure\n");
  177. value = AtomicGetValue(&dest);
  178. EATEST_VERIFY_MSG(value == 4, "AtomicSetValue failure\n");
  179. // AtomicFetchIncrement
  180. value = AtomicFetchIncrement(&dest);
  181. EATEST_VERIFY_MSG(value == 4, "AtomicFetchIncrement failure\n");
  182. value = AtomicGetValue(&dest);
  183. EATEST_VERIFY_MSG(value == 5, "AtomicFetchIncrement failure\n");
  184. // AtomicFetchDecrement
  185. value = AtomicFetchDecrement(&dest);
  186. EATEST_VERIFY_MSG(value == 5, "AtomicFetchDecrement failure\n");
  187. value = AtomicGetValue(&dest);
  188. EATEST_VERIFY_MSG(value == 4, "AtomicFetchDecrement failure\n");
  189. // AtomicFetchAdd
  190. value = AtomicFetchAdd(&dest, 3);
  191. EATEST_VERIFY_MSG(value == 4, "AtomicFetchAdd failure\n");
  192. value = AtomicGetValue(&dest);
  193. EATEST_VERIFY_MSG(value == 7, "AtomicFetchAdd failure\n");
  194. // AtomicFetchSub
  195. value = AtomicFetchSub(&dest, 3);
  196. EATEST_VERIFY_MSG(value == 7, "AtomicFetchSub failure\n");
  197. value = AtomicGetValue(&dest);
  198. EATEST_VERIFY_MSG(value == 4, "AtomicFetchSub failure\n");
  199. value = AtomicFetchSub(&dest, T(-3));
  200. EATEST_VERIFY_MSG(value == 4, "AtomicFetchSub failure\n");
  201. value = AtomicGetValue(&dest);
  202. EATEST_VERIFY_MSG(value == 7, "AtomicFetchSub failure\n");
  203. // AtomicFetchOr
  204. value = AtomicFetchOr(&dest, 8);
  205. EATEST_VERIFY_MSG(value == 7, "AtomicFetchOr failure\n");
  206. value = AtomicGetValue(&dest);
  207. EATEST_VERIFY_MSG(value == 15, "AtomicFetchOr failure\n");
  208. // AtomicFetchAnd
  209. value = AtomicFetchAnd(&dest, 3);
  210. EATEST_VERIFY_MSG(value == 15, "AtomicFetchAnd failure\n");
  211. value = AtomicGetValue(&dest);
  212. EATEST_VERIFY_MSG(value == 3, "AtomicFetchAnd failure\n");
  213. // AtomicFetchXor
  214. value = AtomicFetchXor(&dest, dest);
  215. EATEST_VERIFY_MSG(value == 3, "AtomicFetchXor failure\n");
  216. value = AtomicGetValue(&dest);
  217. EATEST_VERIFY_MSG(value == 0, "AtomicFetchXor failure\n");
  218. // AtomicFetchSwap
  219. value = AtomicFetchSwap(&dest, 5);
  220. EATEST_VERIFY_MSG(value == 0, "AtomicFetchSwap failure\n");
  221. value = AtomicGetValue(&dest);
  222. EATEST_VERIFY_MSG(value == 5, "AtomicFetchSwap failure\n");
  223. // AtomicSetValueConditional
  224. dest = 0;
  225. value = 1;
  226. conditionFail = 4;
  227. conditionSucceed = 0;
  228. // Try to do conditional fetch swap which should fail
  229. value = EA::Thread::AtomicFetchSwapConditional(&dest, 1, conditionFail);
  230. EATEST_VERIFY_MSG(value != conditionFail, "AtomicFetchSwapConditional failure 0\n");
  231. EATEST_VERIFY_MSG(dest == 0, "AtomicFetchSwapConditional failure 1\n");
  232. // Try to do conditional fetch swap which should succeed
  233. value = EA::Thread::AtomicFetchSwapConditional(&dest, 1, conditionSucceed);
  234. EATEST_VERIFY_MSG(value == conditionSucceed, "AtomicFetchSwapConditional failure 2\n");
  235. EATEST_VERIFY_MSG(dest == 1, "AtomicFetchSwapConditional failure 3\n");
  236. // reset before the next test
  237. dest = 0;
  238. value = 1;
  239. // Try to do an update which should fail.
  240. result = EA::Thread::AtomicSetValueConditional(&dest, value, conditionFail);
  241. EATEST_VERIFY_MSG(!result, "AtomicSetValueConditional failure 0\n");
  242. EATEST_VERIFY_MSG(dest == 0, "AtomicSetValueConditional failure 1\n");
  243. // Try to do an update which should succeed.
  244. result = EA::Thread::AtomicSetValueConditional(&dest, value, conditionSucceed);
  245. EATEST_VERIFY_MSG(result, "AtomicSetValueConditional failure 2\n");
  246. EATEST_VERIFY_MSG(dest == 1, "AtomicSetValueConditional failure 3\n");
  247. return nErrorCount;
  248. }
  249. template<typename T>
  250. inline int TestAtomicsSizeBoundaries()
  251. {
  252. static_assert(eastl::is_floating_point<T>::value == false, "atomic floats not supported");
  253. int nErrorCount = 0;
  254. bool result = false;
  255. alignas(16) T value = 0, dest = 0;
  256. T max = eastl::numeric_limits<T>::max();
  257. T lowest = eastl::numeric_limits<T>::lowest();
  258. /// Test the max boundary
  259. ///
  260. value = AtomicSetValue(&dest, max);
  261. EATEST_VERIFY_MSG(value == 0, "max failure\n");
  262. value = AtomicGetValue(&dest);
  263. EATEST_VERIFY_MSG(value == max, "max failure\n");
  264. value = AtomicFetchIncrement(&dest);
  265. EATEST_VERIFY_MSG(value == max, "max failure\n");
  266. value = AtomicGetValue(&dest);
  267. EATEST_VERIFY_MSG(value == lowest, "max failure\n");
  268. value = AtomicFetchDecrement(&dest);
  269. EATEST_VERIFY_MSG(value == lowest, "max failure\n");
  270. value = AtomicGetValue(&dest);
  271. EATEST_VERIFY_MSG(value == max, "max failure\n");
  272. value = AtomicFetchAdd(&dest, 1);
  273. EATEST_VERIFY_MSG(value == max, "max failure\n");
  274. value = AtomicGetValue(&dest);
  275. EATEST_VERIFY_MSG(value == lowest, "max failure\n");
  276. value = AtomicFetchAnd(&dest, lowest);
  277. EATEST_VERIFY_MSG(value == lowest, "max failure\n");
  278. value = AtomicGetValue(&dest);
  279. EATEST_VERIFY_MSG(value == lowest, "max failure\n");
  280. value = AtomicFetchXor(&dest, lowest);
  281. EATEST_VERIFY_MSG(value == lowest, "max failure\n");
  282. value = AtomicGetValue(&dest);
  283. EATEST_VERIFY_MSG(value == 0, "max failure\n");
  284. value = AtomicFetchSwap(&dest, lowest);
  285. EATEST_VERIFY_MSG(value == 0, "max failure\n");
  286. value = AtomicGetValue(&dest);
  287. EATEST_VERIFY_MSG(value == lowest, "max failure\n");
  288. // reset to zero
  289. result = AtomicSetValueConditional(&dest, 0, lowest);
  290. EATEST_VERIFY_MSG(result, "max failure\n");
  291. value = AtomicGetValue(&dest);
  292. EATEST_VERIFY_MSG(value == 0, "max failure\n");
  293. /// Test the lowest boundary
  294. ///
  295. value = AtomicSetValue(&dest, lowest);
  296. EATEST_VERIFY_MSG(value == 0, "lowest failure\n");
  297. value = AtomicGetValue(&dest);
  298. EATEST_VERIFY_MSG(value == lowest, "lowest failure\n");
  299. // decrement the lowest to ensure we rollover to the highest value
  300. value = AtomicFetchDecrement(&dest);
  301. EATEST_VERIFY_MSG(value == lowest, "lowest failure\n");
  302. value = AtomicGetValue(&dest);
  303. EATEST_VERIFY_MSG(value == max, "lowest failure\n");
  304. return nErrorCount;
  305. }
  306. template<typename T>
  307. inline int TestAtomicsConstFetch()
  308. {
  309. int nErrorCount = 0;
  310. {
  311. alignas(16) const T value = 13;
  312. auto r = AtomicGetValue(&value);
  313. EATEST_VERIFY_MSG(r == value, "failure\n");
  314. }
  315. {
  316. struct Foo
  317. {
  318. Foo(uint32_t n) : baz(n) {}
  319. uint32_t getBaz() const { return AtomicGetValue(&this->baz); }
  320. uint32_t baz;
  321. };
  322. Foo foo(42);
  323. auto r = foo.getBaz();
  324. EATEST_VERIFY_MSG(r == 42, "failure\n");
  325. }
  326. return nErrorCount;
  327. }
  328. template<typename T>
  329. int TestAtomicIntT()
  330. {
  331. int nErrorCount = 0;
  332. AtomicInt<T> atomicInt = 0;
  333. ++atomicInt;
  334. --atomicInt;
  335. atomicInt += 5;
  336. atomicInt -= 5;
  337. atomicInt++;
  338. atomicInt--;
  339. EATEST_VERIFY(atomicInt == 0);
  340. return nErrorCount;
  341. }
  342. template<typename T>
  343. int TestNonMemberAtomics()
  344. {
  345. int nErrorCount = 0;
  346. nErrorCount += TestSimpleAtomicOps<T>();
  347. nErrorCount += TestAtomicsSizeBoundaries<T>();
  348. nErrorCount += TestAtomicsConstFetch<T>();
  349. return nErrorCount;
  350. }
  351. #endif // #if EA_THREADS_AVAILABLE
  352. int TestThreadAtomic()
  353. {
  354. int nErrorCount(0);
  355. { // Initial tests of 128 bit atomics
  356. #if EATHREAD_ATOMIC_128_SUPPORTED // This will be true only for 64+ bit platforms.
  357. // To consider: Use __int128_t on GCC for GCC >= 4.1
  358. EA_ALIGN(128) int64_t dest128[2] = { 0, 0 };
  359. EA_ALIGN(128) int64_t value128[2] = { 1, 2 };
  360. EA_ALIGN(128) int64_t condition128Fail[2] = { 4, 5 };
  361. EA_ALIGN(128) int64_t condition128Succeed[2] = { 0, 0 };
  362. bool result;
  363. // Try to do an update which should fail.
  364. result = EA::Thread::AtomicSetValueConditionall28(dest128, value128, condition128Fail);
  365. EATEST_VERIFY_MSG(!result, "AtomicSetValueConditional failure: result should have been false.\n");
  366. EATEST_VERIFY_F((dest128[0] == 0) && (dest128[1] == 0), "AtomicSetValueConditional failure: dest128[0]:%I64d dest128[1]:%I64d\n", dest128[0], dest128[1]);
  367. EATEST_VERIFY_F((value128[0] == 1) && (value128[1] == 2), "AtomicSetValueConditional failure: value128[0]:%I64d value128[1]:%I64d\n", value128[0], value128[1]);
  368. EATEST_VERIFY_F((condition128Fail[0] == 4) && (condition128Fail[1] == 5), "AtomicSetValueConditional failure: condition128Fail[0]:%I64d condition128Fail[1]:%I64d\n", condition128Fail[0], condition128Fail[1]);
  369. EATEST_VERIFY_F((condition128Succeed[0] == 0) && (condition128Succeed[1] == 0), "AtomicSetValueConditional failure: condition128Succeed[0]:%I64d condition128Succeed[1]:%I64d\n", condition128Succeed[0], condition128Succeed[1]);
  370. // Try to do an update which should succeed.
  371. // VC++ for VS2010 misgenerates the atomic code below in optimized builds, by passing what appears to be the wrong value for dest128 to AtomicSetValueConditional128.
  372. // We added some diagnostic code and now the compiler does the right thing. I (Paul Pedriana) wonder if the problem is related to
  373. // the alignment specification for dest, which must be 128 byte aligned for AtomicSetValueConditional128 (cmpxchg16b) to work.
  374. // The dest address is indeed being aligned to 16 bytes, so that's not the problem.
  375. EA::UnitTest::ReportVerbosity(1, "%p %p %p\n", dest128, value128, condition128Succeed);
  376. result = EA::Thread::AtomicSetValueConditionall28(dest128, value128, condition128Succeed);
  377. EATEST_VERIFY_MSG(result, "AtomicSetValueConditional failure: result should have been true.\n");
  378. EATEST_VERIFY_F((dest128[0] == 1) && (dest128[1] == 2), "AtomicSetValueConditional failure: dest128:%p dest128[0]:%I64d dest128[1]:%I64d\n", dest128, dest128[0], dest128[1]);
  379. EATEST_VERIFY_F((value128[0] == 1) && (value128[1] == 2), "AtomicSetValueConditional failure: value128:%p value128[0]:%I64d value128[1]:%I64d\n", value128, value128[0], value128[1]);
  380. EATEST_VERIFY_F((condition128Fail[0] == 4) && (condition128Fail[1] == 5), "AtomicSetValueConditional failure: condition128Fail:%p condition128Fail[0]:%I64d condition128Fail[1]:%I64d\n", condition128Fail, condition128Fail[0], condition128Fail[1]);
  381. EATEST_VERIFY_F((condition128Succeed[0] == 0) && (condition128Succeed[1] == 0), "AtomicSetValueConditional failure: condition128Succeed:%p condition128Succeed[0]:%I64d condition128Succeed[1]:%I64d\n", condition128Succeed, condition128Succeed[0], condition128Succeed[1]);
  382. #if defined(EA_COMPILER_GNUC) // GCC defines __int128_t as a built-in type.
  383. __int128_t dest;
  384. __int128_t value;
  385. __int128_t conditionFail;
  386. __int128_t conditionSucceed;
  387. // AtomicGetValue
  388. dest = 3;
  389. value = AtomicGetValue(&dest);
  390. EATEST_VERIFY_MSG(value == 3, "AtomicGetValue[128] failure\n");
  391. // AtomicSetValue
  392. AtomicSetValue(&dest, 4);
  393. value = AtomicGetValue(&dest);
  394. EATEST_VERIFY_MSG(value == 4, "AtomicSetValue[128] failure\n");
  395. // AtomicIncrement
  396. value = AtomicIncrement(&dest);
  397. EATEST_VERIFY_MSG(value == 5, "AtomicIncrement[128] failure\n");
  398. value = AtomicGetValue(&dest);
  399. EATEST_VERIFY_MSG(value == 5, "AtomicIncrement[128] failure\n");
  400. // AtomicDecrement
  401. value = AtomicDecrement(&dest);
  402. EATEST_VERIFY_MSG(value == 4, "AtomicDecrement[128] failure\n");
  403. value = AtomicGetValue(&dest);
  404. EATEST_VERIFY_MSG(value == 4, "AtomicDecrement[128] failure\n");
  405. // AtomicAdd
  406. value = AtomicAdd(&dest, 3);
  407. EATEST_VERIFY_MSG(value == 7, "AtomicAdd[128] failure\n");
  408. value = AtomicGetValue(&dest);
  409. EATEST_VERIFY_MSG(value == 7, "AtomicAdd[128] failure\n");
  410. // AtomicOr
  411. value = AtomicOr(&dest, 8);
  412. EATEST_VERIFY_MSG(value == 15, "AtomicOr[128] failure\n");
  413. value = AtomicGetValue(&dest);
  414. EATEST_VERIFY_MSG(value == 15, "AtomicOr[128] failure\n");
  415. // AtomicAnd
  416. value = AtomicAnd(&dest, 3);
  417. EATEST_VERIFY_MSG(value == 3, "AtomicAnd[128] failure\n");
  418. value = AtomicGetValue(&dest);
  419. EATEST_VERIFY_MSG(value == 3, "AtomicAnd[128] failure\n");
  420. // AtomicXor
  421. value = AtomicXor(&dest, dest);
  422. EATEST_VERIFY_MSG(value == 0, "AtomicXor[128] failure\n");
  423. value = AtomicGetValue(&dest);
  424. EATEST_VERIFY_MSG(value == 0, "AtomicXor[128] failure\n");
  425. // AtomicSwap
  426. value = AtomicSwap(&dest, 5);
  427. EATEST_VERIFY_MSG(value == 0, "AtomicSwap[128] failure\n");
  428. value = AtomicGetValue(&dest);
  429. EATEST_VERIFY_MSG(value == 5, "AtomicSwap[128] failure\n");
  430. // AtomicSetValueConditional
  431. dest = 0;
  432. value = 1;
  433. conditionFail = 4;
  434. conditionSucceed = 0;
  435. // Try to do an update which should fail.
  436. result = EA::Thread::AtomicSetValueConditional(&dest, value, conditionFail);
  437. EATEST_VERIFY_MSG(!result, "AtomicSetValueConditional failure 0\n");
  438. EATEST_VERIFY_MSG(dest == 0, "AtomicSetValueConditional failure 1\n");
  439. // Try to do an update which should succeed.
  440. result = EA::Thread::AtomicSetValueConditional(&dest, value, conditionSucceed);
  441. EATEST_VERIFY_MSG(result, "AtomicSetValueConditional failure 2\n");
  442. EATEST_VERIFY_MSG(dest == 1, "AtomicSetValueConditional failure 3\n");
  443. if(nErrorCount != 0)
  444. return nErrorCount;
  445. #endif
  446. #endif
  447. }
  448. { // Basic single-threaded Atomic test.
  449. AtomicInt32 i32(1);
  450. AtomicUint32 u32(1);
  451. EATEST_VERIFY_MSG(i32.GetValue() == 1, "AtomicInt32 failure.");
  452. EATEST_VERIFY_MSG(u32.GetValue() == 1, "AtomicUint32 failure.");
  453. char buffer[64];
  454. sprintf(buffer, "%d %u", (signed int)i32.GetValue(), (unsigned int)u32.GetValue());
  455. EATEST_VERIFY_MSG(strcmp(buffer, "1 1") == 0, "AtomicInt32 failure.");
  456. // Copy ctor/operator=.
  457. AtomicInt32 i32CopyA(i32);
  458. AtomicInt32 i32CopyB(i32CopyA);
  459. i32CopyA = i32CopyB;
  460. sprintf(buffer, "%d %d", (signed int)i32CopyA.GetValue(), (signed int)i32CopyB.GetValue());
  461. EATEST_VERIFY_MSG(strcmp(buffer, "1 1") == 0, "AtomicInt32 failure.");
  462. // Test platforms that support 64 bits..
  463. AtomicInt64 i64(1);
  464. AtomicUint64 u64(1);
  465. sprintf(buffer, "%.0f %.0f", (double)i64.GetValue(), (double)u64.GetValue());
  466. EATEST_VERIFY_MSG(strcmp(buffer, "1 1") == 0, "AtomicInt64 failure.");
  467. // Copy ctor/operator=.
  468. AtomicInt64 i64CopyA(i64);
  469. AtomicInt64 i64CopyB(i64CopyA);
  470. i64CopyA = i64CopyB;
  471. sprintf(buffer, "%d %d", (signed int)i64CopyA.GetValue(), (signed int)i64CopyB.GetValue());
  472. EATEST_VERIFY_MSG(strcmp(buffer, "1 1") == 0, "AtomicInt64 failure.");
  473. bool result = i64.SetValueConditional(2, 99999); // This should not set the value to 2.
  474. EATEST_VERIFY_MSG(!result && (i64.GetValue() == 1), "AtomicInt64 failure.");
  475. i64.SetValueConditional(2, 1); // This should set the value to 2.
  476. EATEST_VERIFY_MSG(!result && (i64.GetValue() == 2), "AtomicInt64 failure.");
  477. }
  478. { // Basic single-threaded AtomicInt32 test.
  479. AtomicInt32 i(0); // Note that this assignment goes through AtomicInt32 operator=().
  480. AtomicInt32::ValueType x;
  481. EATEST_VERIFY_MSG(i == 0, "AtomicInt32 failure.");
  482. ++i;
  483. i++;
  484. --i;
  485. i--;
  486. i += 7;
  487. i -= 3;
  488. EATEST_VERIFY_MSG(i == 4, "AtomicInt32 failure.");
  489. i = 2;
  490. x = i.GetValue();
  491. EATEST_VERIFY_MSG(x == 2, "AtomicInt32 failure.");
  492. i.Increment();
  493. i.Decrement();
  494. i.Add(5);
  495. i.Add(-2);
  496. EATEST_VERIFY_MSG(i == 5, "AtomicInt32 failure.");
  497. i.SetValue(6);
  498. EATEST_VERIFY_MSG(i == 6, "AtomicInt32 failure.");
  499. bool bWasEqualTo10000 = i.SetValueConditional(3, 10000);
  500. EATEST_VERIFY_MSG(!bWasEqualTo10000, "AtomicInt32 failure.");
  501. bool bWasEqualTo6 = i.SetValueConditional(3, 6);
  502. EATEST_VERIFY_MSG(bWasEqualTo6, "AtomicInt32 failure.");
  503. }
  504. { // Verify pre-increment/post-increment works as intended.
  505. AtomicInt32 i32(0);
  506. AtomicInt32::ValueType x32;
  507. // ValueType SetValue(ValueType n)
  508. // Safely sets a new value. Returns the old value.
  509. x32 = i32.SetValue(1);
  510. EATEST_VERIFY_MSG(x32 == 0, "AtomicInt return value failure.");
  511. // ValueType Increment()
  512. // Safely increments the value. Returns the new value.
  513. x32 = i32.Increment();
  514. EATEST_VERIFY_MSG(x32 == 2, "AtomicInt return value failure.");
  515. // ValueType Decrement()
  516. // Safely decrements the value. Returns the new value.
  517. x32 = i32.Decrement();
  518. EATEST_VERIFY_MSG(x32 == 1, "AtomicInt return value failure.");
  519. // ValueType Add(ValueType n)
  520. // Safely adds a value, which can be negative. Returns the new value.
  521. x32 = i32.Add(35);
  522. EATEST_VERIFY_MSG(x32 == 36, "AtomicInt return value failure.");
  523. // ValueType operator=(ValueType n)
  524. // Safely assigns the value. Returns the new value.
  525. x32 = (i32 = 17);
  526. EATEST_VERIFY_MSG(x32 == 17, "AtomicInt return value failure.");
  527. // ValueType operator+=(ValueType n)
  528. // Safely adds a value, which can be negative. Returns the new value.
  529. x32 = (i32 += 3);
  530. EATEST_VERIFY_MSG(x32 == 20, "AtomicInt return value failure.");
  531. // ValueType operator-=(ValueType n)
  532. // Safely subtracts a value, which can be negative. Returns the new value.
  533. x32 = (i32 -= 6);
  534. EATEST_VERIFY_MSG(x32 == 14, "AtomicInt return value failure.");
  535. // ValueType operator++()
  536. // pre-increment operator++
  537. x32 = ++i32;
  538. EATEST_VERIFY_MSG(x32 == 15, "AtomicInt return value failure.");
  539. EATEST_VERIFY_MSG(i32 == 15, "AtomicInt return value failure.");
  540. // ValueType operator++(int)
  541. // post-increment operator++
  542. x32 = i32++;
  543. EATEST_VERIFY_MSG(x32 == 15, "AtomicInt return value failure.");
  544. EATEST_VERIFY_MSG(i32 == 16, "AtomicInt return value failure.");
  545. // ValueType operator--()
  546. // pre-increment operator--
  547. x32 = --i32;
  548. EATEST_VERIFY_MSG(x32 == 15, "AtomicInt return value failure.");
  549. EATEST_VERIFY_MSG(i32 == 15, "AtomicInt return value failure.");
  550. // ValueType operator--(int)
  551. // post-increment operator--
  552. x32 = i32--;
  553. EATEST_VERIFY_MSG(x32 == 15, "AtomicInt return value failure.");
  554. EATEST_VERIFY_MSG(i32 == 14, "AtomicInt return value failure.");
  555. }
  556. { // Verify pre-increment/post-increment works as intended.
  557. AtomicInt64 i64(0);
  558. AtomicInt64::ValueType x64;
  559. // ValueType SetValue(ValueType n)
  560. // Safely sets a new value. Returns the old value.
  561. x64 = i64.SetValue(1);
  562. EATEST_VERIFY_MSG(x64 == 0, "AtomicInt return value failure.");
  563. // ValueType Increment()
  564. // Safely increments the value. Returns the new value.
  565. x64 = i64.Increment();
  566. EATEST_VERIFY_MSG(x64 == 2, "AtomicInt return value failure.");
  567. // ValueType Decrement()
  568. // Safely decrements the value. Returns the new value.
  569. x64 = i64.Decrement();
  570. EATEST_VERIFY_MSG(x64 == 1, "AtomicInt return value failure.");
  571. // ValueType Add(ValueType n)
  572. // Safely adds a value, which can be negative. Returns the new value.
  573. x64 = i64.Add(35);
  574. EATEST_VERIFY_MSG(x64 == 36, "AtomicInt return value failure.");
  575. // ValueType operator=(ValueType n)
  576. // Safely assigns the value. Returns the new value.
  577. x64 = (i64 = 17);
  578. EATEST_VERIFY_MSG(x64 == 17, "AtomicInt return value failure.");
  579. // ValueType operator+=(ValueType n)
  580. // Safely adds a value, which can be negative. Returns the new value.
  581. x64 = (i64 += 3);
  582. EATEST_VERIFY_MSG(x64 == 20, "AtomicInt return value failure.");
  583. // ValueType operator-=(ValueType n)
  584. // Safely subtracts a value, which can be negative. Returns the new value.
  585. x64 = (i64 -= 6);
  586. EATEST_VERIFY_MSG(x64 == 14, "AtomicInt return value failure.");
  587. // ValueType operator++()
  588. // pre-increment operator++
  589. x64 = ++i64;
  590. EATEST_VERIFY_MSG(x64 == 15, "AtomicInt return value failure.");
  591. EATEST_VERIFY_MSG(i64 == 15, "AtomicInt return value failure.");
  592. // ValueType operator++(int)
  593. // post-increment operator++
  594. x64 = i64++;
  595. EATEST_VERIFY_MSG(x64 == 15, "AtomicInt return value failure.");
  596. EATEST_VERIFY_MSG(i64 == 16, "AtomicInt return value failure.");
  597. // ValueType operator--()
  598. // pre-increment operator--
  599. x64 = --i64;
  600. EATEST_VERIFY_MSG(x64 == 15, "AtomicInt return value failure.");
  601. EATEST_VERIFY_MSG(i64 == 15, "AtomicInt return value failure.");
  602. // ValueType operator--(int)
  603. // post-increment operator--
  604. x64 = i64--;
  605. EATEST_VERIFY_MSG(x64 == 15, "AtomicInt return value failure.");
  606. EATEST_VERIFY_MSG(i64 == 14, "AtomicInt return value failure.");
  607. }
  608. { // Basic single-threaded AtomicPointer test.
  609. AtomicPointer p(NULL);
  610. AtomicPointer::PointerValueType pTemp;
  611. EATEST_VERIFY_MSG(p.GetValue() == NULL, "AtomicPointer failure.");
  612. ++p;
  613. p++;
  614. --p;
  615. p--;
  616. p += 7;
  617. p -= 3;
  618. EATEST_VERIFY_MSG(p == (void*)4, "AtomicPointer failure.");
  619. p = (void*)2;
  620. pTemp = p.GetValue();
  621. EATEST_VERIFY_MSG((uintptr_t)pTemp == 2, "AtomicPointer failure.");
  622. p.Increment();
  623. p.Decrement();
  624. p.Add(5);
  625. p.Add(-2);
  626. EATEST_VERIFY_MSG(p == (void*)5, "AtomicPointer failure.");
  627. p.SetValue((void*)6);
  628. EATEST_VERIFY_MSG(p == (void*)6, "AtomicPointer failure.");
  629. bool bWasEqualTo10000 = p.SetValueConditional((void*)3, (void*)10000);
  630. EATEST_VERIFY_MSG(!bWasEqualTo10000, "AtomicPointer failure.");
  631. bool bWasEqualTo6 = p.SetValueConditional((void*)3, (void*)6);
  632. EATEST_VERIFY_MSG(bWasEqualTo6, "AtomicPointer failure.");
  633. }
  634. {
  635. AtomicInt32 gA, gB;
  636. gA = gB = 0;
  637. ++gA;
  638. ++gB;
  639. gA = gB = 0;
  640. EATEST_VERIFY_MSG((gA == 0) && (gB == 0), "AtomicInt32 operator= failure.");
  641. }
  642. #if EA_THREADS_AVAILABLE
  643. { // Multithreaded test 1
  644. AWorkData32 workData32;
  645. const int kThreadCount(kMaxConcurrentThreadCount - 1);
  646. Thread thread[kThreadCount];
  647. Thread::Status status;
  648. for(int i(0); i < kThreadCount; i++)
  649. thread[i].Begin(Atomic32TestThreadFunction1, &workData32);
  650. EA::UnitTest::ThreadSleepRandom(gTestLengthSeconds*1000, gTestLengthSeconds*1000);
  651. workData32.mbShouldQuit = true;
  652. for(int i(0); i < kThreadCount; i++)
  653. {
  654. status = thread[i].WaitForEnd(GetThreadTime() + 30000);
  655. EATEST_VERIFY_MSG(status != EA::Thread::Thread::kStatusRunning, "Atomic/Thread failure.");
  656. }
  657. // In the end, the sum must be zero.
  658. EATEST_VERIFY_MSG(workData32.mnAtomicInteger1 == 0, "Atomic/Thread failure.");
  659. EATEST_VERIFY_MSG(workData32.mnAtomicInteger2 == 0, "Atomic/Thread failure.");
  660. nErrorCount += (int)workData32.mnErrorCount;
  661. }
  662. { // Multithreaded test 2
  663. AWorkData32 workData32;
  664. const int kThreadCount(kMaxConcurrentThreadCount - 1);
  665. Thread thread[kThreadCount];
  666. Thread::Status status;
  667. for(int i(0); i < kThreadCount; i++)
  668. thread[i].Begin(Atomic32TestThreadFunction2, &workData32);
  669. EA::UnitTest::ThreadSleepRandom(gTestLengthSeconds*1000, gTestLengthSeconds*1000);
  670. workData32.mbShouldQuit = true;
  671. for(int i(0); i < kThreadCount; i++)
  672. {
  673. status = thread[i].WaitForEnd(GetThreadTime() + 30000);
  674. EATEST_VERIFY_MSG(status != EA::Thread::Thread::kStatusRunning, "Atomic/Thread failure.");
  675. }
  676. nErrorCount += (int)workData32.mnErrorCount;
  677. }
  678. { // Multithreaded test 1
  679. AWorkData64 workData64;
  680. const int kThreadCount(kMaxConcurrentThreadCount - 1);
  681. Thread thread[kThreadCount];
  682. Thread::Status status;
  683. for(int i(0); i < kThreadCount; i++)
  684. thread[i].Begin(Atomic64TestThreadFunction1, &workData64);
  685. EA::UnitTest::ThreadSleepRandom(gTestLengthSeconds*1000, gTestLengthSeconds*1000);
  686. workData64.mbShouldQuit = true;
  687. for(int i(0); i < kThreadCount; i++)
  688. {
  689. status = thread[i].WaitForEnd(GetThreadTime() + 30000);
  690. EATEST_VERIFY_MSG(status != EA::Thread::Thread::kStatusRunning, "Atomic/Thread failure.");
  691. }
  692. // In the end, the sum must be zero.
  693. EATEST_VERIFY_MSG(workData64.mnAtomicInteger1 == 0, "Atomic/Thread failure.");
  694. EATEST_VERIFY_MSG(workData64.mnAtomicInteger2 == 0, "Atomic/Thread failure.");
  695. nErrorCount += (int)workData64.mnErrorCount;
  696. }
  697. { // Multithreaded test 2
  698. AWorkData64 workData64;
  699. const int kThreadCount(kMaxConcurrentThreadCount - 1);
  700. Thread thread[kThreadCount];
  701. Thread::Status status;
  702. for(int i(0); i < kThreadCount; i++)
  703. thread[i].Begin(Atomic64TestThreadFunction2, &workData64);
  704. EA::UnitTest::ThreadSleepRandom(gTestLengthSeconds*1000, gTestLengthSeconds*1000);
  705. workData64.mbShouldQuit = true;
  706. for(int i(0); i < kThreadCount; i++)
  707. {
  708. status = thread[i].WaitForEnd(GetThreadTime() + 30000);
  709. EATEST_VERIFY_MSG(status != EA::Thread::Thread::kStatusRunning, "Atomic/Thread failure.");
  710. }
  711. nErrorCount += (int)workData64.mnErrorCount;
  712. }
  713. { // Multithreaded test 3
  714. AWorkData64 workData64;
  715. const int kThreadCount(kMaxConcurrentThreadCount - 1);
  716. Thread thread[kThreadCount];
  717. Thread::Status status;
  718. for(int i(0); i < kThreadCount; i++)
  719. thread[i].Begin(Atomic64TestThreadFunction3, &workData64);
  720. EA::UnitTest::ThreadSleepRandom(gTestLengthSeconds*1000, gTestLengthSeconds*1000);
  721. workData64.mbShouldQuit = true;
  722. for(int i(0); i < kThreadCount; i++)
  723. {
  724. status = thread[i].WaitForEnd(GetThreadTime() + 30000);
  725. EATEST_VERIFY_MSG(status != EA::Thread::Thread::kStatusRunning, "Atomic/Thread failure.");
  726. }
  727. nErrorCount += (int)workData64.mnErrorCount;
  728. }
  729. #endif
  730. {
  731. nErrorCount += TestAtomicIntT<short>();
  732. nErrorCount += TestAtomicIntT<unsigned short>();
  733. nErrorCount += TestAtomicIntT<int>();
  734. nErrorCount += TestAtomicIntT<unsigned int>();
  735. nErrorCount += TestAtomicIntT<long>();
  736. nErrorCount += TestAtomicIntT<unsigned long>();
  737. nErrorCount += TestAtomicIntT<intptr_t>();
  738. nErrorCount += TestAtomicIntT<uintptr_t>();
  739. nErrorCount += TestAtomicIntT<size_t>();
  740. nErrorCount += TestAtomicIntT<int16_t>();
  741. nErrorCount += TestAtomicIntT<uint16_t>();
  742. nErrorCount += TestAtomicIntT<int32_t>();
  743. nErrorCount += TestAtomicIntT<uint32_t>();
  744. nErrorCount += TestAtomicIntT<char32_t>();
  745. nErrorCount += TestAtomicIntT<long long>();
  746. nErrorCount += TestAtomicIntT<unsigned long long>();
  747. nErrorCount += TestAtomicIntT<int64_t>();
  748. nErrorCount += TestAtomicIntT<uint64_t>();
  749. }
  750. // Non-Member Atomics Tests
  751. {
  752. nErrorCount += TestNonMemberAtomics<short>();
  753. nErrorCount += TestNonMemberAtomics<unsigned short>();
  754. nErrorCount += TestNonMemberAtomics<int>();
  755. nErrorCount += TestNonMemberAtomics<unsigned int>();
  756. nErrorCount += TestNonMemberAtomics<long>();
  757. nErrorCount += TestNonMemberAtomics<unsigned long>();
  758. nErrorCount += TestNonMemberAtomics<intptr_t>();
  759. nErrorCount += TestNonMemberAtomics<uintptr_t>();
  760. nErrorCount += TestNonMemberAtomics<size_t>();
  761. nErrorCount += TestNonMemberAtomics<int16_t>();
  762. nErrorCount += TestNonMemberAtomics<uint16_t>();
  763. nErrorCount += TestNonMemberAtomics<int32_t>();
  764. nErrorCount += TestNonMemberAtomics<uint32_t>();
  765. nErrorCount += TestNonMemberAtomics<char32_t>();
  766. nErrorCount += TestNonMemberAtomics<long long>();
  767. nErrorCount += TestNonMemberAtomics<unsigned long long>();
  768. nErrorCount += TestNonMemberAtomics<int64_t>();
  769. nErrorCount += TestNonMemberAtomics<uint64_t>();
  770. }
  771. return nErrorCount;
  772. }