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.

488 lines
17KB

  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. # ifdef CARLA_OS_64BIT
  43. # pragma intrinsic (_InterlockedExchange, \
  44. _InterlockedExchange64, \
  45. _InterlockedExchangeAdd, \
  46. _InterlockedExchangeAdd64, \
  47. _InterlockedIncrement, \
  48. _InterlockedIncrement64, \
  49. _InterlockedDecrement, \
  50. _InterlockedDecrement64, \
  51. _InterlockedCompareExchange, \
  52. _InterlockedCompareExchange64, \
  53. _ReadWriteBarrier)
  54. # else
  55. # pragma intrinsic (_InterlockedExchange, \
  56. _InterlockedExchangeAdd, \
  57. _InterlockedIncrement, \
  58. _InterlockedDecrement, \
  59. _InterlockedCompareExchange, \
  60. _InterlockedCompareExchange64, \
  61. _ReadWriteBarrier)
  62. # endif
  63. #endif
  64. #if defined(CARLA_OS_64BIT) || !defined(_MSC_VER)
  65. # define WATER_ATOMIC_64_SUPPORTED
  66. #endif
  67. #ifdef CARLA_OS_64BIT
  68. # define WATER_ALIGN_SIZE 8
  69. #else
  70. # define WATER_ALIGN_SIZE 4
  71. #endif
  72. //==============================================================================
  73. /**
  74. Simple class to hold a primitive value and perform atomic operations on it.
  75. The type used must be a 32 or 64 bit primitive, like an int, pointer, etc.
  76. There are methods to perform most of the basic atomic operations.
  77. */
  78. template <typename Type>
  79. class Atomic
  80. {
  81. public:
  82. /** Creates a new value, initialised to zero. */
  83. inline Atomic() noexcept
  84. : value (0)
  85. {
  86. }
  87. /** Creates a new value, with a given initial value. */
  88. inline explicit Atomic (const Type initialValue) noexcept
  89. : value (initialValue)
  90. {
  91. }
  92. /** Copies another value (atomically). */
  93. inline Atomic (const Atomic& other) noexcept
  94. : value (other.get())
  95. {
  96. }
  97. /** Destructor. */
  98. inline ~Atomic() noexcept
  99. {
  100. #ifdef CARLA_PROPER_CPP11_SUPPORT
  101. // This class can only be used for types which are 32 or 64 bits in size.
  102. static_wassert (sizeof (Type) == 4 || sizeof (Type) == 8);
  103. #endif
  104. }
  105. /** Atomically reads and returns the current value. */
  106. Type get() const noexcept;
  107. /** Copies another value onto this one (atomically). */
  108. inline Atomic& operator= (const Atomic& other) noexcept { exchange (other.get()); return *this; }
  109. /** Copies another value onto this one (atomically). */
  110. inline Atomic& operator= (const Type newValue) noexcept { exchange (newValue); return *this; }
  111. /** Atomically sets the current value. */
  112. void set (Type newValue) noexcept { exchange (newValue); }
  113. /** Atomically sets the current value, returning the value that was replaced. */
  114. Type exchange (Type value) noexcept;
  115. /** Atomically adds a number to this value, returning the new value. */
  116. Type operator+= (Type amountToAdd) noexcept;
  117. /** Atomically subtracts a number from this value, returning the new value. */
  118. Type operator-= (Type amountToSubtract) noexcept;
  119. /** Atomically increments this value, returning the new value. */
  120. Type operator++() noexcept;
  121. /** Atomically decrements this value, returning the new value. */
  122. Type operator--() noexcept;
  123. /** Atomically compares this value with a target value, and if it is equal, sets
  124. this to be equal to a new value.
  125. This operation is the atomic equivalent of doing this:
  126. @code
  127. bool compareAndSetBool (Type newValue, Type valueToCompare)
  128. {
  129. if (get() == valueToCompare)
  130. {
  131. set (newValue);
  132. return true;
  133. }
  134. return false;
  135. }
  136. @endcode
  137. @returns true if the comparison was true and the value was replaced; false if
  138. the comparison failed and the value was left unchanged.
  139. @see compareAndSetValue
  140. */
  141. bool compareAndSetBool (Type newValue, Type valueToCompare) noexcept;
  142. /** Atomically compares this value with a target value, and if it is equal, sets
  143. this to be equal to a new value.
  144. This operation is the atomic equivalent of doing this:
  145. @code
  146. Type compareAndSetValue (Type newValue, Type valueToCompare)
  147. {
  148. Type oldValue = get();
  149. if (oldValue == valueToCompare)
  150. set (newValue);
  151. return oldValue;
  152. }
  153. @endcode
  154. @returns the old value before it was changed.
  155. @see compareAndSetBool
  156. */
  157. Type compareAndSetValue (Type newValue, Type valueToCompare) noexcept;
  158. /** Implements a memory read/write barrier. */
  159. static void memoryBarrier() noexcept;
  160. //==============================================================================
  161. /** The raw value that this class operates on.
  162. This is exposed publicly in case you need to manipulate it directly
  163. for performance reasons.
  164. */
  165. #ifdef _MSC_VER
  166. __declspec (align (WATER_ALIGN_SIZE))
  167. #else
  168. __attribute__ ((aligned (WATER_ALIGN_SIZE)))
  169. #endif
  170. mutable volatile Type value;
  171. private:
  172. template <typename Dest, typename Source>
  173. static inline Dest castTo (Source value) noexcept { union { Dest d; Source s; } u; u.s = value; return u.d; }
  174. static inline Type castFrom32Bit (int32 value) noexcept { return castTo <Type, int32> (value); }
  175. static inline Type castFrom64Bit (int64 value) noexcept { return castTo <Type, int64> (value); }
  176. static inline Type castFrom32Bit (uint32 value) noexcept { return castTo <Type, uint32> (value); }
  177. static inline Type castFrom64Bit (uint64 value) noexcept { return castTo <Type, uint64> (value); }
  178. static inline Type castFromLong (long value) noexcept { return castTo <Type, long> (value); }
  179. static inline int32 castTo32Bit (Type value) noexcept { return castTo <int32, Type> (value); }
  180. static inline int64 castTo64Bit (Type value) noexcept { return castTo <int64, Type> (value); }
  181. static inline long castToLong (Type value) noexcept { return castTo <long, Type> (value); }
  182. Type operator++ (int); // better to just use pre-increment with atomics..
  183. Type operator-- (int);
  184. /** This templated negate function will negate pointers as well as integers */
  185. template <typename ValueType>
  186. inline ValueType negateValue (ValueType n) noexcept
  187. {
  188. return sizeof (ValueType) == 1 ? (ValueType) -(signed char) n
  189. : (sizeof (ValueType) == 2 ? (ValueType) -(short) n
  190. : (sizeof (ValueType) == 4 ? (ValueType) -(int) n
  191. : ((ValueType) -(int64) n)));
  192. }
  193. /** This templated negate function will negate pointers as well as integers */
  194. template <typename PointerType>
  195. inline PointerType* negateValue (PointerType* n) noexcept
  196. {
  197. return reinterpret_cast<PointerType*> (-reinterpret_cast<pointer_sized_int> (n));
  198. }
  199. };
  200. //==============================================================================
  201. template<>
  202. inline int32 Atomic<int32>::get() const noexcept
  203. {
  204. #ifdef CARLA_PROPER_CPP11_SUPPORT
  205. static_wassert (sizeof (int32) == 4);
  206. #endif
  207. #ifdef _MSC_VER
  208. return castFromLong (_InterlockedExchangeAdd (reinterpret_cast<volatile long*> (&value), 0));
  209. #else
  210. return castFrom32Bit ((int32) __sync_add_and_fetch (const_cast<volatile int32*> (&value), 0));
  211. #endif
  212. }
  213. template<>
  214. inline uint32 Atomic<uint32>::get() const noexcept
  215. {
  216. #ifdef CARLA_PROPER_CPP11_SUPPORT
  217. static_wassert (sizeof (uint32) == 4);
  218. #endif
  219. #ifdef _MSC_VER
  220. return castFromLong (_InterlockedExchangeAdd (reinterpret_cast<volatile long*> (&value), 0));
  221. #else
  222. return castFrom32Bit ((uint32) __sync_add_and_fetch (const_cast<volatile uint32*> (&value), 0));
  223. #endif
  224. }
  225. #ifdef WATER_ATOMIC_64_SUPPORTED
  226. template<>
  227. inline int64 Atomic<int64>::get() const noexcept
  228. {
  229. #ifdef CARLA_PROPER_CPP11_SUPPORT
  230. static_wassert (sizeof (int64) == 8);
  231. #endif
  232. #ifdef _MSC_VER
  233. return castFrom64Bit (_InterlockedExchangeAdd64 (reinterpret_cast<volatile int64*> (&value), 0));
  234. #else
  235. return castFrom64Bit ((int64) __sync_add_and_fetch (const_cast<volatile int64*> (&value), 0));
  236. #endif
  237. }
  238. template<>
  239. inline uint64 Atomic<uint64>::get() const noexcept
  240. {
  241. #ifdef CARLA_PROPER_CPP11_SUPPORT
  242. static_wassert (sizeof (uint64) == 8);
  243. #endif
  244. #ifdef _MSC_VER
  245. return castFrom64Bit (_InterlockedExchangeAdd64 (reinterpret_cast<volatile int64*> (&value), 0));
  246. #else
  247. return castFrom64Bit ((uint64) __sync_add_and_fetch (const_cast<volatile uint64*> (&value), 0));
  248. #endif
  249. }
  250. #endif // WATER_ATOMIC_64_SUPPORTED
  251. #ifdef _MSC_VER
  252. template <>
  253. inline int32 Atomic<int32>::exchange (const int32 newValue) noexcept
  254. {
  255. return castFromLong (_InterlockedExchange (reinterpret_cast<volatile long*> (&value), castToLong (newValue)));
  256. }
  257. template <>
  258. inline uint32 Atomic<uint32>::exchange (const uint32 newValue) noexcept
  259. {
  260. return castFromLong (_InterlockedExchange (reinterpret_cast<volatile long*> (&value), castToLong (newValue)));
  261. }
  262. template <>
  263. inline int32 Atomic<int32>::operator+= (const int32 amountToAdd) noexcept
  264. {
  265. return castFromLong (_InterlockedExchangeAdd (reinterpret_cast<volatile long*> (&value), castToLong (amountToAdd)));
  266. }
  267. template <>
  268. inline uint32 Atomic<uint32>::operator+= (const uint32 amountToAdd) noexcept
  269. {
  270. return castFromLong (_InterlockedExchangeAdd (reinterpret_cast<volatile long*> (&value), castToLong (amountToAdd)));
  271. }
  272. template <>
  273. inline int32 Atomic<int32>::operator++() noexcept
  274. {
  275. return castFromLong (_InterlockedIncrement (reinterpret_cast<volatile long*> (&value)));
  276. }
  277. template <>
  278. inline uint32 Atomic<uint32>::operator++() noexcept
  279. {
  280. return castFromLong (_InterlockedIncrement (reinterpret_cast<volatile long*> (&value)));
  281. }
  282. template <>
  283. inline int32 Atomic<int32>::operator--() noexcept
  284. {
  285. return castFromLong (_InterlockedDecrement (reinterpret_cast<volatile long*> (&value)));
  286. }
  287. template <>
  288. inline uint32 Atomic<uint32>::operator--() noexcept
  289. {
  290. return castFromLong (_InterlockedDecrement (reinterpret_cast<volatile long*> (&value)));
  291. }
  292. # ifndef CARLA_OS_64BIT
  293. template <>
  294. inline int64 Atomic<int64>::exchange (const int64 newValue) noexcept
  295. {
  296. return castFrom64Bit (_InterlockedCompareExchange64 (reinterpret_cast<volatile int64*> (&value), castTo64Bit (value), castTo64Bit (newValue)));
  297. }
  298. template <>
  299. inline uint64 Atomic<uint64>::exchange (const uint64 newValue) noexcept
  300. {
  301. return castFrom64Bit (_InterlockedCompareExchange64 (reinterpret_cast<volatile int64*> (&value), castTo64Bit (value), castTo64Bit (newValue)));
  302. }
  303. # else
  304. template <>
  305. inline int64 Atomic<int64>::exchange (const int64 newValue) noexcept
  306. {
  307. return castFrom64Bit (_InterlockedExchange64 (reinterpret_cast<volatile int64*> (&value), castTo64Bit (newValue)));
  308. }
  309. template <>
  310. inline uint64 Atomic<uint64>::exchange (const uint64 newValue) noexcept
  311. {
  312. return castFrom64Bit (_InterlockedExchange64 (reinterpret_cast<volatile int64*> (&value), castTo64Bit (newValue)));
  313. }
  314. template <>
  315. inline int64 Atomic<int64>::operator+= (const int64 amountToAdd) noexcept
  316. {
  317. return castFrom64Bit (_InterlockedExchangeAdd64 (reinterpret_cast<volatile int64*> (&value), castTo64Bit (amountToAdd)));
  318. }
  319. template <>
  320. inline uint64 Atomic<uint64>::operator+= (const uint64 amountToAdd) noexcept
  321. {
  322. return castFrom64Bit (_InterlockedExchangeAdd64 (reinterpret_cast<volatile int64*> (&value), castTo64Bit (amountToAdd)));
  323. }
  324. template <>
  325. inline int64 Atomic<int64>::operator++() noexcept
  326. {
  327. return castFrom64Bit (_InterlockedIncrement64 (reinterpret_cast<volatile int64*> (&value)));
  328. }
  329. template <>
  330. inline uint64 Atomic<uint64>::operator++() noexcept
  331. {
  332. return castFrom64Bit (_InterlockedIncrement64 (reinterpret_cast<volatile int64*> (&value)));
  333. }
  334. template <>
  335. inline int64 Atomic<int64>::operator--() noexcept
  336. {
  337. return castFrom64Bit (_InterlockedDecrement64 (reinterpret_cast<volatile int64*> (&value)));
  338. }
  339. template <>
  340. inline uint64 Atomic<uint64>::operator--() noexcept
  341. {
  342. return castFrom64Bit (_InterlockedDecrement64 (reinterpret_cast<volatile int64*> (&value)));
  343. }
  344. # endif
  345. #else // _MSC_VER
  346. template <typename Type>
  347. inline Type Atomic<Type>::exchange (const Type newValue) noexcept
  348. {
  349. Type currentVal = value;
  350. while (! compareAndSetBool (newValue, currentVal)) { currentVal = value; }
  351. return currentVal;
  352. }
  353. template <typename Type>
  354. inline Type Atomic<Type>::operator+= (const Type amountToAdd) noexcept
  355. {
  356. return (Type) __sync_add_and_fetch (&value, amountToAdd);
  357. }
  358. template <typename Type>
  359. inline Type Atomic<Type>::operator++() noexcept
  360. {
  361. return sizeof (Type) == 4 ? (Type) __sync_add_and_fetch (&value, (Type) 1)
  362. : (Type) __sync_add_and_fetch ((volatile int64*) &value, 1);
  363. }
  364. template <typename Type>
  365. inline Type Atomic<Type>::operator--() noexcept
  366. {
  367. return sizeof (Type) == 4 ? (Type) __sync_add_and_fetch (&value, (Type) -1)
  368. : (Type) __sync_add_and_fetch ((volatile int64*) &value, -1);
  369. }
  370. #endif // _MSC_VER
  371. template <typename Type>
  372. inline Type Atomic<Type>::operator-= (const Type amountToSubtract) noexcept
  373. {
  374. return operator+= (negateValue (amountToSubtract));
  375. }
  376. template <typename Type>
  377. inline bool Atomic<Type>::compareAndSetBool (const Type newValue, const Type valueToCompare) noexcept
  378. {
  379. #ifdef _MSC_VER
  380. return compareAndSetValue (newValue, valueToCompare) == valueToCompare;
  381. #else
  382. return sizeof (Type) == 4 ? __sync_bool_compare_and_swap ((volatile int32*) &value, castTo32Bit (valueToCompare), castTo32Bit (newValue))
  383. : __sync_bool_compare_and_swap ((volatile int64*) &value, castTo64Bit (valueToCompare), castTo64Bit (newValue));
  384. #endif
  385. }
  386. template <typename Type>
  387. inline Type Atomic<Type>::compareAndSetValue (const Type newValue, const Type valueToCompare) noexcept
  388. {
  389. #ifdef _MSC_VER
  390. return sizeof (Type) == 4 ? castFromLong (_InterlockedCompareExchange (reinterpret_cast<volatile long*> (&value), castToLong (valueToCompare), castToLong (newValue)))
  391. : castFrom64Bit (_InterlockedCompareExchange64 (reinterpret_cast<volatile int64*> (&value), castTo64Bit (valueToCompare), castTo64Bit (newValue)));
  392. #else
  393. return sizeof (Type) == 4 ? castFrom32Bit ((int32) __sync_val_compare_and_swap (reinterpret_cast<volatile int32*> (&value), castTo32Bit (valueToCompare), castTo32Bit (newValue)))
  394. : castFrom64Bit ((int64) __sync_val_compare_and_swap (reinterpret_cast<volatile int64*> (&value), castTo64Bit (valueToCompare), castTo64Bit (newValue)));
  395. #endif
  396. }
  397. template <typename Type>
  398. inline void Atomic<Type>::memoryBarrier() noexcept
  399. {
  400. #ifdef _MSC_VER
  401. _ReadWriteBarrier();
  402. #else
  403. __sync_synchronize();
  404. #endif
  405. }
  406. #if defined(__clang__)
  407. # pragma clang diagnostic pop
  408. #elif defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))
  409. # pragma GCC diagnostic pop
  410. #elif defined(_MSC_VER)
  411. # pragma warning (pop)
  412. #endif
  413. }
  414. #endif // WATER_ATOMIC_H_INCLUDED