package rsc.subscriber; import java.util.Objects; import java.util.concurrent.atomic.AtomicLongFieldUpdater; import java.util.concurrent.atomic.AtomicReferenceFieldUpdater; import org.reactivestreams.Subscription; import rsc.flow.Receiver; import rsc.flow.Trackable; import rsc.util.BackpressureHelper; /** * Base class for Subscribers that will receive their Subscriptions at any time yet * they need to be cancelled or requested at any time. */ public class DeferredSubscription implements Subscription, Receiver, Trackable { volatile Subscription s; static final AtomicReferenceFieldUpdater<DeferredSubscription, Subscription> S = AtomicReferenceFieldUpdater.newUpdater(DeferredSubscription.class, Subscription.class, "s"); volatile long requested; static final AtomicLongFieldUpdater<DeferredSubscription> REQUESTED = AtomicLongFieldUpdater.newUpdater(DeferredSubscription.class, "requested"); protected final void setInitialRequest(long n) { REQUESTED.lazySet(this, n); } /** * Sets the Subscription once but does not request anything. * @param s the Subscription to set * @return true if successful, false if the current subscription is not null */ protected final boolean setWithoutRequesting(Subscription s) { Objects.requireNonNull(s, "s"); for (;;) { Subscription a = this.s; if (a == SubscriptionHelper.cancelled()) { s.cancel(); return false; } if (a != null) { s.cancel(); SubscriptionHelper.reportSubscriptionSet(); return false; } if (S.compareAndSet(this, null, s)) { return true; } } } /** * Requests the deferred amount if not zero. */ protected final void requestDeferred() { long r = REQUESTED.getAndSet(this, 0L); if (r != 0L) { s.request(r); } } /** * Atomically sets the single subscription and requests the missed amount from it. * * @param s * @return false if this arbiter is cancelled or there was a subscription already set */ public final boolean set(Subscription s) { Objects.requireNonNull(s, "s"); Subscription a = this.s; if (a == SubscriptionHelper.cancelled()) { s.cancel(); return false; } if (a != null) { s.cancel(); SubscriptionHelper.reportSubscriptionSet(); return false; } if (S.compareAndSet(this, null, s)) { long r = REQUESTED.getAndSet(this, 0L); if (r != 0L) { s.request(r); } return true; } a = this.s; if (a != SubscriptionHelper.cancelled()) { s.cancel(); return false; } SubscriptionHelper.reportSubscriptionSet(); return false; } @Override public void request(long n) { Subscription a = s; if (a != null) { a.request(n); } else { BackpressureHelper.getAndAddCap(REQUESTED, this, n); a = s; if (a != null) { long r = REQUESTED.getAndSet(this, 0L); if (r != 0L) { a.request(r); } } } } @Override public void cancel() { Subscription a = s; if (a != SubscriptionHelper.cancelled()) { a = S.getAndSet(this, SubscriptionHelper.cancelled()); if (a != null && a != SubscriptionHelper.cancelled()) { a.cancel(); } } } /** * Returns true if this arbiter has been cancelled. * * @return true if this arbiter has been cancelled */ @Override public final boolean isCancelled() { return s == SubscriptionHelper.cancelled(); } @Override public final boolean isStarted() { return s != null; } @Override public final boolean isTerminated() { return isCancelled(); } @Override public long requestedFromDownstream() { return requested; } @Override public Subscription upstream() { return s; } }