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 #pragma once 24 25 #ifndef __MT_ATOMIC__ 26 #define __MT_ATOMIC__ 27 28 #include <MTConfig.h> 29 #include <intrin.h> 30 #include <cstdint> 31 #include <type_traits> 32 #include <xmmintrin.h> 33 34 #define MT_ATOMIC_COMPILE_TIME_CHECK \ 35 static_assert(std::is_pod< Atomic32Base<T> >::value == true, "Atomic32Base must be a POD (plain old data type)"); \ 36 static_assert(sizeof(T) == sizeof(int32), "Atomic32Base, type T must be equal size as int32"); \ 37 static_assert(sizeof(int32) == sizeof(long), "Incompatible types, Interlocked* will fail."); 38 39 #define MT_ATOMICPTR_COMPILE_TIME_CHECK \ 40 static_assert(std::is_pod< AtomicPtrBase<T> >::value == true, "AtomicPtrBase must be a POD (plain old data type)"); 41 42 #ifdef YieldProcessor 43 #undef YieldProcessor 44 #endif 45 46 47 namespace MT 48 { 49 // 50 // Full memory barrier 51 // 52 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// HardwareFullMemoryBarrier()53 inline void HardwareFullMemoryBarrier() 54 { 55 _mm_mfence(); 56 } 57 58 // 59 // Signals to the processor to give resources to threads that are waiting for them. 60 // 61 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// YieldProcessor()62 inline void YieldProcessor() 63 { 64 _mm_pause(); 65 } 66 67 68 // 69 // Atomic int (pod type) 70 // The operation is ordered in a sequentially consistent manner except for functions marked as relaxed. 71 // 72 // Note: You must use this type when you need to declare static variable instead of Atomic32 73 // 74 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 75 template<typename T> 76 struct Atomic32Base 77 { 78 T _value; 79 80 // The function returns the resulting added value. AddFetchAtomic32Base81 T AddFetch(T sum) 82 { MT_ATOMIC_COMPILE_TIME_CHECK 83 84 mt_release_fence(); 85 T tmp = _InterlockedExchangeAdd((volatile long*)&_value, sum) + sum; 86 mt_acquire_fence(); 87 return tmp; 88 } 89 90 // The function returns the resulting incremented value. IncFetchAtomic32Base91 T IncFetch() 92 { MT_ATOMIC_COMPILE_TIME_CHECK 93 94 mt_release_fence(); 95 T tmp = _InterlockedIncrement((volatile long*)&_value); 96 mt_acquire_fence(); 97 return tmp; 98 } 99 100 // The function returns the resulting decremented value. DecFetchAtomic32Base101 T DecFetch() 102 { MT_ATOMIC_COMPILE_TIME_CHECK 103 104 mt_release_fence(); 105 T tmp = _InterlockedDecrement((volatile long*)&_value); 106 mt_acquire_fence(); 107 return tmp; 108 } 109 LoadAtomic32Base110 T Load() const 111 { MT_ATOMIC_COMPILE_TIME_CHECK 112 113 T tmp = LoadRelaxed(); 114 mt_acquire_fence(); 115 return tmp; 116 } 117 StoreAtomic32Base118 void Store(T val) 119 { MT_ATOMIC_COMPILE_TIME_CHECK 120 121 mt_release_fence(); 122 StoreRelaxed(val); 123 } 124 125 // The function returns the initial value. ExchangeAtomic32Base126 T Exchange(T val) 127 { MT_ATOMIC_COMPILE_TIME_CHECK 128 129 mt_release_fence(); 130 T tmp = _InterlockedExchange((volatile long*)&_value, val); 131 mt_acquire_fence(); 132 return tmp; 133 } 134 135 // The function returns the initial value. CompareAndSwapAtomic32Base136 T CompareAndSwap(T compareValue, T newValue) 137 { MT_ATOMIC_COMPILE_TIME_CHECK 138 139 mt_release_fence(); 140 T tmp = _InterlockedCompareExchange((volatile long*)&_value, newValue, compareValue); 141 mt_acquire_fence(); 142 return tmp; 143 } 144 145 // Relaxed operation: there are no synchronization or ordering constraints LoadRelaxedAtomic32Base146 T LoadRelaxed() const 147 { MT_ATOMIC_COMPILE_TIME_CHECK 148 149 return _value; 150 } 151 152 // Relaxed operation: there are no synchronization or ordering constraints StoreRelaxedAtomic32Base153 void StoreRelaxed(T val) 154 { MT_ATOMIC_COMPILE_TIME_CHECK 155 156 _value = val; 157 } 158 159 }; 160 161 162 163 164 165 // 166 // Atomic pointer (pod type) 167 // 168 // You must use this type when you need to declare static variable instead of AtomicPtr 169 // 170 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 171 template<typename T> 172 struct AtomicPtrBase 173 { 174 T* _value; 175 LoadAtomicPtrBase176 T* Load() const 177 { MT_ATOMICPTR_COMPILE_TIME_CHECK 178 179 T* tmp = LoadRelaxed(); 180 mt_acquire_fence(); 181 return tmp; 182 } 183 StoreAtomicPtrBase184 void Store(const T* val) 185 { MT_ATOMICPTR_COMPILE_TIME_CHECK 186 187 mt_release_fence(); 188 StoreRelaxed(val); 189 } 190 191 // The function returns the initial value. ExchangeAtomicPtrBase192 T* Exchange(const T* val) 193 { MT_ATOMICPTR_COMPILE_TIME_CHECK 194 195 mt_release_fence(); 196 #ifndef MT_PTR64 197 static_assert(sizeof(long) == sizeof(void*), "Incompatible types, _InterlockedExchange will fail"); 198 T* tmp = (T*)_InterlockedExchange((volatile long*)&_value, (long)val); 199 #else 200 T* tmp = (T*)_InterlockedExchangePointer((void* volatile*)&_value, (void*)val); 201 #endif 202 mt_acquire_fence(); 203 return tmp; 204 } 205 206 // The function returns the initial value. CompareAndSwapAtomicPtrBase207 T* CompareAndSwap(const T* compareValue, const T* newValue) 208 { MT_ATOMICPTR_COMPILE_TIME_CHECK 209 210 mt_release_fence(); 211 #ifndef MT_PTR64 212 static_assert(sizeof(long) == sizeof(void*), "Incompatible types, _InterlockedCompareExchange will fail"); 213 T* tmp = (T*)_InterlockedCompareExchange((volatile long*)&_value, (long)newValue, (long)compareValue); 214 #else 215 T* tmp = (T*)_InterlockedCompareExchangePointer((void* volatile*)&_value, (void*)newValue, (void*)compareValue); 216 #endif 217 mt_acquire_fence(); 218 return tmp; 219 } 220 221 // Relaxed operation: there are no synchronization or ordering constraints LoadRelaxedAtomicPtrBase222 T* LoadRelaxed() const 223 { MT_ATOMICPTR_COMPILE_TIME_CHECK 224 225 return _value; 226 } 227 228 // Relaxed operation: there are no synchronization or ordering constraints StoreRelaxedAtomicPtrBase229 void StoreRelaxed(const T* val) 230 { MT_ATOMICPTR_COMPILE_TIME_CHECK 231 232 _value = (T*)val; 233 } 234 235 }; 236 237 238 239 } 240 241 242 #undef MT_ATOMIC_COMPILE_TIME_CHECK 243 244 245 #endif 246