OpenShot Audio Library | OpenShotAudio  0.6.0
juce_Timer.cpp
1 /*
2  ==============================================================================
3 
4  This file is part of the JUCE library.
5  Copyright (c) 2022 - Raw Material Software Limited
6 
7  JUCE is an open source library subject to commercial or open-source
8  licensing.
9 
10  The code included in this file is provided under the terms of the ISC license
11  http://www.isc.org/downloads/software-support-policy/isc-license. Permission
12  To use, copy, modify, and/or distribute this software for any purpose with or
13  without fee is hereby granted provided that the above copyright notice and
14  this permission notice appear in all copies.
15 
16  JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
17  EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
18  DISCLAIMED.
19 
20  ==============================================================================
21 */
22 
23 namespace juce
24 {
25 
26 class Timer::TimerThread final : private Thread,
27  private AsyncUpdater
28 {
29 public:
30  using LockType = CriticalSection; // (mysteriously, using a SpinLock here causes problems on some XP machines..)
31 
32  TimerThread() : Thread ("JUCE Timer")
33  {
34  timers.reserve (32);
35  triggerAsyncUpdate();
36  }
37 
38  ~TimerThread() override
39  {
40  cancelPendingUpdate();
41  signalThreadShouldExit();
42  callbackArrived.signal();
43  stopThread (-1);
44  }
45 
46  void run() override
47  {
48  auto lastTime = Time::getMillisecondCounter();
49  ReferenceCountedObjectPtr<CallTimersMessage> messageToSend (new CallTimersMessage());
50 
51  while (! threadShouldExit())
52  {
53  auto now = Time::getMillisecondCounter();
54  auto elapsed = (int) (now >= lastTime ? (now - lastTime)
55  : (std::numeric_limits<uint32>::max() - (lastTime - now)));
56  lastTime = now;
57 
58  auto timeUntilFirstTimer = getTimeUntilFirstTimer (elapsed);
59 
60  if (timeUntilFirstTimer <= 0)
61  {
62  if (callbackArrived.wait (0))
63  {
64  // already a message in flight - do nothing..
65  }
66  else
67  {
68  messageToSend->post();
69 
70  if (! callbackArrived.wait (300))
71  {
72  // Sometimes our message can get discarded by the OS (e.g. when running as an RTAS
73  // when the app has a modal loop), so this is how long to wait before assuming the
74  // message has been lost and trying again.
75  messageToSend->post();
76  }
77 
78  continue;
79  }
80  }
81 
82  // don't wait for too long because running this loop also helps keep the
83  // Time::getApproximateMillisecondTimer value stay up-to-date
84  wait (jlimit (1, 100, timeUntilFirstTimer));
85  }
86  }
87 
88  void callTimers()
89  {
90  auto timeout = Time::getMillisecondCounter() + 100;
91 
92  const LockType::ScopedLockType sl (lock);
93 
94  while (! timers.empty())
95  {
96  auto& first = timers.front();
97 
98  if (first.countdownMs > 0)
99  break;
100 
101  auto* timer = first.timer;
102  first.countdownMs = timer->timerPeriodMs;
103  shuffleTimerBackInQueue (0);
104  notify();
105 
106  const LockType::ScopedUnlockType ul (lock);
107 
108  JUCE_TRY
109  {
110  timer->timerCallback();
111  }
112  JUCE_CATCH_EXCEPTION
113 
114  // avoid getting stuck in a loop if a timer callback repeatedly takes too long
115  if (Time::getMillisecondCounter() > timeout)
116  break;
117  }
118 
119  callbackArrived.signal();
120  }
121 
122  void callTimersSynchronously()
123  {
124  if (! isThreadRunning())
125  {
126  // (This is relied on by some plugins in cases where the MM has
127  // had to restart and the async callback never started)
128  cancelPendingUpdate();
129  triggerAsyncUpdate();
130  }
131 
132  callTimers();
133  }
134 
135  void addTimer (Timer* t)
136  {
137  const LockType::ScopedLockType sl (lock);
138 
139  // Trying to add a timer that's already here - shouldn't get to this point,
140  // so if you get this assertion, let me know!
141  jassert (std::none_of (timers.begin(), timers.end(),
142  [t] (TimerCountdown i) { return i.timer == t; }));
143 
144  auto pos = timers.size();
145 
146  timers.push_back ({ t, t->timerPeriodMs });
147  t->positionInQueue = pos;
148  shuffleTimerForwardInQueue (pos);
149  notify();
150  }
151 
152  void removeTimer (Timer* t)
153  {
154  const LockType::ScopedLockType sl (lock);
155 
156  auto pos = t->positionInQueue;
157  auto lastIndex = timers.size() - 1;
158 
159  jassert (pos <= lastIndex);
160  jassert (timers[pos].timer == t);
161 
162  for (auto i = pos; i < lastIndex; ++i)
163  {
164  timers[i] = timers[i + 1];
165  timers[i].timer->positionInQueue = i;
166  }
167 
168  timers.pop_back();
169  }
170 
171  void resetTimerCounter (Timer* t) noexcept
172  {
173  const LockType::ScopedLockType sl (lock);
174 
175  auto pos = t->positionInQueue;
176 
177  jassert (pos < timers.size());
178  jassert (timers[pos].timer == t);
179 
180  auto lastCountdown = timers[pos].countdownMs;
181  auto newCountdown = t->timerPeriodMs;
182 
183  if (newCountdown != lastCountdown)
184  {
185  timers[pos].countdownMs = newCountdown;
186 
187  if (newCountdown > lastCountdown)
188  shuffleTimerBackInQueue (pos);
189  else
190  shuffleTimerForwardInQueue (pos);
191 
192  notify();
193  }
194  }
195 
196 private:
197  LockType lock;
198 
199  struct TimerCountdown
200  {
201  Timer* timer;
202  int countdownMs;
203  };
204 
205  std::vector<TimerCountdown> timers;
206 
207  WaitableEvent callbackArrived;
208 
209  struct CallTimersMessage final : public MessageManager::MessageBase
210  {
211  CallTimersMessage() = default;
212 
213  void messageCallback() override
214  {
216  (*instance)->callTimers();
217  }
218  };
219 
220  //==============================================================================
221  void shuffleTimerBackInQueue (size_t pos)
222  {
223  auto numTimers = timers.size();
224 
225  if (pos < numTimers - 1)
226  {
227  auto t = timers[pos];
228 
229  for (;;)
230  {
231  auto next = pos + 1;
232 
233  if (next == numTimers || timers[next].countdownMs >= t.countdownMs)
234  break;
235 
236  timers[pos] = timers[next];
237  timers[pos].timer->positionInQueue = pos;
238 
239  ++pos;
240  }
241 
242  timers[pos] = t;
243  t.timer->positionInQueue = pos;
244  }
245  }
246 
247  void shuffleTimerForwardInQueue (size_t pos)
248  {
249  if (pos > 0)
250  {
251  auto t = timers[pos];
252 
253  while (pos > 0)
254  {
255  auto& prev = timers[(size_t) pos - 1];
256 
257  if (prev.countdownMs <= t.countdownMs)
258  break;
259 
260  timers[pos] = prev;
261  timers[pos].timer->positionInQueue = pos;
262 
263  --pos;
264  }
265 
266  timers[pos] = t;
267  t.timer->positionInQueue = pos;
268  }
269  }
270 
271  int getTimeUntilFirstTimer (int numMillisecsElapsed)
272  {
273  const LockType::ScopedLockType sl (lock);
274 
275  if (timers.empty())
276  return 1000;
277 
278  for (auto& t : timers)
279  t.countdownMs -= numMillisecsElapsed;
280 
281  return timers.front().countdownMs;
282  }
283 
284  void handleAsyncUpdate() override
285  {
286  startThread (Priority::high);
287  }
288 
289  JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TimerThread)
290 };
291 
292 //==============================================================================
293 Timer::Timer() noexcept {}
294 Timer::Timer (const Timer&) noexcept {}
295 
297 {
298  // If you're destroying a timer on a background thread, make sure the timer has
299  // been stopped before execution reaches this point. A simple way to achieve this
300  // is to add a call to `stopTimer()` to the destructor of your class which inherits
301  // from Timer.
302  jassert (! isTimerRunning()
304  || MessageManager::getInstanceWithoutCreating()->currentThreadHasLockedMessageManager());
305 
306  stopTimer();
307 }
308 
309 void Timer::startTimer (int interval) noexcept
310 {
311  // If you're calling this before (or after) the MessageManager is
312  // running, then you're not going to get any timer callbacks!
313  JUCE_ASSERT_MESSAGE_MANAGER_EXISTS
314 
315  bool wasStopped = (timerPeriodMs == 0);
316  timerPeriodMs = jmax (1, interval);
317 
318  if (wasStopped)
319  timerThread->addTimer (this);
320  else
321  timerThread->resetTimerCounter (this);
322 }
323 
324 void Timer::startTimerHz (int timerFrequencyHz) noexcept
325 {
326  if (timerFrequencyHz > 0)
327  startTimer (1000 / timerFrequencyHz);
328  else
329  stopTimer();
330 }
331 
332 void Timer::stopTimer() noexcept
333 {
334  if (timerPeriodMs > 0)
335  {
336  timerThread->removeTimer (this);
337  timerPeriodMs = 0;
338  }
339 }
340 
342 {
344  (*instance)->callTimersSynchronously();
345 }
346 
347 struct LambdaInvoker final : private Timer,
348  private DeletedAtShutdown
349 {
350  LambdaInvoker (int milliseconds, std::function<void()> f)
351  : function (std::move (f))
352  {
353  startTimer (milliseconds);
354  }
355 
356  ~LambdaInvoker() final
357  {
358  stopTimer();
359  }
360 
361  void timerCallback() final
362  {
363  NullCheckedInvocation::invoke (function);
364  delete this;
365  }
366 
367  std::function<void()> function;
368 
369  JUCE_DECLARE_NON_COPYABLE (LambdaInvoker)
370 };
371 
372 void JUCE_CALLTYPE Timer::callAfterDelay (int milliseconds, std::function<void()> f)
373 {
374  new LambdaInvoker (milliseconds, std::move (f));
375 }
376 
377 } // namespace juce
GenericScopedLock< CriticalSection > ScopedLockType
GenericScopedUnlock< CriticalSection > ScopedUnlockType
static MessageManager * getInstanceWithoutCreating() noexcept
static std::optional< SharedResourcePointer > getSharedObjectWithoutCreating()
static uint32 getMillisecondCounter() noexcept
Definition: juce_Time.cpp:241
virtual ~Timer()
Definition: juce_Timer.cpp:296
void stopTimer() noexcept
Definition: juce_Timer.cpp:332
Timer() noexcept
Definition: juce_Timer.cpp:293
void startTimerHz(int timerFrequencyHz) noexcept
Definition: juce_Timer.cpp:324
bool isTimerRunning() const noexcept
Definition: juce_Timer.h:111
static void JUCE_CALLTYPE callPendingTimersSynchronously()
Definition: juce_Timer.cpp:341
static void JUCE_CALLTYPE callAfterDelay(int milliseconds, std::function< void()> functionToCall)
Definition: juce_Timer.cpp:372
void startTimer(int intervalInMilliseconds) noexcept
Definition: juce_Timer.cpp:309