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.

335 lines
9.4KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2015 - ROLI Ltd.
  5. Permission is granted to use this software under the terms of either:
  6. a) the GPL v2 (or any later version)
  7. b) the Affero GPL v3
  8. Details of these licenses can be found at: www.gnu.org/licenses
  9. JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
  10. WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
  11. A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  12. ------------------------------------------------------------------------------
  13. To release a closed-source product which uses JUCE, commercial licenses are
  14. available: visit www.juce.com for more information.
  15. ==============================================================================
  16. */
  17. class Timer::TimerThread : private Thread,
  18. private DeletedAtShutdown,
  19. private AsyncUpdater
  20. {
  21. public:
  22. typedef CriticalSection LockType; // (mysteriously, using a SpinLock here causes problems on some XP machines..)
  23. TimerThread()
  24. : Thread ("Juce Timer"),
  25. firstTimer (nullptr)
  26. {
  27. triggerAsyncUpdate();
  28. }
  29. ~TimerThread() noexcept
  30. {
  31. signalThreadShouldExit();
  32. callbackArrived.signal();
  33. stopThread (4000);
  34. jassert (instance == this || instance == nullptr);
  35. if (instance == this)
  36. instance = nullptr;
  37. }
  38. void run() override
  39. {
  40. uint32 lastTime = Time::getMillisecondCounter();
  41. MessageManager::MessageBase::Ptr messageToSend (new CallTimersMessage());
  42. while (! threadShouldExit())
  43. {
  44. const uint32 now = Time::getMillisecondCounter();
  45. const int elapsed = (int) (now >= lastTime ? (now - lastTime)
  46. : (std::numeric_limits<uint32>::max() - (lastTime - now)));
  47. lastTime = now;
  48. const int timeUntilFirstTimer = getTimeUntilFirstTimer (elapsed);
  49. if (timeUntilFirstTimer <= 0)
  50. {
  51. if (callbackArrived.wait (0))
  52. {
  53. // already a message in flight - do nothing..
  54. }
  55. else
  56. {
  57. messageToSend->post();
  58. if (! callbackArrived.wait (300))
  59. {
  60. // Sometimes our message can get discarded by the OS (e.g. when running as an RTAS
  61. // when the app has a modal loop), so this is how long to wait before assuming the
  62. // message has been lost and trying again.
  63. messageToSend->post();
  64. }
  65. continue;
  66. }
  67. }
  68. // don't wait for too long because running this loop also helps keep the
  69. // Time::getApproximateMillisecondTimer value stay up-to-date
  70. wait (jlimit (1, 100, timeUntilFirstTimer));
  71. }
  72. }
  73. void callTimers()
  74. {
  75. const LockType::ScopedLockType sl (lock);
  76. while (firstTimer != nullptr && firstTimer->timerCountdownMs <= 0)
  77. {
  78. Timer* const t = firstTimer;
  79. t->timerCountdownMs = t->timerPeriodMs;
  80. removeTimer (t);
  81. addTimer (t);
  82. const LockType::ScopedUnlockType ul (lock);
  83. JUCE_TRY
  84. {
  85. t->timerCallback();
  86. }
  87. JUCE_CATCH_EXCEPTION
  88. }
  89. callbackArrived.signal();
  90. }
  91. void callTimersSynchronously()
  92. {
  93. if (! isThreadRunning())
  94. {
  95. // (This is relied on by some plugins in cases where the MM has
  96. // had to restart and the async callback never started)
  97. cancelPendingUpdate();
  98. triggerAsyncUpdate();
  99. }
  100. callTimers();
  101. }
  102. static inline void add (Timer* const tim) noexcept
  103. {
  104. if (instance == nullptr)
  105. instance = new TimerThread();
  106. instance->addTimer (tim);
  107. }
  108. static inline void remove (Timer* const tim) noexcept
  109. {
  110. if (instance != nullptr)
  111. instance->removeTimer (tim);
  112. }
  113. static inline void resetCounter (Timer* const tim, const int newCounter) noexcept
  114. {
  115. if (instance != nullptr)
  116. {
  117. tim->timerCountdownMs = newCounter;
  118. tim->timerPeriodMs = newCounter;
  119. if ((tim->nextTimer != nullptr && tim->nextTimer->timerCountdownMs < tim->timerCountdownMs)
  120. || (tim->previousTimer != nullptr && tim->previousTimer->timerCountdownMs > tim->timerCountdownMs))
  121. {
  122. instance->removeTimer (tim);
  123. instance->addTimer (tim);
  124. }
  125. }
  126. }
  127. static TimerThread* instance;
  128. static LockType lock;
  129. private:
  130. Timer* volatile firstTimer;
  131. WaitableEvent callbackArrived;
  132. struct CallTimersMessage : public MessageManager::MessageBase
  133. {
  134. CallTimersMessage() {}
  135. void messageCallback() override
  136. {
  137. if (instance != nullptr)
  138. instance->callTimers();
  139. }
  140. };
  141. //==============================================================================
  142. void addTimer (Timer* const t) noexcept
  143. {
  144. #if JUCE_DEBUG
  145. // trying to add a timer that's already here - shouldn't get to this point,
  146. // so if you get this assertion, let me know!
  147. jassert (! timerExists (t));
  148. #endif
  149. Timer* i = firstTimer;
  150. if (i == nullptr || i->timerCountdownMs > t->timerCountdownMs)
  151. {
  152. t->nextTimer = firstTimer;
  153. firstTimer = t;
  154. }
  155. else
  156. {
  157. while (i->nextTimer != nullptr && i->nextTimer->timerCountdownMs <= t->timerCountdownMs)
  158. i = i->nextTimer;
  159. jassert (i != nullptr);
  160. t->nextTimer = i->nextTimer;
  161. t->previousTimer = i;
  162. i->nextTimer = t;
  163. }
  164. if (t->nextTimer != nullptr)
  165. t->nextTimer->previousTimer = t;
  166. jassert ((t->nextTimer == nullptr || t->nextTimer->timerCountdownMs >= t->timerCountdownMs)
  167. && (t->previousTimer == nullptr || t->previousTimer->timerCountdownMs <= t->timerCountdownMs));
  168. notify();
  169. }
  170. void removeTimer (Timer* const t) noexcept
  171. {
  172. #if JUCE_DEBUG
  173. // trying to remove a timer that's not here - shouldn't get to this point,
  174. // so if you get this assertion, let me know!
  175. jassert (timerExists (t));
  176. #endif
  177. if (t->previousTimer != nullptr)
  178. {
  179. jassert (firstTimer != t);
  180. t->previousTimer->nextTimer = t->nextTimer;
  181. }
  182. else
  183. {
  184. jassert (firstTimer == t);
  185. firstTimer = t->nextTimer;
  186. }
  187. if (t->nextTimer != nullptr)
  188. t->nextTimer->previousTimer = t->previousTimer;
  189. t->nextTimer = nullptr;
  190. t->previousTimer = nullptr;
  191. }
  192. int getTimeUntilFirstTimer (const int numMillisecsElapsed) const
  193. {
  194. const LockType::ScopedLockType sl (lock);
  195. for (Timer* t = firstTimer; t != nullptr; t = t->nextTimer)
  196. t->timerCountdownMs -= numMillisecsElapsed;
  197. return firstTimer != nullptr ? firstTimer->timerCountdownMs : 1000;
  198. }
  199. void handleAsyncUpdate() override
  200. {
  201. startThread (7);
  202. }
  203. #if JUCE_DEBUG
  204. bool timerExists (Timer* const t) const noexcept
  205. {
  206. for (Timer* tt = firstTimer; tt != nullptr; tt = tt->nextTimer)
  207. if (tt == t)
  208. return true;
  209. return false;
  210. }
  211. #endif
  212. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TimerThread)
  213. };
  214. Timer::TimerThread* Timer::TimerThread::instance = nullptr;
  215. Timer::TimerThread::LockType Timer::TimerThread::lock;
  216. //==============================================================================
  217. Timer::Timer() noexcept
  218. : timerCountdownMs (0),
  219. timerPeriodMs (0),
  220. previousTimer (nullptr),
  221. nextTimer (nullptr)
  222. {
  223. }
  224. Timer::Timer (const Timer&) noexcept
  225. : timerCountdownMs (0),
  226. timerPeriodMs (0),
  227. previousTimer (nullptr),
  228. nextTimer (nullptr)
  229. {
  230. }
  231. Timer::~Timer()
  232. {
  233. stopTimer();
  234. }
  235. void Timer::startTimer (const int interval) noexcept
  236. {
  237. const TimerThread::LockType::ScopedLockType sl (TimerThread::lock);
  238. if (timerPeriodMs == 0)
  239. {
  240. timerCountdownMs = interval;
  241. timerPeriodMs = jmax (1, interval);
  242. TimerThread::add (this);
  243. }
  244. else
  245. {
  246. TimerThread::resetCounter (this, interval);
  247. }
  248. }
  249. void Timer::startTimerHz (int timerFrequencyHz) noexcept
  250. {
  251. if (timerFrequencyHz > 0)
  252. startTimer (1000 / timerFrequencyHz);
  253. else
  254. stopTimer();
  255. }
  256. void Timer::stopTimer() noexcept
  257. {
  258. const TimerThread::LockType::ScopedLockType sl (TimerThread::lock);
  259. if (timerPeriodMs > 0)
  260. {
  261. TimerThread::remove (this);
  262. timerPeriodMs = 0;
  263. }
  264. }
  265. void JUCE_CALLTYPE Timer::callPendingTimersSynchronously()
  266. {
  267. if (TimerThread::instance != nullptr)
  268. TimerThread::instance->callTimersSynchronously();
  269. }