-
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
Observable.using
sliently ignores exception of dispose action
#2464
Comments
Hi. At that point, it can't notify the subscriber because it received a termination event before. The best thing we can do is to route the exception into the plugin system. |
Can it be made so that |
@Laec, according to the Rx design guide, you should not throw an error in the dispose action:
|
Hi @zsxwing the issue is not whether one should or should not throw the exception, sometimes unexpected errors do occur. According to your quote, I would expect a crash, which generates good visibility, but currently it sliently ignores the error and moves on, which is bad. |
I think the essential question is: How RxJava handle exceptions from In many places, RxJava assumes As @akarnokd said, the best solution for RxJava may be sending the exception to the RxJava plugin. |
I don't mind how it gets handled internally, as long as the default behaviour is fail fast - crash, this gives people a chance to notice the issue and fix it. |
How about: class Main {
public static void main(String[] args) {
final AsyncSubject<Void> onFree = AsyncSubject.create();
Observable.using(
new Func0<String>() {
@Override
public String call() {
System.out.println("Calling resourceFactory");
return "resourceFactory";
}
},
new Func1<String, Observable<String>>() {
@Override
public Observable<String> call(String str) {
System.out.println("Calling observableFactory");
return Observable.just("observableFactory");
}
},
new Action1<String>() {
@Override
public void call(String s) {
System.out.println("Calling disposeAction");
try {
throw new RuntimeException("disposeAction");
// onFree.onCompleted();
} catch (Throwable ex) {
onFree.onError(ex);
}
}
}
).mergeWith(onFree).subscribe(
new Action1<String>() {
@Override
public void call(String s) {
System.out.println("onNext: " + s);
}
},
new Action1<Throwable>() {
@Override
public void call(Throwable e) {
System.out.println("onError: " + e.getMessage());
}
},
new Action0() {
@Override
public void call() {
System.out.println("onComplete");
}
}
);
}
} |
I feel that's just a short term workaround for the issue. We can't expect users of |
You can always roll your own operator so you can hide the boilerplate in a reusable fashion. Here is an example: public final class MyUsing<T, Resource> implements OnSubscribe<T> {
private final Func0<Resource> resourceFactory;
private final Func1<? super Resource, ? extends Observable<? extends T>> observableFactory;
private final Action2<? super Resource, Observer<T>> dispose;
public MyUsing(Func0<Resource> resourceFactory,
Func1<? super Resource, ? extends Observable<? extends T>> observableFactory,
Action2<? super Resource, Observer<T>> dispose) {
this.resourceFactory = resourceFactory;
this.observableFactory = observableFactory;
this.dispose = dispose;
}
@Override
public void call(Subscriber<? super T> subscriber) {
try {
final Resource resource = resourceFactory.call();
final AsyncSubject<T> disposeOutcome = AsyncSubject.create();
subscriber.add(Subscriptions.create(new Action0() {
@Override
public void call() {
dispose.call(resource, disposeOutcome);
}
}));
@SuppressWarnings("unchecked")
Observable<T> observable = (Observable<T>)observableFactory.call(resource);
observable.mergeWith(disposeOutcome).subscribe(subscriber);
} catch (Throwable e) {
// eagerly call unsubscribe since this operator is specifically about resource management
subscriber.unsubscribe();
// then propagate error
subscriber.onError(e);
}
}
public Observable<T> toObservable() {
return Observable.create(this);
}
} |
Thanks for the suggestion, but do you agree that the underlying issue is in RxJava itself that it sliently swallows errors? If you do then I think the fix should be done to RxJava itself instead. |
Note that without addressing the core issue of RxJava silently swallowing errors, the eager disposal overload for |
This issue is a bit aged, what is the verdict? |
Looks to me that we should do these things:
|
Looks it's a very simple fix to call the RxJava plugin: just replace this line
|
That would call |
Yep, you're right. So I'll just need code like this in the catch on the dispose action in try {
RxJavaPlugins.getInstance().getErrorHandler().handleError(e);
} catch (Throwable e) {
e.printStackTrace();
} |
Stepping back a bit, we probably should put the catch in try {
unsubscribe();
} catch (Throwable e) {
try {
RxJavaPlugins.getInstance().getErrorHandler().handleError(e);
} catch (Throwable pluginException) {
handlePluginException(pluginException);
}
} |
By the way, the change to |
If unsubscription is asynchronous and SafeSubscriber won't swallow it, it should be caught by the user or handled by the thread's |
Yeah I agree @zsxwing. If some Operator is introduced into the chain that unsubscribes from upstream asynchronously then it becomes its responsibility to handle exceptions (by for example calling the RxJavaPlugin error handler). So I can knock up a PR for this or would you like to do it @zsxwing as it was your fix? |
@davidmoten feel free to send a PR :) |
I've submitted a fix for |
Still thinking about this. I suspect post termination unsubscribe failures are the ones that need |
The |
Code:
Output:
This program finishes without error, and the
RuntimeException
thrown in dispose action is silently ignored.The text was updated successfully, but these errors were encountered: