https://kotlinlang.org logo
#coroutines
Title
# coroutines
d

Dmitry Khalanskiy [JB]

10/18/2023, 2:52 PM
Hey folks, If you're using
kotlinx-coroutines-test
, it is recommended to avoid
runCurrent
,
advanceUntilIdle
, and especially `advanceTimeBy`: https://github.com/Kotlin/kotlinx.coroutines/issues/3919 Already, many tests can be written more idiomatically without them, though clearly far from all. The issue describes the plan to increase the number of tests that don't need these functions. A request to you: the next time you reach for one of these functions, please check the "Alternatives to each manual execution control function" section of the issue to see if the proposed replacements would suit your needs; if not, leave a comment; if yes, leave a thumbs-up under one of the comments.
👀 10
Also, feel free to ask any clarifying questions in this thread or, if you think the question is important to consider down the line, directly under the issue.
p

Pablichjenkov

10/18/2023, 3:12 PM
I have hundreds if not thousands of them in my tests. Especially
advanceUntilIdle
. I will check the thread later.
j

Joffrey

10/18/2023, 3:13 PM
> The next question is, what should happen if line A is replaced with advanceUntilIdle or advanceTimeBy(2.seconds). Neither of them is allowed to wait for one real-life second, as they are blocking functions. Not sure this question actually raises the problem you wanted to raise here. In the example, at the point where
___ A ___
is, all the "real-time" stuff is already done (we already waited for the real-life second with
job1.join()
), so it doesn't show the problem you're trying to discuss there I believe. Maybe you meant to suggest a scenario where
job1.join()
is not called, and instead we have
advanceUntilIdle
or
advanceTimeBy
.
d

Dmitry Khalanskiy [JB]

10/18/2023, 3:17 PM
Thanks @Joffrey, yes, you are right, I moved
A
upwards.
thank you color 1
j

Joffrey

10/18/2023, 3:18 PM
Cool. Also, I just wanted to congratulate you on this post. The extreme clarity is very appreciated, as well as the care to remove footguns in the design, while still providing something useful.
😄 2
2
> There is no way to make time control functions play nicely in these scenarios. It seems like the only sensible way to implement them would be to throw UnsupporteOperationException if they encounter a task that requires waiting for a given amount of real time. What's the problem with making those functions
suspend
and wait for actual time when actual time is necessary (e.g. if there are tasks that run under
NoDelaySkipping
context)?
d

Dmitry Khalanskiy [JB]

10/18/2023, 3:36 PM
The problem is that then they would be different functions. Some people call then from non-suspend contexts today.
j

Joffrey

10/18/2023, 3:38 PM
> The problem is that then they would be different functions Ah ok, so the solution you're proposing for this specific problem is to make those blocking functions throw in these cases, and provide
suspend
equivalents for the ones that cannot be covered by
delay
or
yield
(if such things are in fact needed). Correct?
d

Dmitry Khalanskiy [JB]

10/18/2023, 3:44 PM
This is one possibility, yes. So far, we already know of some use cases that are currently covered by
advanceUntilIdle
and
runCurrent
, but there's a suspicion that they don't properly work even for them: namely,
advanceUntilIdle
is used to wait for spawned coroutines to finish (but doesn't do quite that), and
runCurrent
is used to wait for all initialization to complete (but a
suspend
function could suit the use case better). See the "Alternatives to each manual execution control function" section.
j

Joffrey

10/18/2023, 3:45 PM
> but doesn't do quite that You mean it doesn't wait for coroutines that are outside the test scheduler, right? Or do you mean it doesn't wait for the coroutines to finish, but only to reach a suspension point that doesn't resume unless something else happens?
d

Dmitry Khalanskiy [JB]

10/18/2023, 4:09 PM
You mean it doesn't wait for coroutines that are outside the test scheduler, right?
No, we can't do anything about this. The test framework needs to learn about the coroutines somehow.
Or do you mean it doesn't wait for the coroutines to finish, but only to reach a suspension point that doesn't resume unless something else happens?
Where this "something else" is outside the control of the test scheduler, yes. I think it's exactly the reason why people find
advanceUntilIdle
confusing sometimes: structured concurrency urges us to think in terms of tasks (each represented as a coroutine), but
advanceUntilIdle
breaks this abstraction a little, forgetting about tasks (and the test framework's involvement) in the middle of their execution.