The job2
will throw an ArithmaticException thus making the parent job cancelled. Normally we will get a CancellationException when calling the job2.join()
but there is otherwise.
suspend fun main() = runBlocking {
log(1)
val job = launch(Dispatchers.Unconfined) {
log(2)
val job2 = launch(Dispatchers.Default) {
log(3)
val x = 1 / 0
log(4)
}
job2.join()
log(5)
}
log(6)
job.join()
log(7)
}
val dateFormat = SimpleDateFormat("HH:mm:ss:SSS")
val now = {
dateFormat.format(Date(System.currentTimeMillis()))
}
fun log(msg: Any?) = println("${now()} [${Thread.currentThread().name}] $msg")
The result may be:
09:35:15:065 [main @coroutine#1] 1
09:35:15:074 [main @coroutine#2] 2
09:35:15:082 [DefaultDispatcher-worker-1 @coroutine#3] 3
09:35:15:104 [main @coroutine#2] 5
Exception in thread "main" java.lang.ArithmeticException: / by zero
at com.bennyhuo.coroutines.sample2.exceptions.E2Kt$main$2$job$1$job2$1.invokeSuspend(e2.kt:12)
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
at kotlinx.coroutines.DispatchedTask.run(Dispatched.kt:238)
at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:594)
at kotlinx.coroutines.scheduling.CoroutineScheduler.access$runSafely(CoroutineScheduler.kt:60)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:742)
09:35:15:105 [main @coroutine#1] 6
When falling into the slow-path, in other words, the joinSuspend
path:
public final override suspend fun join() {
if (!joinInternal()) { // fast-path no wait
coroutineContext.checkCompletion()
return // do not suspend
}
return joinSuspend() // slow-path wait
}
it is gonna check the job result again here:
public suspend inline fun <T> suspendCancellableCoroutine(
crossinline block: (CancellableContinuation<T>) -> Unit
): T =
suspendCoroutineUninterceptedOrReturn { uCont ->
val cancellable = CancellableContinuationImpl(uCont.intercepted(), resumeMode = MODE_CANCELLABLE)
// NOTE: Before version 1.1.0 the following invocation was inlined here, so invocation of this
// method indicates that the code was compiled by kotlinx.coroutines < 1.1.0
// cancellable.initCancellability()
block(cancellable)
cancellable.getResult() // HERE!!!!!
}
If the job just completed, the result will be returned.
In our case, job2
completed with a Exception, but the state of the job will turn into Unit
for a short time for a ResumeOnCompletion
is installed when calling joinSuspend
and the ResumeOnCompletion
will be invoked immediately if the result is ready.
private suspend fun joinSuspend() = suspendCancellableCoroutine<Unit> { cont ->
// We have to invoke join() handler only on cancellation, on completion we will be resumed regularly without handlers
cont.disposeOnCancellation(invokeOnCompletion(handler = ResumeOnCompletion(this, cont).asHandler))
}