OpenShot Audio Library | OpenShotAudio: juce_Timer.cpp Source File

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);
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
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 {
215 if (auto instance = SharedResourcePointer<TimerThread>::getSharedObjectWithoutCreating())
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 {}
294 Timer::Timer (const Timer&) noexcept {}
295
296Timer::~Timer()
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()
303 || MessageManager::getInstanceWithoutCreating() == nullptr
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
341 void JUCE_CALLTYPE Timer::callPendingTimersSynchronously()
342{
343 if (auto instance = SharedResourcePointer<TimerThread>::getSharedObjectWithoutCreating())
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
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
juce::AsyncUpdater
Definition juce_AsyncUpdater.h:39
juce::AsyncUpdater::triggerAsyncUpdate
void triggerAsyncUpdate()
Definition juce_AsyncUpdater.cpp:62
juce::AsyncUpdater::cancelPendingUpdate
void cancelPendingUpdate() noexcept
Definition juce_AsyncUpdater.cpp:74
juce::CriticalSection::ScopedLockType
GenericScopedLock< CriticalSection > ScopedLockType
Definition juce_CriticalSection.h:93
juce::CriticalSection::ScopedUnlockType
GenericScopedUnlock< CriticalSection > ScopedUnlockType
Definition juce_CriticalSection.h:96
juce::DeletedAtShutdown
Definition juce_DeletedAtShutdown.h:40
juce::MessageManager::getInstanceWithoutCreating
static MessageManager * getInstanceWithoutCreating() noexcept
Definition juce_MessageManager.cpp:58
juce::SharedResourcePointer::getSharedObjectWithoutCreating
static std::optional< SharedResourcePointer > getSharedObjectWithoutCreating()
Definition juce_SharedResourcePointer.h:125
juce::Thread
Definition juce_Thread.h:43
juce::Thread::wait
bool wait(double timeOutMilliseconds) const
Definition juce_Thread.cpp:288
juce::Thread::Thread
Thread(const String &threadName, size_t threadStackSize=osDefaultStackSize)
Definition juce_Thread.cpp:26
juce::Thread::startThread
bool startThread()
Definition juce_Thread.cpp:145
juce::Thread::threadShouldExit
bool threadShouldExit() const
Definition juce_Thread.cpp:202
juce::Thread::stopThread
bool stopThread(int timeOutMilliseconds)
Definition juce_Thread.cpp:233
juce::Thread::notify
void notify() const
Definition juce_Thread.cpp:293
juce::Thread::signalThreadShouldExit
void signalThreadShouldExit()
Definition juce_Thread.cpp:196
juce::Thread::isThreadRunning
bool isThreadRunning() const
Definition juce_Thread.cpp:180
juce::Time::getMillisecondCounter
static uint32 getMillisecondCounter() noexcept
Definition juce_Time.cpp:241
juce::Timer
Definition juce_Timer.h:52
juce::Timer::~Timer
virtual ~Timer()
Definition juce_Timer.cpp:296
juce::Timer::stopTimer
void stopTimer() noexcept
Definition juce_Timer.cpp:332
juce::Timer::Timer
Timer() noexcept
Definition juce_Timer.cpp:293
juce::Timer::startTimerHz
void startTimerHz(int timerFrequencyHz) noexcept
Definition juce_Timer.cpp:324
juce::Timer::isTimerRunning
bool isTimerRunning() const noexcept
Definition juce_Timer.h:111
juce::Timer::callPendingTimersSynchronously
static void JUCE_CALLTYPE callPendingTimersSynchronously()
Definition juce_Timer.cpp:341
juce::Timer::callAfterDelay
static void JUCE_CALLTYPE callAfterDelay(int milliseconds, std::function< void()> functionToCall)
Definition juce_Timer.cpp:372
juce::Timer::startTimer
void startTimer(int intervalInMilliseconds) noexcept
Definition juce_Timer.cpp:309
juce::WaitableEvent::signal
void signal() const
Definition juce_WaitableEvent.cpp:57