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 < 10000000; iteration++) 156 { 157 uint32 prevA = a.AddFetch(1); 158 uint32 prevB = b.AddFetch(1); 159 160 // A should be less than B, but can also be a equal due to threads race 161 CHECK(prevA <= prevB); 162 if (prevA > prevB) 163 { 164 printf("a = %d, b = %d\n", prevA, prevB); 165 break; 166 } 167 } 168 169 float res = 0.0f; 170 uint32 randDelay = 1 + (rand() % 4); 171 uint32 count = 0; 172 while (count < 10000000) 173 { 174 res = 0.0f; 175 for(uint32 i = 0; i < randDelay; i++) 176 { 177 res += sin((float)i); 178 } 179 180 if (simpleLock.CompareAndSwap(0, 1) == 0) 181 { 182 sharedValue++; 183 simpleLock.Store(0); 184 count++; 185 } 186 } 187 188 //prevent compiler optimization 189 printf("%3.2f\n", res); 190 191 // 192 } 193 194 /* 195 196 Inspired by "This Is Why They Call It a Weakly-Ordered CPU" blog post by Jeff Preshing 197 http://preshing.com/20121019/this-is-why-they-call-it-a-weakly-ordered-cpu/ 198 199 */ 200 TEST(AtomicOrderingTest) 201 { 202 isReady.Store(0); 203 204 a.Store(1); 205 b.Store(2); 206 207 sharedValue = 0; 208 209 simpleLock.Store(0); 210 211 212 MT::Thread threads[2]; 213 uint32 threadsCount = MT_ARRAY_SIZE(threads); 214 215 printf("threads count %d\n", threadsCount); 216 217 for(uint32 i = 0; i < threadsCount; i++) 218 { 219 threads[i].Start(16384, ThreadFunc, nullptr); 220 } 221 222 isReady.Store(1); 223 224 for(uint32 i = 0; i < threadsCount; i++) 225 { 226 threads[i].Join(); 227 } 228 229 uint32 expectedSharedValue = (10000000 * threadsCount); 230 CHECK_EQUAL(sharedValue, expectedSharedValue); 231 232 233 } 234 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 235 } 236