12f083884Ss.makeev_local // The MIT License (MIT) 22f083884Ss.makeev_local // 32f083884Ss.makeev_local // Copyright (c) 2015 Sergey Makeev, Vadim Slyusarev 42f083884Ss.makeev_local // 52f083884Ss.makeev_local // Permission is hereby granted, free of charge, to any person obtaining a copy 62f083884Ss.makeev_local // of this software and associated documentation files (the "Software"), to deal 72f083884Ss.makeev_local // in the Software without restriction, including without limitation the rights 82f083884Ss.makeev_local // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 92f083884Ss.makeev_local // copies of the Software, and to permit persons to whom the Software is 102f083884Ss.makeev_local // furnished to do so, subject to the following conditions: 112f083884Ss.makeev_local // 122f083884Ss.makeev_local // The above copyright notice and this permission notice shall be included in 132f083884Ss.makeev_local // all copies or substantial portions of the Software. 142f083884Ss.makeev_local // 152f083884Ss.makeev_local // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 162f083884Ss.makeev_local // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 172f083884Ss.makeev_local // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 182f083884Ss.makeev_local // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 192f083884Ss.makeev_local // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 202f083884Ss.makeev_local // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 212f083884Ss.makeev_local // THE SOFTWARE. 222f083884Ss.makeev_local 232f083884Ss.makeev_local #include "Tests.h" 24*c307a34bSSergey Makeev #include <math.h> 252f083884Ss.makeev_local #include <UnitTest++.h> 262f083884Ss.makeev_local #include <MTAtomic.h> 27*c307a34bSSergey Makeev #include <MTScheduler.h> 282f083884Ss.makeev_local 292f083884Ss.makeev_local 302f083884Ss.makeev_local SUITE(AtomicTests) 312f083884Ss.makeev_local { 322f083884Ss.makeev_local static const int OLD_VALUE = 1; 332f083884Ss.makeev_local static const int VALUE = 13; 342f083884Ss.makeev_local static const int NEW_VALUE = 16; 352f083884Ss.makeev_local static const int RELAXED_VALUE = 27; 362f083884Ss.makeev_local 372f083884Ss.makeev_local void TestStatics() 382f083884Ss.makeev_local { 392f083884Ss.makeev_local // This variables must be placed to .data / .bss section 402f083884Ss.makeev_local // 412f083884Ss.makeev_local // From "Cpp Standard" 422f083884Ss.makeev_local // 432f083884Ss.makeev_local // 6.7 Declaration statement 442f083884Ss.makeev_local // 452f083884Ss.makeev_local // 4 The zero-initialization (8.5) of all block-scope variables with static storage duration (3.7.1) or thread storage 462f083884Ss.makeev_local // duration (3.7.2) is performed before any other initialization takes place. 472f083884Ss.makeev_local // Constant initialization (3.6.2) of a block-scope entity with static storage duration, if applicable, 482f083884Ss.makeev_local // is performed before its block is first entered. 492f083884Ss.makeev_local // 502f083884Ss.makeev_local static MT::Atomic32Base<int32> test = { 0 }; 512f083884Ss.makeev_local static MT::AtomicPtrBase<void> pTest = { nullptr }; 522f083884Ss.makeev_local 532f083884Ss.makeev_local test.Store(13); 542f083884Ss.makeev_local pTest.Store(nullptr); 552f083884Ss.makeev_local 562f083884Ss.makeev_local CHECK_EQUAL(13, test.Load()); 572f083884Ss.makeev_local CHECK(pTest.Load() == nullptr); 582f083884Ss.makeev_local } 592f083884Ss.makeev_local 602f083884Ss.makeev_local TEST(AtomicSimpleTest) 612f083884Ss.makeev_local { 622f083884Ss.makeev_local TestStatics(); 632f083884Ss.makeev_local 642f083884Ss.makeev_local MT::Atomic32<int32> test_relaxed; 652f083884Ss.makeev_local test_relaxed.StoreRelaxed(RELAXED_VALUE); 662f083884Ss.makeev_local CHECK(test_relaxed.Load() == RELAXED_VALUE); 672f083884Ss.makeev_local 682f083884Ss.makeev_local MT::Atomic32<int32> test; 692f083884Ss.makeev_local test.Store(OLD_VALUE); 702f083884Ss.makeev_local CHECK(test.Load() == OLD_VALUE); 712f083884Ss.makeev_local 722f083884Ss.makeev_local int prevValue = test.Exchange(VALUE); 732f083884Ss.makeev_local CHECK(test.Load() == VALUE); 742f083884Ss.makeev_local CHECK(prevValue == OLD_VALUE); 752f083884Ss.makeev_local 762f083884Ss.makeev_local int nowValue = test.IncFetch(); 772f083884Ss.makeev_local CHECK(nowValue == (VALUE+1)); 782f083884Ss.makeev_local 792f083884Ss.makeev_local nowValue = test.DecFetch(); 802f083884Ss.makeev_local CHECK(nowValue == VALUE); 812f083884Ss.makeev_local 822f083884Ss.makeev_local nowValue = test.AddFetch(VALUE); 832f083884Ss.makeev_local CHECK(nowValue == (VALUE+VALUE)); 842f083884Ss.makeev_local 852f083884Ss.makeev_local MT::Atomic32<int32> test2(VALUE); 862f083884Ss.makeev_local CHECK(test2.Load() == VALUE); 872f083884Ss.makeev_local 882f083884Ss.makeev_local int prevResult = test2.CompareAndSwap(NEW_VALUE, OLD_VALUE); 892f083884Ss.makeev_local CHECK(prevResult == VALUE); 902f083884Ss.makeev_local CHECK(test2.Load() == VALUE); 912f083884Ss.makeev_local 922f083884Ss.makeev_local prevResult = test2.CompareAndSwap(VALUE, NEW_VALUE); 932f083884Ss.makeev_local CHECK(prevResult == VALUE); 942f083884Ss.makeev_local CHECK(test2.Load() == NEW_VALUE); 952f083884Ss.makeev_local 962f083884Ss.makeev_local MT::Atomic32<uint32> test3(UINT32_MAX); 972f083884Ss.makeev_local CHECK_EQUAL(UINT32_MAX, test3.Load()); 982f083884Ss.makeev_local 992f083884Ss.makeev_local //check for wraps 1002f083884Ss.makeev_local uint32 uNowValue = test3.IncFetch(); 1012f083884Ss.makeev_local CHECK(uNowValue == 0); 1022f083884Ss.makeev_local 1032f083884Ss.makeev_local uNowValue = test3.DecFetch(); 1042f083884Ss.makeev_local CHECK(uNowValue == UINT32_MAX); 1052f083884Ss.makeev_local 1062f083884Ss.makeev_local 1072f083884Ss.makeev_local char tempObject; 1082f083884Ss.makeev_local char* testPtr = &tempObject; 1092f083884Ss.makeev_local char* testPtrNew = testPtr + 1; 1102f083884Ss.makeev_local 1112f083884Ss.makeev_local 1122f083884Ss.makeev_local MT::AtomicPtr<char> atomicPtrRelaxed; 1132f083884Ss.makeev_local atomicPtrRelaxed.StoreRelaxed(testPtr); 1142f083884Ss.makeev_local CHECK(atomicPtrRelaxed.Load() == testPtr); 1152f083884Ss.makeev_local 1162f083884Ss.makeev_local MT::AtomicPtr<char> atomicPtr; 1172f083884Ss.makeev_local CHECK(atomicPtr.Load() == nullptr); 1182f083884Ss.makeev_local 1192f083884Ss.makeev_local atomicPtr.Store(testPtr); 1202f083884Ss.makeev_local CHECK(atomicPtr.Load() == testPtr); 1212f083884Ss.makeev_local 1222f083884Ss.makeev_local char* prevPtr = atomicPtr.CompareAndSwap(nullptr, testPtrNew); 1232f083884Ss.makeev_local CHECK(prevPtr == testPtr); 1242f083884Ss.makeev_local CHECK(atomicPtr.Load() == testPtr); 1252f083884Ss.makeev_local 1262f083884Ss.makeev_local prevPtr = atomicPtr.CompareAndSwap(testPtr, testPtrNew); 1272f083884Ss.makeev_local CHECK(prevPtr == testPtr); 1282f083884Ss.makeev_local CHECK(atomicPtr.Load() == testPtrNew); 1292f083884Ss.makeev_local 1302f083884Ss.makeev_local char* prevPtr2 = atomicPtr.Exchange(nullptr); 1312f083884Ss.makeev_local CHECK(prevPtr2 == testPtrNew); 1322f083884Ss.makeev_local CHECK(atomicPtr.Load() == nullptr); 1332f083884Ss.makeev_local } 134*c307a34bSSergey Makeev 135*c307a34bSSergey Makeev 136*c307a34bSSergey Makeev MT::Atomic32<uint32> isReady; 137*c307a34bSSergey Makeev MT::Atomic32<uint32> a; 138*c307a34bSSergey Makeev MT::Atomic32<uint32> b; 139*c307a34bSSergey Makeev 140*c307a34bSSergey Makeev uint32 sharedValue = 0; 141*c307a34bSSergey Makeev 142*c307a34bSSergey Makeev MT::Atomic32<uint32> simpleLock; 143*c307a34bSSergey Makeev 144*c307a34bSSergey Makeev void ThreadFunc( void* userData ) 145*c307a34bSSergey Makeev { 146*c307a34bSSergey Makeev MT_UNUSED(userData); 147*c307a34bSSergey Makeev 148*c307a34bSSergey Makeev MT::SpinWait spinWait; 149*c307a34bSSergey Makeev 150*c307a34bSSergey Makeev while(isReady.LoadRelaxed() == 0) 151*c307a34bSSergey Makeev { 152*c307a34bSSergey Makeev spinWait.SpinOnce(); 153*c307a34bSSergey Makeev } 154*c307a34bSSergey Makeev 155*c307a34bSSergey Makeev for(int iteration = 0; iteration < 100000; iteration++) 156*c307a34bSSergey Makeev { 157*c307a34bSSergey Makeev uint32 prevA = a.AddFetch(3); 158*c307a34bSSergey Makeev uint32 prevB = b.AddFetch(3); 159*c307a34bSSergey Makeev 160*c307a34bSSergey Makeev CHECK(prevA < prevB); 161*c307a34bSSergey Makeev if (prevA < prevB) 162*c307a34bSSergey Makeev { 163*c307a34bSSergey Makeev break; 164*c307a34bSSergey Makeev } 165*c307a34bSSergey Makeev } 166*c307a34bSSergey Makeev 167*c307a34bSSergey Makeev float res = 0.0f; 168*c307a34bSSergey Makeev uint32 randDelay = rand() % 4; 169*c307a34bSSergey Makeev uint32 count = 0; 170*c307a34bSSergey Makeev while (count < 1000000) 171*c307a34bSSergey Makeev { 172*c307a34bSSergey Makeev res = 0.0f; 173*c307a34bSSergey Makeev for(uint i = 0; i < randDelay; i++) 174*c307a34bSSergey Makeev { 175*c307a34bSSergey Makeev res += sin((float)i); 176*c307a34bSSergey Makeev } 177*c307a34bSSergey Makeev 178*c307a34bSSergey Makeev if (simpleLock.CompareAndSwap(0, 1) == 0) 179*c307a34bSSergey Makeev { 180*c307a34bSSergey Makeev sharedValue++; 181*c307a34bSSergey Makeev simpleLock.Store(0); 182*c307a34bSSergey Makeev count++; 183*c307a34bSSergey Makeev } 184*c307a34bSSergey Makeev } 185*c307a34bSSergey Makeev 186*c307a34bSSergey Makeev //prevent compiler optimization 187*c307a34bSSergey Makeev printf("%3.2f\n", res); 188*c307a34bSSergey Makeev 189*c307a34bSSergey Makeev // 190*c307a34bSSergey Makeev } 191*c307a34bSSergey Makeev 192*c307a34bSSergey Makeev /* 193*c307a34bSSergey Makeev 194*c307a34bSSergey Makeev Inspired by "This Is Why They Call It a Weakly-Ordered CPU" blog post by Jeff Preshing 195*c307a34bSSergey Makeev http://preshing.com/20121019/this-is-why-they-call-it-a-weakly-ordered-cpu/ 196*c307a34bSSergey Makeev 197*c307a34bSSergey Makeev */ 198*c307a34bSSergey Makeev TEST(AtomicOrderingTest) 199*c307a34bSSergey Makeev { 200*c307a34bSSergey Makeev isReady.Store(0); 201*c307a34bSSergey Makeev 202*c307a34bSSergey Makeev a.Store(1); 203*c307a34bSSergey Makeev b.Store(2); 204*c307a34bSSergey Makeev 205*c307a34bSSergey Makeev sharedValue = 0; 206*c307a34bSSergey Makeev 207*c307a34bSSergey Makeev simpleLock.Store(0); 208*c307a34bSSergey Makeev 209*c307a34bSSergey Makeev uint32 maxThreadsCount = (uint32)MT::Thread::GetNumberOfHardwareThreads(); 210*c307a34bSSergey Makeev MT::Thread threads[32]; 211*c307a34bSSergey Makeev 212*c307a34bSSergey Makeev uint32 threadsCount = MT::Max(MT::Min(maxThreadsCount, (uint32)MT_ARRAY_SIZE(threads)), (uint32)1); 213*c307a34bSSergey Makeev 214*c307a34bSSergey Makeev printf("threads count %d\n", threadsCount); 215*c307a34bSSergey Makeev 216*c307a34bSSergey Makeev for(uint32 i = 0; i < threadsCount; i++) 217*c307a34bSSergey Makeev { 218*c307a34bSSergey Makeev threads[i].Start(16384, ThreadFunc, nullptr); 219*c307a34bSSergey Makeev } 220*c307a34bSSergey Makeev 221*c307a34bSSergey Makeev isReady.Store(1); 222*c307a34bSSergey Makeev 223*c307a34bSSergey Makeev for(uint32 i = 0; i < threadsCount; i++) 224*c307a34bSSergey Makeev { 225*c307a34bSSergey Makeev threads[i].Join(); 226*c307a34bSSergey Makeev } 227*c307a34bSSergey Makeev 228*c307a34bSSergey Makeev uint32 expectedSharedValue = (1000000 * threadsCount); 229*c307a34bSSergey Makeev CHECK_EQUAL(sharedValue, expectedSharedValue); 230*c307a34bSSergey Makeev 231*c307a34bSSergey Makeev 232*c307a34bSSergey Makeev } 2332f083884Ss.makeev_local //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 2342f083884Ss.makeev_local } 235