26class Timer::TimerThread final :
private Thread,
30 using LockType = CriticalSection;
32 TimerThread() : Thread (
"JUCE Timer")
38 ~TimerThread()
override
40 cancelPendingUpdate();
41 signalThreadShouldExit();
42 callbackArrived.signal();
49 ReferenceCountedObjectPtr<CallTimersMessage> messageToSend (
new CallTimersMessage());
51 while (! threadShouldExit())
54 auto elapsed = (int) (now >= lastTime ? (now - lastTime)
55 : (std::numeric_limits<uint32>::max() - (lastTime - now)));
58 auto timeUntilFirstTimer = getTimeUntilFirstTimer (elapsed);
60 if (timeUntilFirstTimer <= 0)
62 if (callbackArrived.wait (0))
68 messageToSend->post();
70 if (! callbackArrived.wait (300))
75 messageToSend->post();
84 wait (jlimit (1, 100, timeUntilFirstTimer));
94 while (! timers.empty())
96 auto& first = timers.front();
98 if (first.countdownMs > 0)
101 auto* timer = first.timer;
102 first.countdownMs = timer->timerPeriodMs;
103 shuffleTimerBackInQueue (0);
110 timer->timerCallback();
119 callbackArrived.signal();
122 void callTimersSynchronously()
124 if (! isThreadRunning())
128 cancelPendingUpdate();
129 triggerAsyncUpdate();
135 void addTimer (
Timer* t)
141 jassert (std::none_of (timers.begin(), timers.end(),
142 [t] (TimerCountdown i) { return i.timer == t; }));
144 auto pos = timers.size();
146 timers.push_back ({ t, t->timerPeriodMs });
147 t->positionInQueue = pos;
148 shuffleTimerForwardInQueue (pos);
152 void removeTimer (
Timer* t)
156 auto pos = t->positionInQueue;
157 auto lastIndex = timers.size() - 1;
159 jassert (pos <= lastIndex);
160 jassert (timers[pos].timer == t);
162 for (
auto i = pos; i < lastIndex; ++i)
164 timers[i] = timers[i + 1];
165 timers[i].timer->positionInQueue = i;
171 void resetTimerCounter (
Timer* t)
noexcept
175 auto pos = t->positionInQueue;
177 jassert (pos < timers.size());
178 jassert (timers[pos].timer == t);
180 auto lastCountdown = timers[pos].countdownMs;
181 auto newCountdown = t->timerPeriodMs;
183 if (newCountdown != lastCountdown)
185 timers[pos].countdownMs = newCountdown;
187 if (newCountdown > lastCountdown)
188 shuffleTimerBackInQueue (pos);
190 shuffleTimerForwardInQueue (pos);
199 struct TimerCountdown
205 std::vector<TimerCountdown> timers;
207 WaitableEvent callbackArrived;
209 struct CallTimersMessage final :
public MessageManager::MessageBase
211 CallTimersMessage() =
default;
213 void messageCallback()
override
216 (*instance)->callTimers();
221 void shuffleTimerBackInQueue (
size_t pos)
223 auto numTimers = timers.size();
225 if (pos < numTimers - 1)
227 auto t = timers[pos];
233 if (next == numTimers || timers[next].countdownMs >= t.countdownMs)
236 timers[pos] = timers[next];
237 timers[pos].timer->positionInQueue = pos;
243 t.timer->positionInQueue = pos;
247 void shuffleTimerForwardInQueue (
size_t pos)
251 auto t = timers[pos];
255 auto& prev = timers[(size_t) pos - 1];
257 if (prev.countdownMs <= t.countdownMs)
261 timers[pos].timer->positionInQueue = pos;
267 t.timer->positionInQueue = pos;
271 int getTimeUntilFirstTimer (
int numMillisecsElapsed)
278 for (
auto& t : timers)
279 t.countdownMs -= numMillisecsElapsed;
281 return timers.front().countdownMs;
284 void handleAsyncUpdate()
override
289 JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TimerThread)
313 JUCE_ASSERT_MESSAGE_MANAGER_EXISTS
315 bool wasStopped = (timerPeriodMs == 0);
316 timerPeriodMs = jmax (1, interval);
319 timerThread->addTimer (
this);
321 timerThread->resetTimerCounter (
this);
326 if (timerFrequencyHz > 0)
327 startTimer (1000 / timerFrequencyHz);
334 if (timerPeriodMs > 0)
336 timerThread->removeTimer (
this);
344 (*instance)->callTimersSynchronously();
347struct LambdaInvoker final :
private Timer,
350 LambdaInvoker (
int milliseconds, std::function<
void()> f)
351 : function (std::move (f))
353 startTimer (milliseconds);
356 ~LambdaInvoker() final
361 void timerCallback() final
363 NullCheckedInvocation::invoke (function);
367 std::function<void()> function;
369 JUCE_DECLARE_NON_COPYABLE (LambdaInvoker)
374 new LambdaInvoker (milliseconds, std::move (f));
GenericScopedLock< CriticalSection > ScopedLockType
GenericScopedUnlock< CriticalSection > ScopedUnlockType
static MessageManager * getInstanceWithoutCreating() noexcept
static std::optional< SharedResourcePointer > getSharedObjectWithoutCreating()
static uint32 getMillisecondCounter() noexcept
void stopTimer() noexcept
void startTimerHz(int timerFrequencyHz) noexcept
bool isTimerRunning() const noexcept
static void JUCE_CALLTYPE callPendingTimersSynchronously()
static void JUCE_CALLTYPE callAfterDelay(int milliseconds, std::function< void()> functionToCall)
void startTimer(int intervalInMilliseconds) noexcept