-
Notifications
You must be signed in to change notification settings - Fork 1.9k
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
Inconsistent handling of undeliverable errors in RX builders #2173
Comments
So the different handling of fatal exceptions in This problem seems to be specific to the This explains the special handing of fatal exceptions in This would also be in line with the standard RX builders, which also let fatal exceptions thrown by the emitter function propagate to the uncaught exception handler. However, it seems that coroutines don't have the concept of fatal exceptions that should not be caught, so all exceptions are caught and emitted by them, and therefore the current behaviour of |
If an error can't be delivered to the observer due to the observer already having disposed the subscription, then it should be reported to the RX global exception handler. This was already being done in the implementations for rxSingle, rxMaybe, and rxCompletable, but not in rxObservable. Copied the test over from the other implementations as well. Also removed the previous test in ObservableTest, which actually was testing rxSingle instead of rxObservable, and was too complicated and dependent on race conditions (both rxSingle and rxObservable now have a simple and deterministic test for undeliverable error handling). It was also not well implemented, and I originally tried to fix it to be more robust in commit 58ad0cd. Fixes Kotlin#2173.
Thank you for raising this! Yes, your analysis is spot-on, the root cause for the original problem was that Looking at the way RxJava treats fatal exceptions, it doesn't seem though like we could behave consistently with them in spirit: they attempt to let fatal exceptions bubble up to the surface as fast as possible, except in scenarios when the fatal exception is being passed to However, it seems like there is a simple solution imposed by the Reactive Streams specification itself: https://github.com/reactive-streams/reactive-streams-jvm/blob/v1.0.3/README.md#2.13 It says that in case any methods of |
* Fixed `PublisherCoroutine`, `rxObservable`, and `Flow.toPublisher` ignoring cancellations. * Fatal exceptions are not treated in a special manner by us anymore. Instead, we follow the requirement in the reactive streams specification that, in case some method of `Subscriber` throws, that subscriber MUST be considered canceled, and the exception MUST be reported in someplace other than `onError`. * Fixed `trySend` sometimes throwing in `PublisherCoroutine` and `rxObservable`. * When an exception happens inside a cancellation handler, we now consistently throw the original exception passed to the handler, with the new exception added as suppressed. * Fixed `PublisherCoroutine` and `rxObservable` claiming that the channel is not closed for send for some time after `close()` has finished. * Fixed publishers sometimes signalling `onComplete()` after cancellation even though their streams are not finite. Fixes Kotlin#2173
While looking at the implementations of the RX coroutines builders in the
kotlinx-coroutines-rx*
modules, I noticed some inconsistencies in the handling of undeliverable errors between therxObservable
implementation on the one hand, and therxSingle
,rxMaybe
, andrxCompletable
implementations on the other.In the
rxSingle
,rxMaybe
, andrxCompletable
implementations, when the coroutine is cancelled with an exception, it first tries to deliver it to the observer, and if that's not possible then it is reported to the RX global exception handler. This behaviour is the same regardless of whether the error is fatal or not.Whereas in the
rxObservable
implementation, it drops undeliverable non-fatal errors, but always reports fatal errors to the RX global exception handler after attempting to deliver it to the observer, regardless of whether the observer could handle it or not.The
rxFlowable
/publish
implementation seems to be a combination of both, as it reports all undeliverable or fatal exceptions to the global exception handler. This seems to be the best and safest approach.The text was updated successfully, but these errors were encountered: