1f25ce65dSSergey Makeev // The MIT License (MIT)
2f25ce65dSSergey Makeev //
3f25ce65dSSergey Makeev // 	Copyright (c) 2015 Sergey Makeev, Vadim Slyusarev
4f25ce65dSSergey Makeev //
5f25ce65dSSergey Makeev // 	Permission is hereby granted, free of charge, to any person obtaining a copy
6f25ce65dSSergey Makeev // 	of this software and associated documentation files (the "Software"), to deal
7f25ce65dSSergey Makeev // 	in the Software without restriction, including without limitation the rights
8f25ce65dSSergey Makeev // 	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9f25ce65dSSergey Makeev // 	copies of the Software, and to permit persons to whom the Software is
10f25ce65dSSergey Makeev // 	furnished to do so, subject to the following conditions:
11f25ce65dSSergey Makeev //
12f25ce65dSSergey Makeev //  The above copyright notice and this permission notice shall be included in
13f25ce65dSSergey Makeev // 	all copies or substantial portions of the Software.
14f25ce65dSSergey Makeev //
15f25ce65dSSergey Makeev // 	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16f25ce65dSSergey Makeev // 	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17f25ce65dSSergey Makeev // 	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18f25ce65dSSergey Makeev // 	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19f25ce65dSSergey Makeev // 	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20f25ce65dSSergey Makeev // 	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21f25ce65dSSergey Makeev // 	THE SOFTWARE.
22f25ce65dSSergey Makeev 
2347d53e4dSSergey Makeev #pragma once
2447d53e4dSSergey Makeev 
2581ec7369SSergey Makeev #ifndef __MT_ATOMIC__
2681ec7369SSergey Makeev #define __MT_ATOMIC__
2781ec7369SSergey Makeev 
281e78cb24Ss.makeev_local #include <MTConfig.h>
2981ec7369SSergey Makeev #include <intrin.h>
3081ec7369SSergey Makeev #include <cstdint>
31498f3d32SSergey Makeev #include <type_traits>
322e846c40SSergey Makeev #include <xmmintrin.h>
3381ec7369SSergey Makeev 
34721f8c0bSs.makeev_local #define MT_ATOMIC_COMPILE_TIME_CHECK \
35721f8c0bSs.makeev_local 	static_assert(std::is_pod< Atomic32Base<T> >::value == true, "Atomic32Base must be a POD (plain old data type)"); \
36721f8c0bSs.makeev_local 	static_assert(sizeof(T) == sizeof(int32), "Atomic32Base, type T must be equal size as int32"); \
37721f8c0bSs.makeev_local 	static_assert(sizeof(int32) == sizeof(long), "Incompatible types, Interlocked* will fail.");
38721f8c0bSs.makeev_local 
39721f8c0bSs.makeev_local #define MT_ATOMICPTR_COMPILE_TIME_CHECK \
40721f8c0bSs.makeev_local 	static_assert(std::is_pod< AtomicPtrBase<T> >::value == true, "AtomicPtrBase must be a POD (plain old data type)");
41721f8c0bSs.makeev_local 
42*d10ac48cSSergey Makeev #ifdef YieldProcessor
43*d10ac48cSSergey Makeev 	#undef YieldProcessor
44*d10ac48cSSergey Makeev #endif
45*d10ac48cSSergey Makeev 
46721f8c0bSs.makeev_local 
4747d53e4dSSergey Makeev namespace MT
4847d53e4dSSergey Makeev {
49498f3d32SSergey Makeev 	//
50498f3d32SSergey Makeev 	// Full memory barrier
51498f3d32SSergey Makeev 	//
52498f3d32SSergey Makeev 	////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
HardwareFullMemoryBarrier()5381ec7369SSergey Makeev 	inline void HardwareFullMemoryBarrier()
5481ec7369SSergey Makeev 	{
5581ec7369SSergey Makeev 		_mm_mfence();
5681ec7369SSergey Makeev 	}
5781ec7369SSergey Makeev 
5847d53e4dSSergey Makeev 	//
592e846c40SSergey Makeev 	// Signals to the processor to give resources to threads that are waiting for them.
602e846c40SSergey Makeev 	//
612e846c40SSergey Makeev 	////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
YieldProcessor()62*d10ac48cSSergey Makeev 	inline void YieldProcessor()
632e846c40SSergey Makeev 	{
642e846c40SSergey Makeev 		_mm_pause();
652e846c40SSergey Makeev 	}
662e846c40SSergey Makeev 
672e846c40SSergey Makeev 
682e846c40SSergey Makeev 	//
69498f3d32SSergey Makeev 	// Atomic int (pod type)
70721f8c0bSs.makeev_local 	// The operation is ordered in a sequentially consistent manner except for functions marked as relaxed.
7147d53e4dSSergey Makeev 	//
72721f8c0bSs.makeev_local 	// Note: You must use this type when you need to declare static variable instead of Atomic32
73498f3d32SSergey Makeev 	//
74498f3d32SSergey Makeev 	////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
75721f8c0bSs.makeev_local 	template<typename T>
76721f8c0bSs.makeev_local 	struct Atomic32Base
7747d53e4dSSergey Makeev 	{
789c716f68Ss.makeev_local 		T _value;
7947d53e4dSSergey Makeev 
8047d53e4dSSergey Makeev 		// The function returns the resulting added value.
AddFetchAtomic32Base81721f8c0bSs.makeev_local 		T AddFetch(T sum)
82721f8c0bSs.makeev_local 		{ MT_ATOMIC_COMPILE_TIME_CHECK
83721f8c0bSs.makeev_local 
84721f8c0bSs.makeev_local 			mt_release_fence();
85721f8c0bSs.makeev_local 			T tmp = _InterlockedExchangeAdd((volatile long*)&_value, sum) + sum;
86721f8c0bSs.makeev_local 			mt_acquire_fence();
87721f8c0bSs.makeev_local 			return tmp;
8847d53e4dSSergey Makeev 		}
8947d53e4dSSergey Makeev 
9047d53e4dSSergey Makeev 		// The function returns the resulting incremented value.
IncFetchAtomic32Base91721f8c0bSs.makeev_local 		T IncFetch()
92721f8c0bSs.makeev_local 		{ MT_ATOMIC_COMPILE_TIME_CHECK
93721f8c0bSs.makeev_local 
94721f8c0bSs.makeev_local 			mt_release_fence();
95721f8c0bSs.makeev_local 			T tmp = _InterlockedIncrement((volatile long*)&_value);
96721f8c0bSs.makeev_local 			mt_acquire_fence();
97721f8c0bSs.makeev_local 			return tmp;
9847d53e4dSSergey Makeev 		}
9947d53e4dSSergey Makeev 
10047d53e4dSSergey Makeev 		// The function returns the resulting decremented value.
DecFetchAtomic32Base101721f8c0bSs.makeev_local 		T DecFetch()
102721f8c0bSs.makeev_local 		{ MT_ATOMIC_COMPILE_TIME_CHECK
103721f8c0bSs.makeev_local 
104721f8c0bSs.makeev_local 			mt_release_fence();
105721f8c0bSs.makeev_local 			T tmp = _InterlockedDecrement((volatile long*)&_value);
106721f8c0bSs.makeev_local 			mt_acquire_fence();
107721f8c0bSs.makeev_local 			return tmp;
10847d53e4dSSergey Makeev 		}
10947d53e4dSSergey Makeev 
LoadAtomic32Base110721f8c0bSs.makeev_local 		T Load() const
111721f8c0bSs.makeev_local 		{ MT_ATOMIC_COMPILE_TIME_CHECK
112721f8c0bSs.makeev_local 
113721f8c0bSs.makeev_local 			T tmp = LoadRelaxed();
114721f8c0bSs.makeev_local 			mt_acquire_fence();
115721f8c0bSs.makeev_local 			return tmp;
116721f8c0bSs.makeev_local 		}
117721f8c0bSs.makeev_local 
StoreAtomic32Base1185d2fe9cbSs.makeev_local 		void Store(T val)
119721f8c0bSs.makeev_local 		{ MT_ATOMIC_COMPILE_TIME_CHECK
120721f8c0bSs.makeev_local 
121721f8c0bSs.makeev_local 			mt_release_fence();
122721f8c0bSs.makeev_local 			StoreRelaxed(val);
12347d53e4dSSergey Makeev 		}
12447d53e4dSSergey Makeev 
12547d53e4dSSergey Makeev 		// The function returns the initial value.
ExchangeAtomic32Base126721f8c0bSs.makeev_local 		T Exchange(T val)
127721f8c0bSs.makeev_local 		{ MT_ATOMIC_COMPILE_TIME_CHECK
128721f8c0bSs.makeev_local 
129721f8c0bSs.makeev_local 			mt_release_fence();
130721f8c0bSs.makeev_local 			T tmp = _InterlockedExchange((volatile long*)&_value, val);
131721f8c0bSs.makeev_local 			mt_acquire_fence();
132721f8c0bSs.makeev_local 			return tmp;
13347d53e4dSSergey Makeev 		}
13446be836dSSergey Makeev 
13546be836dSSergey Makeev 		// The function returns the initial value.
CompareAndSwapAtomic32Base136721f8c0bSs.makeev_local 		T CompareAndSwap(T compareValue, T newValue)
137721f8c0bSs.makeev_local 		{ MT_ATOMIC_COMPILE_TIME_CHECK
138721f8c0bSs.makeev_local 
139721f8c0bSs.makeev_local 			mt_release_fence();
140721f8c0bSs.makeev_local 			T tmp = _InterlockedCompareExchange((volatile long*)&_value, newValue, compareValue);
141721f8c0bSs.makeev_local 			mt_acquire_fence();
142721f8c0bSs.makeev_local 			return tmp;
14346be836dSSergey Makeev 		}
14481ec7369SSergey Makeev 
14581ec7369SSergey Makeev 		// Relaxed operation: there are no synchronization or ordering constraints
LoadRelaxedAtomic32Base146721f8c0bSs.makeev_local 		T LoadRelaxed() const
147721f8c0bSs.makeev_local 		{ MT_ATOMIC_COMPILE_TIME_CHECK
148721f8c0bSs.makeev_local 
149498f3d32SSergey Makeev 			return _value;
15081ec7369SSergey Makeev 		}
15181ec7369SSergey Makeev 
15281ec7369SSergey Makeev 		// Relaxed operation: there are no synchronization or ordering constraints
StoreRelaxedAtomic32Base1535d2fe9cbSs.makeev_local 		void StoreRelaxed(T val)
154721f8c0bSs.makeev_local 		{ MT_ATOMIC_COMPILE_TIME_CHECK
155721f8c0bSs.makeev_local 
156498f3d32SSergey Makeev 			_value = val;
15781ec7369SSergey Makeev 		}
15881ec7369SSergey Makeev 
15947d53e4dSSergey Makeev 	};
16047d53e4dSSergey Makeev 
161efd3d0d5SSergey Makeev 
1628dd496e0SSergey Makeev 
1638dd496e0SSergey Makeev 
164498f3d32SSergey Makeev 
165efd3d0d5SSergey Makeev 	//
166498f3d32SSergey Makeev 	// Atomic pointer (pod type)
167efd3d0d5SSergey Makeev 	//
1682f083884Ss.makeev_local 	// You must use this type when you need to declare static variable instead of AtomicPtr
169498f3d32SSergey Makeev 	//
170498f3d32SSergey Makeev 	////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
171721f8c0bSs.makeev_local 	template<typename T>
172498f3d32SSergey Makeev 	struct AtomicPtrBase
173efd3d0d5SSergey Makeev 	{
1749c716f68Ss.makeev_local 		T* _value;
175efd3d0d5SSergey Makeev 
LoadAtomicPtrBase176721f8c0bSs.makeev_local 		T* Load() const
177721f8c0bSs.makeev_local 		{ MT_ATOMICPTR_COMPILE_TIME_CHECK
178721f8c0bSs.makeev_local 
179721f8c0bSs.makeev_local 			T* tmp = LoadRelaxed();
180721f8c0bSs.makeev_local 			mt_acquire_fence();
181721f8c0bSs.makeev_local 			return tmp;
182721f8c0bSs.makeev_local 		}
183721f8c0bSs.makeev_local 
StoreAtomicPtrBase184721f8c0bSs.makeev_local 		void Store(const T* val)
185721f8c0bSs.makeev_local 		{ MT_ATOMICPTR_COMPILE_TIME_CHECK
186721f8c0bSs.makeev_local 
187721f8c0bSs.makeev_local 			mt_release_fence();
188721f8c0bSs.makeev_local 			StoreRelaxed(val);
189efd3d0d5SSergey Makeev 		}
190efd3d0d5SSergey Makeev 
191efd3d0d5SSergey Makeev 		// The function returns the initial value.
ExchangeAtomicPtrBase192721f8c0bSs.makeev_local 		T* Exchange(const T* val)
193721f8c0bSs.makeev_local 		{ MT_ATOMICPTR_COMPILE_TIME_CHECK
194721f8c0bSs.makeev_local 
195721f8c0bSs.makeev_local 			mt_release_fence();
1961e78cb24Ss.makeev_local #ifndef MT_PTR64
197ed6f50edSSergey Makeev 			static_assert(sizeof(long) == sizeof(void*), "Incompatible types, _InterlockedExchange will fail");
198721f8c0bSs.makeev_local 			T* tmp = (T*)_InterlockedExchange((volatile long*)&_value, (long)val);
19981ec7369SSergey Makeev #else
200721f8c0bSs.makeev_local 			T* tmp = (T*)_InterlockedExchangePointer((void* volatile*)&_value, (void*)val);
20181ec7369SSergey Makeev #endif
202721f8c0bSs.makeev_local 			mt_acquire_fence();
203721f8c0bSs.makeev_local 			return tmp;
204efd3d0d5SSergey Makeev 		}
205efd3d0d5SSergey Makeev 
206efd3d0d5SSergey Makeev 		// The function returns the initial value.
CompareAndSwapAtomicPtrBase207721f8c0bSs.makeev_local 		T* CompareAndSwap(const T* compareValue, const T* newValue)
208721f8c0bSs.makeev_local 		{ MT_ATOMICPTR_COMPILE_TIME_CHECK
209721f8c0bSs.makeev_local 
210721f8c0bSs.makeev_local 			mt_release_fence();
2111e78cb24Ss.makeev_local #ifndef MT_PTR64
212ed6f50edSSergey Makeev 			static_assert(sizeof(long) == sizeof(void*), "Incompatible types, _InterlockedCompareExchange will fail");
213721f8c0bSs.makeev_local 			T* tmp = (T*)_InterlockedCompareExchange((volatile long*)&_value, (long)newValue, (long)compareValue);
21481ec7369SSergey Makeev #else
215721f8c0bSs.makeev_local 			T* tmp = (T*)_InterlockedCompareExchangePointer((void* volatile*)&_value, (void*)newValue, (void*)compareValue);
21681ec7369SSergey Makeev #endif
217721f8c0bSs.makeev_local 			mt_acquire_fence();
218721f8c0bSs.makeev_local 			return tmp;
219efd3d0d5SSergey Makeev 		}
22081ec7369SSergey Makeev 
22181ec7369SSergey Makeev 		// Relaxed operation: there are no synchronization or ordering constraints
LoadRelaxedAtomicPtrBase222721f8c0bSs.makeev_local 		T* LoadRelaxed() const
223721f8c0bSs.makeev_local 		{ MT_ATOMICPTR_COMPILE_TIME_CHECK
224721f8c0bSs.makeev_local 
225721f8c0bSs.makeev_local 			return _value;
22681ec7369SSergey Makeev 		}
22781ec7369SSergey Makeev 
22881ec7369SSergey Makeev 		// Relaxed operation: there are no synchronization or ordering constraints
StoreRelaxedAtomicPtrBase229721f8c0bSs.makeev_local 		void StoreRelaxed(const T* val)
230721f8c0bSs.makeev_local 		{ MT_ATOMICPTR_COMPILE_TIME_CHECK
231721f8c0bSs.makeev_local 
232721f8c0bSs.makeev_local 			_value = (T*)val;
23381ec7369SSergey Makeev 		}
23481ec7369SSergey Makeev 
235efd3d0d5SSergey Makeev 	};
236efd3d0d5SSergey Makeev 
237efd3d0d5SSergey Makeev 
238498f3d32SSergey Makeev 
23947d53e4dSSergey Makeev }
24081ec7369SSergey Makeev 
24181ec7369SSergey Makeev 
242721f8c0bSs.makeev_local #undef MT_ATOMIC_COMPILE_TIME_CHECK
243721f8c0bSs.makeev_local 
244721f8c0bSs.makeev_local 
24581ec7369SSergey Makeev #endif
246