OpenShot Audio Library | OpenShotAudio 0.3.2
Loading...
Searching...
No Matches
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
23namespace juce
24{
25
26class Timer::TimerThread : private Thread,
27 private DeletedAtShutdown,
28 private AsyncUpdater
29{
30public:
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
162private:
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
311Timer::TimerThread* Timer::TimerThread::instance = nullptr;
312Timer::TimerThread::LockType Timer::TimerThread::lock;
313
314//==============================================================================
316Timer::Timer (const Timer&) noexcept {}
317
319{
320 stopTimer();
321}
322
323void 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
341{
342 if (timerFrequencyHz > 0)
343 startTimer (1000 / timerFrequencyHz);
344 else
345 stopTimer();
346}
347
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
365struct 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
384void JUCE_CALLTYPE Timer::callAfterDelay (int milliseconds, std::function<void()> f)
385{
386 new LambdaInvoker (milliseconds, f);
387}
388
389} // namespace juce
void cancelPendingUpdate() noexcept
GenericScopedLock< CriticalSection > ScopedLockType
GenericScopedUnlock< CriticalSection > ScopedUnlockType
bool wait(int timeOutMilliseconds) const
bool threadShouldExit() const
bool stopThread(int timeOutMilliseconds)
void notify() const
void signalThreadShouldExit()
bool isThreadRunning() const
static uint32 getMillisecondCounter() noexcept
virtual ~Timer()
void stopTimer() noexcept
Timer() noexcept
void startTimerHz(int timerFrequencyHz) noexcept
static void JUCE_CALLTYPE callPendingTimersSynchronously()
static void JUCE_CALLTYPE callAfterDelay(int milliseconds, std::function< void()> functionToCall)
void startTimer(int intervalInMilliseconds) noexcept