-
Notifications
You must be signed in to change notification settings - Fork 7.6k
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
StackOverflowError is swallowed #748
Comments
The proper fix seems to be to immediately re-throw any VirtualMachineError by introducing a more specialized catch. |
Not surprised we ended up with something like this happening after migrating from I agree @duarten that Any others we should include? |
Maybe it's better not to catch any of the |
Re-throwing the |
In general not catching So it may only be the |
But swallowing them is not good either, and then that will put the onus on the users to re-throw those exceptions, because there's really no way to handle them. If there are uncaught Errors won't the JVM shutdown even if there are non-daemon threads? Interestingly, Scala defines a NonFatal exception as: object NonFatal {
def apply(t: Throwable): Boolean = t match {
case _: StackOverflowError => true // StackOverflowError ok even though it is a VirtualMachineError
case _: VirtualMachineError | _: ThreadDeath | _: InterruptedException | _: LinkageError | _: ControlThrowable | _: NotImplementedError => false
case _ => true
}
} which they use in their Futures library to check if it should be handed to the user for handling. |
It won't be swallowing them, they are or should all be passed via onError to the user provided Observer. StackOverflow is 'swallowed' because of a recursive loop. |
Sorry, my point is that users may not be expecting to be handed such exception, and may swallow them themselves, by for example just logging them. The correct behaviour, IMHO, would be to let them bring down the JVM asap. |
Background on The Scala definition seems good. I think we should follow that same logic for defining something |
That sounds good! |
protected void _onError(Throwable e) {
try {
RxJavaPlugins.getInstance().getErrorHandler().handleError(e);
actual.onError(e);
} catch (Throwable e2) {
e2.printStackTrace();
if (e2 instanceof OnErrorNotImplementedException) {
/*
* onError isn't implemented so throw
*
* https://github.com/Netflix/RxJava/issues/198
*
* Rx Design Guidelines 5.2
*
* "when calling the Subscribe method that only has an onNext argument, the OnError behavior will be
* to rethrow the exception on the thread that the message comes out from the observable sequence.
* The OnCompleted behavior in this case is to do nothing."
*/
try {
subscription.unsubscribe();
} catch (Throwable unsubscribeException) {
RxJavaPlugins.getInstance().getErrorHandler().handleError(unsubscribeException);
throw new RuntimeException("Observer.onError not implemented and error while unsubscribing.", new CompositeException(Arrays.asList(e, unsubscribeException)));
}
throw (OnErrorNotImplementedException) e2;
} else {
/*
* throw since the Rx contract is broken if onError failed
*
* https://github.com/Netflix/RxJava/issues/198
*/
RxJavaPlugins.getInstance().getErrorHandler().handleError(e2);
try {
subscription.unsubscribe();
} catch (Throwable unsubscribeException) {
RxJavaPlugins.getInstance().getErrorHandler().handleError(unsubscribeException);
throw new RuntimeException("Error occurred when trying to propagate error to Observer.onError and during unsubscription.", new CompositeException(Arrays.asList(e, e2, unsubscribeException)));
}
throw new RuntimeException("Error occurred when trying to propagate error to Observer.onError", new CompositeException(Arrays.asList(e, e2)));
}
}
// if we did not throw about we will unsubscribe here, if onError failed then unsubscribe happens in the catch
try {
subscription.unsubscribe();
} catch (RuntimeException unsubscribeException) {
RxJavaPlugins.getInstance().getErrorHandler().handleError(unsubscribeException);
throw unsubscribeException;
}
}
Sorry, I made a mistake. It's not a recursive loop. |
A thought to solve the swallowed StackOverflowError: can we call |
Just dig deeply and found StackOverflowError is swallowed by SafeObserver.
Next,
Next,
Next,
Next,
Next,
Next,
Next,
Here StackOverflowError is sent to so4.onError again, so4 will swallow it since isFinished is true. |
So if |
Dealing with in pull request #839 |
- ReactiveX#748 (comment) - ReactiveX#771 - ReactiveX#789 - SynchronizedObserver is for synchronization, not error handling or contract enforcements, that's the job of SafeSubscriber - Removed some unit tests that were asserting unsubscribe behavior that relied on SynchronizedObserver. They were testing something they are not responsible for.
Should be fixed in #839 |
) Looks like [the linked comment][1] was misinterpreted (but not in a way that affected the implementation) as Scala considered StackOverflowError as non-fatal but RxJava always considered it fatal. As such, its explicit check was redundant. [1]: #748 (comment)
) Looks like [the linked comment][1] was misinterpreted (but not in a way that affected the implementation) as Scala considered StackOverflowError as non-fatal but RxJava always considered it fatal. As such, its explicit check was redundant. [1]: #748 (comment)
The following code should have thrown StackOverflowError:
However, StackOverflowError is swallowed.
The problem is the following line in SafeObserver: https://github.com/Netflix/RxJava/blob/0b1b6e7a91d89ad63263b80747f0bd4683fe4ba2/rxjava-core/src/main/java/rx/operators/SafeObserver.java#L117
When StackOverflowError is thrown, there is few stack space. However, this line will generate too big stack frame and cause the thread crash. If I comment out this line, I can observe StackOverflowError.
Reported by Samuel at https://groups.google.com/forum/#!topic/rxjava/eraZ-32w1gQ
The text was updated successfully, but these errors were encountered: