Reputation: 432
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!
Upvotes: 2
Views: 583
Reputation: 2318
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
Upvotes: 0
Reputation: 314
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
.
Upvotes: 1