-
Notifications
You must be signed in to change notification settings - Fork 7.6k
New operators: concatEmptyWith
and mergeEmptyWith
.
#3060
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
Changes from all commits
009b3e2
d77a655
fb2e540
09957fb
a641321
ad29ab2
aa6361a
048b435
59b539b
79c7cd2
ccddec4
efefdb7
336327f
b5cec41
56da24d
e886052
1aab0f7
2f815f7
36167ff
9263fc2
e0476ed
038e1ac
bee4f4b
8b5cbb0
04c03fb
34dfe3b
86f071c
662c954
6287105
829c6ce
893b417
df9d3cb
e31c0d3
7980405
9004776
25bbcf1
adbbd61
55dccd0
996f366
6808ce9
3b8976d
fac460d
b757ece
49692e4
6d4f25c
5804bed
e5ec3f0
619abfb
3dcfe18
55fcd41
f2a8400
2df3c64
9cc4b1b
eb945d8
1e56564
df2d934
135312e
9334b2a
f9ab970
d6aabba
8859bcf
3dd46e6
6f73a02
bf0f3e9
bf1359f
730be58
29775c9
9522235
53c523c
79f2ae7
c929e0d
4cac9f5
3bafb53
782b0e7
2d0ff46
7aef4f8
324514a
50c2234
6634212
ffcd875
22f6ba7
8569ff2
3f6ed32
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,203 @@ | ||
/** | ||
* Copyright 2014 Netflix, Inc. | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not | ||
* use this file except in compliance with the License. You may obtain a copy of | ||
* the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | ||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | ||
* License for the specific language governing permissions and limitations under | ||
* the License. | ||
*/ | ||
package rx.internal.operators; | ||
|
||
import rx.Observable; | ||
import rx.Observable.Operator; | ||
import rx.Producer; | ||
import rx.Subscriber; | ||
import rx.internal.producers.ProducerArbiter; | ||
import rx.subscriptions.SerialSubscription; | ||
|
||
/** | ||
* Returns an Observable that emits an error if any item is emitted by the source and emits items from the supplied | ||
* alternate {@code Observable} after the source completes. | ||
* | ||
* @param <T> the source value type | ||
* @param <R> the result value type | ||
*/ | ||
public final class OperatorConcatEmptyWith<T, R> implements Operator<R, T> { | ||
|
||
private final Observable<? extends R> alternate; | ||
|
||
public OperatorConcatEmptyWith(Observable<? extends R> alternate) { | ||
this.alternate = alternate; | ||
} | ||
|
||
@Override | ||
public Subscriber<? super T> call(Subscriber<? super R> child) { | ||
final SerialSubscription ssub = new SerialSubscription(); | ||
final ParentSubscriber parent = new ParentSubscriber(child, ssub, alternate); | ||
ssub.set(parent); | ||
child.add(ssub); | ||
child.setProducer(parent.emptyProducer); | ||
return parent; | ||
} | ||
|
||
private final class ParentSubscriber extends Subscriber<T> { | ||
|
||
private final Subscriber<? super R> child; | ||
private final SerialSubscription ssub; | ||
private final EmptyProducer emptyProducer; | ||
private final Observable<? extends R> alternate; | ||
|
||
ParentSubscriber(Subscriber<? super R> child, final SerialSubscription ssub, Observable<? extends R> alternate) { | ||
this.child = child; | ||
this.ssub = ssub; | ||
this.emptyProducer = new EmptyProducer(); | ||
this.alternate = alternate; | ||
} | ||
|
||
@Override | ||
public void setProducer(final Producer producer) { | ||
/* | ||
* Always request Max from the parent as we never really expect the parent to emit an item, so the | ||
* actual value does not matter. However, if the parent producer is waiting for a request to emit | ||
* a terminal event, not requesting the same will cause a deadlock of the parent never completing and | ||
* the child never subscribed. | ||
*/ | ||
producer.request(Long.MAX_VALUE); | ||
} | ||
|
||
@Override | ||
public void onCompleted() { | ||
if (!child.isUnsubscribed()) { | ||
AlternateSubscriber as = new AlternateSubscriber(child, emptyProducer); | ||
ssub.set(as); | ||
alternate.unsafeSubscribe(as); | ||
} | ||
} | ||
|
||
@Override | ||
public void onError(Throwable e) { | ||
child.onError(e); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I suggest calling There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I see, I was assuming that there will be a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Generally, if an There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I actually do not understand the need for calling There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You are calling onError from onNext and the upstream may not receive an unsubscribe in time and keeps emitting onNexts. Better yet, you should call unsubscribe in onNext to stop the upstream immediately. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So, this is designed to be concatenating an |
||
} | ||
|
||
@Override | ||
public void onNext(T t) { | ||
onError(new IllegalStateException("Concat empty with source emitted an item: " + t)); | ||
} | ||
} | ||
|
||
private final class AlternateSubscriber extends Subscriber<R> { | ||
|
||
private final EmptyProducer emptyProducer; | ||
private final Subscriber<? super R> child; | ||
|
||
AlternateSubscriber(Subscriber<? super R> child, EmptyProducer emptyProducer) { | ||
this.child = child; | ||
this.emptyProducer = emptyProducer; | ||
} | ||
|
||
@Override | ||
public void setProducer(final Producer producer) { | ||
emptyProducer.setAltProducer(producer); | ||
} | ||
|
||
@Override | ||
public void onCompleted() { | ||
child.onCompleted(); | ||
} | ||
|
||
@Override | ||
public void onError(Throwable e) { | ||
child.onError(e); | ||
} | ||
|
||
@Override | ||
public void onNext(R r) { | ||
child.onNext(r); | ||
} | ||
} | ||
|
||
/** | ||
* This is a producer implementation that does the following: | ||
* | ||
* <ul> | ||
* <li>If the alternate producer has not yet arrived, store the total requested count from downstream.</li> | ||
* <li>If the alternate producer has arrived, then relay the request demand to it.</li> | ||
* <li>Request {@link Long#MAX_VALUE} from the parent producer, the first time the child requests anything.</li> | ||
* </ul> | ||
* | ||
* Since, this is only applicable to this operator, it does not check for emissions from the source, as the source | ||
* is never expected to emit any item. Thus it is "lighter" weight than {@link ProducerArbiter} | ||
*/ | ||
private static final class EmptyProducer implements Producer { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This class is unnecessary, the functionality is already covered by There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I started off with I have put this reasoning behind this class in the javadoc as a disclaimer :) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Okay. |
||
|
||
/*Total requested items till the time the alternate producer arrives.*/ | ||
private long missedRequested; /*Guarded by this*/ | ||
/*Producer from the alternate Observable for this operator*/ | ||
private Producer altProducer; /*Guarded by this*/ | ||
|
||
@Override | ||
public void request(final long requested) { | ||
if (requested < 0) { | ||
throw new IllegalArgumentException("Requested items can not be negative."); | ||
} | ||
|
||
if (requested == 0) { | ||
return; | ||
} | ||
|
||
boolean requestToAlternate = false; | ||
Producer _altProducer; | ||
synchronized (this) { | ||
if (null == altProducer) { | ||
/*Accumulate requested till the time an alternate producer arrives.*/ | ||
long r = this.missedRequested; | ||
long u = r + requested; | ||
if (u < 0) { | ||
u = Long.MAX_VALUE; | ||
} | ||
this.missedRequested = u; | ||
} else { | ||
/*If the alternate producer exists, then relay a valid request. The missed requested will be | ||
requested from the alt producer on setProducer()*/ | ||
requestToAlternate = true; | ||
} | ||
|
||
_altProducer = altProducer; | ||
} | ||
|
||
if (requestToAlternate) { | ||
_altProducer.request(requested); | ||
} | ||
} | ||
|
||
private void setAltProducer(final Producer altProducer) { | ||
if (null == altProducer) { | ||
throw new IllegalArgumentException("Producer can not be null."); | ||
} | ||
|
||
boolean requestToAlternate = false; | ||
long _missedRequested; | ||
|
||
synchronized (this) { | ||
if (0 != missedRequested) { | ||
/*Something was requested from the source Observable, relay that to the new producer*/ | ||
requestToAlternate = true; | ||
} | ||
|
||
this.altProducer = altProducer; | ||
_missedRequested = missedRequested; | ||
} | ||
|
||
if (requestToAlternate) { | ||
altProducer.request(_missedRequested); | ||
} | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You should use
ProducerArbiter
instead.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See my reasoning down.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okay.