Florian Walther (live streaming)
01/06/2022, 12:05 PMcountDownInterval
is 0 I want to skip the loop and just delay until the time is over. The problem is that delay is inexact (that's why I do the comparison with the SystemClock time). How could I achieve a (somewhat) exact delay?Florian Walther (live streaming)
01/06/2022, 12:05 PMfun startTimer(
durationMillis: Long,
countDownInterval: Long,
onTick: (millisUntilFinished: Long) -> Unit,
onFinish: () -> Unit,
) {
millisUntilFinished = durationMillis
timerJob = scope.launch {
val startTime = timeSource.elapsedRealTime
val targetTime = startTime + durationMillis
while (true) {
if (timeSource.elapsedRealTime < targetTime) {
delay(countDownInterval)
millisUntilFinished = targetTime - timeSource.elapsedRealTime
onTick(millisUntilFinished)
} else {
onFinish()
break
}
}
}
}
Florian Walther (live streaming)
01/06/2022, 12:09 PMTimer.schedule
exact?Stylianos Gakis
01/06/2022, 12:14 PMFlorian Walther (live streaming)
01/06/2022, 12:17 PMJoffrey
01/06/2022, 12:22 PMdelay()
is inexact? Do you mean that it's sensitive to coroutine scheduling? This should not be too much of a problem if the other coroutines running on the same thread pool are well behaved and don't block the threads.
If you want to hold on to your thread for precision, you could also delay for "almost" durationMillis
and then perform a fast loop for the remaining small time (of your choosing).
That said, if you have badly behaved coroutines holding the threads for long, this won't save you. You could also use a dedicated pool (maybe single threaded) for running your timers.Florian Walther (live streaming)
01/06/2022, 12:25 PMFlorian Walther (live streaming)
01/06/2022, 12:25 PMJoffrey
01/06/2022, 12:26 PMstartTime
within the launch, which is probably not what the caller of the method wants. They probably want to start the timer according to "now", not according to when you decide to schedule the coroutineFlorian Walther (live streaming)
01/06/2022, 12:27 PMJoffrey
01/06/2022, 12:28 PMI think delays can run for much longer by default if you are running something elseThat depends on the workload you're dispatching on the same threads as your timers. If you have blocking code or long-running computations without suspensions, that may be true. If you only schedule your timers there, it should be accurate enough for all intents and purposes IMO. If your
scope
has a dedicated thread pool, you could ensure this by calling the callbacks on another dispatcher, such as Dispatchers.Default
. You can even avoid the callback time problem by launching those callbacks in separate coroutines instead of synchronously waiting for themJoffrey
01/06/2022, 12:28 PMIs it enough to move the startTime line above the launch call?Yes
Florian Walther (live streaming)
01/06/2022, 12:33 PM