OpenShot Library | libopenshot-audio  0.2.0
juce_Timer.cpp
1 /*
2  ==============================================================================
3 
4  This file is part of the JUCE library.
5  Copyright (c) 2017 - ROLI Ltd.
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 : private Thread,
27  private DeletedAtShutdown,
28  private AsyncUpdater
29 {
30 public:
31  using LockType = CriticalSection; // (mysteriously, using a SpinLock here causes problems on some XP machines..)
32 
33  TimerThread() : Thread ("JUCE Timer")
34  {
35  timers.reserve (32);
37  }
38 
39  ~TimerThread() override
40  {
42  callbackArrived.signal();
43  stopThread (4000);
44  jassert (instance == this || instance == nullptr);
45 
46  if (instance == this)
47  instance = nullptr;
48  }
49 
50  void run() override
51  {
52  auto lastTime = Time::getMillisecondCounter();
53  ReferenceCountedObjectPtr<CallTimersMessage> messageToSend (new CallTimersMessage());
54 
55  while (! threadShouldExit())
56  {
57  auto now = Time::getMillisecondCounter();
58  auto elapsed = (int) (now >= lastTime ? (now - lastTime)
59  : (std::numeric_limits<uint32>::max() - (lastTime - now)));
60  lastTime = now;
61 
62  auto timeUntilFirstTimer = getTimeUntilFirstTimer (elapsed);
63 
64  if (timeUntilFirstTimer <= 0)
65  {
66  if (callbackArrived.wait (0))
67  {
68  // already a message in flight - do nothing..
69  }
70  else
71  {
72  messageToSend->post();
73 
74  if (! callbackArrived.wait (300))
75  {
76  // Sometimes our message can get discarded by the OS (e.g. when running as an RTAS
77  // when the app has a modal loop), so this is how long to wait before assuming the
78  // message has been lost and trying again.
79  messageToSend->post();
80  }
81 
82  continue;
83  }
84  }
85 
86  // don't wait for too long because running this loop also helps keep the
87  // Time::getApproximateMillisecondTimer value stay up-to-date
88  wait (jlimit (1, 100, timeUntilFirstTimer));
89  }
90  }
91 
92  void callTimers()
93  {
94  auto timeout = Time::getMillisecondCounter() + 100;
95 
96  const LockType::ScopedLockType sl (lock);
97 
98  while (! timers.empty())
99  {
100  auto& first = timers.front();
101 
102  if (first.countdownMs > 0)
103  break;
104 
105  auto* timer = first.timer;
106  first.countdownMs = timer->timerPeriodMs;
107  shuffleTimerBackInQueue (0);
108  notify();
109 
110  const LockType::ScopedUnlockType ul (lock);
111 
112  JUCE_TRY
113  {
114  timer->timerCallback();
115  }
116  JUCE_CATCH_EXCEPTION
117 
118  // avoid getting stuck in a loop if a timer callback repeatedly takes too long
119  if (Time::getMillisecondCounter() > timeout)
120  break;
121  }
122 
123  callbackArrived.signal();
124  }
125 
126  void callTimersSynchronously()
127  {
128  if (! isThreadRunning())
129  {
130  // (This is relied on by some plugins in cases where the MM has
131  // had to restart and the async callback never started)
134  }
135 
136  callTimers();
137  }
138 
139  static inline void add (Timer* tim) noexcept
140  {
141  if (instance == nullptr)
142  instance = new TimerThread();
143 
144  instance->addTimer (tim);
145  }
146 
147  static inline void remove (Timer* tim) noexcept
148  {
149  if (instance != nullptr)
150  instance->removeTimer (tim);
151  }
152 
153  static inline void resetCounter (Timer* tim) noexcept
154  {
155  if (instance != nullptr)
156  instance->resetTimerCounter (tim);
157  }
158 
159  static TimerThread* instance;
160  static LockType lock;
161 
162 private:
163  struct TimerCountdown
164  {
165  Timer* timer;
166  int countdownMs;
167  };
168 
169  std::vector<TimerCountdown> timers;
170 
171  WaitableEvent callbackArrived;
172 
173  struct CallTimersMessage : public MessageManager::MessageBase
174  {
175  CallTimersMessage() {}
176 
177  void messageCallback() override
178  {
179  if (instance != nullptr)
180  instance->callTimers();
181  }
182  };
183 
184  //==============================================================================
185  void addTimer (Timer* t)
186  {
187  // Trying to add a timer that's already here - shouldn't get to this point,
188  // so if you get this assertion, let me know!
189  jassert (std::find_if (timers.begin(), timers.end(),
190  [t](TimerCountdown i) { return i.timer == t; }) == timers.end());
191 
192  auto pos = timers.size();
193 
194  timers.push_back ({ t, t->timerPeriodMs });
195  t->positionInQueue = pos;
196  shuffleTimerForwardInQueue (pos);
197  notify();
198  }
199 
200  void removeTimer (Timer* t)
201  {
202  auto pos = t->positionInQueue;
203  auto lastIndex = timers.size() - 1;
204 
205  jassert (pos <= lastIndex);
206  jassert (timers[pos].timer == t);
207 
208  for (auto i = pos; i < lastIndex; ++i)
209  {
210  timers[i] = timers[i + 1];
211  timers[i].timer->positionInQueue = i;
212  }
213 
214  timers.pop_back();
215  }
216 
217  void resetTimerCounter (Timer* t) noexcept
218  {
219  auto pos = t->positionInQueue;
220 
221  jassert (pos < timers.size());
222  jassert (timers[pos].timer == t);
223 
224  auto lastCountdown = timers[pos].countdownMs;
225  auto newCountdown = t->timerPeriodMs;
226 
227  if (newCountdown != lastCountdown)
228  {
229  timers[pos].countdownMs = newCountdown;
230 
231  if (newCountdown > lastCountdown)
232  shuffleTimerBackInQueue (pos);
233  else
234  shuffleTimerForwardInQueue (pos);
235 
236  notify();
237  }
238  }
239 
240  void shuffleTimerBackInQueue (size_t pos)
241  {
242  auto numTimers = timers.size();
243 
244  if (pos < numTimers - 1)
245  {
246  auto t = timers[pos];
247 
248  for (;;)
249  {
250  auto next = pos + 1;
251 
252  if (next == numTimers || timers[next].countdownMs >= t.countdownMs)
253  break;
254 
255  timers[pos] = timers[next];
256  timers[pos].timer->positionInQueue = pos;
257 
258  ++pos;
259  }
260 
261  timers[pos] = t;
262  t.timer->positionInQueue = pos;
263  }
264  }
265 
266  void shuffleTimerForwardInQueue (size_t pos)
267  {
268  if (pos > 0)
269  {
270  auto t = timers[pos];
271 
272  while (pos > 0)
273  {
274  auto& prev = timers[(size_t) pos - 1];
275 
276  if (prev.countdownMs <= t.countdownMs)
277  break;
278 
279  timers[pos] = prev;
280  timers[pos].timer->positionInQueue = pos;
281 
282  --pos;
283  }
284 
285  timers[pos] = t;
286  t.timer->positionInQueue = pos;
287  }
288  }
289 
290  int getTimeUntilFirstTimer (int numMillisecsElapsed)
291  {
292  const LockType::ScopedLockType sl (lock);
293 
294  if (timers.empty())
295  return 1000;
296 
297  for (auto& t : timers)
298  t.countdownMs -= numMillisecsElapsed;
299 
300  return timers.front().countdownMs;
301  }
302 
303  void handleAsyncUpdate() override
304  {
305  startThread (7);
306  }
307 
308  JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TimerThread)
309 };
310 
311 Timer::TimerThread* Timer::TimerThread::instance = nullptr;
312 Timer::TimerThread::LockType Timer::TimerThread::lock;
313 
314 //==============================================================================
315 Timer::Timer() noexcept {}
316 Timer::Timer (const Timer&) noexcept {}
317 
319 {
320  stopTimer();
321 }
322 
323 void Timer::startTimer (int interval) noexcept
324 {
325  // If you're calling this before (or after) the MessageManager is
326  // running, then you're not going to get any timer callbacks!
327  JUCE_ASSERT_MESSAGE_MANAGER_EXISTS
328 
329  const TimerThread::LockType::ScopedLockType sl (TimerThread::lock);
330 
331  bool wasStopped = (timerPeriodMs == 0);
332  timerPeriodMs = jmax (1, interval);
333 
334  if (wasStopped)
335  TimerThread::add (this);
336  else
337  TimerThread::resetCounter (this);
338 }
339 
340 void Timer::startTimerHz (int timerFrequencyHz) noexcept
341 {
342  if (timerFrequencyHz > 0)
343  startTimer (1000 / timerFrequencyHz);
344  else
345  stopTimer();
346 }
347 
348 void Timer::stopTimer() noexcept
349 {
350  const TimerThread::LockType::ScopedLockType sl (TimerThread::lock);
351 
352  if (timerPeriodMs > 0)
353  {
354  TimerThread::remove (this);
355  timerPeriodMs = 0;
356  }
357 }
358 
360 {
361  if (TimerThread::instance != nullptr)
362  TimerThread::instance->callTimersSynchronously();
363 }
364 
365 struct LambdaInvoker : private Timer
366 {
367  LambdaInvoker (int milliseconds, std::function<void()> f) : function (f)
368  {
369  startTimer (milliseconds);
370  }
371 
372  void timerCallback() override
373  {
374  auto f = function;
375  delete this;
376  f();
377  }
378 
379  std::function<void()> function;
380 
381  JUCE_DECLARE_NON_COPYABLE (LambdaInvoker)
382 };
383 
384 void JUCE_CALLTYPE Timer::callAfterDelay (int milliseconds, std::function<void()> f)
385 {
386  new LambdaInvoker (milliseconds, f);
387 }
388 
389 } // namespace juce
juce::CriticalSection::ScopedUnlockType
GenericScopedUnlock< CriticalSection > ScopedUnlockType
Provides the type of scoped unlocker to use with a CriticalSection.
Definition: juce_CriticalSection.h:100
juce::Thread::notify
void notify() const
Wakes up the thread.
Definition: juce_Thread.cpp:305
juce::Thread::startThread
void startThread()
Starts the thread running.
Definition: juce_Thread.cpp:122
juce::LambdaInvoker::timerCallback
void timerCallback() override
The user-defined callback routine that actually gets called periodically.
Definition: juce_Timer.cpp:372
juce::CriticalSection::ScopedLockType
GenericScopedLock< CriticalSection > ScopedLockType
Provides the type of scoped lock to use with a CriticalSection.
Definition: juce_CriticalSection.h:97
juce::Thread
Encapsulates a thread.
Definition: juce_Thread.h:46
juce::Timer::startTimer
void startTimer(int intervalInMilliseconds) noexcept
Starts the timer and sets the length of interval required.
Definition: juce_Timer.cpp:323
juce::Timer
Makes repeated callbacks to a virtual method at a specified time interval.
Definition: juce_Timer.h:55
juce::AsyncUpdater::triggerAsyncUpdate
void triggerAsyncUpdate()
Causes the callback to be triggered at a later time.
Definition: juce_AsyncUpdater.cpp:62
juce::Time::getMillisecondCounter
static uint32 getMillisecondCounter() noexcept
Returns the number of millisecs since a fixed event (usually system startup).
Definition: juce_Time.cpp:226
juce::Timer::callAfterDelay
static void JUCE_CALLTYPE callAfterDelay(int milliseconds, std::function< void()> functionToCall)
Invokes a lambda after a given number of milliseconds.
Definition: juce_Timer.cpp:384
juce::WaitableEvent::signal
void signal() const noexcept
Wakes up any threads that are currently waiting on this object.
juce::Thread::threadShouldExit
bool threadShouldExit() const
Checks whether the thread has been told to stop running.
Definition: juce_Thread.cpp:182
juce::Thread::isThreadRunning
bool isThreadRunning() const
Returns true if the thread is currently active.
Definition: juce_Thread.cpp:160
juce::Thread::signalThreadShouldExit
void signalThreadShouldExit()
Sets a flag to tell the thread it should stop.
Definition: juce_Thread.cpp:176
juce::Timer::stopTimer
void stopTimer() noexcept
Stops the timer.
Definition: juce_Timer.cpp:348
juce::AsyncUpdater::cancelPendingUpdate
void cancelPendingUpdate() noexcept
This will stop any pending updates from happening.
Definition: juce_AsyncUpdater.cpp:74
juce::Thread::wait
bool wait(int timeOutMilliseconds) const
Suspends the execution of this thread until either the specified timeout period has elapsed,...
Definition: juce_Thread.cpp:300
juce::Timer::TimerThread::run
void run() override
Must be implemented to perform the thread's actual code.
Definition: juce_Timer.cpp:50
juce::Thread::stopThread
bool stopThread(int timeOutMilliseconds)
Attempts to stop the thread running.
Definition: juce_Thread.cpp:213
juce::AsyncUpdater
Has a callback method that is triggered asynchronously.
Definition: juce_AsyncUpdater.h:42
juce::WaitableEvent::wait
bool wait(int timeOutMilliseconds=-1) const noexcept
Suspends the calling thread until the event has been signalled.
juce::Timer::~Timer
virtual ~Timer()
Destructor.
Definition: juce_Timer.cpp:318
juce::Timer::startTimerHz
void startTimerHz(int timerFrequencyHz) noexcept
Starts the timer with an interval specified in Hertz.
Definition: juce_Timer.cpp:340
juce::GenericScopedLock
Automatically locks and unlocks a mutex object.
Definition: juce_ScopedLock.h:58
juce::Timer::TimerThread
Definition: juce_Timer.cpp:26
juce::ReferenceCountedObjectPtr
A smart-pointer class which points to a reference-counted object.
Definition: juce_ReferenceCountedObject.h:245
juce::DeletedAtShutdown
Classes derived from this will be automatically deleted when the application exits.
Definition: juce_DeletedAtShutdown.h:43
juce::Timer::callPendingTimersSynchronously
static void JUCE_CALLTYPE callPendingTimersSynchronously()
For internal use only: invokes any timers that need callbacks.
Definition: juce_Timer.cpp:359
juce::CriticalSection
A re-entrant mutex.
Definition: juce_CriticalSection.h:46
juce::Thread::Thread
Thread(const String &threadName, size_t threadStackSize=0)
Creates a thread.
Definition: juce_Thread.cpp:26
juce::LambdaInvoker
Definition: juce_Timer.cpp:365
juce::Timer::Timer
Timer() noexcept
Creates a Timer.
Definition: juce_Timer.cpp:315