2 ==============================================================================
4 This file is part of the JUCE library - "Jules' Utility Class Extensions"
5 Copyright 2004-11 by Raw Material Software Ltd.
7 ------------------------------------------------------------------------------
9 JUCE can be redistributed and/or modified under the terms of the GNU General
10 Public License (Version 2), as published by the Free Software Foundation.
11 A copy of the license is included in the JUCE distribution, or can be found
12 online at www.gnu.org/licenses.
14 JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
15 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
16 A PARTICULAR PURPOSE. See the GNU General Public License for more details.
18 ------------------------------------------------------------------------------
20 To release a closed-source product which uses JUCE, commercial licenses are
21 available: visit www.rawmaterialsoftware.com/juce for more information.
23 ==============================================================================
26 #include "../core/juce_StandardHeader.h"
30 #include "juce_Timer.h"
31 #include "juce_AsyncUpdater.h"
32 #include "../containers/juce_SortedSet.h"
33 #include "../application/juce_Application.h"
34 #include "../utilities/juce_DeletedAtShutdown.h"
35 #include "../core/juce_Time.h"
36 #include "../threads/juce_Thread.h"
39 //==============================================================================
40 class InternalTimerThread
: private Thread
,
41 private MessageListener
,
42 private DeletedAtShutdown
,
46 typedef CriticalSection LockType
; // (mysteriously, using a SpinLock here causes problems on some XP machines..)
49 : Thread ("Juce Timer"),
56 ~InternalTimerThread() noexcept
60 jassert (instance
== this || instance
== nullptr);
67 uint32 lastTime
= Time::getMillisecondCounter();
68 Message::Ptr
messageToSend (new Message());
70 while (! threadShouldExit())
72 const uint32 now
= Time::getMillisecondCounter();
80 const int elapsed
= now
>= lastTime
? (now
- lastTime
)
81 : (std::numeric_limits
<uint32
>::max() - (lastTime
- now
));
84 const int timeUntilFirstTimer
= getTimeUntilFirstTimer (elapsed
);
86 if (timeUntilFirstTimer
<= 0)
88 /* If we managed to set the atomic boolean to true then send a message, this is needed
89 as a memory barrier so the message won't be sent before callbackNeeded is set to true,
90 but if it fails it means the message-thread changed the value from under us so at least
91 some processing is happenening and we can just loop around and try again
93 if (callbackNeeded
.compareAndSetBool (1, 0))
95 postMessage (messageToSend
);
97 /* Sometimes our message can get discarded by the OS (e.g. when running as an RTAS
98 when the app has a modal loop), so this is how long to wait before assuming the
99 message has been lost and trying again.
101 const uint32 messageDeliveryTimeout
= now
+ 2000;
103 while (callbackNeeded
.get() != 0)
107 if (threadShouldExit())
110 if (Time::getMillisecondCounter() > messageDeliveryTimeout
)
117 // don't wait for too long because running this loop also helps keep the
118 // Time::getApproximateMillisecondTimer value stay up-to-date
119 wait (jlimit (1, 50, timeUntilFirstTimer
));
126 const LockType::ScopedLockType
sl (lock
);
128 while (firstTimer
!= nullptr && firstTimer
->countdownMs
<= 0)
130 Timer
* const t
= firstTimer
;
131 t
->countdownMs
= t
->periodMs
;
136 const LockType::ScopedUnlockType
ul (lock
);
145 /* This is needed as a memory barrier to make sure all processing of current timers is done
146 before the boolean is set. This set should never fail since if it was false in the first place,
147 we wouldn't get a message (so it can't be changed from false to true from under us), and if we
148 get a message then the value is true and the other thread can only set it to true again and
149 we will get another callback to set it to false.
151 callbackNeeded
.set (0);
154 void handleMessage (const Message
&)
159 void callTimersSynchronously()
161 if (! isThreadRunning())
163 // (This is relied on by some plugins in cases where the MM has
164 // had to restart and the async callback never started)
165 cancelPendingUpdate();
166 triggerAsyncUpdate();
172 static void callAnyTimersSynchronously()
174 if (InternalTimerThread::instance
!= nullptr)
175 InternalTimerThread::instance
->callTimersSynchronously();
178 static inline void add (Timer
* const tim
) noexcept
180 if (instance
== nullptr)
181 instance
= new InternalTimerThread();
183 instance
->addTimer (tim
);
186 static inline void remove (Timer
* const tim
) noexcept
188 if (instance
!= nullptr)
189 instance
->removeTimer (tim
);
192 static inline void resetCounter (Timer
* const tim
, const int newCounter
) noexcept
194 if (instance
!= nullptr)
196 tim
->countdownMs
= newCounter
;
197 tim
->periodMs
= newCounter
;
199 if ((tim
->next
!= nullptr && tim
->next
->countdownMs
< tim
->countdownMs
)
200 || (tim
->previous
!= nullptr && tim
->previous
->countdownMs
> tim
->countdownMs
))
202 instance
->removeTimer (tim
);
203 instance
->addTimer (tim
);
210 static InternalTimerThread
* instance
;
211 static LockType lock
;
212 Timer
* volatile firstTimer
;
213 Atomic
<int> callbackNeeded
;
215 //==============================================================================
216 void addTimer (Timer
* const t
) noexcept
219 Timer
* tt
= firstTimer
;
221 while (tt
!= nullptr)
223 // trying to add a timer that's already here - shouldn't get to this point,
224 // so if you get this assertion, let me know!
230 jassert (t
->previous
== nullptr && t
->next
== nullptr);
233 Timer
* i
= firstTimer
;
235 if (i
== nullptr || i
->countdownMs
> t
->countdownMs
)
237 t
->next
= firstTimer
;
242 while (i
->next
!= nullptr && i
->next
->countdownMs
<= t
->countdownMs
)
245 jassert (i
!= nullptr);
252 if (t
->next
!= nullptr)
253 t
->next
->previous
= t
;
255 jassert ((t
->next
== nullptr || t
->next
->countdownMs
>= t
->countdownMs
)
256 && (t
->previous
== nullptr || t
->previous
->countdownMs
<= t
->countdownMs
));
261 void removeTimer (Timer
* const t
) noexcept
264 Timer
* tt
= firstTimer
;
267 while (tt
!= nullptr)
278 // trying to remove a timer that's not here - shouldn't get to this point,
279 // so if you get this assertion, let me know!
283 if (t
->previous
!= nullptr)
285 jassert (firstTimer
!= t
);
286 t
->previous
->next
= t
->next
;
290 jassert (firstTimer
== t
);
291 firstTimer
= t
->next
;
294 if (t
->next
!= nullptr)
295 t
->next
->previous
= t
->previous
;
298 t
->previous
= nullptr;
301 int getTimeUntilFirstTimer (const int numMillisecsElapsed
) const
303 const LockType::ScopedLockType
sl (lock
);
305 for (Timer
* t
= firstTimer
; t
!= nullptr; t
= t
->next
)
306 t
->countdownMs
-= numMillisecsElapsed
;
308 return firstTimer
!= nullptr ? firstTimer
->countdownMs
: 1000;
311 void handleAsyncUpdate()
316 JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (InternalTimerThread
);
319 InternalTimerThread
* InternalTimerThread::instance
= nullptr;
320 InternalTimerThread::LockType
InternalTimerThread::lock
;
322 void juce_callAnyTimersSynchronously()
324 InternalTimerThread::callAnyTimersSynchronously();
327 //==============================================================================
329 static SortedSet
<Timer
*> activeTimers
;
332 Timer::Timer() noexcept
339 const InternalTimerThread::LockType::ScopedLockType
sl (InternalTimerThread::lock
);
340 activeTimers
.add (this);
344 Timer::Timer (const Timer
&) noexcept
351 const InternalTimerThread::LockType::ScopedLockType
sl (InternalTimerThread::lock
);
352 activeTimers
.add (this);
361 activeTimers
.removeValue (this);
365 void Timer::startTimer (const int interval
) noexcept
367 const InternalTimerThread::LockType::ScopedLockType
sl (InternalTimerThread::lock
);
370 // this isn't a valid object! Your timer might be a dangling pointer or something..
371 jassert (activeTimers
.contains (this));
376 countdownMs
= interval
;
377 periodMs
= jmax (1, interval
);
378 InternalTimerThread::add (this);
382 InternalTimerThread::resetCounter (this, interval
);
386 void Timer::stopTimer() noexcept
388 const InternalTimerThread::LockType::ScopedLockType
sl (InternalTimerThread::lock
);
391 // this isn't a valid object! Your timer might be a dangling pointer or something..
392 jassert (activeTimers
.contains (this));
397 InternalTimerThread::remove (this);