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.

329 lines
8.2KB

  1. // SPDX-FileCopyrightText: 2011-2025 Filipe Coelho <falktx@falktx.com>
  2. // SPDX-License-Identifier: GPL-2.0-or-later
  3. #ifndef CARLA_THREAD_HPP_INCLUDED
  4. #define CARLA_THREAD_HPP_INCLUDED
  5. #include "CarlaMutex.hpp"
  6. #include "CarlaProcessUtils.hpp"
  7. #include "distrho/extra/Sleep.hpp"
  8. #include "distrho/extra/String.hpp"
  9. #ifdef CARLA_OS_WASM
  10. # error Threads do not work under wasm!
  11. #endif
  12. using DISTRHO_NAMESPACE::d_msleep;
  13. // -----------------------------------------------------------------------
  14. // CarlaThread class
  15. class CarlaThread
  16. {
  17. protected:
  18. /*
  19. * Constructor.
  20. */
  21. CarlaThread(const char* const threadName) noexcept
  22. : fLock(),
  23. fSignal(),
  24. fName(threadName),
  25. #ifdef PTW32_DLLPORT
  26. fHandle({nullptr, 0}),
  27. #else
  28. fHandle(0),
  29. #endif
  30. fShouldExit(false) {}
  31. /*
  32. * Destructor.
  33. */
  34. virtual ~CarlaThread() /*noexcept*/
  35. {
  36. CARLA_SAFE_ASSERT(! isThreadRunning());
  37. stopThread(-1);
  38. }
  39. /*
  40. * Virtual function to be implemented by the subclass.
  41. */
  42. virtual void run() = 0;
  43. // -------------------------------------------------------------------
  44. public:
  45. /*
  46. * Check if the thread is running.
  47. */
  48. bool isThreadRunning() const noexcept
  49. {
  50. #ifdef PTW32_DLLPORT
  51. return fHandle.p != nullptr;
  52. #else
  53. return fHandle != 0;
  54. #endif
  55. }
  56. /*
  57. * Check if the thread should exit.
  58. */
  59. bool shouldThreadExit() const noexcept
  60. {
  61. return fShouldExit;
  62. }
  63. /*
  64. * Start the thread.
  65. */
  66. bool startThread(bool withRealtimePriority = false) noexcept
  67. {
  68. // check if already running
  69. CARLA_SAFE_ASSERT_RETURN(! isThreadRunning(), true);
  70. if (withRealtimePriority && std::getenv("CARLA_BRIDGE_DUMMY") != nullptr)
  71. withRealtimePriority = false;
  72. pthread_t handle;
  73. pthread_attr_t attr;
  74. pthread_attr_init(&attr);
  75. struct sched_param sched_param;
  76. carla_zeroStruct(sched_param);
  77. if (withRealtimePriority)
  78. {
  79. sched_param.sched_priority = 80;
  80. #ifndef CARLA_OS_HAIKU
  81. if (pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM) == 0 &&
  82. pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED) == 0 &&
  83. #ifndef CARLA_OS_WIN
  84. (pthread_attr_setschedpolicy(&attr, SCHED_FIFO) == 0 ||
  85. pthread_attr_setschedpolicy(&attr, SCHED_RR) == 0) &&
  86. #endif
  87. pthread_attr_setschedparam(&attr, &sched_param) == 0)
  88. {
  89. carla_stdout("CarlaThread setup with realtime priority successful");
  90. }
  91. else
  92. #endif
  93. {
  94. carla_stdout("CarlaThread setup with realtime priority failed, going with normal priority instead");
  95. pthread_attr_destroy(&attr);
  96. pthread_attr_init(&attr);
  97. }
  98. }
  99. pthread_attr_setdetachstate(&attr, 1);
  100. const CarlaMutexLocker cml(fLock);
  101. fShouldExit = false;
  102. bool ok = pthread_create(&handle, &attr, _entryPoint, this) == 0;
  103. pthread_attr_destroy(&attr);
  104. if (withRealtimePriority && !ok)
  105. {
  106. carla_stdout("CarlaThread with realtime priority failed on creation, going with normal priority instead");
  107. pthread_attr_init(&attr);
  108. pthread_attr_setdetachstate(&attr, 1);
  109. ok = pthread_create(&handle, &attr, _entryPoint, this) == 0;
  110. pthread_attr_destroy(&attr);
  111. }
  112. CARLA_SAFE_ASSERT_RETURN(ok, false);
  113. #ifdef PTW32_DLLPORT
  114. CARLA_SAFE_ASSERT_RETURN(handle.p != nullptr, false);
  115. #else
  116. CARLA_SAFE_ASSERT_RETURN(handle != 0, false);
  117. #endif
  118. _copyFrom(handle);
  119. // wait for thread to start
  120. fSignal.wait();
  121. return true;
  122. }
  123. /*
  124. * Stop the thread.
  125. * In the 'timeOutMilliseconds':
  126. * = 0 -> no wait
  127. * > 0 -> wait timeout value
  128. * < 0 -> wait forever
  129. */
  130. bool stopThread(const int timeOutMilliseconds) noexcept
  131. {
  132. const CarlaMutexLocker cml(fLock);
  133. if (isThreadRunning())
  134. {
  135. signalThreadShouldExit();
  136. if (timeOutMilliseconds != 0)
  137. {
  138. // Wait for the thread to stop
  139. int timeOutCheck = (timeOutMilliseconds == 1 || timeOutMilliseconds == -1) ? timeOutMilliseconds : timeOutMilliseconds/2;
  140. for (; isThreadRunning();)
  141. {
  142. d_msleep(2);
  143. if (timeOutCheck < 0)
  144. continue;
  145. if (timeOutCheck > 0)
  146. timeOutCheck -= 1;
  147. else
  148. break;
  149. }
  150. }
  151. if (isThreadRunning())
  152. {
  153. // should never happen!
  154. carla_stderr2("Carla assertion failure: \"! isThreadRunning()\" in file %s, line %i", __FILE__, __LINE__);
  155. // copy thread id so we can clear our one
  156. pthread_t threadId;
  157. _copyTo(threadId);
  158. _init();
  159. pthread_detach(threadId);
  160. return false;
  161. }
  162. }
  163. return true;
  164. }
  165. /*
  166. * Tell the thread to stop as soon as possible.
  167. */
  168. void signalThreadShouldExit() noexcept
  169. {
  170. fShouldExit = true;
  171. }
  172. // -------------------------------------------------------------------
  173. /*
  174. * Returns the name of the thread.
  175. * This is the name that gets set in the constructor.
  176. */
  177. const String& getThreadName() const noexcept
  178. {
  179. return fName;
  180. }
  181. /*
  182. * Returns the Id/handle of the thread.
  183. */
  184. pthread_t getThreadId() const noexcept
  185. {
  186. #ifdef PTW32_DLLPORT
  187. const pthread_t handle = { fHandle.p, fHandle.x };
  188. return handle;
  189. #else
  190. return fHandle;
  191. #endif
  192. }
  193. /*
  194. * Changes the name of the caller thread.
  195. */
  196. static void setCurrentThreadName(const char* const name) noexcept
  197. {
  198. CARLA_SAFE_ASSERT_RETURN(name != nullptr && name[0] != '\0',);
  199. carla_setProcessName(name);
  200. #if defined(__GLIBC__) && (__GLIBC__ * 1000 + __GLIBC_MINOR__) >= 2012 && !defined(CARLA_OS_GNU_HURD)
  201. pthread_setname_np(pthread_self(), name);
  202. #endif
  203. }
  204. // -------------------------------------------------------------------
  205. private:
  206. CarlaMutex fLock; // Thread lock
  207. CarlaSignal fSignal; // Thread start wait signal
  208. const String fName; // Thread name
  209. volatile pthread_t fHandle; // Handle for this thread
  210. volatile bool fShouldExit; // true if thread should exit
  211. /*
  212. * Init pthread type.
  213. */
  214. void _init() noexcept
  215. {
  216. #ifdef PTW32_DLLPORT
  217. fHandle.p = nullptr;
  218. fHandle.x = 0;
  219. #else
  220. fHandle = 0;
  221. #endif
  222. }
  223. /*
  224. * Copy our pthread type from another var.
  225. */
  226. void _copyFrom(const pthread_t& handle) noexcept
  227. {
  228. #ifdef PTW32_DLLPORT
  229. fHandle.p = handle.p;
  230. fHandle.x = handle.x;
  231. #else
  232. fHandle = handle;
  233. #endif
  234. }
  235. /*
  236. * Copy our pthread type to another var.
  237. */
  238. void _copyTo(volatile pthread_t& handle) const noexcept
  239. {
  240. #ifdef PTW32_DLLPORT
  241. handle.p = fHandle.p;
  242. handle.x = fHandle.x;
  243. #else
  244. handle = fHandle;
  245. #endif
  246. }
  247. /*
  248. * Thread entry point.
  249. */
  250. void _runEntryPoint() noexcept
  251. {
  252. if (fName.isNotEmpty())
  253. setCurrentThreadName(fName);
  254. // report ready
  255. fSignal.signal();
  256. try {
  257. run();
  258. } catch(...) {}
  259. // done
  260. _init();
  261. }
  262. /*
  263. * Thread entry point.
  264. */
  265. static void* _entryPoint(void* userData) noexcept
  266. {
  267. static_cast<CarlaThread*>(userData)->_runEntryPoint();
  268. return nullptr;
  269. }
  270. CARLA_DECLARE_NON_COPYABLE(CarlaThread)
  271. };
  272. // -----------------------------------------------------------------------
  273. #endif // CARLA_THREAD_HPP_INCLUDED