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.

345 lines
9.9KB

  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. // avoid getting stuck in a loop if a timer callback repeatedly takes too long
  76. const uint32 timeout = Time::getMillisecondCounter() + 100;
  77. const LockType::ScopedLockType sl (lock);
  78. while (firstTimer != nullptr && firstTimer->timerCountdownMs <= 0)
  79. {
  80. Timer* const t = firstTimer;
  81. t->timerCountdownMs = t->timerPeriodMs;
  82. removeTimer (t);
  83. addTimer (t);
  84. const LockType::ScopedUnlockType ul (lock);
  85. JUCE_TRY
  86. {
  87. t->timerCallback();
  88. }
  89. JUCE_CATCH_EXCEPTION
  90. if (Time::getMillisecondCounter() > timeout)
  91. break;
  92. }
  93. callbackArrived.signal();
  94. }
  95. void callTimersSynchronously()
  96. {
  97. if (! isThreadRunning())
  98. {
  99. // (This is relied on by some plugins in cases where the MM has
  100. // had to restart and the async callback never started)
  101. cancelPendingUpdate();
  102. triggerAsyncUpdate();
  103. }
  104. callTimers();
  105. }
  106. static inline void add (Timer* const tim) noexcept
  107. {
  108. if (instance == nullptr)
  109. instance = new TimerThread();
  110. instance->addTimer (tim);
  111. }
  112. static inline void remove (Timer* const tim) noexcept
  113. {
  114. if (instance != nullptr)
  115. instance->removeTimer (tim);
  116. }
  117. static inline void resetCounter (Timer* const tim, const int newCounter) noexcept
  118. {
  119. if (instance != nullptr)
  120. {
  121. tim->timerCountdownMs = newCounter;
  122. tim->timerPeriodMs = newCounter;
  123. if ((tim->nextTimer != nullptr && tim->nextTimer->timerCountdownMs < tim->timerCountdownMs)
  124. || (tim->previousTimer != nullptr && tim->previousTimer->timerCountdownMs > tim->timerCountdownMs))
  125. {
  126. instance->removeTimer (tim);
  127. instance->addTimer (tim);
  128. }
  129. }
  130. }
  131. static TimerThread* instance;
  132. static LockType lock;
  133. private:
  134. Timer* volatile firstTimer;
  135. WaitableEvent callbackArrived;
  136. struct CallTimersMessage : public MessageManager::MessageBase
  137. {
  138. CallTimersMessage() {}
  139. void messageCallback() override
  140. {
  141. if (instance != nullptr)
  142. instance->callTimers();
  143. }
  144. };
  145. //==============================================================================
  146. void addTimer (Timer* const t) noexcept
  147. {
  148. #if JUCE_DEBUG
  149. // trying to add a timer that's already here - shouldn't get to this point,
  150. // so if you get this assertion, let me know!
  151. jassert (! timerExists (t));
  152. #endif
  153. Timer* i = firstTimer;
  154. if (i == nullptr || i->timerCountdownMs > t->timerCountdownMs)
  155. {
  156. t->nextTimer = firstTimer;
  157. firstTimer = t;
  158. }
  159. else
  160. {
  161. while (i->nextTimer != nullptr && i->nextTimer->timerCountdownMs <= t->timerCountdownMs)
  162. i = i->nextTimer;
  163. jassert (i != nullptr);
  164. t->nextTimer = i->nextTimer;
  165. t->previousTimer = i;
  166. i->nextTimer = t;
  167. }
  168. if (t->nextTimer != nullptr)
  169. t->nextTimer->previousTimer = t;
  170. jassert ((t->nextTimer == nullptr || t->nextTimer->timerCountdownMs >= t->timerCountdownMs)
  171. && (t->previousTimer == nullptr || t->previousTimer->timerCountdownMs <= t->timerCountdownMs));
  172. notify();
  173. }
  174. void removeTimer (Timer* const t) noexcept
  175. {
  176. #if JUCE_DEBUG
  177. // trying to remove a timer that's not here - shouldn't get to this point,
  178. // so if you get this assertion, let me know!
  179. jassert (timerExists (t));
  180. #endif
  181. if (t->previousTimer != nullptr)
  182. {
  183. jassert (firstTimer != t);
  184. t->previousTimer->nextTimer = t->nextTimer;
  185. }
  186. else
  187. {
  188. jassert (firstTimer == t);
  189. firstTimer = t->nextTimer;
  190. }
  191. if (t->nextTimer != nullptr)
  192. t->nextTimer->previousTimer = t->previousTimer;
  193. t->nextTimer = nullptr;
  194. t->previousTimer = nullptr;
  195. }
  196. int getTimeUntilFirstTimer (const int numMillisecsElapsed) const
  197. {
  198. const LockType::ScopedLockType sl (lock);
  199. for (Timer* t = firstTimer; t != nullptr; t = t->nextTimer)
  200. t->timerCountdownMs -= numMillisecsElapsed;
  201. return firstTimer != nullptr ? firstTimer->timerCountdownMs : 1000;
  202. }
  203. void handleAsyncUpdate() override
  204. {
  205. startThread (7);
  206. }
  207. #if JUCE_DEBUG
  208. bool timerExists (Timer* const t) const noexcept
  209. {
  210. for (Timer* tt = firstTimer; tt != nullptr; tt = tt->nextTimer)
  211. if (tt == t)
  212. return true;
  213. return false;
  214. }
  215. #endif
  216. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TimerThread)
  217. };
  218. Timer::TimerThread* Timer::TimerThread::instance = nullptr;
  219. Timer::TimerThread::LockType Timer::TimerThread::lock;
  220. //==============================================================================
  221. Timer::Timer() noexcept
  222. : timerCountdownMs (0),
  223. timerPeriodMs (0),
  224. previousTimer (nullptr),
  225. nextTimer (nullptr)
  226. {
  227. }
  228. Timer::Timer (const Timer&) noexcept
  229. : timerCountdownMs (0),
  230. timerPeriodMs (0),
  231. previousTimer (nullptr),
  232. nextTimer (nullptr)
  233. {
  234. }
  235. Timer::~Timer()
  236. {
  237. stopTimer();
  238. }
  239. void Timer::startTimer (const int interval) noexcept
  240. {
  241. // If you're calling this before (or after) the MessageManager is
  242. // running, then you're not going to get any timer callbacks!
  243. jassert (MessageManager::getInstanceWithoutCreating() != nullptr);
  244. const TimerThread::LockType::ScopedLockType sl (TimerThread::lock);
  245. if (timerPeriodMs == 0)
  246. {
  247. timerCountdownMs = interval;
  248. timerPeriodMs = jmax (1, interval);
  249. TimerThread::add (this);
  250. }
  251. else
  252. {
  253. TimerThread::resetCounter (this, interval);
  254. }
  255. }
  256. void Timer::startTimerHz (int timerFrequencyHz) noexcept
  257. {
  258. if (timerFrequencyHz > 0)
  259. startTimer (1000 / timerFrequencyHz);
  260. else
  261. stopTimer();
  262. }
  263. void Timer::stopTimer() noexcept
  264. {
  265. const TimerThread::LockType::ScopedLockType sl (TimerThread::lock);
  266. if (timerPeriodMs > 0)
  267. {
  268. TimerThread::remove (this);
  269. timerPeriodMs = 0;
  270. }
  271. }
  272. void JUCE_CALLTYPE Timer::callPendingTimersSynchronously()
  273. {
  274. if (TimerThread::instance != nullptr)
  275. TimerThread::instance->callTimersSynchronously();
  276. }