Add remaining files
[juce-lv2.git] / juce / source / src / events / juce_Timer.cpp
blob968261a03d305543c9da3716f751a35d47c7b2fd
1 /*
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"
28 BEGIN_JUCE_NAMESPACE
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,
43 private AsyncUpdater
45 public:
46 typedef CriticalSection LockType; // (mysteriously, using a SpinLock here causes problems on some XP machines..)
48 InternalTimerThread()
49 : Thread ("Juce Timer"),
50 firstTimer (nullptr),
51 callbackNeeded (0)
53 triggerAsyncUpdate();
56 ~InternalTimerThread() noexcept
58 stopThread (4000);
60 jassert (instance == this || instance == nullptr);
61 if (instance == this)
62 instance = nullptr;
65 void run()
67 uint32 lastTime = Time::getMillisecondCounter();
68 Message::Ptr messageToSend (new Message());
70 while (! threadShouldExit())
72 const uint32 now = Time::getMillisecondCounter();
74 if (now == lastTime)
76 wait (1);
77 continue;
80 const int elapsed = now >= lastTime ? (now - lastTime)
81 : (std::numeric_limits<uint32>::max() - (lastTime - now));
82 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)
105 wait (4);
107 if (threadShouldExit())
108 return;
110 if (Time::getMillisecondCounter() > messageDeliveryTimeout)
111 break;
115 else
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));
124 void callTimers()
126 const LockType::ScopedLockType sl (lock);
128 while (firstTimer != nullptr && firstTimer->countdownMs <= 0)
130 Timer* const t = firstTimer;
131 t->countdownMs = t->periodMs;
133 removeTimer (t);
134 addTimer (t);
136 const LockType::ScopedUnlockType ul (lock);
138 JUCE_TRY
140 t->timerCallback();
142 JUCE_CATCH_EXCEPTION
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&)
156 callTimers();
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();
169 callTimers();
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);
208 private:
209 friend class Timer;
210 static InternalTimerThread* instance;
211 static LockType lock;
212 Timer* volatile firstTimer;
213 Atomic <int> callbackNeeded;
215 //==============================================================================
216 void addTimer (Timer* const t) noexcept
218 #if JUCE_DEBUG
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!
225 jassert (tt != t);
227 tt = tt->next;
230 jassert (t->previous == nullptr && t->next == nullptr);
231 #endif
233 Timer* i = firstTimer;
235 if (i == nullptr || i->countdownMs > t->countdownMs)
237 t->next = firstTimer;
238 firstTimer = t;
240 else
242 while (i->next != nullptr && i->next->countdownMs <= t->countdownMs)
243 i = i->next;
245 jassert (i != nullptr);
247 t->next = i->next;
248 t->previous = i;
249 i->next = t;
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));
258 notify();
261 void removeTimer (Timer* const t) noexcept
263 #if JUCE_DEBUG
264 Timer* tt = firstTimer;
265 bool found = false;
267 while (tt != nullptr)
269 if (tt == t)
271 found = true;
272 break;
275 tt = tt->next;
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!
280 jassert (found);
281 #endif
283 if (t->previous != nullptr)
285 jassert (firstTimer != t);
286 t->previous->next = t->next;
288 else
290 jassert (firstTimer == t);
291 firstTimer = t->next;
294 if (t->next != nullptr)
295 t->next->previous = t->previous;
297 t->next = nullptr;
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()
313 startThread (7);
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 //==============================================================================
328 #if JUCE_DEBUG
329 static SortedSet <Timer*> activeTimers;
330 #endif
332 Timer::Timer() noexcept
333 : countdownMs (0),
334 periodMs (0),
335 previous (nullptr),
336 next (nullptr)
338 #if JUCE_DEBUG
339 const InternalTimerThread::LockType::ScopedLockType sl (InternalTimerThread::lock);
340 activeTimers.add (this);
341 #endif
344 Timer::Timer (const Timer&) noexcept
345 : countdownMs (0),
346 periodMs (0),
347 previous (nullptr),
348 next (nullptr)
350 #if JUCE_DEBUG
351 const InternalTimerThread::LockType::ScopedLockType sl (InternalTimerThread::lock);
352 activeTimers.add (this);
353 #endif
356 Timer::~Timer()
358 stopTimer();
360 #if JUCE_DEBUG
361 activeTimers.removeValue (this);
362 #endif
365 void Timer::startTimer (const int interval) noexcept
367 const InternalTimerThread::LockType::ScopedLockType sl (InternalTimerThread::lock);
369 #if JUCE_DEBUG
370 // this isn't a valid object! Your timer might be a dangling pointer or something..
371 jassert (activeTimers.contains (this));
372 #endif
374 if (periodMs == 0)
376 countdownMs = interval;
377 periodMs = jmax (1, interval);
378 InternalTimerThread::add (this);
380 else
382 InternalTimerThread::resetCounter (this, interval);
386 void Timer::stopTimer() noexcept
388 const InternalTimerThread::LockType::ScopedLockType sl (InternalTimerThread::lock);
390 #if JUCE_DEBUG
391 // this isn't a valid object! Your timer might be a dangling pointer or something..
392 jassert (activeTimers.contains (this));
393 #endif
395 if (periodMs > 0)
397 InternalTimerThread::remove (this);
398 periodMs = 0;
402 END_JUCE_NAMESPACE