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.interval(1, 1, TimeUnit.SECONDS, mainThread()) continues execution if unsubscribed from onNext #214
Comments
Interesting. It looks like we aren't checking for unsubscription before scheduling new work, which (at least according to akanorkd) seems to be a good idea. I'll work on a PR; I've already got a fix (it's simple) but I'm trying to figure out the best way to test it. |
Fixed on |
I've checked the patch and it doesn't completely solve the problem. If there is an usubscription race between the new Maybe I haven't properly expressed this in the relevant blog post, but you need to schedule first then register the remove action with the ScheduledAction. This way, even if the task gets scheduled despite the worker is unsubscribed, it will be cancelled right away. In the current form, if the unsubscription triggers the remove, the task is not scheduled yet and nothing happens, then the task gets scheduled. The correct way to do this: public Subscription schedule(Action0 action, long delayTime, TimeUnit unit) {
if (compositeSubscription.isUnsubscribed()) {
return Subscriptions.unsubscribed();
}
action = RxAndroidPlugins.getInstance().getSchedulersHook().onSchedule(action);
final ScheduledAction scheduledAction = new ScheduledAction(action);
scheduledAction.addParent(compositeSubscription);
compositeSubscription.add(scheduledAction);
handler.postDelayed(scheduledAction, unit.toMillis(delayTime));
scheduledAction.add(Subscriptions.create(new Action0() {
@Override
public void call() {
handler.removeCallbacks(scheduledAction);
}
}));
return scheduledAction;
} Unrelated but:
The scheduled task may get executed before the |
The truth is that I haven't gotten to that post yet. (It's a lot of information to digest so I've been taking them slowly.) I highly appreciate the help. I'll submit another PR with the correct ordering. Do you know if there's a way to test this race condition? |
@akarnokd Actually, another question (while we're on correctness). The linked post wraps the public Subscription schedule(Action0 action, long delayTime, TimeUnit unit) {
if (compositeSubscription.isUnsubscribed()) {
return Subscriptions.unsubscribed();
}
action = RxAndroidPlugins.getInstance().getSchedulersHook().onSchedule(action);
final ScheduledAction scheduledAction = new ScheduledAction(action);
scheduledAction.addParent(compositeSubscription);
compositeSubscription.add(scheduledAction);
final Runnable r = new Runnable() {
@Override
public void run() {
if (!scheduledAction.isUnsubscribed()) {
scheduledAction.run();
}
}
};
handler.postDelayed(r, unit.toMillis(delayTime));
scheduledAction.add(Subscriptions.create(new Action0() {
@Override
public void call() {
handler.removeCallbacks(r);
}
}));
return scheduledAction;
} |
Add a scheduler hook that unsubscribes the worker and schedule a task with 100ms delay. The old code will execute this task and my new code won't. |
It depends on how eager you want to be by not executing a task that got cancelled just before it started to run. I assume the call to In RxJava, the threadpools use CAS to cancel a future atomically. If that succeeds, the task won't run. If it fails, another CAS enters an interrupting state and then interrupts the thread. Since you don't have the ability check for isUnsubscribed() in the actual task anyway, it doesn't really matter if you check |
Subj.
Reproduction:
Output:
INTERVAL﹕ 0
INTERVAL﹕ 1
INTERVAL﹕ 2
INTERVAL﹕ 3
etc.
Workaround:
Output:
INTERVAL﹕ 0
The text was updated successfully, but these errors were encountered: