Vsevolod Ganin
12/05/2019, 11:06 AMCoroutineExceptionHandler
installed in top-most runBlocking
is not being called? I read all docs but can’t quite grasp the sense of it. Example:
fun main(): Unit = runBlocking(CoroutineExceptionHandler { _, e ->
println("Boom: $e")
}) {
(0..10).map {
launch {
println("Throw in attempt #$it")
throw RuntimeException("Attempt #$it")
}
}
Unit
}
streetsofboston
12/05/2019, 12:49 PMlaunc(ceh) {...}
instead?Vsevolod Ganin
12/05/2019, 12:57 PMlaunch
with supervisorScope
helps thoughstreetsofboston
12/05/2019, 1:04 PMVsevolod Ganin
12/05/2019, 1:20 PMstreetsofboston
12/05/2019, 2:38 PMsuspend
functions and such:
https://medium.com/the-kotlin-chronicle/coroutine-exceptions-3378f51a7d33Vsevolod Ganin
12/05/2019, 2:46 PMstreetsofboston
12/05/2019, 2:47 PMVsevolod Ganin
12/05/2019, 3:28 PMfun main(): Unit = runBlocking {
val errorHandler = CoroutineExceptionHandler { _, e ->
println("Boom: $e")
}
val scope = CoroutineScope(errorHandler)
scope.launch {
throw RuntimeException()
}
Unit
}
It does not crash and it does not print anything at all. The code is similar to one you have in the blogspotstreetsofboston
12/05/2019, 3:31 PMfun main() {
val ceh = CoroutineExceptionHandler { _, e ->
println("CEH Handled Crash [$e]")
}
CoroutineScope(context + ceh).launch {
oops()
}
Thread.sleep(1000)
}
context
is just a dispatcher…runBlocking
provides the top-most scope and it handles the exception. I think it just throws it to the caller of `runBlocking`…., even though you create a brand new top-scope right in your sample…. needs more investigation 🙂Vsevolod Ganin
12/05/2019, 3:35 PMrunBlocking
catches the exceptionstreetsofboston
12/05/2019, 3:37 PMval scope = CoroutineScope(errorHandler)
into val scope = CoroutineScope(otherDispatcher + errorHandler)
where otherDispatcher
is a different dispatcher than the one the runBlocking is using…. It shouldn’t matter, but i’m curious….Vsevolod Ganin
12/05/2019, 3:37 PMstreetsofboston
12/05/2019, 3:38 PMVsevolod Ganin
12/05/2019, 3:38 PMthrow
it disconnects 😞streetsofboston
12/05/2019, 3:43 PMVsevolod Ganin
12/05/2019, 3:49 PMfun main(): Unit = runBlocking {
val errorHandler = CoroutineExceptionHandler { _, e ->
println("Boom: $e")
}
val scope = CoroutineScope(errorHandler)
scope.launch {
throw RuntimeException()
}.join()
Unit
}
join
join
I’m able to go further nowalllex
12/05/2019, 4:19 PMlaunch
running under a SupervisorJob
. If you call async
under supervisor, the exections are caught be the Deferred
job, and will be rethrown when await
is invoked. Therefore they are not “uncaught”. Calling both launch
or async
under a regular Job
results in rethrown/propagated exceptions as well. Therefore they are not “uncaught” either.
So, in your original example you either need to launch
under a supervisorScope
, or you need to wrap your runBlocking
in try-catch.
https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-coroutine-exception-handler/Vsevolod Ganin
12/05/2019, 4:47 PMawait
streetsofboston
12/05/2019, 4:57 PMawait()
allows you to try-catch an exception. Still, if the top-most scope called launch
, its CEH will still be handling that exception. From my blog-post:
> We need to be aware that even if we handle the exception by calling await()
, it will still go up the child-parent Coroutine chain.Vsevolod Ganin
12/05/2019, 5:08 PMasync
? If we don’t call await
the CEH on top will not get the exception. This is consistent with explanation above: the async
handles the exception so it is not given to CEH. But if a user ceases to call await
(let’s imagine he forgets) then the exception is lost. Example:
fun main() = runBlocking {
val scope = CoroutineScope(CoroutineExceptionHandler { _, e ->
println("Boom: $e")
})
scope.async {
throw RuntimeException()
}.join()
}
alllex
12/05/2019, 7:05 PM