Search code examples
kotlinkotlin-coroutineskotlin-android-extensionscancellation

Kotlin: CancellationException unwrapping in coroutines


I have found next statement in the documentation:

Cancellation exceptions are transparent and are unwrapped by default:

val handler = CoroutineExceptionHandler { _, exception -> println("CoroutineExceptionHandler got $exception")
}
val job = GlobalScope.launch(handler) {
    val inner = launch { // all this stack of coroutines will get cancelled 
        launch {
            launch {
                throw IOException() // the original exception
            } 
        }
    }
    try {
        inner.join()
    } catch (e: CancellationException) {
        println("Rethrowing CancellationException with original cause")
        throw e // cancellation exception is rethrown, yet the original IOException gets to the handler
    } 
}
job.join()

What do this documentation means in terms of this code?

UPDATE 27-03-2021

import kotlinx.coroutines.*
import java.io.*

fun main() = runBlocking {
    val handler = CoroutineExceptionHandler { _, exception ->
        println("CoroutineExceptionHandler got $exception")
    }
    val job = GlobalScope.launch(handler) {
        launch {
            throw IOException() // the original exception
        }
        launch {
            throw CancellationException("Test", ArithmeticException("Arithmetic"))
        }
    }
    job.join()
}

Why in this case result is:

CoroutineExceptionHandler got java.io.IOException

Without Arithmetic exception?


Solution

  • inner.join() throws a CancellationException with cause set to the exception that was thrown inside the job. But when a CancellationException is thown at top level, the CoroutineExceptionHandler will only see an IOException (the cause of the CancellationException).