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

rx.internal.producers.ProducerObserverArbiter Maven / Gradle / Ivy

/**
 * Copyright 2015 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.internal.producers;

import java.util.*;

import rx.*;
import rx.Observer;
import rx.exceptions.*;
import rx.internal.operators.BackpressureUtils;

/**
 * Producer that serializes any event emission with requesting and producer changes.
 * 

* The implementation shortcuts on error and overwrites producers that got delayed, similar * to ProducerArbiter. * * @param the value type */ public final class ProducerObserverArbiter implements Producer, Observer { final Subscriber child; boolean emitting; List queue; Producer currentProducer; long requested; long missedRequested; Producer missedProducer; Object missedTerminal; volatile boolean hasError; static final Producer NULL_PRODUCER = new Producer() { @Override public void request(long n) { // deliberately ignored } }; public ProducerObserverArbiter(Subscriber child) { this.child = child; } @Override public void onNext(T t) { synchronized (this) { if (emitting) { List q = queue; if (q == null) { q = new ArrayList(4); queue = q; } q.add(t); return; } emitting = true; } boolean skipFinal = false; try { child.onNext(t); long r = requested; if (r != Long.MAX_VALUE) { requested = r - 1; } emitLoop(); skipFinal = true; } finally { if (!skipFinal) { synchronized (this) { emitting = false; } } } } @Override public void onError(Throwable e) { boolean emit; synchronized (this) { if (emitting) { missedTerminal = e; emit = false; } else { emitting = true; emit = true; } } if (emit) { child.onError(e); } else { hasError = true; } } @Override public void onCompleted() { synchronized (this) { if (emitting) { missedTerminal = true; return; } emitting = true; } child.onCompleted(); } @Override public void request(long n) { if (n < 0) { throw new IllegalArgumentException("n >= 0 required"); } if (n == 0) { return; } synchronized (this) { if (emitting) { missedRequested += n; return; } emitting = true; } Producer p = currentProducer; boolean skipFinal = false; try { long r = requested; long u = r + n; if (u < 0) { u = Long.MAX_VALUE; } requested = u; emitLoop(); skipFinal = true; } finally { if (!skipFinal) { synchronized (this) { emitting = false; } } } if (p != null) { p.request(n); } } public void setProducer(Producer p) { synchronized (this) { if (emitting) { missedProducer = p != null ? p : NULL_PRODUCER; return; } emitting = true; } boolean skipFinal = false; currentProducer = p; long r = requested; try { emitLoop(); skipFinal = true; } finally { if (!skipFinal) { synchronized (this) { emitting = false; } } } if (p != null && r != 0) { p.request(r); } } void emitLoop() { final Subscriber c = child; long toRequest = 0L; Producer requestFrom = null; outer: for (;;) { long localRequested; Producer localProducer; Object localTerminal; List q; boolean quit = false; synchronized (this) { localRequested = missedRequested; localProducer = missedProducer; localTerminal = missedTerminal; q = queue; if (localRequested == 0L && localProducer == null && q == null && localTerminal == null) { emitting = false; quit = true; } else { missedRequested = 0L; missedProducer = null; queue = null; missedTerminal = null; } } if (quit) { if (toRequest != 0L && requestFrom != null) { requestFrom.request(toRequest); } return; } boolean empty = q == null || q.isEmpty(); if (localTerminal != null) { if (localTerminal != Boolean.TRUE) { c.onError((Throwable)localTerminal); return; } else if (empty) { c.onCompleted(); return; } } long e = 0; if (q != null) { for (T v : q) { if (c.isUnsubscribed()) { return; } else if (hasError) { continue outer; // if an error has been set, shortcut the loop and act on it } try { c.onNext(v); } catch (Throwable ex) { Exceptions.throwOrReport(ex, c, v); return; } } e += q.size(); } long r = requested; // if requested is max, we don't do any accounting if (r != Long.MAX_VALUE) { // if there were missing requested, add it up if (localRequested != 0L) { long u = r + localRequested; if (u < 0) { u = Long.MAX_VALUE; } r = u; } // if there were emissions and we don't run on max since the last check, subtract if (e != 0L && r != Long.MAX_VALUE) { long u = r - e; if (u < 0) { throw new IllegalStateException("More produced than requested"); } r = u; } requested = r; } if (localProducer != null) { if (localProducer == NULL_PRODUCER) { currentProducer = null; } else { currentProducer = localProducer; if (r != 0L) { toRequest = BackpressureUtils.addCap(toRequest, r); requestFrom = localProducer; } } } else { Producer p = currentProducer; if (p != null && localRequested != 0L) { toRequest = BackpressureUtils.addCap(toRequest, localRequested); requestFrom = p; } } } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy