27class HighResolutionTimer::Impl :
private PlatformTimerListener
33 void startTimer (
int newIntervalMs)
35 shouldCancelCallbacks.store (
true);
37 const auto shouldWaitForPendingCallbacks = [&]
39 const std::scoped_lock lock { timerMutex };
41 if (timer.getIntervalMs() > 0)
44 jassert (timer.getIntervalMs() == 0);
46 if (newIntervalMs > 0)
47 timer.startTimer (jmax (0, newIntervalMs));
49 return callbackThreadId != std::this_thread::get_id()
50 && timer.getIntervalMs() <= 0;
53 if (shouldWaitForPendingCallbacks)
54 std::scoped_lock lock { callbackMutex };
57 int getIntervalMs()
const
59 const std::scoped_lock lock { timerMutex };
60 return timer.getIntervalMs();
63 bool isTimerRunning()
const
65 return getIntervalMs() > 0;
69 void onTimerExpired() final
71 callbackThreadId.store (std::this_thread::get_id());
74 std::scoped_lock lock { callbackMutex };
92 callbackThreadId.store ({});
96 mutable std::mutex timerMutex;
97 std::mutex callbackMutex;
98 std::atomic<std::thread::id> callbackThreadId{};
99 std::atomic<bool> shouldCancelCallbacks {
false };
100 PlatformTimer timer { *
this };
102 JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Impl)
103 JUCE_DECLARE_NON_MOVEABLE (Impl)
125 impl->startTimer (0);
130 return impl->getIntervalMs();
135 return impl->isTimerRunning();
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;
241 std::atomic<bool> timerCallbackFinished {
false };
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;
271 std::atomic<bool> timerCallbackFinished {
false };
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;
296 std::atomic<int> lastCallbackCount {0};
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 };
396 std::vector<std::unique_ptr<Timer>> timers;
397 timers.reserve (maxNumTimers);
399 for (
int i = 0; i < maxNumTimers; ++i)
401 auto timer = std::make_unique<Timer> ([]{});
402 timer->startTimer (1);
404 if (! timer->isTimerRunning())
407 timers.push_back (std::move (timer));
410 expect (timers.size() >= 16);
414 class Timer final :
public HighResolutionTimer
417 explicit Timer (std::function<
void()> fn)
418 : callback (std::move (fn)) {}
420 ~Timer()
override { stopTimer(); }
422 void hiResTimerCallback()
override { callback(); }
425 std::function<void()> callback;
429static HighResolutionTimerTests highResolutionTimerTests;
virtual ~HighResolutionTimer()
virtual void hiResTimerCallback()=0
int getTimerInterval() const noexcept
bool isTimerRunning() const noexcept
void startTimer(int intervalInMilliseconds)
static void JUCE_CALLTYPE sleep(int milliseconds)