
reactor.core.publisher.FluxRefCount Maven / Gradle / Ivy
/*
* Copyright (c) 2011-2017 Pivotal Software Inc, All Rights Reserved.
*
* 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 reactor.core.publisher;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
import java.util.function.Consumer;
import org.reactivestreams.Subscription;
import reactor.core.CoreSubscriber;
import reactor.core.Disposable;
import reactor.core.Fuseable;
import reactor.core.Scannable;
import reactor.util.annotation.Nullable;
/**
* Connects to the underlying Flux once the given number of Subscribers subscribed
* to it and disconnects once all Subscribers cancelled their Subscriptions.
*
* @param the value type
* @see Reactive-Streams-Commons
*/
final class FluxRefCount extends Flux implements Scannable, Fuseable {
final ConnectableFlux extends T> source;
final int n;
volatile RefCountMonitor connection;
@SuppressWarnings("rawtypes")
static final AtomicReferenceFieldUpdater CONNECTION =
AtomicReferenceFieldUpdater.newUpdater(FluxRefCount.class, RefCountMonitor.class, "connection");
FluxRefCount(ConnectableFlux extends T> source, int n) {
if (n <= 0) {
throw new IllegalArgumentException("n > 0 required but it was " + n);
}
this.source = Objects.requireNonNull(source, "source");
this.n = n;
}
@Override
public int getPrefetch() {
return source.getPrefetch();
}
@Override
public void subscribe(CoreSubscriber super T> actual) {
RefCountMonitor state;
for (;;) {
state = connection;
if (state == null || OperatorDisposables.isDisposed(state.disconnect)) {
RefCountMonitor u = new RefCountMonitor<>(n, this);
if (!CONNECTION.compareAndSet(this, state, u)) {
continue;
}
state = u;
}
state.subscribe(actual);
break;
}
}
@Override
@Nullable
public Object scanUnsafe(Attr key) {
if (key == Attr.PREFETCH) return getPrefetch();
if (key == Attr.PARENT) return source;
return null;
}
static final class RefCountMonitor implements Consumer {
final int n;
final FluxRefCount extends T> parent;
volatile int subscribers;
@SuppressWarnings("rawtypes")
static final AtomicIntegerFieldUpdater SUBSCRIBERS =
AtomicIntegerFieldUpdater.newUpdater(RefCountMonitor.class, "subscribers");
volatile Disposable disconnect;
@SuppressWarnings("rawtypes")
static final AtomicReferenceFieldUpdater DISCONNECT =
AtomicReferenceFieldUpdater.newUpdater(RefCountMonitor.class, Disposable.class, "disconnect");
RefCountMonitor(int n, FluxRefCount extends T> parent) {
this.n = n;
this.parent = parent;
}
void subscribe(CoreSubscriber super T> s) {
// FIXME think about what happens when subscribers come and go below the connection threshold concurrently
RefCountInner inner = new RefCountInner<>(s, this);
parent.source.subscribe(inner);
if (SUBSCRIBERS.incrementAndGet(this) == n) {
parent.source.connect(this);
}
}
@Override
public void accept(Disposable r) {
if (!DISCONNECT.compareAndSet(this, null, r)) {
r.dispose();
}
}
void innerCancelled() {
if (SUBSCRIBERS.decrementAndGet(this) == 0) {
OperatorDisposables.dispose(DISCONNECT, this);
}
}
void upstreamFinished() {
Disposable a = disconnect;
if (a != OperatorDisposables.DISPOSED) {
DISCONNECT.getAndSet(this, OperatorDisposables.DISPOSED);
}
}
}
static final class RefCountInner
implements QueueSubscription, InnerOperator {
final CoreSubscriber super T> actual;
final RefCountMonitor parent;
Subscription s;
QueueSubscription qs;
RefCountInner(CoreSubscriber super T> actual, RefCountMonitor parent) {
this.actual = actual;
this.parent = parent;
}
@Override
@Nullable
public Object scanUnsafe(Attr key) {
if(key== Attr. PARENT) return s;
return InnerOperator.super.scanUnsafe(key);
}
@Override
public void onSubscribe(Subscription s) {
if (Operators.validate(this.s, s)) {
this.s = s;
actual.onSubscribe(this);
}
}
@Override
public void onNext(T t) {
actual.onNext(t);
}
@Override
public void onError(Throwable t) {
actual.onError(t);
parent.upstreamFinished();
}
@Override
public void onComplete() {
actual.onComplete();
parent.upstreamFinished();
}
@Override
public void request(long n) {
s.request(n);
}
@Override
public void cancel() {
s.cancel();
parent.innerCancelled();
}
@Override
public CoreSubscriber super T> actual() {
return actual;
}
@Override
@SuppressWarnings("unchecked")
public int requestFusion(int requestedMode) {
if(s instanceof QueueSubscription){
qs = (QueueSubscription)s;
return qs.requestFusion(requestedMode);
}
return Fuseable.NONE;
}
@Override
@Nullable
public T poll() {
return qs.poll();
}
@Override
public int size() {
return qs.size();
}
@Override
public boolean isEmpty() {
return qs.isEmpty();
}
@Override
public void clear() {
qs.clear();
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy