1 // The MIT License (MIT) 2 // 3 // Copyright (c) 2015 Sergey Makeev, Vadim Slyusarev 4 // 5 // Permission is hereby granted, free of charge, to any person obtaining a copy 6 // of this software and associated documentation files (the "Software"), to deal 7 // in the Software without restriction, including without limitation the rights 8 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 // copies of the Software, and to permit persons to whom the Software is 10 // furnished to do so, subject to the following conditions: 11 // 12 // The above copyright notice and this permission notice shall be included in 13 // all copies or substantial portions of the Software. 14 // 15 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 // THE SOFTWARE. 22 23 #include "Tests.h" 24 #include <math.h> 25 #include <UnitTest++.h> 26 #include <MTAtomic.h> 27 #include <MTScheduler.h> 28 29 30 SUITE(AtomicTests) 31 { 32 static const int OLD_VALUE = 1; 33 static const int VALUE = 13; 34 static const int NEW_VALUE = 16; 35 static const int RELAXED_VALUE = 27; 36 37 void TestStatics() 38 { 39 // This variables must be placed to .data / .bss section 40 // 41 // From "Cpp Standard" 42 // 43 // 6.7 Declaration statement 44 // 45 // 4 The zero-initialization (8.5) of all block-scope variables with static storage duration (3.7.1) or thread storage 46 // duration (3.7.2) is performed before any other initialization takes place. 47 // Constant initialization (3.6.2) of a block-scope entity with static storage duration, if applicable, 48 // is performed before its block is first entered. 49 // 50 static MT::Atomic32Base<int32> test = { 0 }; 51 static MT::AtomicPtrBase<void> pTest = { nullptr }; 52 53 test.Store(13); 54 pTest.Store(nullptr); 55 56 CHECK_EQUAL(13, test.Load()); 57 CHECK(pTest.Load() == nullptr); 58 } 59 60 TEST(AtomicSimpleTest) 61 { 62 TestStatics(); 63 64 MT::Atomic32<int32> test_relaxed; 65 test_relaxed.StoreRelaxed(RELAXED_VALUE); 66 CHECK(test_relaxed.Load() == RELAXED_VALUE); 67 68 MT::Atomic32<int32> test; 69 test.Store(OLD_VALUE); 70 CHECK(test.Load() == OLD_VALUE); 71 72 int prevValue = test.Exchange(VALUE); 73 CHECK(test.Load() == VALUE); 74 CHECK(prevValue == OLD_VALUE); 75 76 int nowValue = test.IncFetch(); 77 CHECK(nowValue == (VALUE+1)); 78 79 nowValue = test.DecFetch(); 80 CHECK(nowValue == VALUE); 81 82 nowValue = test.AddFetch(VALUE); 83 CHECK(nowValue == (VALUE+VALUE)); 84 85 MT::Atomic32<int32> test2(VALUE); 86 CHECK(test2.Load() == VALUE); 87 88 int prevResult = test2.CompareAndSwap(NEW_VALUE, OLD_VALUE); 89 CHECK(prevResult == VALUE); 90 CHECK(test2.Load() == VALUE); 91 92 prevResult = test2.CompareAndSwap(VALUE, NEW_VALUE); 93 CHECK(prevResult == VALUE); 94 CHECK(test2.Load() == NEW_VALUE); 95 96 MT::Atomic32<uint32> test3(UINT32_MAX); 97 CHECK_EQUAL(UINT32_MAX, test3.Load()); 98 99 //check for wraps 100 uint32 uNowValue = test3.IncFetch(); 101 CHECK(uNowValue == 0); 102 103 uNowValue = test3.DecFetch(); 104 CHECK(uNowValue == UINT32_MAX); 105 106 107 char tempObject; 108 char* testPtr = &tempObject; 109 char* testPtrNew = testPtr + 1; 110 111 112 MT::AtomicPtr<char> atomicPtrRelaxed; 113 atomicPtrRelaxed.StoreRelaxed(testPtr); 114 CHECK(atomicPtrRelaxed.Load() == testPtr); 115 116 MT::AtomicPtr<char> atomicPtr; 117 CHECK(atomicPtr.Load() == nullptr); 118 119 atomicPtr.Store(testPtr); 120 CHECK(atomicPtr.Load() == testPtr); 121 122 char* prevPtr = atomicPtr.CompareAndSwap(nullptr, testPtrNew); 123 CHECK(prevPtr == testPtr); 124 CHECK(atomicPtr.Load() == testPtr); 125 126 prevPtr = atomicPtr.CompareAndSwap(testPtr, testPtrNew); 127 CHECK(prevPtr == testPtr); 128 CHECK(atomicPtr.Load() == testPtrNew); 129 130 char* prevPtr2 = atomicPtr.Exchange(nullptr); 131 CHECK(prevPtr2 == testPtrNew); 132 CHECK(atomicPtr.Load() == nullptr); 133 } 134 135 136 MT::Atomic32<uint32> isReady; 137 MT::Atomic32<uint32> a; 138 MT::Atomic32<uint32> b; 139 140 uint32 sharedValue = 0; 141 142 MT::Atomic32<uint32> simpleLock; 143 144 void ThreadFunc( void* userData ) 145 { 146 MT_UNUSED(userData); 147 148 MT::SpinWait spinWait; 149 150 while(isReady.LoadRelaxed() == 0) 151 { 152 spinWait.SpinOnce(); 153 } 154 155 for(int iteration = 0; iteration < 100000; iteration++) 156 { 157 uint32 prevA = a.AddFetch(3); 158 uint32 prevB = b.AddFetch(3); 159 160 CHECK(prevA < prevB); 161 if (prevA < prevB) 162 { 163 break; 164 } 165 } 166 167 float res = 0.0f; 168 uint32 randDelay = rand() % 4; 169 uint32 count = 0; 170 while (count < 1000000) 171 { 172 res = 0.0f; 173 for(uint i = 0; i < randDelay; i++) 174 { 175 res += sin((float)i); 176 } 177 178 if (simpleLock.CompareAndSwap(0, 1) == 0) 179 { 180 sharedValue++; 181 simpleLock.Store(0); 182 count++; 183 } 184 } 185 186 //prevent compiler optimization 187 printf("%3.2f\n", res); 188 189 // 190 } 191 192 /* 193 194 Inspired by "This Is Why They Call It a Weakly-Ordered CPU" blog post by Jeff Preshing 195 http://preshing.com/20121019/this-is-why-they-call-it-a-weakly-ordered-cpu/ 196 197 */ 198 TEST(AtomicOrderingTest) 199 { 200 isReady.Store(0); 201 202 a.Store(1); 203 b.Store(2); 204 205 sharedValue = 0; 206 207 simpleLock.Store(0); 208 209 uint32 maxThreadsCount = (uint32)MT::Thread::GetNumberOfHardwareThreads(); 210 MT::Thread threads[32]; 211 212 uint32 threadsCount = MT::Max(MT::Min(maxThreadsCount, (uint32)MT_ARRAY_SIZE(threads)), (uint32)1); 213 214 printf("threads count %d\n", threadsCount); 215 216 for(uint32 i = 0; i < threadsCount; i++) 217 { 218 threads[i].Start(16384, ThreadFunc, nullptr); 219 } 220 221 isReady.Store(1); 222 223 for(uint32 i = 0; i < threadsCount; i++) 224 { 225 threads[i].Join(); 226 } 227 228 uint32 expectedSharedValue = (1000000 * threadsCount); 229 CHECK_EQUAL(sharedValue, expectedSharedValue); 230 231 232 } 233 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 234 } 235