Reputation: 3307
In the test2, I found the exception is not caught even if I pass an exception handler. It only works I launch a coroutine to wrap the call like the test1... my expectation is the first runSuspend
call can catch the exception and the code can continue to do the second runSuspend
call.
package coroutine.exceptions
import kotlinx.coroutines.*
fun log(msg: String) = println("$msg (${Thread.currentThread().name})")
val exceptionHandler = CoroutineExceptionHandler { _, e ->
log(e.localizedMessage)
}
fun main() = runBlocking {
val test1 = TestReuseCoroutineAfterException2("test1")
test1.launch(exceptionHandler) {
test1.runSuspend(true)
// below won't be run because an exception is thrown above.
delay(2000)
test1.runSuspend(false)
delay(2000)
}.join()
println()
val test2 = TestReuseCoroutineAfterException2("test2")
test2.runSuspend(true)
// below won't be run because an exception is thrown above.
delay(2000)
test2.runSuspend(false)
delay(2000)
log("finished")
}
class TestReuseCoroutineAfterException2(private val testName: String) :
CoroutineScope by CoroutineScope(Dispatchers.Default) {
suspend fun runSuspend(throwException: Boolean) = coroutineScope {
log("$testName: callSuspend - started")
launch(exceptionHandler) {
if (throwException)
throw Exception("$testName: callSuspend - throw exception")
else
log("$testName: callSuspend - done")
}
log("$testName: callSuspend - ended")
}
}
Output
test1: call
Suspend - started (DefaultDispatcher-worker-2)
test1: callSuspend - ended (DefaultDispatcher-worker-2)
test1: callSuspend - throw exception (DefaultDispatcher-worker-1)
test2: callSuspend - started (main)
test2: callSuspend - ended (main)
Exception in thread "main" java.lang.Exception: test2: callSuspend - throw exception
at coroutine.exceptions.TestReuseCoroutineAfterException2$callSuspend$2$1.invokeSuspend(TestReuseCoroutineAfterException2.kt:40)
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
at kotlinx.coroutines.DispatchedTask.run(Dispatched.kt:241)
at kotlinx.coroutines.EventLoopImplBase.processNextEvent(EventLoop.kt:116)
at kotlinx.coroutines.BlockingCoroutine.joinBlocking(Builders.kt:79)
at kotlinx.coroutines.BuildersKt__BuildersKt.runBlocking(Builders.kt:54)
at kotlinx.coroutines.BuildersKt.runBlocking(Unknown Source)
at kotlinx.coroutines.BuildersKt__BuildersKt.runBlocking$default(Builders.kt:36)
at kotlinx.coroutines.BuildersKt.runBlocking$default(Unknown Source)
at coroutine.exceptions.TestReuseCoroutineAfterException2Kt.main(TestReuseCoroutineAfterException2.kt:10)
at coroutine.exceptions.TestReuseCoroutineAfterException2Kt.main(TestReuseCoroutineAfterException2.kt)
Process finished with exit code 1
Upvotes: 0
Views: 472
Reputation: 3890
CoroutineScope
created via coroutineScope
builder fails if its child coroutine fails. If you don't want your scope to fail in this case, use supervisorScope
builder:
suspend fun runSuspend(throwException: Boolean) = supervisorScope {
With this change your code prints:
test1: callSuspend - started (DefaultDispatcher-worker-1)
test1: callSuspend - ended (DefaultDispatcher-worker-1)
test1: callSuspend - throw exception (DefaultDispatcher-worker-1)
test1: callSuspend - started (DefaultDispatcher-worker-1)
test1: callSuspend - ended (DefaultDispatcher-worker-1)
test1: callSuspend - done (DefaultDispatcher-worker-1)
test2: callSuspend - started (main)
test2: callSuspend - ended (main)
test2: callSuspend - throw exception (main)
test2: callSuspend - started (main)
test2: callSuspend - ended (main)
test2: callSuspend - done (main)
finished (main)
Upvotes: 3