Audio plugin host https://kx.studio/carla
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

Atomic.h 11KB

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