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