OpenShot Audio Library | OpenShotAudio 0.4.0
Loading...
Searching...
No Matches
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
23namespace juce
24{
25
26class Timer::TimerThread final : private Thread,
27 private AsyncUpdater
28{
29public:
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);
36 }
37
38 ~TimerThread() override
39 {
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);
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)
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
196private:
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//==============================================================================
293Timer::Timer() noexcept {}
294Timer::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
309void 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
324void Timer::startTimerHz (int timerFrequencyHz) noexcept
325{
326 if (timerFrequencyHz > 0)
327 startTimer (1000 / timerFrequencyHz);
328 else
329 stopTimer();
330}
331
332void 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
347struct 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
372void JUCE_CALLTYPE Timer::callAfterDelay (int milliseconds, std::function<void()> f)
373{
374 new LambdaInvoker (milliseconds, std::move (f));
375}
376
377} // namespace juce
virtual void handleAsyncUpdate()=0
void cancelPendingUpdate() noexcept
GenericScopedLock< CriticalSection > ScopedLockType
GenericScopedUnlock< CriticalSection > ScopedUnlockType
static MessageManager * getInstanceWithoutCreating() noexcept
static std::optional< SharedResourcePointer > getSharedObjectWithoutCreating()
bool wait(double timeOutMilliseconds) const
Thread(const String &threadName, size_t threadStackSize=osDefaultStackSize)
virtual void run()=0
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
bool isTimerRunning() const noexcept
Definition juce_Timer.h:111
static void JUCE_CALLTYPE callPendingTimersSynchronously()
static void JUCE_CALLTYPE callAfterDelay(int milliseconds, std::function< void()> functionToCall)
void startTimer(int intervalInMilliseconds) noexcept
virtual void timerCallback()=0