You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Rewrite concat operation to not block on subscribe
The concat operator previously blocked on calling subscribe until all the
sequences had finished. In quite some cases this results in unwanted (and
unexpected) behaviour, such as when prefixing an infinite Observable
with a fixed one, for example when using startWith (which calls concat):
someInputStream.startWith(123).subscribe(x -> print(x));
This statement will block indefinitely if the input stream is infinite. Also
on finite sequences it seems silly to have to wait for them to finish.
In this new approach the incoming observables are put into a queue, instead
of waiting for the whole sequence to finish. When the first observable
completes, the next one is taken from the queue and subscribed to, and so
on. The queue can be extended while processing the observables, and
onCompleted is only called when both the source of observables has completed
and all observables in the queue have been read.
// NB: a well-behaved observable will not interleave on{Next,Error,Completed} calls.
130
-
observer.onError(e);
128
+
if (completedOrErred.compareAndSet(false, true)) {
129
+
if (innerSubscription != null) {
130
+
innerSubscription.unsubscribe();
131
+
}
132
+
observer.onError(e);
133
+
}
131
134
}
132
135
@Override
133
136
publicvoidonCompleted() {
134
-
// NB: a well-behaved observable will not interleave on{Next,Error,Completed} calls.
135
-
if (!innerError.get()) {
136
-
observer.onCompleted();
137
+
allSequencesReceived.set(true);
138
+
if (innerSubscription == null) {
139
+
// We are not subscribed to any sequence, and none are coming anymore
140
+
if (completedOrErred.compareAndSet(false, true)) {
141
+
observer.onCompleted();
142
+
}
137
143
}
138
144
}
139
145
}));
146
+
147
+
returnnewSubscription() {
148
+
@Override
149
+
publicvoidunsubscribe() {
150
+
synchronized (nextSequences) {
151
+
if (innerSubscription != null)
152
+
innerSubscription.unsubscribe();
153
+
outerSubscription.unsubscribe();
154
+
}
155
+
}
156
+
};
140
157
}
141
158
}
142
159
@@ -445,7 +462,7 @@ public void testConcatConcurrentWithInfinity() {
445
462
446
463
447
464
/**
448
-
* The outer observable is running on the same thread and subscribe() in this case is a blocking call. Calling unsubscribe() is no-op because the sequence is complete.
465
+
* Test unsubscribing the concatenated Observable in a single thread.
449
466
*/
450
467
@Test
451
468
publicvoidtestConcatUnsubscribe() {
@@ -459,20 +476,13 @@ public void testConcatUnsubscribe() {
0 commit comments