简体   繁体   中英

Why is exception handled by the handler of a non-root, non-supervisor coroutine?

I was testing the exception handling mechanism of coroutines with this test function:

suspend fun test(){
    supervisorScope {launch(createExceptionHandler(1)) {
        coroutineScope {launch(createExceptionHandler(2)) {
            supervisorScope {launch { //SUPERVISOR WITH NO HANDLER
                coroutineScope {launch(createExceptionHandler(4)) {
                    coroutineScope {launch(createExceptionHandler(5)) {
                        throw Exception("Testing")
                    }}
                }}
            }}
        }}
    }}
}

fun createExceptionHandler(i: Int) = CoroutineExceptionHandler { _, throwable ->
    "---> exception handler #$i caught: ${throwable}".log()
}

Result:

---> exception handler #2 caught: java.lang.Exception: Testing

I was expecting handler #1 to catch the exception, and to my surprise, it was handler #2 who caught it!


Reading the docs , I expect handler #2, #4, #5 to be completely ignored:

... In particular, all children coroutines (coroutines created in the context of another Job) delegate handling of their exceptions to their parent coroutine, which also delegates to the parent, and so on until the root, so the CoroutineExceptionHandler installed in their context is never used.

What I understand was that exceptions stop propagating when it reaches the root, or a supervisorScope with an exception handler. So I thought handler #1 would have handled the exception.

This test function (2) seems to confirm my beliefs:

suspend fun test2(){
    supervisorScope {launch(createExceptionHandler(1)) {
        supervisorScope {launch(createExceptionHandler(2)) {
            supervisorScope {launch {
                supervisorScope {launch {
                    supervisorScope {launch {
                        throw Exception("Testing")
                    }}
                }}
            }}
        }}
    }}
}

Result:

---> exception handler #2 caught: java.lang.Exception: Testing

I have read numerous guides online on exception propagation and handling and I am quite stuck on this...

Any clues would help, thanks for reading!

In your first example, supervisorScope that launches a coroutine without an exception handler actually inherits the exception handler from the outer scope. You can verify by printing the CoroutineContext after launch .

launch in supervisorScope without CoroutineExceptionHandler is top launch and it inherited CoroutineExceptionHandler from #2. Exception in your coroutine with launch which propagates the exception to parent (when possible) or delivers it to the exception handler. With supervisorScope there is no propagation, so a crashed launch coroutine delivers its uncaught exception to the inherited CoroutineExceptionHandler . More details about CoroutineExceptionHandler inheritance: elizarov.medium.com/coroutine-context-and-scope-c8b255d59055

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM