/////////////////////////////////////////////////////////////////////////////// // Copyright (c) Electronic Arts Inc. All rights reserved. /////////////////////////////////////////////////////////////////////////////// #include "TestThread.h" #include #include #include EA_DISABLE_VC_WARNING(4265 4365 4836 4571 4625 4626 4628 4193 4127 4548) #include EA_RESTORE_VC_WARNING() #if defined(_MSC_VER) #pragma warning(disable: 4996) // This function or variable may be unsafe / deprecated. #endif #include using namespace EA::Thread; #if EA_THREADS_AVAILABLE const int kMaxConcurrentThreadCount = EATHREAD_MAX_CONCURRENT_THREAD_COUNT; struct AWorkData32 { volatile bool mbShouldQuit; AtomicInt32 mnAtomicInteger1; AtomicInt32 mnAtomicInteger2; AtomicInt32 mnErrorCount; AWorkData32() : mbShouldQuit(false), mnAtomicInteger1(0), mnAtomicInteger2(0), mnErrorCount(0) {} }; static intptr_t Atomic32TestThreadFunction1(void* pvWorkData) { int nErrorCount = 0; AWorkData32* pWorkData = (AWorkData32*)pvWorkData; const ThreadId threadId = GetThreadId(); EA::UnitTest::ReportVerbosity(1, "Atomic test function 1 created, thread id %s\n", EAThreadThreadIdToString(threadId)); // Do a series of operations, the final result of which is zero. while(!pWorkData->mbShouldQuit) { ++pWorkData->mnAtomicInteger1; ++pWorkData->mnAtomicInteger2; --pWorkData->mnAtomicInteger1; --pWorkData->mnAtomicInteger2; pWorkData->mnAtomicInteger1 += 5; pWorkData->mnAtomicInteger2 += 5; pWorkData->mnAtomicInteger1 -= 5; pWorkData->mnAtomicInteger2 -= 5; pWorkData->mnAtomicInteger1++; pWorkData->mnAtomicInteger2++; pWorkData->mnAtomicInteger1--; pWorkData->mnAtomicInteger2--; ThreadCooperativeYield(); } pWorkData->mnErrorCount += nErrorCount; EA::UnitTest::ReportVerbosity(1, "Atomic test function 1 exiting, thread id %s\n", EAThreadThreadIdToString(threadId)); return 0; } static intptr_t Atomic32TestThreadFunction2(void* pvWorkData) { int nErrorCount = 0; AWorkData32* pWorkData = (AWorkData32*)pvWorkData; const ThreadId threadId = GetThreadId(); ThreadUniqueId threadUniqueId; EAThreadGetUniqueId(threadUniqueId); int32_t threadUniqueId32 = (int32_t)threadUniqueId; EA::UnitTest::ReportVerbosity(1, "Atomic test function 2 created, thread id %s\n", EAThreadThreadIdToString(threadId)); // Test the SetValueConditional function. We basically create a spinlock here. while(!pWorkData->mbShouldQuit) { if(pWorkData->mnAtomicInteger1.SetValueConditional(threadUniqueId32, 0x11223344)) { EATEST_VERIFY_MSG(pWorkData->mnAtomicInteger1 == threadUniqueId32, "AtomicInt SetValueConditional failure."); pWorkData->mnAtomicInteger1 = 0x11223344; } ThreadCooperativeYield(); } pWorkData->mnErrorCount += nErrorCount; EA::UnitTest::ReportVerbosity(1, "Atomic test function 2 exiting, thread id %s\n", EAThreadThreadIdToString(threadId)); return 0; } struct AWorkData64 { volatile bool mbShouldQuit; AtomicInt64 mnAtomicInteger1; AtomicInt64 mnAtomicInteger2; AtomicInt64 mnErrorCount; AWorkData64() : mbShouldQuit(false), mnAtomicInteger1(0), mnAtomicInteger2(0), mnErrorCount(0) {} }; static intptr_t Atomic64TestThreadFunction1(void* pvWorkData) { int nErrorCount = 0; AWorkData64* pWorkData = (AWorkData64*)pvWorkData; const ThreadId threadId = GetThreadId(); EA::UnitTest::ReportVerbosity(1, "Atomic64 test function 1 created, thread id %s\n", EAThreadThreadIdToString(threadId)); // Do a series of operations, the final result of which is zero. while(!pWorkData->mbShouldQuit) { ++pWorkData->mnAtomicInteger1; ++pWorkData->mnAtomicInteger2; --pWorkData->mnAtomicInteger1; --pWorkData->mnAtomicInteger2; pWorkData->mnAtomicInteger1 += UINT64_C(0x0000000fffffffff); pWorkData->mnAtomicInteger2 += UINT64_C(0x0000000ffffffffe); pWorkData->mnAtomicInteger1 -= UINT64_C(0x0000000fffffffff); pWorkData->mnAtomicInteger2 -= UINT64_C(0x0000000ffffffffe); pWorkData->mnAtomicInteger1++; pWorkData->mnAtomicInteger2++; pWorkData->mnAtomicInteger1--; pWorkData->mnAtomicInteger2--; ThreadCooperativeYield(); } pWorkData->mnErrorCount += nErrorCount; EA::UnitTest::ReportVerbosity(1, "Atomic64 test function 1 exiting, thread id %s\n", EAThreadThreadIdToString(threadId)); return 0; } static intptr_t Atomic64TestThreadFunction2(void* pvWorkData) { int nErrorCount = 0; AWorkData64* pWorkData = (AWorkData64*)pvWorkData; const ThreadId threadId = GetThreadId(); ThreadUniqueId threadUniqueId; EAThreadGetUniqueId(threadUniqueId); uint64_t threadUnqueId64 = (uint64_t)threadUniqueId | UINT64_C(0xeeeeddddffffffff); EA::UnitTest::ReportVerbosity(1, "Atomic64 test function 2 created, thread id %s\n", EAThreadThreadIdToString(threadId)); // Test the SetValueConditional function. We basically create a spinlock here. while(!pWorkData->mbShouldQuit) { if(pWorkData->mnAtomicInteger1.SetValueConditional(threadUnqueId64, 0x1122334455667788)) { EATEST_VERIFY_MSG(pWorkData->mnAtomicInteger1 == static_cast(threadUnqueId64), "AtomicInt64 SetValueConditional failure."); pWorkData->mnAtomicInteger1.SetValue(0x1122334455667788); } ThreadCooperativeYield(); } pWorkData->mnErrorCount += nErrorCount; EA::UnitTest::ReportVerbosity(1, "Atomic64 test function 2 exiting, thread id %s\n", EAThreadThreadIdToString(threadId)); return 0; } static intptr_t Atomic64TestThreadFunction3(void* pvWorkData) { int nErrorCount = 0; AWorkData64* pWorkData = (AWorkData64*)pvWorkData; const ThreadId threadId = GetThreadId(); const uint64_t value0 = UINT64_C(0x0000000000000000); const uint64_t value1 = UINT64_C(0xffffffffffffffff); EA::UnitTest::ReportVerbosity(1, "Atomic64 test function 2 created, thread id %s\n", EAThreadThreadIdToString(threadId)); // Test the SetValueConditional function. while(!pWorkData->mbShouldQuit) { pWorkData->mnAtomicInteger1.SetValueConditional(value0, value1); uint64_t currentValue = pWorkData->mnAtomicInteger1.GetValue(); EATEST_VERIFY_MSG((currentValue == value0) || (currentValue == value1), "AtomicInt64 SetValueConditional failure."); pWorkData->mnAtomicInteger1.SetValueConditional(value1, value0); currentValue = pWorkData->mnAtomicInteger1.GetValue(); EATEST_VERIFY_MSG((currentValue == value0) || (currentValue == value1), "AtomicInt64 SetValueConditional failure."); ThreadCooperativeYield(); } pWorkData->mnErrorCount += nErrorCount; EA::UnitTest::ReportVerbosity(1, "Atomic64 test function 2 exiting, thread id %s\n", EAThreadThreadIdToString(threadId)); return 0; } template int TestSimpleAtomicOps() { int nErrorCount = 0; bool result = false; alignas(16) T value = 0; alignas(16) T dest = 0; alignas(16) T conditionFail = 4; alignas(16) T conditionSucceed = 0; // AtomicGetValue dest = 3; value = AtomicGetValue(&dest); EATEST_VERIFY_MSG(value == 3, "AtomicGetValue failure\n"); // AtomicSetValue value = AtomicSetValue(&dest, 4); EATEST_VERIFY_MSG(value == 3, "AtomicSetValue failure\n"); value = AtomicGetValue(&dest); EATEST_VERIFY_MSG(value == 4, "AtomicSetValue failure\n"); // AtomicFetchIncrement value = AtomicFetchIncrement(&dest); EATEST_VERIFY_MSG(value == 4, "AtomicFetchIncrement failure\n"); value = AtomicGetValue(&dest); EATEST_VERIFY_MSG(value == 5, "AtomicFetchIncrement failure\n"); // AtomicFetchDecrement value = AtomicFetchDecrement(&dest); EATEST_VERIFY_MSG(value == 5, "AtomicFetchDecrement failure\n"); value = AtomicGetValue(&dest); EATEST_VERIFY_MSG(value == 4, "AtomicFetchDecrement failure\n"); // AtomicFetchAdd value = AtomicFetchAdd(&dest, 3); EATEST_VERIFY_MSG(value == 4, "AtomicFetchAdd failure\n"); value = AtomicGetValue(&dest); EATEST_VERIFY_MSG(value == 7, "AtomicFetchAdd failure\n"); // AtomicFetchSub value = AtomicFetchSub(&dest, 3); EATEST_VERIFY_MSG(value == 7, "AtomicFetchSub failure\n"); value = AtomicGetValue(&dest); EATEST_VERIFY_MSG(value == 4, "AtomicFetchSub failure\n"); value = AtomicFetchSub(&dest, T(-3)); EATEST_VERIFY_MSG(value == 4, "AtomicFetchSub failure\n"); value = AtomicGetValue(&dest); EATEST_VERIFY_MSG(value == 7, "AtomicFetchSub failure\n"); // AtomicFetchOr value = AtomicFetchOr(&dest, 8); EATEST_VERIFY_MSG(value == 7, "AtomicFetchOr failure\n"); value = AtomicGetValue(&dest); EATEST_VERIFY_MSG(value == 15, "AtomicFetchOr failure\n"); // AtomicFetchAnd value = AtomicFetchAnd(&dest, 3); EATEST_VERIFY_MSG(value == 15, "AtomicFetchAnd failure\n"); value = AtomicGetValue(&dest); EATEST_VERIFY_MSG(value == 3, "AtomicFetchAnd failure\n"); // AtomicFetchXor value = AtomicFetchXor(&dest, dest); EATEST_VERIFY_MSG(value == 3, "AtomicFetchXor failure\n"); value = AtomicGetValue(&dest); EATEST_VERIFY_MSG(value == 0, "AtomicFetchXor failure\n"); // AtomicFetchSwap value = AtomicFetchSwap(&dest, 5); EATEST_VERIFY_MSG(value == 0, "AtomicFetchSwap failure\n"); value = AtomicGetValue(&dest); EATEST_VERIFY_MSG(value == 5, "AtomicFetchSwap failure\n"); // AtomicSetValueConditional dest = 0; value = 1; conditionFail = 4; conditionSucceed = 0; // Try to do conditional fetch swap which should fail value = EA::Thread::AtomicFetchSwapConditional(&dest, 1, conditionFail); EATEST_VERIFY_MSG(value != conditionFail, "AtomicFetchSwapConditional failure 0\n"); EATEST_VERIFY_MSG(dest == 0, "AtomicFetchSwapConditional failure 1\n"); // Try to do conditional fetch swap which should succeed value = EA::Thread::AtomicFetchSwapConditional(&dest, 1, conditionSucceed); EATEST_VERIFY_MSG(value == conditionSucceed, "AtomicFetchSwapConditional failure 2\n"); EATEST_VERIFY_MSG(dest == 1, "AtomicFetchSwapConditional failure 3\n"); // reset before the next test dest = 0; value = 1; // Try to do an update which should fail. result = EA::Thread::AtomicSetValueConditional(&dest, value, conditionFail); EATEST_VERIFY_MSG(!result, "AtomicSetValueConditional failure 0\n"); EATEST_VERIFY_MSG(dest == 0, "AtomicSetValueConditional failure 1\n"); // Try to do an update which should succeed. result = EA::Thread::AtomicSetValueConditional(&dest, value, conditionSucceed); EATEST_VERIFY_MSG(result, "AtomicSetValueConditional failure 2\n"); EATEST_VERIFY_MSG(dest == 1, "AtomicSetValueConditional failure 3\n"); return nErrorCount; } template inline int TestAtomicsSizeBoundaries() { static_assert(eastl::is_floating_point::value == false, "atomic floats not supported"); int nErrorCount = 0; bool result = false; alignas(16) T value = 0, dest = 0; T max = eastl::numeric_limits::max(); T lowest = eastl::numeric_limits::lowest(); /// Test the max boundary /// value = AtomicSetValue(&dest, max); EATEST_VERIFY_MSG(value == 0, "max failure\n"); value = AtomicGetValue(&dest); EATEST_VERIFY_MSG(value == max, "max failure\n"); value = AtomicFetchIncrement(&dest); EATEST_VERIFY_MSG(value == max, "max failure\n"); value = AtomicGetValue(&dest); EATEST_VERIFY_MSG(value == lowest, "max failure\n"); value = AtomicFetchDecrement(&dest); EATEST_VERIFY_MSG(value == lowest, "max failure\n"); value = AtomicGetValue(&dest); EATEST_VERIFY_MSG(value == max, "max failure\n"); value = AtomicFetchAdd(&dest, 1); EATEST_VERIFY_MSG(value == max, "max failure\n"); value = AtomicGetValue(&dest); EATEST_VERIFY_MSG(value == lowest, "max failure\n"); value = AtomicFetchAnd(&dest, lowest); EATEST_VERIFY_MSG(value == lowest, "max failure\n"); value = AtomicGetValue(&dest); EATEST_VERIFY_MSG(value == lowest, "max failure\n"); value = AtomicFetchXor(&dest, lowest); EATEST_VERIFY_MSG(value == lowest, "max failure\n"); value = AtomicGetValue(&dest); EATEST_VERIFY_MSG(value == 0, "max failure\n"); value = AtomicFetchSwap(&dest, lowest); EATEST_VERIFY_MSG(value == 0, "max failure\n"); value = AtomicGetValue(&dest); EATEST_VERIFY_MSG(value == lowest, "max failure\n"); // reset to zero result = AtomicSetValueConditional(&dest, 0, lowest); EATEST_VERIFY_MSG(result, "max failure\n"); value = AtomicGetValue(&dest); EATEST_VERIFY_MSG(value == 0, "max failure\n"); /// Test the lowest boundary /// value = AtomicSetValue(&dest, lowest); EATEST_VERIFY_MSG(value == 0, "lowest failure\n"); value = AtomicGetValue(&dest); EATEST_VERIFY_MSG(value == lowest, "lowest failure\n"); // decrement the lowest to ensure we rollover to the highest value value = AtomicFetchDecrement(&dest); EATEST_VERIFY_MSG(value == lowest, "lowest failure\n"); value = AtomicGetValue(&dest); EATEST_VERIFY_MSG(value == max, "lowest failure\n"); return nErrorCount; } template inline int TestAtomicsConstFetch() { int nErrorCount = 0; { alignas(16) const T value = 13; auto r = AtomicGetValue(&value); EATEST_VERIFY_MSG(r == value, "failure\n"); } { struct Foo { Foo(uint32_t n) : baz(n) {} uint32_t getBaz() const { return AtomicGetValue(&this->baz); } uint32_t baz; }; Foo foo(42); auto r = foo.getBaz(); EATEST_VERIFY_MSG(r == 42, "failure\n"); } return nErrorCount; } template int TestAtomicIntT() { int nErrorCount = 0; AtomicInt atomicInt = 0; ++atomicInt; --atomicInt; atomicInt += 5; atomicInt -= 5; atomicInt++; atomicInt--; EATEST_VERIFY(atomicInt == 0); return nErrorCount; } template int TestNonMemberAtomics() { int nErrorCount = 0; nErrorCount += TestSimpleAtomicOps(); nErrorCount += TestAtomicsSizeBoundaries(); nErrorCount += TestAtomicsConstFetch(); return nErrorCount; } #endif // #if EA_THREADS_AVAILABLE int TestThreadAtomic() { int nErrorCount(0); { // Initial tests of 128 bit atomics #if EATHREAD_ATOMIC_128_SUPPORTED // This will be true only for 64+ bit platforms. // To consider: Use __int128_t on GCC for GCC >= 4.1 EA_ALIGN(128) int64_t dest128[2] = { 0, 0 }; EA_ALIGN(128) int64_t value128[2] = { 1, 2 }; EA_ALIGN(128) int64_t condition128Fail[2] = { 4, 5 }; EA_ALIGN(128) int64_t condition128Succeed[2] = { 0, 0 }; bool result; // Try to do an update which should fail. result = EA::Thread::AtomicSetValueConditionall28(dest128, value128, condition128Fail); EATEST_VERIFY_MSG(!result, "AtomicSetValueConditional failure: result should have been false.\n"); EATEST_VERIFY_F((dest128[0] == 0) && (dest128[1] == 0), "AtomicSetValueConditional failure: dest128[0]:%I64d dest128[1]:%I64d\n", dest128[0], dest128[1]); EATEST_VERIFY_F((value128[0] == 1) && (value128[1] == 2), "AtomicSetValueConditional failure: value128[0]:%I64d value128[1]:%I64d\n", value128[0], value128[1]); EATEST_VERIFY_F((condition128Fail[0] == 4) && (condition128Fail[1] == 5), "AtomicSetValueConditional failure: condition128Fail[0]:%I64d condition128Fail[1]:%I64d\n", condition128Fail[0], condition128Fail[1]); EATEST_VERIFY_F((condition128Succeed[0] == 0) && (condition128Succeed[1] == 0), "AtomicSetValueConditional failure: condition128Succeed[0]:%I64d condition128Succeed[1]:%I64d\n", condition128Succeed[0], condition128Succeed[1]); // Try to do an update which should succeed. // VC++ for VS2010 misgenerates the atomic code below in optimized builds, by passing what appears to be the wrong value for dest128 to AtomicSetValueConditional128. // We added some diagnostic code and now the compiler does the right thing. I (Paul Pedriana) wonder if the problem is related to // the alignment specification for dest, which must be 128 byte aligned for AtomicSetValueConditional128 (cmpxchg16b) to work. // The dest address is indeed being aligned to 16 bytes, so that's not the problem. EA::UnitTest::ReportVerbosity(1, "%p %p %p\n", dest128, value128, condition128Succeed); result = EA::Thread::AtomicSetValueConditionall28(dest128, value128, condition128Succeed); EATEST_VERIFY_MSG(result, "AtomicSetValueConditional failure: result should have been true.\n"); 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]); 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]); 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]); 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]); #if defined(EA_COMPILER_GNUC) // GCC defines __int128_t as a built-in type. __int128_t dest; __int128_t value; __int128_t conditionFail; __int128_t conditionSucceed; // AtomicGetValue dest = 3; value = AtomicGetValue(&dest); EATEST_VERIFY_MSG(value == 3, "AtomicGetValue[128] failure\n"); // AtomicSetValue AtomicSetValue(&dest, 4); value = AtomicGetValue(&dest); EATEST_VERIFY_MSG(value == 4, "AtomicSetValue[128] failure\n"); // AtomicIncrement value = AtomicIncrement(&dest); EATEST_VERIFY_MSG(value == 5, "AtomicIncrement[128] failure\n"); value = AtomicGetValue(&dest); EATEST_VERIFY_MSG(value == 5, "AtomicIncrement[128] failure\n"); // AtomicDecrement value = AtomicDecrement(&dest); EATEST_VERIFY_MSG(value == 4, "AtomicDecrement[128] failure\n"); value = AtomicGetValue(&dest); EATEST_VERIFY_MSG(value == 4, "AtomicDecrement[128] failure\n"); // AtomicAdd value = AtomicAdd(&dest, 3); EATEST_VERIFY_MSG(value == 7, "AtomicAdd[128] failure\n"); value = AtomicGetValue(&dest); EATEST_VERIFY_MSG(value == 7, "AtomicAdd[128] failure\n"); // AtomicOr value = AtomicOr(&dest, 8); EATEST_VERIFY_MSG(value == 15, "AtomicOr[128] failure\n"); value = AtomicGetValue(&dest); EATEST_VERIFY_MSG(value == 15, "AtomicOr[128] failure\n"); // AtomicAnd value = AtomicAnd(&dest, 3); EATEST_VERIFY_MSG(value == 3, "AtomicAnd[128] failure\n"); value = AtomicGetValue(&dest); EATEST_VERIFY_MSG(value == 3, "AtomicAnd[128] failure\n"); // AtomicXor value = AtomicXor(&dest, dest); EATEST_VERIFY_MSG(value == 0, "AtomicXor[128] failure\n"); value = AtomicGetValue(&dest); EATEST_VERIFY_MSG(value == 0, "AtomicXor[128] failure\n"); // AtomicSwap value = AtomicSwap(&dest, 5); EATEST_VERIFY_MSG(value == 0, "AtomicSwap[128] failure\n"); value = AtomicGetValue(&dest); EATEST_VERIFY_MSG(value == 5, "AtomicSwap[128] failure\n"); // AtomicSetValueConditional dest = 0; value = 1; conditionFail = 4; conditionSucceed = 0; // Try to do an update which should fail. result = EA::Thread::AtomicSetValueConditional(&dest, value, conditionFail); EATEST_VERIFY_MSG(!result, "AtomicSetValueConditional failure 0\n"); EATEST_VERIFY_MSG(dest == 0, "AtomicSetValueConditional failure 1\n"); // Try to do an update which should succeed. result = EA::Thread::AtomicSetValueConditional(&dest, value, conditionSucceed); EATEST_VERIFY_MSG(result, "AtomicSetValueConditional failure 2\n"); EATEST_VERIFY_MSG(dest == 1, "AtomicSetValueConditional failure 3\n"); if(nErrorCount != 0) return nErrorCount; #endif #endif } { // Basic single-threaded Atomic test. AtomicInt32 i32(1); AtomicUint32 u32(1); EATEST_VERIFY_MSG(i32.GetValue() == 1, "AtomicInt32 failure."); EATEST_VERIFY_MSG(u32.GetValue() == 1, "AtomicUint32 failure."); char buffer[64]; sprintf(buffer, "%d %u", (signed int)i32.GetValue(), (unsigned int)u32.GetValue()); EATEST_VERIFY_MSG(strcmp(buffer, "1 1") == 0, "AtomicInt32 failure."); // Copy ctor/operator=. AtomicInt32 i32CopyA(i32); AtomicInt32 i32CopyB(i32CopyA); i32CopyA = i32CopyB; sprintf(buffer, "%d %d", (signed int)i32CopyA.GetValue(), (signed int)i32CopyB.GetValue()); EATEST_VERIFY_MSG(strcmp(buffer, "1 1") == 0, "AtomicInt32 failure."); // Test platforms that support 64 bits.. AtomicInt64 i64(1); AtomicUint64 u64(1); sprintf(buffer, "%.0f %.0f", (double)i64.GetValue(), (double)u64.GetValue()); EATEST_VERIFY_MSG(strcmp(buffer, "1 1") == 0, "AtomicInt64 failure."); // Copy ctor/operator=. AtomicInt64 i64CopyA(i64); AtomicInt64 i64CopyB(i64CopyA); i64CopyA = i64CopyB; sprintf(buffer, "%d %d", (signed int)i64CopyA.GetValue(), (signed int)i64CopyB.GetValue()); EATEST_VERIFY_MSG(strcmp(buffer, "1 1") == 0, "AtomicInt64 failure."); bool result = i64.SetValueConditional(2, 99999); // This should not set the value to 2. EATEST_VERIFY_MSG(!result && (i64.GetValue() == 1), "AtomicInt64 failure."); i64.SetValueConditional(2, 1); // This should set the value to 2. EATEST_VERIFY_MSG(!result && (i64.GetValue() == 2), "AtomicInt64 failure."); } { // Basic single-threaded AtomicInt32 test. AtomicInt32 i(0); // Note that this assignment goes through AtomicInt32 operator=(). AtomicInt32::ValueType x; EATEST_VERIFY_MSG(i == 0, "AtomicInt32 failure."); ++i; i++; --i; i--; i += 7; i -= 3; EATEST_VERIFY_MSG(i == 4, "AtomicInt32 failure."); i = 2; x = i.GetValue(); EATEST_VERIFY_MSG(x == 2, "AtomicInt32 failure."); i.Increment(); i.Decrement(); i.Add(5); i.Add(-2); EATEST_VERIFY_MSG(i == 5, "AtomicInt32 failure."); i.SetValue(6); EATEST_VERIFY_MSG(i == 6, "AtomicInt32 failure."); bool bWasEqualTo10000 = i.SetValueConditional(3, 10000); EATEST_VERIFY_MSG(!bWasEqualTo10000, "AtomicInt32 failure."); bool bWasEqualTo6 = i.SetValueConditional(3, 6); EATEST_VERIFY_MSG(bWasEqualTo6, "AtomicInt32 failure."); } { // Verify pre-increment/post-increment works as intended. AtomicInt32 i32(0); AtomicInt32::ValueType x32; // ValueType SetValue(ValueType n) // Safely sets a new value. Returns the old value. x32 = i32.SetValue(1); EATEST_VERIFY_MSG(x32 == 0, "AtomicInt return value failure."); // ValueType Increment() // Safely increments the value. Returns the new value. x32 = i32.Increment(); EATEST_VERIFY_MSG(x32 == 2, "AtomicInt return value failure."); // ValueType Decrement() // Safely decrements the value. Returns the new value. x32 = i32.Decrement(); EATEST_VERIFY_MSG(x32 == 1, "AtomicInt return value failure."); // ValueType Add(ValueType n) // Safely adds a value, which can be negative. Returns the new value. x32 = i32.Add(35); EATEST_VERIFY_MSG(x32 == 36, "AtomicInt return value failure."); // ValueType operator=(ValueType n) // Safely assigns the value. Returns the new value. x32 = (i32 = 17); EATEST_VERIFY_MSG(x32 == 17, "AtomicInt return value failure."); // ValueType operator+=(ValueType n) // Safely adds a value, which can be negative. Returns the new value. x32 = (i32 += 3); EATEST_VERIFY_MSG(x32 == 20, "AtomicInt return value failure."); // ValueType operator-=(ValueType n) // Safely subtracts a value, which can be negative. Returns the new value. x32 = (i32 -= 6); EATEST_VERIFY_MSG(x32 == 14, "AtomicInt return value failure."); // ValueType operator++() // pre-increment operator++ x32 = ++i32; EATEST_VERIFY_MSG(x32 == 15, "AtomicInt return value failure."); EATEST_VERIFY_MSG(i32 == 15, "AtomicInt return value failure."); // ValueType operator++(int) // post-increment operator++ x32 = i32++; EATEST_VERIFY_MSG(x32 == 15, "AtomicInt return value failure."); EATEST_VERIFY_MSG(i32 == 16, "AtomicInt return value failure."); // ValueType operator--() // pre-increment operator-- x32 = --i32; EATEST_VERIFY_MSG(x32 == 15, "AtomicInt return value failure."); EATEST_VERIFY_MSG(i32 == 15, "AtomicInt return value failure."); // ValueType operator--(int) // post-increment operator-- x32 = i32--; EATEST_VERIFY_MSG(x32 == 15, "AtomicInt return value failure."); EATEST_VERIFY_MSG(i32 == 14, "AtomicInt return value failure."); } { // Verify pre-increment/post-increment works as intended. AtomicInt64 i64(0); AtomicInt64::ValueType x64; // ValueType SetValue(ValueType n) // Safely sets a new value. Returns the old value. x64 = i64.SetValue(1); EATEST_VERIFY_MSG(x64 == 0, "AtomicInt return value failure."); // ValueType Increment() // Safely increments the value. Returns the new value. x64 = i64.Increment(); EATEST_VERIFY_MSG(x64 == 2, "AtomicInt return value failure."); // ValueType Decrement() // Safely decrements the value. Returns the new value. x64 = i64.Decrement(); EATEST_VERIFY_MSG(x64 == 1, "AtomicInt return value failure."); // ValueType Add(ValueType n) // Safely adds a value, which can be negative. Returns the new value. x64 = i64.Add(35); EATEST_VERIFY_MSG(x64 == 36, "AtomicInt return value failure."); // ValueType operator=(ValueType n) // Safely assigns the value. Returns the new value. x64 = (i64 = 17); EATEST_VERIFY_MSG(x64 == 17, "AtomicInt return value failure."); // ValueType operator+=(ValueType n) // Safely adds a value, which can be negative. Returns the new value. x64 = (i64 += 3); EATEST_VERIFY_MSG(x64 == 20, "AtomicInt return value failure."); // ValueType operator-=(ValueType n) // Safely subtracts a value, which can be negative. Returns the new value. x64 = (i64 -= 6); EATEST_VERIFY_MSG(x64 == 14, "AtomicInt return value failure."); // ValueType operator++() // pre-increment operator++ x64 = ++i64; EATEST_VERIFY_MSG(x64 == 15, "AtomicInt return value failure."); EATEST_VERIFY_MSG(i64 == 15, "AtomicInt return value failure."); // ValueType operator++(int) // post-increment operator++ x64 = i64++; EATEST_VERIFY_MSG(x64 == 15, "AtomicInt return value failure."); EATEST_VERIFY_MSG(i64 == 16, "AtomicInt return value failure."); // ValueType operator--() // pre-increment operator-- x64 = --i64; EATEST_VERIFY_MSG(x64 == 15, "AtomicInt return value failure."); EATEST_VERIFY_MSG(i64 == 15, "AtomicInt return value failure."); // ValueType operator--(int) // post-increment operator-- x64 = i64--; EATEST_VERIFY_MSG(x64 == 15, "AtomicInt return value failure."); EATEST_VERIFY_MSG(i64 == 14, "AtomicInt return value failure."); } { // Basic single-threaded AtomicPointer test. AtomicPointer p(NULL); AtomicPointer::PointerValueType pTemp; EATEST_VERIFY_MSG(p.GetValue() == NULL, "AtomicPointer failure."); ++p; p++; --p; p--; p += 7; p -= 3; EATEST_VERIFY_MSG(p == (void*)4, "AtomicPointer failure."); p = (void*)2; pTemp = p.GetValue(); EATEST_VERIFY_MSG((uintptr_t)pTemp == 2, "AtomicPointer failure."); p.Increment(); p.Decrement(); p.Add(5); p.Add(-2); EATEST_VERIFY_MSG(p == (void*)5, "AtomicPointer failure."); p.SetValue((void*)6); EATEST_VERIFY_MSG(p == (void*)6, "AtomicPointer failure."); bool bWasEqualTo10000 = p.SetValueConditional((void*)3, (void*)10000); EATEST_VERIFY_MSG(!bWasEqualTo10000, "AtomicPointer failure."); bool bWasEqualTo6 = p.SetValueConditional((void*)3, (void*)6); EATEST_VERIFY_MSG(bWasEqualTo6, "AtomicPointer failure."); } { AtomicInt32 gA, gB; gA = gB = 0; ++gA; ++gB; gA = gB = 0; EATEST_VERIFY_MSG((gA == 0) && (gB == 0), "AtomicInt32 operator= failure."); } #if EA_THREADS_AVAILABLE { // Multithreaded test 1 AWorkData32 workData32; const int kThreadCount(kMaxConcurrentThreadCount - 1); Thread thread[kThreadCount]; Thread::Status status; for(int i(0); i < kThreadCount; i++) thread[i].Begin(Atomic32TestThreadFunction1, &workData32); EA::UnitTest::ThreadSleepRandom(gTestLengthSeconds*1000, gTestLengthSeconds*1000); workData32.mbShouldQuit = true; for(int i(0); i < kThreadCount; i++) { status = thread[i].WaitForEnd(GetThreadTime() + 30000); EATEST_VERIFY_MSG(status != EA::Thread::Thread::kStatusRunning, "Atomic/Thread failure."); } // In the end, the sum must be zero. EATEST_VERIFY_MSG(workData32.mnAtomicInteger1 == 0, "Atomic/Thread failure."); EATEST_VERIFY_MSG(workData32.mnAtomicInteger2 == 0, "Atomic/Thread failure."); nErrorCount += (int)workData32.mnErrorCount; } { // Multithreaded test 2 AWorkData32 workData32; const int kThreadCount(kMaxConcurrentThreadCount - 1); Thread thread[kThreadCount]; Thread::Status status; for(int i(0); i < kThreadCount; i++) thread[i].Begin(Atomic32TestThreadFunction2, &workData32); EA::UnitTest::ThreadSleepRandom(gTestLengthSeconds*1000, gTestLengthSeconds*1000); workData32.mbShouldQuit = true; for(int i(0); i < kThreadCount; i++) { status = thread[i].WaitForEnd(GetThreadTime() + 30000); EATEST_VERIFY_MSG(status != EA::Thread::Thread::kStatusRunning, "Atomic/Thread failure."); } nErrorCount += (int)workData32.mnErrorCount; } { // Multithreaded test 1 AWorkData64 workData64; const int kThreadCount(kMaxConcurrentThreadCount - 1); Thread thread[kThreadCount]; Thread::Status status; for(int i(0); i < kThreadCount; i++) thread[i].Begin(Atomic64TestThreadFunction1, &workData64); EA::UnitTest::ThreadSleepRandom(gTestLengthSeconds*1000, gTestLengthSeconds*1000); workData64.mbShouldQuit = true; for(int i(0); i < kThreadCount; i++) { status = thread[i].WaitForEnd(GetThreadTime() + 30000); EATEST_VERIFY_MSG(status != EA::Thread::Thread::kStatusRunning, "Atomic/Thread failure."); } // In the end, the sum must be zero. EATEST_VERIFY_MSG(workData64.mnAtomicInteger1 == 0, "Atomic/Thread failure."); EATEST_VERIFY_MSG(workData64.mnAtomicInteger2 == 0, "Atomic/Thread failure."); nErrorCount += (int)workData64.mnErrorCount; } { // Multithreaded test 2 AWorkData64 workData64; const int kThreadCount(kMaxConcurrentThreadCount - 1); Thread thread[kThreadCount]; Thread::Status status; for(int i(0); i < kThreadCount; i++) thread[i].Begin(Atomic64TestThreadFunction2, &workData64); EA::UnitTest::ThreadSleepRandom(gTestLengthSeconds*1000, gTestLengthSeconds*1000); workData64.mbShouldQuit = true; for(int i(0); i < kThreadCount; i++) { status = thread[i].WaitForEnd(GetThreadTime() + 30000); EATEST_VERIFY_MSG(status != EA::Thread::Thread::kStatusRunning, "Atomic/Thread failure."); } nErrorCount += (int)workData64.mnErrorCount; } { // Multithreaded test 3 AWorkData64 workData64; const int kThreadCount(kMaxConcurrentThreadCount - 1); Thread thread[kThreadCount]; Thread::Status status; for(int i(0); i < kThreadCount; i++) thread[i].Begin(Atomic64TestThreadFunction3, &workData64); EA::UnitTest::ThreadSleepRandom(gTestLengthSeconds*1000, gTestLengthSeconds*1000); workData64.mbShouldQuit = true; for(int i(0); i < kThreadCount; i++) { status = thread[i].WaitForEnd(GetThreadTime() + 30000); EATEST_VERIFY_MSG(status != EA::Thread::Thread::kStatusRunning, "Atomic/Thread failure."); } nErrorCount += (int)workData64.mnErrorCount; } #endif { nErrorCount += TestAtomicIntT(); nErrorCount += TestAtomicIntT(); nErrorCount += TestAtomicIntT(); nErrorCount += TestAtomicIntT(); nErrorCount += TestAtomicIntT(); nErrorCount += TestAtomicIntT(); nErrorCount += TestAtomicIntT(); nErrorCount += TestAtomicIntT(); nErrorCount += TestAtomicIntT(); nErrorCount += TestAtomicIntT(); nErrorCount += TestAtomicIntT(); nErrorCount += TestAtomicIntT(); nErrorCount += TestAtomicIntT(); nErrorCount += TestAtomicIntT(); nErrorCount += TestAtomicIntT(); nErrorCount += TestAtomicIntT(); nErrorCount += TestAtomicIntT(); nErrorCount += TestAtomicIntT(); } // Non-Member Atomics Tests { nErrorCount += TestNonMemberAtomics(); nErrorCount += TestNonMemberAtomics(); nErrorCount += TestNonMemberAtomics(); nErrorCount += TestNonMemberAtomics(); nErrorCount += TestNonMemberAtomics(); nErrorCount += TestNonMemberAtomics(); nErrorCount += TestNonMemberAtomics(); nErrorCount += TestNonMemberAtomics(); nErrorCount += TestNonMemberAtomics(); nErrorCount += TestNonMemberAtomics(); nErrorCount += TestNonMemberAtomics(); nErrorCount += TestNonMemberAtomics(); nErrorCount += TestNonMemberAtomics(); nErrorCount += TestNonMemberAtomics(); nErrorCount += TestNonMemberAtomics(); nErrorCount += TestNonMemberAtomics(); nErrorCount += TestNonMemberAtomics(); nErrorCount += TestNonMemberAtomics(); } return nErrorCount; }