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