All Downloads are FREE. Search and download functionalities are using the official Maven repository.

reactor.rx.subscription.ReactiveSubscription Maven / Gradle / Ivy

/*
 * Copyright (c) 2011-2014 Pivotal Software, 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 reactor.rx.subscription;

import org.reactivestreams.Subscriber;
import reactor.core.queue.CompletableLinkedQueue;
import reactor.core.queue.CompletableQueue;
import reactor.rx.Stream;
import reactor.rx.action.Action;

/**
 * Relationship between a Stream (Publisher) and a Subscriber.
 * 

* A Reactive Subscription using a pattern called "reactive-pull" to dynamically adapt to the downstream subscriber * capacity: * - If no capacity (no previous request or capacity drained), queue data into the buffer {@link CompletableQueue} * - If capacity (previous request and capacity remaining), call subscriber onNext *

* Queued data will be polled when the next request(n) signal is received. If there is remaining requested volume, * it will be added to the current capacity and therefore will let the next signals to be directly pushed. * Each next signal will decrement the capacity by 1. * * @author Stephane Maldini * @since 2.0 */ public class ReactiveSubscription extends PushSubscription { protected final CompletableQueue buffer; //Guarded by this protected boolean draining = false; //Only read from subscriber context protected volatile long currentNextSignals = 0l; //Can be set outside of publisher and subscriber contexts protected volatile long maxCapacity = Long.MAX_VALUE; public ReactiveSubscription(Stream publisher, Subscriber subscriber) { this(publisher, subscriber, new CompletableLinkedQueue()); } public ReactiveSubscription(Stream publisher, Subscriber subscriber, CompletableQueue buffer) { super(publisher, subscriber); this.buffer = buffer; } @Override public void request(long elements) { try { Action.checkRequest(elements); O element; FastList list = null; long toRequest = elements;// Math.min(maxCapacity, elements); boolean last; do { synchronized (this) { //Subscription terminated, Buffer done, return immediately if (terminated == 1) { return; } //If unbounded request, set and return if (toRequest == Long.MAX_VALUE) { //System.out.println(subscriber.getClass().getSimpleName()+" el:"+elements); if (pendingRequestSignals == Long.MAX_VALUE) { return; } else { pendingRequestSignals = Long.MAX_VALUE; } } else { long previous = pendingRequestSignals; if (previous != Long.MAX_VALUE && PENDING_UPDATER.addAndGet(this, toRequest) < 0l) { PENDING_UPDATER.set(this, Long.MAX_VALUE); //onError(SpecificationExceptions.spec_3_17_exception(publisher, subscriber, previous, toRequest)); return; } } draining = !buffer.isEmpty(); if (draining) { list = new FastList(); while (list.size < toRequest && (element = buffer.poll()) != null) { list.add(element); } if (list.size != 0 && pendingRequestSignals != Long.MAX_VALUE) { if (PENDING_UPDATER.addAndGet(this, -list.size) < 0) { pendingRequestSignals = 0l; } } } else { currentNextSignals = 0; } } if (list != null) { drainNext(list); } else { //started if(terminated == 0) { onRequest(elements); } return; } synchronized (this) { draining = !buffer.isEmpty(); last = !draining && buffer.isComplete(); } if (last) { onComplete(); } else { if (elements != Long.MAX_VALUE) { elements -= list.size; } if (draining) { toRequest = elements; } else if (elements > 0l) { //started if(terminated == 0) { onRequest(elements); }else{ updatePendingRequests(elements); } toRequest = 0; } } } while (draining && toRequest > 0); } catch (Exception e) { onError(e); } } @SuppressWarnings("unchecked") private void drainNext(FastList list) { if (list.size > 0) { for (Object el : list.array) { if (el == null) break; currentNextSignals++; subscriber.onNext((O) el); } } } @Override public void onNext(O ev) { synchronized (this) { /*if (draining) { if (ev != null) { if (pendingRequestSignals != Long.MAX_VALUE) { PENDING_UPDATER.incrementAndGet(this); } buffer.add(ev); } return; } else */ if (pendingRequestSignals != Long.MAX_VALUE && PENDING_UPDATER.decrementAndGet(this) < 0l) { PENDING_UPDATER.incrementAndGet(this); if (ev != null) { buffer.add(ev); } return; } else { currentNextSignals++; } } subscriber.onNext(ev); } @Override public void onComplete() { boolean complete = false; if (terminated == 1) return; synchronized (this) { buffer.complete(); if (buffer.isEmpty()) { if (TERMINAL_UPDATER.compareAndSet(this, 0, 1) && subscriber != null) { complete = true; } } } if (complete) { subscriber.onComplete(); } } public long currentNextSignals() { return currentNextSignals; } @Override public void updatePendingRequests(long n) { long oldPending; long newPending; synchronized (this) { oldPending = pendingRequestSignals; newPending = n == 0l ? 0l : oldPending + n; if (newPending < 0) { newPending = n > 0 ? Long.MAX_VALUE : 0; } pendingRequestSignals = newPending; } } @Override public boolean shouldRequestPendingSignals() { synchronized (this) { return pendingRequestSignals > 0 && pendingRequestSignals != Long.MAX_VALUE && (!buffer.isEmpty() || currentNextSignals == maxCapacity); } } @Override public final void maxCapacity(long maxCapacity) { this.maxCapacity = maxCapacity; } public final long getBufferSize() { return buffer != null ? buffer.size() : -1l; } public final long capacity() { return pendingRequestSignals; } public final CompletableQueue getBuffer() { return buffer; } @Override public final boolean isComplete() { synchronized (this) { return buffer.isEmpty() && buffer.isComplete(); } } @Override public String toString() { return "{" + "current=" + currentNextSignals + ", pending=" + (pendingRequestSignals() == Long.MAX_VALUE ? "infinite" : pendingRequestSignals()) + (buffer != null ? (terminated == 1 ? ", complete" : "") + (", waiting=" + buffer.size()) : "") + '}'; } static final class FastList { Object[] array; int size; public void add(Object o) { int s = size; Object[] a = array; if (a == null) { a = new Object[16]; array = a; } else if (s == a.length) { Object[] array2 = new Object[s + (s >> 2)]; System.arraycopy(a, 0, array2, 0, s); a = array2; array = a; } a[s] = o; size = s + 1; } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy