-
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
"Interrupted while waiting for subscription to complete." in 1.0.0-rc8 #1804
Comments
The only thing in rc8 that seems related is this: #1793 |
I can not replicate with this code: import java.util.List;
import rx.functions.Func1;
public class Testing {
public static void main(String... args) {
List<String> subThings = Observable.range(0, 1000)
.map(new Func1<Integer, String>() {
@Override
public String call(Integer i) {
return String.valueOf(i + 1);
}
})
.filter(new Func1<String, Boolean>() {
@Override
public Boolean call(String t) {
return t != null;
}
}) //
.distinct() //
.toList() //
.toBlocking() //
.single();
System.out.println(subThings);
}
} |
The code that is being interrupted is this: https://github.com/ReactiveX/RxJava/blob/1.x/src/main/java/rx/observables/BlockingObservable.java#L473 try {
latch.await();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new RuntimeException("Interrupted while waiting for subscription to complete.", e);
} This means the thread was blocked on the latch waiting for a result but while it was waiting it got interrupted. I don't know what thread you are doing this on, nor do I know much about Android. If Android is using pools of threads it could perhaps retain the right to interrupt and reclaim threads? I know event loops in some frameworks do that kind of thing if something is blocked in them and new work is scheduled. RxJava itself does not interrupt threads anywhere (that I'm aware of ... and I can't think of any reason we would) so I'm inclined to suggest looking at what thread you are blocking in and see if Android itself could be interrupting it? Also, could you move to a model where you don't block and instead compose it all together reactively? |
Moving to 1.0.x as it's not obvious yet what is going on here. |
Encountered this on Jellybean. Code in my Application subclass: Observable<SomeSharedPreferencesWrapper> prefsPrefetch = Async.start(this::prefetchPrefs); And later in same class: return prefsPrefetch.toBlocking().single(); I can't grasp exact circumstances, when issue reproduces itself. At first glance they seem to be random, but happens often enough to make prefetching stuff with RxJava pretty much impossible. |
I'm gonna work on putting together a minimal test Android app to try and isolate the issue. |
https://dl.dropboxusercontent.com/u/7829307/RxJavaBlockingBug.zip I wasn't able to repro until I added the PublishSubject layer...maybe the issue is the nested toBlocking() calls? |
Does Android ever interrupt a thread based on user or system activity? If so then the Is request.onNext ever being called concurrently, or is it sequentially? I can't tell for sure but it looks okay as it looks like it's only ever the single UI thread that would trigger an onNext. If you intend on calling it concurrently wrap it in a SerializedSubject. If the UI event listener triggers before I don't particularly see anything wrong in this code ... though to be more idiomatic I would suggest using the Observable sequence to manipulate the data instead of doing it inside the
In this case, instead of doing flatMap.subscribe(all logic here) do something like this instead: request.flatMap(mapResult)
.map(transformFunction)
.filter(notNull())
.distinct()
.toList()
.subscribe(yourListHere) This is more idiomatic, doesn't involve creating another Observable and never requires doing the |
MainActivity all happens on the main thread, as does the onClickHandler. I've worked around the problem in our app by no longer using |
You should really only ever call to blocking in command line test scripts but never in production code. If you really need to end up blocking, you may as well push all the blocking backwardsvand write regular synchrounius code. |
Maybe we should mark toblocking as depricated (forever) such that you get a warning every time you use it. |
Deprecated seems wrong to me since it's never actually going to be removed. I'm all in favor of updating the docs to reflect the recommended uses of toBlocking, though. Or maybe deprecate |
Not everyone have their entire codebase under their control. Also in Android there are already plenty of ways to move tasks to background threads: all those legacy
I am not sure about this one, but FutureTask/ExecutorService combo was working just fine before migrating to RxJava (so was the previous version of RxJava, before updating to rc8). |
Can you provide a reference to documentation about how Android does this? That would be helpful. If it does this then any sort of blocking would be vulnerable to interruption.
Understood, though I still suggest putting most of the processing into the Observable chain and not even having a |
If this doesn't happen on rc7 but does on rc8 then let's binary search what is causing it. Here is what changed: https://github.com/ReactiveX/RxJava/releases/tag/v1.0.0-rc.8 This is the only change that may be related: https://github.com/ReactiveX/RxJava/pull/1793/files Note how it now triggers the unsubscribe upwards before the onNext is emitted whereas before it was after. This could theoretically result in work upstream canceling a Future which interrupts a thread if everything is happening on the same thread. |
Can't find any Android docs that describe what we're seeing. Here's the docs on ANR (Application Not Responding) but we're seeing an actual crash. Maybe worth seeing if it's rxjava itself that is interrupting the thread? |
Yup, I pointed to a change in rc8 above that could potentially cause this. I'm trying to hunt down options. |
Here are the two places I can see that may result in an interrupt:
The first one is used by virtually all use of Schedulers to allow canceling work on a Scheduler. I still can't replicate an interrupt with |
I can replicate: @Test
public void testInterrupt() throws InterruptedException {
final AtomicReference<Object> exception = new AtomicReference<Object>();
final CountDownLatch latch = new CountDownLatch(1);
Observable.just(1).subscribeOn(Schedulers.computation()).take(1).subscribe(new Action1<Integer>() {
@Override
public void call(Integer t1) {
try {
Observable.just(t1).toBlocking().single();
} catch (Exception e) {
exception.set(e);
e.printStackTrace();
} finally {
latch.countDown();
}
}
});
latch.await();
assertNull(exception.get());
}
Reverting #1793 does fix this. So now to figure out if #1793 can be achieved in a different way, or if we need to solve the interruption issue. |
Here is a simpler test: @Test
public void testInterrupt() throws InterruptedException {
final AtomicReference<Object> exception = new AtomicReference<Object>();
final CountDownLatch latch = new CountDownLatch(1);
Observable.just(1).subscribeOn(Schedulers.computation()).take(1).subscribe(new Action1<Integer>() {
@Override
public void call(Integer t1) {
try {
Thread.sleep(100);
} catch (Exception e) {
exception.set(e);
e.printStackTrace();
} finally {
latch.countDown();
}
}
});
latch.await();
assertNull(exception.get());
} |
Ugh ... choosing whether to default to interrupting or not is a difficult one. It seems that perhaps we should not. Any insights on this? |
I have submitted a possible fix but want to think about this more. I can't find an authoritative answer on whether we should default to interrupting or not when the scheduled future is canceled. I think we should change to not interrupting but want to be sure that's correct as we've had it set to interrupt all along. |
same here. |
A quick note about However, there is a case when I use On Android we can create a component that will be plugged to the sync part of the framework. Basically Android framework spawns a new thread for you and invokes your code in that thread. Application sync is supposed to be finished when this thread finishes. In our sync adapter code we build a chain of observables that can perform some operations concurrently using other threads. So the sync adapter code invoked in the sync thread looks like |
@headinthebox A consideration here is that the place we are interrupting the threads is really just for unscheduling any scheduled actions from a Scheduler. I think we intend It seems to me that we should not interrupt a thread automatically and that if a developer needs to do that their We can work around this particular Therefore I suggest we change from |
@roman-mazur Thanks for that information, it is useful information. If I understand correctly you are saying there are normal cases when Android can and will interrupt a thread and thus application code must account for this. Is this what you're saying? As far as RxJava is concerned, I'm suggesting we eliminate the two places where RxJava is the culprit for interrupting the thread (despite #1832 working around the particular issue of using |
Wait, so you DO interrupt threads right now, don't you? Does that mean, that unsubscribing from Observale (in currently released version), during interruptable operation will interrupt it? Will operations, created with If former, it would be really cool to leave it be.
I wouldn't call |
@benjchristensen I would rather say that Android framework is unlikely to interrupt an application thread. The sync worker is the only known example. And interruption is a default behaviour that can be changed. But note that I'm forced to use |
If it is interrupted while waiting on the single item we just fail right now. I suppose we could call This is what currently happens: try {
latch.await();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new RuntimeException("Interrupted while waiting for subscription to complete.", e);
} |
RxJava core libraries only interrupt when unsubscribing a scheduled action on a The async-utils are just utility methods on top of Schedulers so unless there is some effort to specifically prevent interrupt (nothing I can see while browsing the code) they will also be interrupted if they are still executing when unsubscribe is invoked.
If a Scheduler is not involved RxJava itself does nothing other than invoke I'm wondering if that is better for an Observable implementation (via |
Doesn't happen on rc7. I can repro 100% of the time. Happens on both KitKat and Lollipop so I don't think it's specific to any particular Android version.
CachedThing.buildCachedThing
has this block of code that I think is the culprit:I tried writing a testcase to repro but couldn't get it to fail. I'm guessing there's something broken between the android runtime and rxjava's thread management, but I couldn't get any further than that.
The text was updated successfully, but these errors were encountered: