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.

388 lines
8.8KB

  1. /*
  2. * Carla Mutex
  3. * Copyright (C) 2013-2023 Filipe Coelho <falktx@falktx.com>
  4. *
  5. * This program is free software; you can redistribute it and/or
  6. * modify it under the terms of the GNU General Public License as
  7. * published by the Free Software Foundation; either version 2 of
  8. * the License, or any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. *
  15. * For a full copy of the GNU General Public License see the doc/GPL.txt file.
  16. */
  17. #ifndef CARLA_MUTEX_HPP_INCLUDED
  18. #define CARLA_MUTEX_HPP_INCLUDED
  19. #include "CarlaUtils.hpp"
  20. #include <pthread.h>
  21. class CarlaSignal;
  22. // -----------------------------------------------------------------------
  23. // CarlaMutex class
  24. class CarlaMutex
  25. {
  26. public:
  27. /*
  28. * Constructor.
  29. */
  30. CarlaMutex(const bool inheritPriority = true) noexcept
  31. : fMutex(),
  32. fTryLockWasCalled(false)
  33. {
  34. pthread_mutexattr_t attr;
  35. pthread_mutexattr_init(&attr);
  36. #ifdef __GNUC__
  37. pthread_mutexattr_setprotocol(&attr, inheritPriority ? PTHREAD_PRIO_INHERIT : PTHREAD_PRIO_NONE);
  38. #else
  39. // unsupported?
  40. (void)inheritPriority;
  41. #endif
  42. pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_NORMAL);
  43. pthread_mutex_init(&fMutex, &attr);
  44. pthread_mutexattr_destroy(&attr);
  45. }
  46. /*
  47. * Destructor.
  48. */
  49. ~CarlaMutex() noexcept
  50. {
  51. pthread_mutex_destroy(&fMutex);
  52. }
  53. /*
  54. * Check if "tryLock()" was called before.
  55. */
  56. bool wasTryLockCalled() const noexcept
  57. {
  58. const bool ret(fTryLockWasCalled);
  59. fTryLockWasCalled = false;
  60. return ret;
  61. }
  62. /*
  63. * Lock the mutex.
  64. */
  65. bool lock() const noexcept
  66. {
  67. return (pthread_mutex_lock(&fMutex) == 0);
  68. }
  69. /*
  70. * Try to lock the mutex.
  71. * Returns true if successful.
  72. */
  73. bool tryLock() const noexcept
  74. {
  75. fTryLockWasCalled = true;
  76. return (pthread_mutex_trylock(&fMutex) == 0);
  77. }
  78. /*
  79. * Unlock the mutex, optionally resetting the tryLock check.
  80. */
  81. void unlock(const bool resetTryLock = false) const noexcept
  82. {
  83. if (resetTryLock)
  84. fTryLockWasCalled = false;
  85. pthread_mutex_unlock(&fMutex);
  86. }
  87. private:
  88. mutable pthread_mutex_t fMutex;
  89. mutable volatile bool fTryLockWasCalled; // true if "tryLock()" was called at least once
  90. CARLA_DECLARE_NON_COPYABLE(CarlaMutex)
  91. };
  92. // -----------------------------------------------------------------------
  93. // CarlaRecursiveMutex class
  94. class CarlaRecursiveMutex
  95. {
  96. public:
  97. /*
  98. * Constructor.
  99. */
  100. CarlaRecursiveMutex() noexcept
  101. #ifdef CARLA_OS_WIN
  102. : fSection()
  103. #else
  104. : fMutex()
  105. #endif
  106. {
  107. #ifdef CARLA_OS_WIN
  108. InitializeCriticalSection(&fSection);
  109. #else
  110. pthread_mutexattr_t attr;
  111. pthread_mutexattr_init(&attr);
  112. pthread_mutexattr_setprotocol(&attr, PTHREAD_PRIO_INHERIT);
  113. pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
  114. pthread_mutex_init(&fMutex, &attr);
  115. pthread_mutexattr_destroy(&attr);
  116. #endif
  117. }
  118. /*
  119. * Destructor.
  120. */
  121. ~CarlaRecursiveMutex() noexcept
  122. {
  123. #ifdef CARLA_OS_WIN
  124. DeleteCriticalSection(&fSection);
  125. #else
  126. pthread_mutex_destroy(&fMutex);
  127. #endif
  128. }
  129. /*
  130. * Lock the mutex.
  131. */
  132. bool lock() const noexcept
  133. {
  134. #ifdef CARLA_OS_WIN
  135. EnterCriticalSection(&fSection);
  136. return true;
  137. #else
  138. return (pthread_mutex_lock(&fMutex) == 0);
  139. #endif
  140. }
  141. /*
  142. * Try to lock the mutex.
  143. * Returns true if successful.
  144. */
  145. bool tryLock() const noexcept
  146. {
  147. #ifdef CARLA_OS_WIN
  148. return (TryEnterCriticalSection(&fSection) != FALSE);
  149. #else
  150. return (pthread_mutex_trylock(&fMutex) == 0);
  151. #endif
  152. }
  153. /*
  154. * Unlock the mutex.
  155. */
  156. void unlock() const noexcept
  157. {
  158. #ifdef CARLA_OS_WIN
  159. LeaveCriticalSection(&fSection);
  160. #else
  161. pthread_mutex_unlock(&fMutex);
  162. #endif
  163. }
  164. private:
  165. #ifdef CARLA_OS_WIN
  166. mutable CRITICAL_SECTION fSection;
  167. #else
  168. mutable pthread_mutex_t fMutex;
  169. #endif
  170. CARLA_DECLARE_NON_COPYABLE(CarlaRecursiveMutex)
  171. };
  172. // -----------------------------------------------------------------------
  173. // CarlaSignal class
  174. class CarlaSignal
  175. {
  176. public:
  177. /*
  178. * Constructor.
  179. */
  180. CarlaSignal() noexcept
  181. : fCondition(),
  182. fMutex(),
  183. fTriggered(false)
  184. {
  185. pthread_condattr_t cattr;
  186. pthread_condattr_init(&cattr);
  187. pthread_condattr_setpshared(&cattr, PTHREAD_PROCESS_PRIVATE);
  188. pthread_cond_init(&fCondition, &cattr);
  189. pthread_condattr_destroy(&cattr);
  190. pthread_mutexattr_t mattr;
  191. pthread_mutexattr_init(&mattr);
  192. #ifdef __GNUC__
  193. pthread_mutexattr_setprotocol(&mattr, PTHREAD_PRIO_INHERIT);
  194. #endif
  195. pthread_mutexattr_settype(&mattr, PTHREAD_MUTEX_NORMAL);
  196. pthread_mutex_init(&fMutex, &mattr);
  197. pthread_mutexattr_destroy(&mattr);
  198. }
  199. /*
  200. * Destructor.
  201. */
  202. ~CarlaSignal() noexcept
  203. {
  204. pthread_cond_destroy(&fCondition);
  205. pthread_mutex_destroy(&fMutex);
  206. }
  207. /*
  208. * Wait for a signal.
  209. */
  210. void wait() noexcept
  211. {
  212. pthread_mutex_lock(&fMutex);
  213. while (! fTriggered)
  214. {
  215. try {
  216. pthread_cond_wait(&fCondition, &fMutex);
  217. } CARLA_SAFE_EXCEPTION("pthread_cond_wait");
  218. }
  219. fTriggered = false;
  220. pthread_mutex_unlock(&fMutex);
  221. }
  222. /*
  223. * Wake up all waiting threads.
  224. */
  225. void signal() noexcept
  226. {
  227. pthread_mutex_lock(&fMutex);
  228. if (! fTriggered)
  229. {
  230. fTriggered = true;
  231. pthread_cond_broadcast(&fCondition);
  232. }
  233. pthread_mutex_unlock(&fMutex);
  234. }
  235. private:
  236. pthread_cond_t fCondition;
  237. pthread_mutex_t fMutex;
  238. volatile bool fTriggered;
  239. CARLA_PREVENT_HEAP_ALLOCATION
  240. CARLA_DECLARE_NON_COPYABLE(CarlaSignal)
  241. };
  242. // -----------------------------------------------------------------------
  243. // Helper class to lock&unlock a mutex during a function scope.
  244. template <class Mutex>
  245. class CarlaScopeLocker
  246. {
  247. public:
  248. CarlaScopeLocker(const Mutex& mutex) noexcept
  249. : fMutex(mutex)
  250. {
  251. fMutex.lock();
  252. }
  253. ~CarlaScopeLocker() noexcept
  254. {
  255. fMutex.unlock();
  256. }
  257. private:
  258. const Mutex& fMutex;
  259. CARLA_PREVENT_HEAP_ALLOCATION
  260. CARLA_DECLARE_NON_COPYABLE(CarlaScopeLocker)
  261. };
  262. // -----------------------------------------------------------------------
  263. // Helper class to try-lock&unlock a mutex during a function scope.
  264. template <class Mutex>
  265. class CarlaScopeTryLocker
  266. {
  267. public:
  268. CarlaScopeTryLocker(const Mutex& mutex) noexcept
  269. : fMutex(mutex),
  270. fLocked(mutex.tryLock()) {}
  271. CarlaScopeTryLocker(const Mutex& mutex, const bool forceLock) noexcept
  272. : fMutex(mutex),
  273. fLocked(forceLock ? mutex.lock() : mutex.tryLock()) {}
  274. ~CarlaScopeTryLocker() noexcept
  275. {
  276. if (fLocked)
  277. fMutex.unlock();
  278. }
  279. bool wasLocked() const noexcept
  280. {
  281. return fLocked;
  282. }
  283. bool wasNotLocked() const noexcept
  284. {
  285. return !fLocked;
  286. }
  287. bool tryAgain() const noexcept
  288. {
  289. return fMutex.tryLock();
  290. }
  291. private:
  292. const Mutex& fMutex;
  293. const bool fLocked;
  294. CARLA_PREVENT_HEAP_ALLOCATION
  295. CARLA_DECLARE_NON_COPYABLE(CarlaScopeTryLocker)
  296. };
  297. // -----------------------------------------------------------------------
  298. // Helper class to unlock&lock a mutex during a function scope.
  299. template <class Mutex>
  300. class CarlaScopeUnlocker
  301. {
  302. public:
  303. CarlaScopeUnlocker(const Mutex& mutex) noexcept
  304. : fMutex(mutex)
  305. {
  306. fMutex.unlock();
  307. }
  308. ~CarlaScopeUnlocker() noexcept
  309. {
  310. fMutex.lock();
  311. }
  312. private:
  313. const Mutex& fMutex;
  314. CARLA_PREVENT_HEAP_ALLOCATION
  315. CARLA_DECLARE_NON_COPYABLE(CarlaScopeUnlocker)
  316. };
  317. // -----------------------------------------------------------------------
  318. // Define types
  319. typedef CarlaScopeLocker<CarlaMutex> CarlaMutexLocker;
  320. typedef CarlaScopeLocker<CarlaRecursiveMutex> CarlaRecursiveMutexLocker;
  321. typedef CarlaScopeTryLocker<CarlaMutex> CarlaMutexTryLocker;
  322. typedef CarlaScopeTryLocker<CarlaRecursiveMutex> CarlaRecursiveMutexTryLocker;
  323. typedef CarlaScopeUnlocker<CarlaMutex> CarlaMutexUnlocker;
  324. typedef CarlaScopeUnlocker<CarlaRecursiveMutex> CarlaRecursiveMutexUnlocker;
  325. // -----------------------------------------------------------------------
  326. #endif // CARLA_MUTEX_HPP_INCLUDED