YSE sound engine  1.0
cross platform sound engine
 All Classes Namespaces Functions Pages
atomicOps.hpp
1 /*
2  ==============================================================================
3 
4  atomicOps.hpp
5  Created: 8 Mar 2014 2:06:09pm
6  Author: yvan
7 
8  ==============================================================================
9 */
10 
11 #ifndef ATOMICOPS_HPP_INCLUDED
12 #define ATOMICOPS_HPP_INCLUDED
13 
14 // )2013 Cameron Desrochers.
15 // Distributed under the simplified BSD license (see the license file that
16 // should have come with this header).
17 
18 // Provides portable (VC++2010+, Intel ICC 13, GCC 4.7+, and anything C++11 compliant) implementation
19 // of low-level memory barriers, plus a few semi-portable utility macros (for inlining and alignment).
20 // Also has a basic atomic type (limited to hardware-supported atomics with no memory ordering guarantees).
21 // Uses the AE_* prefix for macros (historical reasons), and the "moodycamel" namespace for symbols.
22 
23 #include <cassert>
24 
25 
26 // Platform detection
27 #if defined(__INTEL_COMPILER)
28 #define AE_ICC
29 #elif defined(_MSC_VER)
30 #define AE_VCPP
31 #elif defined(__GNUC__)
32 #define AE_GCC
33 #endif
34 
35 #if defined(_M_IA64) || defined(__ia64__)
36 #define AE_ARCH_IA64
37 #elif defined(_WIN64) || defined(__amd64__) || defined(_M_X64) || defined(__x86_64__)
38 #define AE_ARCH_X64
39 #elif defined(_M_IX86) || defined(__i386__)
40 #define AE_ARCH_X86
41 #elif defined(_M_PPC) || defined(__powerpc__)
42 #define AE_ARCH_PPC
43 #else
44 #define AE_ARCH_UNKNOWN
45 #endif
46 
47 
48 // AE_UNUSED
49 #define AE_UNUSED(x) ((void)x)
50 
51 
52 // AE_FORCEINLINE
53 #if defined(AE_VCPP) || defined(AE_ICC)
54 #define AE_FORCEINLINE __forceinline
55 #elif defined(AE_GCC)
56 //#define AE_FORCEINLINE __attribute__((always_inline))
57 #define AE_FORCEINLINE inline
58 #else
59 #define AE_FORCEINLINE inline
60 #endif
61 
62 
63 // AE_ALIGN
64 #if defined(AE_VCPP) || defined(AE_ICC)
65 #define AE_ALIGN(x) __declspec(align(x))
66 #elif defined(AE_GCC)
67 #define AE_ALIGN(x) __attribute__((aligned(x)))
68 #else
69 // Assume GCC compliant syntax...
70 #define AE_ALIGN(x) __attribute__((aligned(x)))
71 #endif
72 
73 
74 // Portable atomic fences implemented below:
75 
76 namespace YSE {
77 
78  enum memory_order {
79  memory_order_relaxed,
80  memory_order_acquire,
81  memory_order_release,
82  memory_order_acq_rel,
83  memory_order_seq_cst,
84 
85  // memory_order_sync: Forces a full sync:
86  // #LoadLoad, #LoadStore, #StoreStore, and most significantly, #StoreLoad
87  memory_order_sync = memory_order_seq_cst
88  };
89 
90 } // end namespace moodycamel
91 
92 #if defined(AE_VCPP) || defined(AE_ICC)
93 // VS2010 and ICC13 don't support std::atomic_*_fence, implement our own fences
94 
95 #include <intrin.h>
96 
97 #if defined(AE_ARCH_X64) || defined(AE_ARCH_X86)
98 #define AeFullSync _mm_mfence
99 #define AeLiteSync _mm_mfence
100 #elif defined(AE_ARCH_IA64)
101 #define AeFullSync __mf
102 #define AeLiteSync __mf
103 #elif defined(AE_ARCH_PPC)
104 #include <ppcintrinsics.h>
105 #define AeFullSync __sync
106 #define AeLiteSync __lwsync
107 #endif
108 
109 
110 #ifdef AE_VCPP
111 #pragma warning(push)
112 #pragma warning(disable: 4316)
113 #pragma warning(disable: 4365) // Disable erroneous 'conversion from long to unsigned int, signed/unsigned mismatch' error when using `assert`
114 #endif
115 
116 namespace YSE {
117 
118  AE_FORCEINLINE void compiler_fence(memory_order order)
119  {
120  switch (order) {
121  case memory_order_relaxed: break;
122  case memory_order_acquire: _ReadBarrier(); break;
123  case memory_order_release: _WriteBarrier(); break;
124  case memory_order_acq_rel: _ReadWriteBarrier(); break;
125  case memory_order_seq_cst: _ReadWriteBarrier(); break;
126  default: assert(false);
127  }
128  }
129 
130  // x86/x64 have a strong memory model -- all loads and stores have
131  // acquire and release semantics automatically (so only need compiler
132  // barriers for those).
133 #if defined(AE_ARCH_X86) || defined(AE_ARCH_X64)
134  AE_FORCEINLINE void fence(memory_order order)
135  {
136  switch (order) {
137  case memory_order_relaxed: break;
138  case memory_order_acquire: _ReadBarrier(); break;
139  case memory_order_release: _WriteBarrier(); break;
140  case memory_order_acq_rel: _ReadWriteBarrier(); break;
141  case memory_order_seq_cst:
142  _ReadWriteBarrier();
143  AeFullSync();
144  _ReadWriteBarrier();
145  break;
146  default: assert(false);
147  }
148  }
149 #else
150  AE_FORCEINLINE void fence(memory_order order)
151  {
152  // Non-specialized arch, use heavier memory barriers everywhere just in case :-(
153  switch (order) {
154  case memory_order_relaxed:
155  break;
156  case memory_order_acquire:
157  _ReadBarrier();
158  AeLiteSync();
159  _ReadBarrier();
160  break;
161  case memory_order_release:
162  _WriteBarrier();
163  AeLiteSync();
164  _WriteBarrier();
165  break;
166  case memory_order_acq_rel:
167  _ReadWriteBarrier();
168  AeLiteSync();
169  _ReadWriteBarrier();
170  break;
171  case memory_order_seq_cst:
172  _ReadWriteBarrier();
173  AeFullSync();
174  _ReadWriteBarrier();
175  break;
176  default: assert(false);
177  }
178  }
179 #endif
180 } // end namespace YSE
181 #else
182 // Use standard library of atomics
183 #include <atomic>
184 
185 namespace YSE {
186 
187  AE_FORCEINLINE void compiler_fence(memory_order order)
188  {
189  switch (order) {
190  case memory_order_relaxed: break;
191  case memory_order_acquire: std::atomic_signal_fence(std::memory_order_acquire); break;
192  case memory_order_release: std::atomic_signal_fence(std::memory_order_release); break;
193  case memory_order_acq_rel: std::atomic_signal_fence(std::memory_order_acq_rel); break;
194  case memory_order_seq_cst: std::atomic_signal_fence(std::memory_order_seq_cst); break;
195  default: assert(false);
196  }
197  }
198 
199  AE_FORCEINLINE void fence(memory_order order)
200  {
201  switch (order) {
202  case memory_order_relaxed: break;
203  case memory_order_acquire: std::atomic_thread_fence(std::memory_order_acquire); break;
204  case memory_order_release: std::atomic_thread_fence(std::memory_order_release); break;
205  case memory_order_acq_rel: std::atomic_thread_fence(std::memory_order_acq_rel); break;
206  case memory_order_seq_cst: std::atomic_thread_fence(std::memory_order_seq_cst); break;
207  default: assert(false);
208  }
209  }
210 
211 } // end namespace moodycamel
212 
213 #endif
214 
215 
216 
217 
218 #if !defined(AE_VCPP) || _MSC_VER >= 1700
219 #define AE_USE_STD_ATOMIC_FOR_WEAK_ATOMIC
220 #endif
221 
222 #ifdef AE_USE_STD_ATOMIC_FOR_WEAK_ATOMIC
223 #include <atomic>
224 #endif
225 #include <utility>
226 
227 // WARNING: *NOT* A REPLACEMENT FOR std::atomic. READ CAREFULLY:
228 // Provides basic support for atomic variables -- no memory ordering guarantees are provided.
229 // The guarantee of atomicity is only made for types that already have atomic load and store guarantees
230 // at the hardware level -- on most platforms this generally means aligned pointers and integers (only).
231 namespace YSE {
232  template<typename T>
234  {
235  public:
236  weak_atomic() { }
237 #ifdef AE_VCPP
238 #pragma warning(disable: 4100) // Get rid of (erroneous) 'unreferenced formal parameter' warning
239 #endif
240  template<typename U> weak_atomic(U&& x) : value(std::forward<U>(x)) { }
241  weak_atomic(weak_atomic const& other) : value(other.value) { }
242  weak_atomic(weak_atomic&& other) : value(std::move(other.value)) { }
243 #ifdef AE_VCPP
244 #pragma warning(default: 4100)
245 #endif
246 
247  AE_FORCEINLINE operator T() const { return load(); }
248 
249 
250 #ifndef AE_USE_STD_ATOMIC_FOR_WEAK_ATOMIC
251  template<typename U> AE_FORCEINLINE weak_atomic const& operator=(U&& x) { value = std::forward<U>(x); return *this; }
252  AE_FORCEINLINE weak_atomic const& operator=(weak_atomic const& other) { value = other.value; return *this; }
253 
254  AE_FORCEINLINE T load() const { return value; }
255 #else
256  template<typename U>
257  AE_FORCEINLINE weak_atomic const& operator=(U&& x)
258  {
259  value.store(std::forward<U>(x), std::memory_order_relaxed);
260  return *this;
261  }
262 
263  AE_FORCEINLINE weak_atomic const& operator=(weak_atomic const& other)
264  {
265  value.store(other.value.load(std::memory_order_relaxed), std::memory_order_relaxed);
266  return *this;
267  }
268 
269  AE_FORCEINLINE T load() const { return value.load(std::memory_order_relaxed); }
270 #endif
271 
272 
273  private:
274 #ifndef AE_USE_STD_ATOMIC_FOR_WEAK_ATOMIC
275  // No std::atomic support, but still need to circumvent compiler optimizations.
276  // `volatile` will make memory access slow, but is guaranteed to be reliable.
277  volatile T value;
278 #else
279  std::atomic<T> value;
280 #endif
281  };
282 
283 } // end namespace YSE
284 
285 
286 #ifdef AE_VCPP
287 #pragma warning(pop)
288 #endif
289 
290 
291 
292 #endif // ATOMICOPS_HPP_INCLUDED