You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
suspendfunproduceData(): ReceiveChannel<String> {
// I cannot use coroutineScope builder, since it makes this method wait for ever for channel to complete.// Use CorotuineScope(coroutineContext) insteadreturnCoroutineScope(coroutineContext).produce(capacity =0) {
send("Something")
throwIllegalStateException("Data production failed")
}
}
funmain() = runBlocking {
Thread.setDefaultUncaughtExceptionHandler { thread, throwable ->println("Got uncaught exception $throwable") }
try {
produceData().consumeEach {
println("Received $it")
}
} catch (e:Exception) {
println("Caught exception $e!")
}
}
Running above code will both catch exception in the catch block of the main method AND throw it into uncaught exception handler.
I found two workarounds for this so far:
Remove CoroutineScope(coroutineContext) and just use GlobalScope instead, but that could potentially leave channel dangling if parent is cancelled before it starts consuming the channel.
Use cancel(Exception()) instead of throw Exception(), but that still causes problem if unexpected exception is thrown in the producer.
Is this a bug or is there a fundamental flaw somewhere in my code?
The text was updated successfully, but these errors were encountered:
This is the same problem as #763. All scoped builders cancel the parent scope on failure. This highlights the general complexity of exception handling in concurrent code. The solution is to use coroutineScope { ... } to delimit the blocks of concurrently executed code and put exception handling around it:
Also, the better way to define produceData is by making it an extension on CoroutineScope, because it makes your intent to start a new coroutine from produceData function explicit:
suspend fun CoroutineScope.produceData(): ReceiveChannel<String> =
produce(capacity = 0) {
send("Something")
throw IllegalStateException("Data production failed")
}
Thanks for the info. CoroutineScope extension convenction actually sounds like a pretty good idea. However, one problem I see is how to do this when method is part of a class? For example:
class DataProducer {
fun CoroutineScope.produceData(): ReceiveChannel<String> {
return produce(capacity = 0) {
send("Something")
throw IllegalStateException("Data production failed")
}
}
}
Kotlin does not let me call DataProducer().produceData() even if I'm inside coroutine scope.
Running above code will both catch exception in the catch block of the main method AND throw it into uncaught exception handler.
I found two workarounds for this so far:
CoroutineScope(coroutineContext)
and just useGlobalScope
instead, but that could potentially leave channel dangling if parent is cancelled before it starts consuming the channel.cancel(Exception())
instead ofthrow Exception()
, but that still causes problem if unexpected exception is thrown in the producer.Is this a bug or is there a fundamental flaw somewhere in my code?
The text was updated successfully, but these errors were encountered: