1 /*
2 ==============================================================================
3
4 This file is part of the Water library.
5 Copyright (c) 2016 ROLI Ltd.
6 Copyright (C) 2017 Filipe Coelho <falktx@falktx.com>
7
8 Permission is granted to use this software under the terms of the ISC license
9 http://www.isc.org/downloads/software-support-policy/isc-license/
10
11 Permission to use, copy, modify, and/or distribute this software for any
12 purpose with or without fee is hereby granted, provided that the above
13 copyright notice and this permission notice appear in all copies.
14
15 THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH REGARD
16 TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
17 FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT,
18 OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
19 USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
20 TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
21 OF THIS SOFTWARE.
22
23 ==============================================================================
24 */
25
26 #ifndef WATER_ATOMIC_H_INCLUDED
27 #define WATER_ATOMIC_H_INCLUDED
28
29 #include "../water.h"
30
31 #include <stdint.h>
32
33 namespace water {
34
35 #if defined(__clang__)
36 # pragma clang diagnostic push
37 # pragma clang diagnostic ignored "-Weffc++"
38 #elif defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))
39 # pragma GCC diagnostic push
40 # pragma GCC diagnostic ignored "-Weffc++"
41 #endif
42
43 //==============================================================================
44 /**
45 Simple class to hold a primitive value and perform atomic operations on it.
46
47 The type used must be a 32 or 64 bit primitive, like an int, pointer, etc.
48 There are methods to perform most of the basic atomic operations.
49 */
50 template <typename Type>
51 class Atomic
52 {
53 public:
54 /** Creates a new value, initialised to zero. */
Atomic()55 inline Atomic() noexcept
56 : value (0)
57 {
58 }
59
60 /** Creates a new value, with a given initial value. */
Atomic(const Type initialValue)61 inline explicit Atomic (const Type initialValue) noexcept
62 : value (initialValue)
63 {
64 }
65
66 /** Copies another value (atomically). */
Atomic(const Atomic & other)67 inline Atomic (const Atomic& other) noexcept
68 : value (other.get())
69 {
70 }
71
72 /** Destructor. */
~Atomic()73 inline ~Atomic() noexcept
74 {
75 #ifdef CARLA_PROPER_CPP11_SUPPORT
76 // This class can only be used for types which are 32 or 64 bits in size.
77 static_wassert (sizeof (Type) == 4 || sizeof (Type) == 8);
78 #endif
79 }
80
81 /** Atomically reads and returns the current value. */
82 Type get() const noexcept;
83
84 /** Copies another value onto this one (atomically). */
85 inline Atomic& operator= (const Atomic& other) noexcept { exchange (other.get()); return *this; }
86
87 /** Copies another value onto this one (atomically). */
88 inline Atomic& operator= (const Type newValue) noexcept { exchange (newValue); return *this; }
89
90 /** Atomically sets the current value. */
set(Type newValue)91 void set (Type newValue) noexcept { exchange (newValue); }
92
93 /** Atomically sets the current value, returning the value that was replaced. */
94 Type exchange (Type value) noexcept;
95
96 /** Atomically adds a number to this value, returning the new value. */
97 Type operator+= (Type amountToAdd) noexcept;
98
99 /** Atomically subtracts a number from this value, returning the new value. */
100 Type operator-= (Type amountToSubtract) noexcept;
101
102 /** Atomically increments this value, returning the new value. */
103 Type operator++() noexcept;
104
105 /** Atomically decrements this value, returning the new value. */
106 Type operator--() noexcept;
107
108 /** Atomically compares this value with a target value, and if it is equal, sets
109 this to be equal to a new value.
110
111 This operation is the atomic equivalent of doing this:
112 @code
113 bool compareAndSetBool (Type newValue, Type valueToCompare)
114 {
115 if (get() == valueToCompare)
116 {
117 set (newValue);
118 return true;
119 }
120
121 return false;
122 }
123 @endcode
124
125 @returns true if the comparison was true and the value was replaced; false if
126 the comparison failed and the value was left unchanged.
127 @see compareAndSetValue
128 */
129 bool compareAndSetBool (Type newValue, Type valueToCompare) noexcept;
130
131 /** Atomically compares this value with a target value, and if it is equal, sets
132 this to be equal to a new value.
133
134 This operation is the atomic equivalent of doing this:
135 @code
136 Type compareAndSetValue (Type newValue, Type valueToCompare)
137 {
138 Type oldValue = get();
139 if (oldValue == valueToCompare)
140 set (newValue);
141
142 return oldValue;
143 }
144 @endcode
145
146 @returns the old value before it was changed.
147 @see compareAndSetBool
148 */
149 Type compareAndSetValue (Type newValue, Type valueToCompare) noexcept;
150
151 /** Implements a memory read/write barrier. */
152 static void memoryBarrier() noexcept;
153
154 //==============================================================================
155 /** The raw value that this class operates on.
156 This is exposed publicly in case you need to manipulate it directly
157 for performance reasons.
158 */
159 #ifdef CARLA_OS_64BIT
160 __attribute__ ((aligned (8)))
161 #else
162 __attribute__ ((aligned (4)))
163 #endif
164 volatile Type value;
165
166 private:
167 template <typename Dest, typename Source>
castTo(Source value)168 static inline Dest castTo (Source value) noexcept { union { Dest d; Source s; } u; u.s = value; return u.d; }
169
castFrom32Bit(int32 value)170 static inline Type castFrom32Bit (int32 value) noexcept { return castTo <Type, int32> (value); }
castFrom64Bit(int64 value)171 static inline Type castFrom64Bit (int64 value) noexcept { return castTo <Type, int64> (value); }
castFrom32Bit(uint32 value)172 static inline Type castFrom32Bit (uint32 value) noexcept { return castTo <Type, uint32> (value); }
castFrom64Bit(uint64 value)173 static inline Type castFrom64Bit (uint64 value) noexcept { return castTo <Type, uint64> (value); }
castTo32Bit(Type value)174 static inline int32 castTo32Bit (Type value) noexcept { return castTo <int32, Type> (value); }
castTo64Bit(Type value)175 static inline int64 castTo64Bit (Type value) noexcept { return castTo <int64, Type> (value); }
176
177 Type operator++ (int); // better to just use pre-increment with atomics..
178 Type operator-- (int);
179
180 /** This templated negate function will negate pointers as well as integers */
181 template <typename ValueType>
negateValue(ValueType n)182 inline ValueType negateValue (ValueType n) noexcept
183 {
184 return sizeof (ValueType) == 1 ? (ValueType) -(signed char) n
185 : (sizeof (ValueType) == 2 ? (ValueType) -(short) n
186 : (sizeof (ValueType) == 4 ? (ValueType) -(int) n
187 : ((ValueType) -(int64) n)));
188 }
189
190 /** This templated negate function will negate pointers as well as integers */
191 template <typename PointerType>
negateValue(PointerType * n)192 inline PointerType* negateValue (PointerType* n) noexcept
193 {
194 return reinterpret_cast<PointerType*> (-reinterpret_cast<pointer_sized_int> (n));
195 }
196 };
197
198 //==============================================================================
199 template<>
get()200 inline int32 Atomic<int32>::get() const noexcept
201 {
202 #ifdef CARLA_PROPER_CPP11_SUPPORT
203 static_wassert (sizeof (int32) == 4);
204 #endif
205 return castFrom32Bit ((int32) __sync_add_and_fetch (const_cast<volatile int32*>(&value), 0));
206 }
207
208 template<>
get()209 inline int64 Atomic<int64>::get() const noexcept
210 {
211 #ifdef CARLA_PROPER_CPP11_SUPPORT
212 static_wassert (sizeof (int64) == 8);
213 #endif
214 return castFrom64Bit ((int64) __sync_add_and_fetch (const_cast<volatile int64*>(&value), 0));
215 }
216
217 template<>
get()218 inline uint32 Atomic<uint32>::get() const noexcept
219 {
220 #ifdef CARLA_PROPER_CPP11_SUPPORT
221 static_wassert (sizeof (uint32) == 4);
222 #endif
223 return castFrom32Bit ((uint32) __sync_add_and_fetch (const_cast<volatile uint32*>(&value), 0));
224 }
225
226 template<>
get()227 inline uint64 Atomic<uint64>::get() const noexcept
228 {
229 #ifdef CARLA_PROPER_CPP11_SUPPORT
230 static_wassert (sizeof (uint64) == 8);
231 #endif
232 return castFrom64Bit ((uint64) __sync_add_and_fetch (const_cast<volatile uint64*>(&value), 0));
233 }
234
235 template <typename Type>
exchange(const Type newValue)236 inline Type Atomic<Type>::exchange (const Type newValue) noexcept
237 {
238 Type currentVal = value;
239 while (! compareAndSetBool (newValue, currentVal)) { currentVal = value; }
240 return currentVal;
241 }
242
243 template <typename Type>
244 inline Type Atomic<Type>::operator+= (const Type amountToAdd) noexcept
245 {
246 return (Type) __sync_add_and_fetch (&value, amountToAdd);
247 }
248
249 template <typename Type>
250 inline Type Atomic<Type>::operator-= (const Type amountToSubtract) noexcept
251 {
252 return operator+= (negateValue (amountToSubtract));
253 }
254
255 template <typename Type>
256 inline Type Atomic<Type>::operator++() noexcept
257 {
258 return sizeof (Type) == 4 ? (Type) __sync_add_and_fetch (&value, (Type) 1)
259 : (Type) __sync_add_and_fetch ((volatile int64*) &value, 1);
260 }
261
262 template <typename Type>
263 inline Type Atomic<Type>::operator--() noexcept
264 {
265 return sizeof (Type) == 4 ? (Type) __sync_add_and_fetch (&value, (Type) -1)
266 : (Type) __sync_add_and_fetch ((volatile int64*) &value, -1);
267 }
268
269 template <typename Type>
compareAndSetBool(const Type newValue,const Type valueToCompare)270 inline bool Atomic<Type>::compareAndSetBool (const Type newValue, const Type valueToCompare) noexcept
271 {
272 return sizeof (Type) == 4 ? __sync_bool_compare_and_swap ((volatile int32*) &value, castTo32Bit (valueToCompare), castTo32Bit (newValue))
273 : __sync_bool_compare_and_swap ((volatile int64*) &value, castTo64Bit (valueToCompare), castTo64Bit (newValue));
274 }
275
276 template <typename Type>
compareAndSetValue(const Type newValue,const Type valueToCompare)277 inline Type Atomic<Type>::compareAndSetValue (const Type newValue, const Type valueToCompare) noexcept
278 {
279 return sizeof (Type) == 4 ? castFrom32Bit ((int32) __sync_val_compare_and_swap ((volatile int32*) &value, castTo32Bit (valueToCompare), castTo32Bit (newValue)))
280 : castFrom64Bit ((int64) __sync_val_compare_and_swap ((volatile int64*) &value, castTo64Bit (valueToCompare), castTo64Bit (newValue)));
281 }
282
283 template <typename Type>
memoryBarrier()284 inline void Atomic<Type>::memoryBarrier() noexcept
285 {
286 __sync_synchronize();
287 }
288
289 #if defined(__clang__)
290 # pragma clang diagnostic pop
291 #elif defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))
292 # pragma GCC diagnostic pop
293 #endif
294
295 }
296
297 #endif // WATER_ATOMIC_H_INCLUDED
298