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.

379 lines
14KB

  1. /*
  2. ==============================================================================
  3. This file is part of the Water library.
  4. Copyright (c) 2016 ROLI Ltd.
  5. Copyright (C) 2017-2023 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. #ifdef _MSC_VER
  24. # ifndef NOMINMAX
  25. # define NOMINMAX
  26. # endif
  27. # define WIN32_LEAN_AND_MEAN 1
  28. # include <winsock2.h>
  29. # include <windows.h>
  30. #endif
  31. #include <stdint.h>
  32. namespace water {
  33. #if defined(__clang__)
  34. # pragma clang diagnostic push
  35. # pragma clang diagnostic ignored "-Weffc++"
  36. #elif defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))
  37. # pragma GCC diagnostic push
  38. # pragma GCC diagnostic ignored "-Weffc++"
  39. #elif defined(_MSC_VER)
  40. # pragma warning (push)
  41. # pragma warning (disable: 4311) /* truncation warning */
  42. # pragma intrinsic (_InterlockedExchange, \
  43. _InterlockedExchange64, \
  44. _InterlockedExchangeAdd, \
  45. _InterlockedExchangeAdd64, \
  46. _InterlockedIncrement, \
  47. _InterlockedIncrement64, \
  48. _InterlockedDecrement, \
  49. _InterlockedDecrement64, \
  50. _InterlockedCompareExchange, \
  51. _InterlockedCompareExchange64, \
  52. _ReadWriteBarrier)
  53. #endif
  54. //==============================================================================
  55. /**
  56. Simple class to hold a primitive value and perform atomic operations on it.
  57. The type used must be a 32 or 64 bit primitive, like an int, pointer, etc.
  58. There are methods to perform most of the basic atomic operations.
  59. */
  60. template <typename Type>
  61. class Atomic
  62. {
  63. public:
  64. /** Creates a new value, initialised to zero. */
  65. inline Atomic() noexcept
  66. : value (0)
  67. {
  68. }
  69. /** Creates a new value, with a given initial value. */
  70. inline explicit Atomic (const Type initialValue) noexcept
  71. : value (initialValue)
  72. {
  73. }
  74. /** Copies another value (atomically). */
  75. inline Atomic (const Atomic& other) noexcept
  76. : value (other.get())
  77. {
  78. }
  79. /** Destructor. */
  80. inline ~Atomic() noexcept
  81. {
  82. #ifdef CARLA_PROPER_CPP11_SUPPORT
  83. // This class can only be used for types which are 32 or 64 bits in size.
  84. static_wassert (sizeof (Type) == 4 || sizeof (Type) == 8);
  85. #endif
  86. }
  87. /** Atomically reads and returns the current value. */
  88. Type get() const noexcept;
  89. /** Copies another value onto this one (atomically). */
  90. inline Atomic& operator= (const Atomic& other) noexcept { exchange (other.get()); return *this; }
  91. /** Copies another value onto this one (atomically). */
  92. inline Atomic& operator= (const Type newValue) noexcept { exchange (newValue); return *this; }
  93. /** Atomically sets the current value. */
  94. void set (Type newValue) noexcept { exchange (newValue); }
  95. /** Atomically sets the current value, returning the value that was replaced. */
  96. Type exchange (Type value) noexcept;
  97. /** Atomically adds a number to this value, returning the new value. */
  98. Type operator+= (Type amountToAdd) noexcept;
  99. /** Atomically subtracts a number from this value, returning the new value. */
  100. Type operator-= (Type amountToSubtract) noexcept;
  101. /** Atomically increments this value, returning the new value. */
  102. Type operator++() noexcept;
  103. /** Atomically decrements this value, returning the new value. */
  104. Type operator--() noexcept;
  105. /** Atomically compares this value with a target value, and if it is equal, sets
  106. this to be equal to a new value.
  107. This operation is the atomic equivalent of doing this:
  108. @code
  109. bool compareAndSetBool (Type newValue, Type valueToCompare)
  110. {
  111. if (get() == valueToCompare)
  112. {
  113. set (newValue);
  114. return true;
  115. }
  116. return false;
  117. }
  118. @endcode
  119. @returns true if the comparison was true and the value was replaced; false if
  120. the comparison failed and the value was left unchanged.
  121. @see compareAndSetValue
  122. */
  123. bool compareAndSetBool (Type newValue, Type valueToCompare) noexcept;
  124. /** Atomically compares this value with a target value, and if it is equal, sets
  125. this to be equal to a new value.
  126. This operation is the atomic equivalent of doing this:
  127. @code
  128. Type compareAndSetValue (Type newValue, Type valueToCompare)
  129. {
  130. Type oldValue = get();
  131. if (oldValue == valueToCompare)
  132. set (newValue);
  133. return oldValue;
  134. }
  135. @endcode
  136. @returns the old value before it was changed.
  137. @see compareAndSetBool
  138. */
  139. Type compareAndSetValue (Type newValue, Type valueToCompare) noexcept;
  140. /** Implements a memory read/write barrier. */
  141. static void memoryBarrier() noexcept;
  142. //==============================================================================
  143. /** The raw value that this class operates on.
  144. This is exposed publicly in case you need to manipulate it directly
  145. for performance reasons.
  146. */
  147. #ifdef CARLA_OS_64BIT
  148. #define WATER_ALIGN_SIZE 8
  149. #else
  150. #define WATER_ALIGN_SIZE 4
  151. #endif
  152. #ifdef _MSC_VER
  153. __declspec (align (WATER_ALIGN_SIZE))
  154. #else
  155. __attribute__ ((aligned (WATER_ALIGN_SIZE)))
  156. #endif
  157. mutable volatile Type value;
  158. private:
  159. template <typename Dest, typename Source>
  160. static inline Dest castTo (Source value) noexcept { union { Dest d; Source s; } u; u.s = value; return u.d; }
  161. static inline Type castFrom32Bit (int32 value) noexcept { return castTo <Type, int32> (value); }
  162. static inline Type castFrom64Bit (int64 value) noexcept { return castTo <Type, int64> (value); }
  163. static inline Type castFrom32Bit (uint32 value) noexcept { return castTo <Type, uint32> (value); }
  164. static inline Type castFrom64Bit (uint64 value) noexcept { return castTo <Type, uint64> (value); }
  165. static inline Type castFromLong (long value) noexcept { return castTo <Type, long> (value); }
  166. static inline int32 castTo32Bit (Type value) noexcept { return castTo <int32, Type> (value); }
  167. static inline int64 castTo64Bit (Type value) noexcept { return castTo <int64, Type> (value); }
  168. static inline long castToLong (Type value) noexcept { return castTo <long, Type> (value); }
  169. Type operator++ (int); // better to just use pre-increment with atomics..
  170. Type operator-- (int);
  171. /** This templated negate function will negate pointers as well as integers */
  172. template <typename ValueType>
  173. inline ValueType negateValue (ValueType n) noexcept
  174. {
  175. return sizeof (ValueType) == 1 ? (ValueType) -(signed char) n
  176. : (sizeof (ValueType) == 2 ? (ValueType) -(short) n
  177. : (sizeof (ValueType) == 4 ? (ValueType) -(int) n
  178. : ((ValueType) -(int64) n)));
  179. }
  180. /** This templated negate function will negate pointers as well as integers */
  181. template <typename PointerType>
  182. inline PointerType* negateValue (PointerType* n) noexcept
  183. {
  184. return reinterpret_cast<PointerType*> (-reinterpret_cast<pointer_sized_int> (n));
  185. }
  186. };
  187. //==============================================================================
  188. template<>
  189. inline int32 Atomic<int32>::get() const noexcept
  190. {
  191. #ifdef CARLA_PROPER_CPP11_SUPPORT
  192. static_wassert (sizeof (int32) == 4);
  193. #endif
  194. #ifdef _MSC_VER
  195. return castFromLong (_InterlockedExchangeAdd (reinterpret_cast<volatile long*> (&value), 0));
  196. #else
  197. return castFrom32Bit ((int32) __sync_add_and_fetch (const_cast<volatile int32*> (&value), 0));
  198. #endif
  199. }
  200. template<>
  201. inline int64 Atomic<int64>::get() const noexcept
  202. {
  203. #ifdef CARLA_PROPER_CPP11_SUPPORT
  204. static_wassert (sizeof (int64) == 8);
  205. #endif
  206. #ifdef _MSC_VER
  207. return castFrom64Bit (_InterlockedExchangeAdd64 (reinterpret_cast<volatile int64*> (&value), 0));
  208. #else
  209. return castFrom64Bit ((int64) __sync_add_and_fetch (const_cast<volatile int64*> (&value), 0));
  210. #endif
  211. }
  212. template<>
  213. inline uint32 Atomic<uint32>::get() const noexcept
  214. {
  215. #ifdef CARLA_PROPER_CPP11_SUPPORT
  216. static_wassert (sizeof (uint32) == 4);
  217. #endif
  218. #ifdef _MSC_VER
  219. return castFromLong (_InterlockedExchangeAdd (reinterpret_cast<volatile long*> (&value), 0));
  220. #else
  221. return castFrom32Bit ((uint32) __sync_add_and_fetch (const_cast<volatile uint32*> (&value), 0));
  222. #endif
  223. }
  224. template<>
  225. inline uint64 Atomic<uint64>::get() const noexcept
  226. {
  227. #ifdef CARLA_PROPER_CPP11_SUPPORT
  228. static_wassert (sizeof (uint64) == 8);
  229. #endif
  230. #ifdef _MSC_VER
  231. return castFrom64Bit (_InterlockedExchangeAdd64 (reinterpret_cast<volatile int64*> (&value), 0));
  232. #else
  233. return castFrom64Bit ((uint64) __sync_add_and_fetch (const_cast<volatile uint64*> (&value), 0));
  234. #endif
  235. }
  236. template <typename Type>
  237. inline Type Atomic<Type>::exchange (const Type newValue) noexcept
  238. {
  239. #ifdef _MSC_VER
  240. return sizeof (Type) == 4 ? castFromLong (_InterlockedExchange (reinterpret_cast<volatile long*> (&value), castToLong (newValue)))
  241. : castFrom64Bit (_InterlockedExchange64 (reinterpret_cast<volatile int64*> (&value), castTo64Bit (newValue)));
  242. #else
  243. Type currentVal = value;
  244. while (! compareAndSetBool (newValue, currentVal)) { currentVal = value; }
  245. return currentVal;
  246. #endif
  247. }
  248. template <typename Type>
  249. inline Type Atomic<Type>::operator+= (const Type amountToAdd) noexcept
  250. {
  251. #ifdef _MSC_VER
  252. return sizeof (Type) == 4 ? castFromLong (_InterlockedExchangeAdd (reinterpret_cast<volatile long*> (&value), castToLong (amountToAdd)))
  253. : castFrom64Bit (_InterlockedExchangeAdd64 (reinterpret_cast<volatile int64*> (&value), castTo64Bit (amountToAdd)));
  254. #else
  255. return (Type) __sync_add_and_fetch (&value, amountToAdd);
  256. #endif
  257. }
  258. template <typename Type>
  259. inline Type Atomic<Type>::operator-= (const Type amountToSubtract) noexcept
  260. {
  261. return operator+= (negateValue (amountToSubtract));
  262. }
  263. template <typename Type>
  264. inline Type Atomic<Type>::operator++() noexcept
  265. {
  266. #ifdef _MSC_VER
  267. return sizeof (Type) == 4 ? castFromLong (_InterlockedIncrement (reinterpret_cast<volatile long*> (&value)))
  268. : castFrom64Bit (_InterlockedIncrement64 (reinterpret_cast<volatile int64*> (&value)));
  269. #else
  270. return sizeof (Type) == 4 ? (Type) __sync_add_and_fetch (&value, (Type) 1)
  271. : (Type) __sync_add_and_fetch ((volatile int64*) &value, 1);
  272. #endif
  273. }
  274. template <typename Type>
  275. inline Type Atomic<Type>::operator--() noexcept
  276. {
  277. #ifdef _MSC_VER
  278. return sizeof (Type) == 4 ? castFromLong (_InterlockedDecrement (reinterpret_cast<volatile long*> (&value)))
  279. : castFrom64Bit (_InterlockedDecrement64 (reinterpret_cast<volatile int64*> (&value)));
  280. #else
  281. return sizeof (Type) == 4 ? (Type) __sync_add_and_fetch (&value, (Type) -1)
  282. : (Type) __sync_add_and_fetch ((volatile int64*) &value, -1);
  283. #endif
  284. }
  285. template <typename Type>
  286. inline bool Atomic<Type>::compareAndSetBool (const Type newValue, const Type valueToCompare) noexcept
  287. {
  288. #ifdef _MSC_VER
  289. return compareAndSetValue (newValue, valueToCompare) == valueToCompare;
  290. #else
  291. return sizeof (Type) == 4 ? __sync_bool_compare_and_swap ((volatile int32*) &value, castTo32Bit (valueToCompare), castTo32Bit (newValue))
  292. : __sync_bool_compare_and_swap ((volatile int64*) &value, castTo64Bit (valueToCompare), castTo64Bit (newValue));
  293. #endif
  294. }
  295. template <typename Type>
  296. inline Type Atomic<Type>::compareAndSetValue (const Type newValue, const Type valueToCompare) noexcept
  297. {
  298. #ifdef _MSC_VER
  299. return sizeof (Type) == 4 ? castFromLong (_InterlockedCompareExchange (reinterpret_cast<volatile long*> (&value), castToLong (valueToCompare), castToLong (newValue)))
  300. : castFrom64Bit (_InterlockedCompareExchange64 (reinterpret_cast<volatile int64*> (&value), castTo64Bit (valueToCompare), castTo64Bit (newValue)));
  301. #else
  302. return sizeof (Type) == 4 ? castFrom32Bit ((int32) __sync_val_compare_and_swap (reinterpret_cast<volatile int32*> (&value), castTo32Bit (valueToCompare), castTo32Bit (newValue)))
  303. : castFrom64Bit ((int64) __sync_val_compare_and_swap (reinterpret_cast<volatile int64*> (&value), castTo64Bit (valueToCompare), castTo64Bit (newValue)));
  304. #endif
  305. }
  306. template <typename Type>
  307. inline void Atomic<Type>::memoryBarrier() noexcept
  308. {
  309. #ifdef _MSC_VER
  310. _ReadWriteBarrier();
  311. #else
  312. __sync_synchronize();
  313. #endif
  314. }
  315. #if defined(__clang__)
  316. # pragma clang diagnostic pop
  317. #elif defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))
  318. # pragma GCC diagnostic pop
  319. #elif defined(_MSC_VER)
  320. # pragma warning (pop)
  321. #endif
  322. }
  323. #endif // WATER_ATOMIC_H_INCLUDED