33 void startTimer (
int newIntervalMs)
35 shouldCancelCallbacks.
store (
true);
37 const auto shouldWaitForPendingCallbacks = [&]
41 if (timer.getIntervalMs() > 0)
44 jassert (timer.getIntervalMs() == 0);
46 if (newIntervalMs > 0)
47 timer.startTimer (
jmax (0, newIntervalMs));
50 && timer.getIntervalMs() <= 0;
53 if (shouldWaitForPendingCallbacks)
57 int getIntervalMs()
const
60 return timer.getIntervalMs();
63 bool isTimerRunning()
const
65 return getIntervalMs() > 0;
69 void onTimerExpired()
final
92 callbackThreadId.
store ({});
108 : impl (
std::make_unique<
Impl> (*this)) {}
120 impl->startTimer (newIntervalMs);
125 impl->startTimer (0);
130 return impl->getIntervalMs();
135 return impl->isTimerRunning();
141class HighResolutionTimerTests final :
public UnitTest
144 HighResolutionTimerTests()
145 :
UnitTest (
"HighResolutionTimer", UnitTestCategories::threads) {}
147 void runTest()
override
149 constexpr int maximumTimeoutMs {30'000};
151 beginTest (
"Start/stop a timer");
153 WaitableEvent timerFiredOnce;
154 WaitableEvent timerFiredTwice;
156 Timer timer {[&, callbackCount = 0]()
mutable
158 switch (++callbackCount)
160 case 1: timerFiredOnce.signal();
return;
161 case 2: timerFiredTwice.signal();
return;
166 expect (! timer.isTimerRunning());
167 expect (timer.getTimerInterval() == 0);
169 timer.startTimer (1);
170 expect (timer.isTimerRunning());
171 expect (timer.getTimerInterval() == 1);
172 expect (timerFiredOnce.wait (maximumTimeoutMs));
173 expect (timerFiredTwice.wait (maximumTimeoutMs));
176 expect (! timer.isTimerRunning());
177 expect (timer.getTimerInterval() == 0);
180 beginTest (
"Stop a timer from the timer callback");
182 WaitableEvent stoppedTimer;
184 auto timerCallback = [&] (Timer& timer)
186 expect (timer.isTimerRunning());
188 expect (! timer.isTimerRunning());
189 stoppedTimer.signal();
192 Timer timer {[&]{ timerCallback (timer); }};
193 timer.startTimer (1);
194 expect (stoppedTimer.wait (maximumTimeoutMs));
197 beginTest (
"Restart a timer from the timer callback");
199 WaitableEvent restartTimer;
200 WaitableEvent timerRestarted;
201 WaitableEvent timerFiredAfterRestart;
203 Timer timer {[&, callbackCount = 0]()
mutable
205 switch (++callbackCount)
208 expect (restartTimer.wait (maximumTimeoutMs));
209 expect (timer.getTimerInterval() == 1);
211 timer.startTimer (2);
212 expect (timer.getTimerInterval() == 2);
213 timerRestarted.signal();
217 expect (timer.getTimerInterval() == 2);
218 timerFiredAfterRestart.signal();
226 timer.startTimer (1);
227 expect (timer.getTimerInterval() == 1);
229 restartTimer.signal();
230 expect (timerRestarted.wait (maximumTimeoutMs));
231 expect (timer.getTimerInterval() == 2);
232 expect (timerFiredAfterRestart.wait (maximumTimeoutMs));
237 beginTest (
"Calling stopTimer on a timer, waits for any timer callbacks to finish");
239 WaitableEvent timerCallbackStarted;
240 WaitableEvent stoppingTimer;
243 Timer timer {[&, callbackCount = 0]()
mutable
245 switch (++callbackCount)
248 timerCallbackStarted.signal();
249 expect (stoppingTimer.wait (maximumTimeoutMs));
251 timerCallbackFinished =
true;
259 timer.startTimer (1);
260 expect (timerCallbackStarted.wait (maximumTimeoutMs));
262 stoppingTimer.signal();
264 expect (timerCallbackFinished);
267 beginTest (
"Calling stopTimer on a timer, waits for any timer callbacks to finish, even if the timer callback calls stopTimer first");
269 WaitableEvent stoppedFromInsideTimerCallback;
270 WaitableEvent stoppingFromOutsideTimerCallback;
276 stoppedFromInsideTimerCallback.signal();
277 expect (stoppingFromOutsideTimerCallback.wait (maximumTimeoutMs));
279 timerCallbackFinished =
true;
283 timer.startTimer (1);
284 expect (stoppedFromInsideTimerCallback.wait (maximumTimeoutMs));
286 stoppingFromOutsideTimerCallback.signal();
288 expect (timerCallbackFinished);
291 beginTest (
"Adjusting a timer period from outside the timer callback doesn't cause data races");
293 WaitableEvent timerCallbackStarted;
294 WaitableEvent timerRestarted;
295 WaitableEvent timerFiredAfterRestart;
298 Timer timer {[&, callbackCount = 0]()
mutable
300 switch (++callbackCount)
303 expect (timer.getTimerInterval() == 1);
304 timerCallbackStarted.signal();
306 lastCallbackCount = 1;
310 expect (timerRestarted.wait (maximumTimeoutMs));
311 expect (timer.getTimerInterval() == 2);
312 lastCallbackCount = 2;
313 timerFiredAfterRestart.signal();
321 timer.startTimer (1);
322 expect (timerCallbackStarted.wait (maximumTimeoutMs));
324 timer.startTimer (2);
325 timerRestarted.signal();
327 expect (timerFiredAfterRestart.wait (maximumTimeoutMs));
328 expect (lastCallbackCount == 2);
331 expect (lastCallbackCount == 2);
334 beginTest (
"A timer can be restarted externally, after being stopped internally");
336 WaitableEvent timerStopped;
337 WaitableEvent timerFiredAfterRestart;
339 Timer timer {[&, callbackCount = 0]()
mutable
341 switch (++callbackCount)
345 timerStopped.signal();
349 timerFiredAfterRestart.signal();
357 expect (! timer.isTimerRunning());
358 timer.startTimer (1);
359 expect (timer.isTimerRunning());
361 expect (timerStopped.wait (maximumTimeoutMs));
362 expect (! timer.isTimerRunning());
364 timer.startTimer (1);
365 expect (timer.isTimerRunning());
366 expect (timerFiredAfterRestart.wait (maximumTimeoutMs));
369 beginTest (
"Calls to `startTimer` and `getTimerInterval` succeed while a callback is blocked");
371 WaitableEvent timerBlocked;
372 WaitableEvent unblockTimer;
376 timerBlocked.signal();
381 timer.startTimer (1);
384 expect (timer.getTimerInterval() == 1);
385 timer.startTimer (2);
386 expect (timer.getTimerInterval() == 2);
388 unblockTimer.signal();
392 beginTest (
"Stress test");
394 constexpr auto maxNumTimers { 100 };
399 for (
int i = 0; i < maxNumTimers; ++i)
402 timer->startTimer (1);
404 if (! timer->isTimerRunning())
410 expect (timers.
size() >= 16);
414 class Timer final :
public HighResolutionTimer
420 ~Timer()
override { stopTimer(); }
422 void hiResTimerCallback()
override { callback(); }
429static HighResolutionTimerTests highResolutionTimerTests;
A high-resolution periodic timer.
virtual ~HighResolutionTimer()
Destructor.
virtual void hiResTimerCallback()=0
The user-defined callback routine that actually gets called periodically.
int getTimerInterval() const noexcept
Returns the timer's interval.
HighResolutionTimer()
Creates a HighResolutionTimer.
bool isTimerRunning() const noexcept
Checks if the timer has been started.
void stopTimer()
Stops the timer.
void startTimer(int intervalInMilliseconds)
Starts the timer and sets the length of interval required.
This is a base class for classes that perform a unit test.
constexpr Type jmax(Type a, Type b)
Returns the larger of two values.