JUCE-7.0.12-0-g4f43011b96 JUCE-7.0.12-0-g4f43011b96
JUCE — C++ application framework with suport for VST, VST3, LV2 audio plug-ins

« « « Anklang Documentation
Loading...
Searching...
No Matches
juce_Timer.cpp
Go to the documentation of this file.
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
27{
28public:
29 using LockType = CriticalSection; // (mysteriously, using a SpinLock here causes problems on some XP machines..)
30
31 TimerThread() : Thread ("JUCE Timer")
32 {
33 timers.reserve (32);
34 }
35
36 ~TimerThread() override
37 {
39 callbackArrived.signal();
40 stopThread (-1);
41 }
42
43 void run() override
44 {
45 auto lastTime = Time::getMillisecondCounter();
47
48 while (! threadShouldExit())
49 {
50 auto now = Time::getMillisecondCounter();
51 auto elapsed = (int) (now >= lastTime ? (now - lastTime)
52 : (std::numeric_limits<uint32>::max() - (lastTime - now)));
53 lastTime = now;
54
55 auto timeUntilFirstTimer = getTimeUntilFirstTimer (elapsed);
56
57 if (timeUntilFirstTimer <= 0)
58 {
59 if (callbackArrived.wait (0))
60 {
61 // already a message in flight - do nothing..
62 }
63 else
64 {
65 messageToSend->post();
66
67 if (! callbackArrived.wait (300))
68 {
69 // Sometimes our message can get discarded by the OS (e.g. when running as an RTAS
70 // when the app has a modal loop), so this is how long to wait before assuming the
71 // message has been lost and trying again.
72 messageToSend->post();
73 }
74
75 continue;
76 }
77 }
78
79 // don't wait for too long because running this loop also helps keep the
80 // Time::getApproximateMillisecondTimer value stay up-to-date
82 }
83 }
84
85 void callTimers()
86 {
87 auto timeout = Time::getMillisecondCounter() + 100;
88
89 const LockType::ScopedLockType sl (lock);
91 while (! timers.empty())
92 {
93 auto& first = timers.front();
94
95 if (first.countdownMs > 0)
96 break;
97
98 auto* timer = first.timer;
99 first.countdownMs = timer->timerPeriodMs;
100 shuffleTimerBackInQueue (0);
101 notify();
102
103 const LockType::ScopedUnlockType ul (lock);
104
106 {
107 timer->timerCallback();
108 }
110
111 // avoid getting stuck in a loop if a timer callback repeatedly takes too long
112 if (Time::getMillisecondCounter() > timeout)
113 break;
114 }
115
116 callbackArrived.signal();
117 }
118
119 void callTimersSynchronously()
120 {
121 callTimers();
122 }
123
124 void addTimer (Timer* t)
125 {
126 const LockType::ScopedLockType sl (lock);
127
128 if (! isThreadRunning())
130
131 // Trying to add a timer that's already here - shouldn't get to this point,
132 // so if you get this assertion, let me know!
133 jassert (std::none_of (timers.begin(), timers.end(),
134 [t] (TimerCountdown i) { return i.timer == t; }));
135
136 auto pos = timers.size();
137
138 timers.push_back ({ t, t->timerPeriodMs });
139 t->positionInQueue = pos;
140 shuffleTimerForwardInQueue (pos);
141 notify();
142 }
143
144 void removeTimer (Timer* t)
145 {
146 const LockType::ScopedLockType sl (lock);
147
148 auto pos = t->positionInQueue;
149 auto lastIndex = timers.size() - 1;
150
151 jassert (pos <= lastIndex);
152 jassert (timers[pos].timer == t);
153
154 for (auto i = pos; i < lastIndex; ++i)
155 {
156 timers[i] = timers[i + 1];
157 timers[i].timer->positionInQueue = i;
158 }
159
160 timers.pop_back();
161 }
162
163 void resetTimerCounter (Timer* t) noexcept
164 {
165 const LockType::ScopedLockType sl (lock);
166
167 auto pos = t->positionInQueue;
168
169 jassert (pos < timers.size());
170 jassert (timers[pos].timer == t);
171
172 auto lastCountdown = timers[pos].countdownMs;
173 auto newCountdown = t->timerPeriodMs;
174
176 {
177 timers[pos].countdownMs = newCountdown;
178
180 shuffleTimerBackInQueue (pos);
181 else
182 shuffleTimerForwardInQueue (pos);
183
184 notify();
185 }
186 }
187
188private:
189 LockType lock;
190
191 struct TimerCountdown
192 {
193 Timer* timer;
194 int countdownMs;
195 };
196
198
199 WaitableEvent callbackArrived;
200
201 struct CallTimersMessage final : public MessageManager::MessageBase
202 {
203 CallTimersMessage() = default;
204
205 void messageCallback() override
206 {
208 (*instance)->callTimers();
209 }
210 };
211
212 //==============================================================================
213 void shuffleTimerBackInQueue (size_t pos)
214 {
215 auto numTimers = timers.size();
216
217 if (pos < numTimers - 1)
218 {
219 auto t = timers[pos];
220
221 for (;;)
222 {
223 auto next = pos + 1;
224
225 if (next == numTimers || timers[next].countdownMs >= t.countdownMs)
226 break;
227
228 timers[pos] = timers[next];
229 timers[pos].timer->positionInQueue = pos;
230
231 ++pos;
232 }
233
234 timers[pos] = t;
235 t.timer->positionInQueue = pos;
236 }
237 }
238
239 void shuffleTimerForwardInQueue (size_t pos)
240 {
241 if (pos > 0)
242 {
243 auto t = timers[pos];
244
245 while (pos > 0)
246 {
247 auto& prev = timers[(size_t) pos - 1];
248
249 if (prev.countdownMs <= t.countdownMs)
250 break;
251
252 timers[pos] = prev;
253 timers[pos].timer->positionInQueue = pos;
254
255 --pos;
256 }
257
258 timers[pos] = t;
259 t.timer->positionInQueue = pos;
260 }
261 }
262
263 int getTimeUntilFirstTimer (int numMillisecsElapsed)
264 {
265 const LockType::ScopedLockType sl (lock);
266
267 if (timers.empty())
268 return 1000;
269
270 for (auto& t : timers)
271 t.countdownMs -= numMillisecsElapsed;
272
273 return timers.front().countdownMs;
274 }
275
277};
278
279//==============================================================================
280Timer::Timer() noexcept {}
281Timer::Timer (const Timer&) noexcept {}
282
284{
285 // If you're destroying a timer on a background thread, make sure the timer has
286 // been stopped before execution reaches this point. A simple way to achieve this
287 // is to add a call to `stopTimer()` to the destructor of your class which inherits
288 // from Timer.
291 || MessageManager::getInstanceWithoutCreating()->currentThreadHasLockedMessageManager());
292
293 stopTimer();
294}
295
296void Timer::startTimer (int interval) noexcept
297{
298 // If you're calling this before (or after) the MessageManager is
299 // running, then you're not going to get any timer callbacks!
301
302 bool wasStopped = (timerPeriodMs == 0);
303 timerPeriodMs = jmax (1, interval);
304
305 if (wasStopped)
306 timerThread->addTimer (this);
307 else
308 timerThread->resetTimerCounter (this);
309}
310
312{
313 if (timerFrequencyHz > 0)
314 startTimer (1000 / timerFrequencyHz);
315 else
316 stopTimer();
317}
318
319void Timer::stopTimer() noexcept
320{
321 if (timerPeriodMs > 0)
322 {
323 timerThread->removeTimer (this);
324 timerPeriodMs = 0;
325 }
326}
327
329{
331 (*instance)->callTimersSynchronously();
332}
333
334struct LambdaInvoker final : private Timer,
335 private DeletedAtShutdown
336{
337 LambdaInvoker (int milliseconds, std::function<void()> f)
338 : function (std::move (f))
339 {
340 startTimer (milliseconds);
341 }
342
343 ~LambdaInvoker() final
344 {
345 stopTimer();
346 }
347
349 {
350 NullCheckedInvocation::invoke (function);
351 delete this;
352 }
353
354 std::function<void()> function;
355
357};
358
359void JUCE_CALLTYPE Timer::callAfterDelay (int milliseconds, std::function<void()> f)
360{
361 new LambdaInvoker (milliseconds, std::move (f));
362}
363
364} // namespace juce
T none_of(T... args)
T begin(T... args)
GenericScopedLock< CriticalSection > ScopedLockType
Provides the type of scoped lock to use with a CriticalSection.
GenericScopedUnlock< CriticalSection > ScopedUnlockType
Provides the type of scoped unlocker to use with a CriticalSection.
Classes derived from this will be automatically deleted when the application exits.
static MessageManager * getInstanceWithoutCreating() noexcept
Returns the global instance of the MessageManager, or nullptr if it doesn't exist.
A smart-pointer class which points to a reference-counted object.
A smart-pointer that automatically creates and manages the lifetime of a shared static instance of a ...
static std::optional< SharedResourcePointer > getSharedObjectWithoutCreating()
Returns the SharedResourcePointer if one already exists, or a null optional otherwise.
Encapsulates a thread.
Definition juce_Thread.h:43
bool startThread()
Attempts to start a new thread with default ('Priority::normal') priority.
bool threadShouldExit() const
Checks whether the thread has been told to stop running.
bool stopThread(int timeOutMilliseconds)
Attempts to stop the thread running.
void notify() const
Wakes up the thread.
void signalThreadShouldExit()
Sets a flag to tell the thread it should stop.
@ high
Makes use of performance cores and higher clocks.
bool isThreadRunning() const
Returns true if the thread is currently active.
static uint32 getMillisecondCounter() noexcept
Returns the number of millisecs since a fixed event (usually system startup).
void run() override
Must be implemented to perform the thread's actual code.
Makes repeated callbacks to a virtual method at a specified time interval.
Definition juce_Timer.h:52
virtual ~Timer()
Destructor.
void stopTimer() noexcept
Stops the timer.
Timer() noexcept
Creates a Timer.
void startTimerHz(int timerFrequencyHz) noexcept
Starts the timer with an interval specified in Hertz.
bool isTimerRunning() const noexcept
Returns true if the timer is currently running.
Definition juce_Timer.h:111
static void JUCE_CALLTYPE callPendingTimersSynchronously()
For internal use only: invokes any timers that need callbacks.
static void JUCE_CALLTYPE callAfterDelay(int milliseconds, std::function< void()> functionToCall)
Invokes a lambda after a given number of milliseconds.
void startTimer(int intervalInMilliseconds) noexcept
Starts the timer and sets the length of interval required.
void signal() const
Wakes up any threads that are currently waiting on this object.
bool wait(double timeOutMilliseconds=-1.0) const
Suspends the calling thread until the event has been signalled.
T empty(T... args)
T end(T... args)
T front(T... args)
#define JUCE_TRY
The JUCE_TRY/JUCE_CATCH_EXCEPTION wrappers can be used to pass any uncaught exceptions to the JUCEApp...
#define JUCE_CATCH_EXCEPTION
The JUCE_TRY/JUCE_CATCH_EXCEPTION wrappers can be used to pass any uncaught exceptions to the JUCEApp...
#define JUCE_ASSERT_MESSAGE_MANAGER_EXISTS
This macro is used to catch unsafe use of functions which expect to not be called outside the lifetim...
#define jassert(expression)
Platform-independent assertion macro.
#define JUCE_DECLARE_NON_COPYABLE(className)
This is a shorthand macro for deleting a class's copy constructor and copy assignment operator.
#define JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(className)
This is a shorthand way of writing both a JUCE_DECLARE_NON_COPYABLE and JUCE_LEAK_DETECTOR macro for ...
#define JUCE_CALLTYPE
This macro defines the C calling convention used as the standard for JUCE calls.
typedef int
T max(T... args)
JUCE Namespace.
constexpr Type jmax(Type a, Type b)
Returns the larger of two values.
Type jlimit(Type lowerLimit, Type upperLimit, Type valueToConstrain) noexcept
Constrains a value to keep it within a given range.
Type unalignedPointerCast(void *ptr) noexcept
Casts a pointer to another type via void*, which suppresses the cast-align warning which sometimes ar...
Definition juce_Memory.h:88
T next(T... args)
T pop_back(T... args)
T prev(T... args)
T push_back(T... args)
T reserve(T... args)
T size(T... args)
void timerCallback() final
The user-defined callback routine that actually gets called periodically.
typedef size_t
wait