In my following code, if `countDownInterval` is 0 ...
# coroutines
f
In my following code, if
countDownInterval
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?
Copy code
fun 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
            }
        }
    }
}
Is
Timer.schedule
exact?
s
Just don't delay and have the while true loop trigger super fast? If you don't want the callback to trigger make an if countDownInterval != 0 check around it. And if you want it to trigger at the end make it happen in the else statement?
f
I'm just afraid of executing an infinite loop for many minutes on end. It seems to make more sense to just delay in this case.
j
What exactly do you mean by
delay()
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.
f
I think delays can run for much longer by default
if you are running something else
j
Also, note that you're setting your
startTime
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 coroutine
f
@Joffrey Thank you. Is it enough to move the startTime line above the launch call?
j
I think delays can run for much longer by default if you are running something else
That 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 them
Is it enough to move the startTime line above the launch call?
Yes
f
thank you!