-
Notifications
You must be signed in to change notification settings - Fork 1.9k
rxAwait calls the uncaught exception handler #252
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
I may be wrong, but I think it's more an Rx issue. I have the same problem without coroutines import io.reactivex.Observable
import io.reactivex.Single
import java.util.concurrent.TimeUnit
fun main(args: Array<String>) {
Observable
.interval(1, TimeUnit.MILLISECONDS)
.switchMapSingle {
timeBomb()
}
.blockingSubscribe(
{ println("element = $it") },
{ println("error: ${it.message}") }
)
}
private fun timeBomb() = Single.timer(1, TimeUnit.MILLISECONDS)
.doOnSuccess { throw Exception("something went wrong") } |
Nope, that one calls the |
Yes indeed sorry. However, Rx shouldn't throw an So what is your expected behavior ? |
I expect exceptions that can't be delivered inside the RxJava chain to be delivered to the RxJavaPlugins. @akarnokd What do you think is the correct behavior here? |
You have Many blocking (IO) APIs respond to async cancellations with exceptions so that their blocking awaiters can be unblocked and resumed with a failure. I assume coroutines do the same minus the blocking part. |
I'm not sure if there anything that can be done in coroutines, however I find it related to the issue #251 Maybe we can find some common solution to both. |
Well in RxJava, exceptions that cannot be delivered, get wrapped in an This is nice because I can set an error handler and don't crash my app. I expect the coroutine to catch that excpetion if it's not deliverable and forward it to the plugins too. |
The current approach in |
Ah that's nice. So what about installing this by default in all rxXYZ methods? object RxCoroutineExceptionHandler : AbstractCoroutineContextElement(CoroutineExceptionHandler),
CoroutineExceptionHandler {
override fun handleException(context: CoroutineContext, exception: Throwable) {
RxJavaPlugins.onError(exception)
}
} So it can be used like fun <T> rxSingle(
context: CoroutineContext = DefaultDispatcher,
parent: Job? = null,
block: suspend CoroutineScope.() -> T
): Single<T> = Single.create { subscriber ->
val newContext = newCoroutineContext(context + RxCoroutineExceptionHandler, parent)
val coroutine = RxSingleCoroutine(newContext, subscriber)
subscriber.setCancellable(coroutine)
coroutine.start(CoroutineStart.DEFAULT, coroutine, block)
} |
I don't know if that is the right approach. I'm wary of statics. This way, the behavior of some library code that uses |
Any news on this? This makes coroutines-rx integration quite painful for me. |
I've revisited topic starter code and I still don't see what can be done about it in
then I see that it is being invoked from time to time. How is that related to |
If that was the case I'd be happy. For me it doesn't: import io.reactivex.Observable
import io.reactivex.Single
import io.reactivex.plugins.RxJavaPlugins
import kotlinx.coroutines.experimental.rx2.await
import kotlinx.coroutines.experimental.rx2.rxSingle
import java.io.IOException
import java.util.concurrent.TimeUnit
fun main(args: Array<String>) {
RxJavaPlugins.setErrorHandler {
println("error handler called")
}
Observable
.interval(1, TimeUnit.MILLISECONDS)
.switchMapSingle {
rxSingle {
timeBomb().await()
}
}
.subscribe({}, {})
Thread.sleep(1000)
}
private fun timeBomb() = Single.timer(1, TimeUnit.MILLISECONDS)
.doOnSuccess { throw IOException("") } It crashes the current thread:
|
Actually it was not happening all the time: This code is reproducible for me: fun main(args: Array<String>) {
RxJavaPlugins.setErrorHandler {
println("error handler called")
}
val timeBomb = Single.timer(1, TimeUnit.NANOSECONDS)
.doOnSuccess { throw IOException("") }
Observable
.interval(1, TimeUnit.MILLISECONDS)
.switchMapSingle {
rxSingle {
timeBomb.await()
}.onErrorReturnItem(1)
}
.test()
.await()
} |
Thanks a lot for this reproducer. It indeed clearly shows a problem. The solution is not obvious though. What I can recommend so far is to install
That will ensure that your "async exceptions" are handled in the same way regardless of whether they were caught by Rx or by coroutines machinery. |
For v2, it is |
Thank's, I do that for some time now. It is crutial to catch import io.reactivex.plugins.RxJavaPlugins
import kotlinx.coroutines.experimental.CancellationException
import kotlinx.coroutines.experimental.CoroutineExceptionHandler
import kotlin.coroutines.experimental.AbstractCoroutineContextElement
import kotlin.coroutines.experimental.CoroutineContext
object RxCoroutineExceptionHandler : AbstractCoroutineContextElement(CoroutineExceptionHandler),
CoroutineExceptionHandler {
override fun handleException(context: CoroutineContext, exception: Throwable) {
if (exception is CancellationException) return
RxJavaPlugins.onError(exception)
}
} My initial suggestion was to install this by default in the |
We could use a default coroutine exception handler for |
In general, it's not an rx-specific issue and the same problem arises in any cancellable asynchronous API. And there is no good solution: one is to ignore unhandled exceptions (which can lead to a lot of hidden/undebuggable bugs), the other is to always handle them and crash related coroutines (which can lead to a lot of pain with Thus unconditionally installing a default rx handler can lead to an undesired behaviour. Moreover, it's easy to forget rx-handler during the launch of not rx-based nested coroutines. We may do the following:
Then you can choose between (standard) |
Hey everyone, any updates about this issue? We had the same problem described in this issue, we have fixed it using the Is this the best way to solve it? Should |
…tegrations Use tryOnError in RxJava to make exception delivery check-and-act race free. Deliver undeliverable exceptions via RxJavaPlugins instead of handleCoroutineException. This is a deliberate choice for a multiple reasons: * When using Rx (whether with coroutines or not), undeliverable exceptions are inevitable and users should hook into RxJavaPlugins anyway. We don't want to force them using Rx-specific CoroutineExceptionHandler all over the place * Undeliverable exceptions provide additional helpful stacktrace and proper way to distinguish them from other unhandled exceptions * Be consistent with reactor where we don't have try*, thus cannot provide a completely consistent experience with CEH (at least, without wrapping all the subscribers) Do the similar in Reactor integration, but without try*, Reactor does not have notion of undeliverable exceoptions and handles them via Operators.* on its own. Also, get rid of ASCII tables that are not properly render in IDEA Fixes #252 Fixes #1614
@fabioCollini yes, I've made the fix for 1.3.3. |
…tegrations Use tryOnError in RxJava to make exception delivery check-and-act race free. Deliver undeliverable exceptions via RxJavaPlugins instead of handleCoroutineException. This is a deliberate choice for a multiple reasons: * When using Rx (whether with coroutines or not), undeliverable exceptions are inevitable and users should hook into RxJavaPlugins anyway. We don't want to force them using Rx-specific CoroutineExceptionHandler all over the place * Undeliverable exceptions provide additional helpful stacktrace and proper way to distinguish them from other unhandled exceptions * Be consistent with reactor where we don't have try*, thus cannot provide a completely consistent experience with CEH (at least, without wrapping all the subscribers) Do the similar in Reactor integration, but without try*, Reactor does not have notion of undeliverable exceoptions and handles them via Operators.* on its own. Also, get rid of ASCII tables that are not properly render in IDEA Fixes #252 Fixes #1614
…tegrations Use tryOnError in RxJava to make exception delivery check-and-act race free. Deliver undeliverable exceptions via RxJavaPlugins instead of handleCoroutineException. This is a deliberate choice for a multiple reasons: * When using Rx (whether with coroutines or not), undeliverable exceptions are inevitable and users should hook into RxJavaPlugins anyway. We don't want to force them using Rx-specific CoroutineExceptionHandler all over the place * Undeliverable exceptions provide additional helpful stacktrace and proper way to distinguish them from other unhandled exceptions * Be consistent with reactor where we don't have try*, thus cannot provide a completely consistent experience with CEH (at least, without wrapping all the subscribers)\ Do the similar in Reactor integration, but without try*, Reactor does not have notion of undeliverable exceoptions and handles them via Operators.* on its own. Also, get rid of ASCII tables that are not properly render in IDEA Fixes #252 Fixes #1614
Why does the following code calls the uncaught exception handler?
As there is an onError supplied I expect it to be called instead.
The text was updated successfully, but these errors were encountered: