Dontsu
09/13/2025, 2:25 AMdelay() functions. So, I couldn’t tell which happens first, exception propagation or cancellation propagation.
I realized that depending on the coroutine’s progress, it’s impossible to know which one will terminate first. But, I’m still curious whether, at the exact moment an error occurs, the coroutine internally attempts to propagate cancellation to its children first or propagate the exception to its parent first.
fun main(): Unit = runBlocking {
val job1 = launch(CoroutineName("Coroutine1")) {
val job2 = launch(CoroutineName("Coroutine2")) {
val job4 = launch(CoroutineName("Coroutine4")) {
println("Coroutine4 run")
delay(600)
}
job4.invokeOnCompletion {
println("Coroutine4 end")
}
val job5 = launch(CoroutineName("Coroutine5")) {
println("Coroutine5 run")
delay(600)
}
job5.invokeOnCompletion {
println("Coroutine5 end")
}
println("Coroutine2 run")
}
job2.invokeOnCompletion {
println("Coroutine2 end")
}
val job3 = launch(CoroutineName("Coroutine3")) {
val job6 = launch(CoroutineName("Coroutine6")) {
delay(600)
println("Coroutine6 run")
}
job6.invokeOnCompletion {
println("Coroutine6 end")
}
println("Coroutine3 run")
delay(500)
throw Exception() // here! exception occurs!
}
job3.invokeOnCompletion {
println("Coroutine3 end")
}
println("Coroutine1 run")
}
job1.invokeOnCompletion {
println("Coroutine1 end")
}
}Dmitry Khalanskiy [JB]
09/15/2025, 9:41 AMinvokeOnCompletion does not get invoked whenever a coroutine gets cancelled: instead, it gets invoked when the coroutine finishes its work. This is an important distinction whenever there's some cleanup code: https://pl.kotl.in/Jnrbnw2Yi
Also, invokeOnCompletion is a niche API that's mostly useful for implementing coroutine-aware concurrent data structures. It's almost always a good idea to avoid it in straightforward production code. See https://github.com/Kotlin/kotlinx.coroutines/issues/4180Dontsu
09/17/2025, 1:38 PMDmitry Khalanskiy [JB]
09/17/2025, 1:41 PMDontsu
09/17/2025, 2:25 PMDontsu
09/17/2025, 2:28 PM