/* * 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.time.Duration; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicIntegerFieldUpdater; import java.util.concurrent.atomic.AtomicReferenceFieldUpdater; import java.util.function.Consumer; import org.reactivestreams.Subscriber; import org.reactivestreams.Subscription; import reactor.core.Disposable; import reactor.core.Disposables; import reactor.core.Disposables.SequentialDisposable; import reactor.core.Fuseable; import reactor.core.Scannable; import reactor.core.scheduler.Scheduler; /** * @author Simon Baslé * @author David Karnok */ //adapted from RxJava2Extensions: https://github.com/akarnokd/RxJava2Extensions/blob/master/src/main/java/hu/akarnokd/rxjava2/operators/FlowableRefCountTimeout.java final class FluxRefCountGrace<T> extends Flux<T> implements Scannable, Fuseable { final ConnectableFlux<T> source; final int n; final Duration gracePeriod; final Scheduler scheduler; RefConnection connection; volatile Disposable timeoutTask; static final AtomicReferenceFieldUpdater<FluxRefCountGrace, Disposable> TIMEOUT_TASK = AtomicReferenceFieldUpdater.newUpdater(FluxRefCountGrace.class, Disposable.class, "timeoutTask"); FluxRefCountGrace(ConnectableFlux<T> source, int n, Duration gracePeriod, Scheduler scheduler) { this.source = source; this.n = n; this.gracePeriod = gracePeriod; this.scheduler = scheduler; } @Override public int getPrefetch() { return source.getPrefetch(); } @Override public Object scan(Attr key) { switch (key){ case PREFETCH: return getPrefetch(); case PARENT: return source; } return null; } @Override public void subscribe(Subscriber<? super T> s) { for (; ; ) { RefConnection conn; boolean connect = false; synchronized (this) { conn = connection; if (conn == null || conn.terminated) { conn = new RefConnection(this); connection = conn; } long c = conn.subscriberCount; if (c == 0L && conn.timer != null) { conn.timer.dispose(); } conn.subscriberCount = c + 1; if (!conn.connected && c + 1 == n) { connect = true; conn.connected = true; } } source.subscribe(new RefCountSubscriber<>(s, this, conn)); if (connect) { source.connect(conn); } break; } } void cancel(RefConnection rc) { SequentialDisposable sd; synchronized (this) { if (rc.terminated) { return; } long c = rc.subscriberCount - 1; rc.subscriberCount = c; if (c != 0L || !rc.connected) { return; } if (gracePeriod.isZero()) { timeout(rc); return; } sd = new SequentialDisposable(); rc.timer = sd; } sd.replace(scheduler.schedule(rc, gracePeriod.toMillis(), TimeUnit.MILLISECONDS)); } void terminated(RefConnection rc) { synchronized (this) { if (!rc.terminated) { rc.terminated = true; connection = null; } } } void timeout(RefConnection rc) { synchronized (this) { if (rc.subscriberCount == 0 && rc == connection) { Disposables.dispose(RefConnection.SOURCE_DISCONNECTOR, rc); if (source instanceof Disposable) { ((Disposable) source).dispose(); } connection = null; } } } static final class RefConnection implements Runnable, Consumer<Disposable> { final FluxRefCountGrace<?> parent; Disposable timer; long subscriberCount; boolean connected; boolean terminated; volatile Disposable sourceDisconnector; static final AtomicReferenceFieldUpdater<RefConnection, Disposable> SOURCE_DISCONNECTOR = AtomicReferenceFieldUpdater.newUpdater(RefConnection.class, Disposable.class, "sourceDisconnector"); RefConnection(FluxRefCountGrace<?> parent) { this.parent = parent; } @Override public void run() { parent.timeout(this); } @Override public void accept(Disposable t) { Disposables.replace(SOURCE_DISCONNECTOR, this, t); } } static final class RefCountSubscriber<T> implements Subscriber<T>, Subscription { final Subscriber<? super T> actual; final FluxRefCountGrace<T> parent; final RefConnection connection; Subscription upstream; volatile int parentDone; static final AtomicIntegerFieldUpdater<RefCountSubscriber> PARENT_DONE = AtomicIntegerFieldUpdater.newUpdater(RefCountSubscriber.class, "parentDone"); RefCountSubscriber(Subscriber<? super T> actual, FluxRefCountGrace<T> parent, RefConnection connection) { this.actual = actual; this.parent = parent; this.connection = connection; } @Override public void onNext(T t) { actual.onNext(t); } @Override public void onError(Throwable t) { actual.onError(t); if (PARENT_DONE.compareAndSet(this, 0, 1)) { parent.terminated(connection); } } @Override public void onComplete() { actual.onComplete(); if (PARENT_DONE.compareAndSet(this, 0, 1)) { parent.terminated(connection); } } @Override public void request(long n) { upstream.request(n); } @Override public void cancel() { upstream.cancel(); if (PARENT_DONE.compareAndSet(this, 0, 1)) { parent.cancel(connection); } } @Override public void onSubscribe(Subscription s) { if (Operators.validate(upstream, s)) { this.upstream = s; actual.onSubscribe(this); } } } }