rx.subjects.SubjectSubscriptionManager Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of rxjava-core Show documentation
Show all versions of rxjava-core Show documentation
rxjava-core developed by Netflix
/**
* 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.subjects;
import java.util.Arrays;
import java.util.Collection;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicReference;
import rx.Observable.OnSubscribe;
import rx.Observer;
import rx.Subscriber;
import rx.Subscription;
import rx.functions.Action0;
import rx.functions.Action1;
import rx.operators.SafeObservableSubscription;
import rx.subscriptions.Subscriptions;
/* package */class SubjectSubscriptionManager {
private AtomicReference> state = new AtomicReference>(new State());
/**
*
* @param onSubscribe
* Always runs at the beginning of 'subscribe' regardless of terminal state.
* @param onTerminated
* Only runs if Subject is in terminal state and the Observer ends up not being registered.
* @param onUnsubscribe called after the child subscription is removed from the state
* @return
*/
public OnSubscribe getOnSubscribeFunc(final Action1> onSubscribe,
final Action1> onTerminated,
final Action1> onUnsubscribe) {
return new OnSubscribe() {
@Override
public void call(Subscriber super T> actualOperator) {
final SubjectObserver observer = new SubjectObserver(actualOperator);
// invoke onSubscribe logic
if (onSubscribe != null) {
onSubscribe.call(observer);
}
State current;
State newState = null;
boolean addedObserver = false;
do {
current = state.get();
if (current.terminated) {
// we are terminated so don't need to do anything
addedObserver = false;
// break out and don't try to modify state
newState = current;
// wait for termination to complete if
try {
current.terminationLatch.await();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new RuntimeException("Interrupted waiting for termination.", e);
}
break;
} else {
final SafeObservableSubscription subscription = new SafeObservableSubscription();
actualOperator.add(subscription); // add to parent if the Subject itself is unsubscribed
addedObserver = true;
subscription.wrap(Subscriptions.create(new Action0() {
@Override
public void call() {
State current;
State newState;
do {
current = state.get();
// on unsubscribe remove it from the map of outbound observers to notify
newState = current.removeObserver(subscription);
} while (!state.compareAndSet(current, newState));
if (onUnsubscribe != null) {
onUnsubscribe.call(observer);
}
}
}));
// on subscribe add it to the map of outbound observers to notify
newState = current.addObserver(subscription, observer);
}
} while (!state.compareAndSet(current, newState));
/**
* Whatever happened above, if we are terminated we run `onTerminated`
*/
if (newState.terminated && !addedObserver) {
onTerminated.call(observer);
}
}
};
}
@SuppressWarnings({ "unchecked", "rawtypes" })
protected void terminate(Action1>> onTerminate) {
State current;
State newState = null;
do {
current = state.get();
if (current.terminated) {
// already terminated so do nothing
return;
} else {
newState = current.terminate();
}
} while (!state.compareAndSet(current, newState));
/*
* if we get here then we won setting the state to terminated
* and have a deterministic set of Observers to emit to (concurrent subscribes
* will have failed and will try again and see we are term
* inated)
*/
try {
// had to circumvent type check, we know what the array contains
onTerminate.call((Collection) Arrays.asList(newState.observers));
} finally {
// mark that termination is completed
newState.terminationLatch.countDown();
}
}
/**
* Returns the array of observers directly.
* Don't modify the array!
*
* @return the array of current observers
*/
@SuppressWarnings("unchecked")
public SubjectObserver
© 2015 - 2024 Weber Informatics LLC | Privacy Policy