Marc Knaup
12/02/2022, 5:45 PMTestCoroutineScheduler.advanceTimeBy()
and get a callback whenever the currentTime
changes in order to execute side-effects? Or at least query the next event time?Dmitry Khalanskiy [JB]
12/03/2022, 11:01 AMMarc Knaup
12/03/2022, 10:30 PMDmitry Khalanskiy [JB]
12/04/2022, 10:52 AMX
, Y
, and Z
) that need to have virtual time. Also, let's, as you propose, designate the time as seen by X
as the real time.
Y
is registered as the first thing to get a callback for a time change from X
, Z
is registered as the second thing.
• X
has a task scheduled in 10 seconds,
• Y
has a task scheduled in 9 seconds,
• Z
has a task scheduled in 2 seconds.
X
proceeds to grab another task from the queue and announces: "The virtual time is now 10". Y
urgently runs the task, and then, Z
starts running its task. We're left with an insane and completely unrealistic interleaving.
The Proper™ thing to do here would be to have a queue of tasks shared by all three things and advance the virtual time for all of them. Here, the knowledge of when the next task is planned would help you, that's a nice use case for that.
The Proper™ thing may be too difficult to do though, depending on the provided APIs of X
, Y
, and Z
, so maybe something like this could be the best way instead:
val timerResolution = 50.milliseconds
while (testNotFinished) {
X.advanceTimeBy(timerResolution)
Y.advanceTimeBy(timerResolution)
Z.advanceTimeBy(timerResolution)
}
Marc Knaup
12/04/2022, 12:16 PMfun advanceTimeBy(delay: Long) {
var remainingDelay = delay
while (true) {
val delayToNextEvent = listOfNotNull(X.delayToNextEvent, Y.delayToNextEvent, Z.delayToNextEvent)
.minOrNull()
?.takeIf { it <= remainingDelay }
?: break
X.advanceTimeBy(delayToNextEvent)
Y.advanceTimeBy(delayToNextEvent)
Z.advanceTimeBy(delayToNextEvent)
remainingDelay -= delayToNextEvent
}
}
Dmitry Khalanskiy [JB]
12/04/2022, 1:34 PMX.advanceTimeBy(remainingDelay)
and such at the end so the virtual time is properly updated, but other than that, yeah, I agree that this would also work… unless someone is adding new tasks to X
, Y
, and Z
in parallel to advanceTimeBy
. So, the tricky thing with this API is the existence of code that runs outside the test dispatchers.
I think this issue relates to https://github.com/Kotlin/kotlinx.coroutines/issues/3189, which asks for notifications about the scheduler being idle, but this could be extended to also providing the information about the next pending task when not idle. It seems that, if we decide how to treat the code in other threads concerning idleness, both problems can be solved.