rx.internal.operators.OnSubscribeDetach Maven / Gradle / Ivy
The newest version!
/**
* Copyright 2016 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 java.util.concurrent.atomic.*;
import rx.*;
import rx.Observable.OnSubscribe;
import rx.plugins.RxJavaHooks;
/**
* Nulls out references to upstream data structures when the source terminates or
* the child unsubscribes.
* @param the value type
*/
public final class OnSubscribeDetach implements OnSubscribe {
final Observable source;
public OnSubscribeDetach(Observable source) {
this.source = source;
}
@Override
public void call(Subscriber t) {
DetachSubscriber parent = new DetachSubscriber(t);
DetachProducer producer = new DetachProducer(parent);
t.add(producer);
t.setProducer(producer);
source.unsafeSubscribe(parent);
}
/**
* The parent subscriber that forwards events and cleans up on a terminal state.
* @param the value type
*/
static final class DetachSubscriber extends Subscriber {
final AtomicReference> actual;
final AtomicReference producer;
final AtomicLong requested;
public DetachSubscriber(Subscriber actual) {
this.actual = new AtomicReference>(actual);
this.producer = new AtomicReference();
this.requested = new AtomicLong();
}
@Override
public void onNext(T t) {
Subscriber a = actual.get();
if (a != null) {
a.onNext(t);
}
}
@Override
public void onError(Throwable e) {
producer.lazySet(TerminatedProducer.INSTANCE);
Subscriber a = actual.getAndSet(null);
if (a != null) {
a.onError(e);
} else {
RxJavaHooks.onError(e);
}
}
@Override
public void onCompleted() {
producer.lazySet(TerminatedProducer.INSTANCE);
Subscriber a = actual.getAndSet(null);
if (a != null) {
a.onCompleted();
}
}
void innerRequest(long n) {
if (n < 0L) {
throw new IllegalArgumentException("n >= 0 required but it was " + n);
}
Producer p = producer.get();
if (p != null) {
p.request(n);
} else {
BackpressureUtils.getAndAddRequest(requested, n);
p = producer.get();
if (p != null && p != TerminatedProducer.INSTANCE) {
long r = requested.getAndSet(0L);
p.request(r);
}
}
}
@Override
public void setProducer(Producer p) {
if (producer.compareAndSet(null, p)) {
long r = requested.getAndSet(0L);
p.request(r);
} else {
if (producer.get() != TerminatedProducer.INSTANCE) {
throw new IllegalStateException("Producer already set!");
}
}
}
void innerUnsubscribe() {
producer.lazySet(TerminatedProducer.INSTANCE);
actual.lazySet(null);
// full barrier in unsubscribe()
unsubscribe();
}
}
/**
* Callbacks from the child Subscriber.
* @param the value type
*/
static final class DetachProducer implements Producer, Subscription {
final DetachSubscriber parent;
public DetachProducer(DetachSubscriber parent) {
this.parent = parent;
}
@Override
public void request(long n) {
parent.innerRequest(n);
}
@Override
public boolean isUnsubscribed() {
return parent.isUnsubscribed();
}
@Override
public void unsubscribe() {
parent.innerUnsubscribe();
}
}
/**
* Singleton instance via enum.
*/
enum TerminatedProducer implements Producer {
INSTANCE;
@Override
public void request(long n) {
// ignored
}
}
}