Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/**
* 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 java.util.Queue;
import java.util.concurrent.atomic.*;
import rx.*;
import rx.exceptions.*;
import rx.functions.*;
import rx.internal.util.*;
import rx.internal.util.unsafe.*;
import rx.observables.ConnectableObservable;
import rx.subscriptions.Subscriptions;
/**
* A connectable observable which shares an underlying source and dispatches source values to subscribers in a backpressure-aware
* manner.
* @param the value type
*/
public final class OperatorPublish extends ConnectableObservable {
/** The source observable. */
final Observable extends T> source;
/** Holds the current subscriber that is, will be or just was subscribed to the source observable. */
final AtomicReference> current;
/**
* Creates a OperatorPublish instance to publish values of the given source observable.
* @param the value type
* @param source the source observable
* @return the connectable observable
*/
public static ConnectableObservable create(Observable extends T> source) {
// the current connection to source needs to be shared between the operator and its onSubscribe call
final AtomicReference> curr = new AtomicReference>();
OnSubscribe onSubscribe = new OnSubscribe() {
@Override
public void call(Subscriber super T> child) {
// concurrent connection/disconnection may change the state,
// we loop to be atomic while the child subscribes
for (;;) {
// get the current subscriber-to-source
PublishSubscriber r = curr.get();
// if there isn't one or it is unsubscribed
if (r == null || r.isUnsubscribed()) {
// create a new subscriber to source
PublishSubscriber u = new PublishSubscriber(curr);
// perform extra initialization to avoid 'this' to escape during construction
u.init();
// let's try setting it as the current subscriber-to-source
if (!curr.compareAndSet(r, u)) {
// didn't work, maybe someone else did it or the current subscriber
// to source has just finished
continue;
}
// we won, let's use it going onwards
r = u;
}
// create the backpressure-managing producer for this child
InnerProducer inner = new InnerProducer(r, child);
/*
* Try adding it to the current subscriber-to-source, add is atomic in respect
* to other adds and the termination of the subscriber-to-source.
*/
if (!r.add(inner)) {
/*
* The current PublishSubscriber has been terminated, try with a newer one.
*/
continue;
/*
* Note: although technically correct, concurrent disconnects can cause
* unexpected behavior such as child subscribers never receiving anything
* (unless connected again). An alternative approach, similar to
* PublishSubject would be to immediately terminate such child
* subscribers as well:
*
* Object term = r.terminalEvent;
* if (r.nl.isCompleted(term)) {
* child.onCompleted();
* } else {
* child.onError(r.nl.getError(term));
* }
* return;
*
* The original concurrent behavior was non-deterministic in this regard as well.
* Allowing this behavior, however, may introduce another unexpected behavior:
* after disconnecting a previous connection, one might not be able to prepare
* a new connection right after a previous termination by subscribing new child
* subscribers asynchronously before a connect call.
*/
}
// the producer has been registered with the current subscriber-to-source so
// at least it will receive the next terminal event
child.add(inner);
// setting the producer will trigger the first request to be considered by
// the subscriber-to-source.
child.setProducer(inner);
break; // NOPMD
}
}
};
return new OperatorPublish(onSubscribe, source, curr);
}
public static Observable create(final Observable extends T> source,
final Func1 super Observable, ? extends Observable> selector) {
return create(source, selector, false);
}
public static Observable create(final Observable extends T> source,
final Func1 super Observable, ? extends Observable> selector, final boolean delayError) {
return create(new OnSubscribe() {
@Override
public void call(final Subscriber super R> child) {
final OnSubscribePublishMulticast op = new OnSubscribePublishMulticast(RxRingBuffer.SIZE, delayError);
Subscriber subscriber = new Subscriber() {
@Override
public void onNext(R t) {
child.onNext(t);
}
@Override
public void onError(Throwable e) {
op.unsubscribe();
child.onError(e);
}
@Override
public void onCompleted() {
op.unsubscribe();
child.onCompleted();
}
@Override
public void setProducer(Producer p) {
child.setProducer(p);
}
};
child.add(op);
child.add(subscriber);
selector.call(Observable.create(op)).unsafeSubscribe(subscriber);
source.unsafeSubscribe(op.subscriber());
}
});
}
private OperatorPublish(OnSubscribe onSubscribe, Observable extends T> source,
final AtomicReference> current) {
super(onSubscribe);
this.source = source;
this.current = current;
}
@Override
public void connect(Action1 super Subscription> connection) {
boolean doConnect = false;
PublishSubscriber ps;
// we loop because concurrent connect/disconnect and termination may change the state
for (;;) {
// retrieve the current subscriber-to-source instance
ps = current.get();
// if there is none yet or the current has unsubscribed
if (ps == null || ps.isUnsubscribed()) {
// create a new subscriber-to-source
PublishSubscriber u = new PublishSubscriber(current);
// initialize out the constructor to avoid 'this' to escape
u.init();
// try setting it as the current subscriber-to-source
if (!current.compareAndSet(ps, u)) {
// did not work, perhaps a new subscriber arrived
// and created a new subscriber-to-source as well, retry
continue;
}
ps = u;
}
// if connect() was called concurrently, only one of them should actually
// connect to the source
doConnect = !ps.shouldConnect.get() && ps.shouldConnect.compareAndSet(false, true);
break; // NOPMD
}
/*
* Notify the callback that we have a (new) connection which it can unsubscribe
* but since ps is unique to a connection, multiple calls to connect() will return the
* same Subscription and even if there was a connect-disconnect-connect pair, the older
* references won't disconnect the newer connection.
* Synchronous source consumers have the opportunity to disconnect via unsubscribe on the
* Subscription as unsafeSubscribe may never return in its own.
*
* Note however, that asynchronously disconnecting a running source might leave
* child-subscribers without any terminal event; PublishSubject does not have this
* issue because the unsubscription was always triggered by the child-subscribers
* themselves.
*/
connection.call(ps);
if (doConnect) {
source.unsafeSubscribe(ps);
}
}
@SuppressWarnings("rawtypes")
static final class PublishSubscriber extends Subscriber implements Subscription {
/** Holds notifications from upstream. */
final Queue