@Override public void call(final Subscriber<? super T> s) { if (state.casFirst(0, 1)) { final NotificationLite<T> nl = NotificationLite.instance(); // drain queued notifications before subscription // we do this here before PassThruObserver so the consuming thread can do this before // putting itself in the line of the producer BufferedObserver<? super T> buffered = (BufferedObserver<? super T>) state.observerRef; Object o; while ((o = buffered.buffer.poll()) != null) { nl.accept(s, o); } // register real observer for pass-thru ... and drain any further events received on first // notification state.setObserverRef(new PassThruObserver<T>(s, buffered.buffer, state)); s.add( Subscriptions.create( new Action0() { @Override public void call() { state.setObserverRef(Subscribers.empty()); } })); } else { s.onError(new IllegalStateException("Only one subscriber allowed!")); } }
private void drainIfNeededAndSwitchToActual() { final NotificationLite<T> nl = NotificationLite.instance(); Object o; while ((o = buffer.poll()) != null) { nl.accept(this, o); } // now we can safely change over to the actual and get rid of the pass-thru // but only if not unsubscribed state.casObserverRef(this, actual); }
void complete(int id) { List<Object> localQueue; synchronized (guard) { if (id != index) { return; } active = false; if (!mainDone) { return; } if (emitting) { if (queue == null) { queue = new ArrayList<Object>(); } queue.add(nl.completed()); return; } localQueue = queue; queue = null; emitting = true; } drain(localQueue); serializedChild.onCompleted(); unsubscribe(); }
/** The common state. */ static final class State<T> { /** Lite notifications of type T. */ final NotificationLite<T> nl = NotificationLite.instance(); /** The first observer or the one which buffers until the first arrives. */ final AtomicReference<Observer<? super T>> observerRef = new AtomicReference<Observer<? super T>>(new BufferedObserver<T>()); /** Allow a single subscriber only. */ final AtomicBoolean first = new AtomicBoolean(); }
private void drainIfNeededAndSwitchToActual() { Object o; while ((o = buffer.poll()) != null) { nl.accept(this, o); } // now we can safely change over to the actual and get rid of the pass-thru // but only if not unsubscribed observerRef.compareAndSet(this, actual); }
void drain(List<Object> localQueue) { if (localQueue == null) { return; } for (Object o : localQueue) { if (nl.isCompleted(o)) { serializedChild.onCompleted(); break; } else if (nl.isError(o)) { serializedChild.onError(nl.getError(o)); break; } else { @SuppressWarnings("unchecked") T t = (T) o; serializedChild.onNext(t); arbiter.produced(1); } } }
/** * This is a temporary observer between buffering and the actual that gets into the line of * notifications from the producer and will drain the queue of any items received during the race * of the initial drain and switching this. * * <p>It will then immediately swap itself out for the actual (after a single notification), but * since this is now being done on the same producer thread no further buffering will occur. */ private static final class PassThruObserver<T> extends Subscriber<T> { private final Observer<? super T> actual; // this assumes single threaded synchronous notifications (the Rx contract for a single // Observer) private final ConcurrentLinkedQueue<Object> buffer; private final AtomicReference<Observer<? super T>> observerRef; private final NotificationLite<T> nl = NotificationLite.instance(); PassThruObserver( Observer<? super T> actual, ConcurrentLinkedQueue<Object> buffer, AtomicReference<Observer<? super T>> observerRef) { this.actual = actual; this.buffer = buffer; this.observerRef = observerRef; } @Override public void onCompleted() { drainIfNeededAndSwitchToActual(); actual.onCompleted(); } @Override public void onError(Throwable e) { drainIfNeededAndSwitchToActual(); actual.onError(e); } @Override public void onNext(T t) { drainIfNeededAndSwitchToActual(); actual.onNext(t); } private void drainIfNeededAndSwitchToActual() { Object o; while ((o = buffer.poll()) != null) { nl.accept(this, o); } // now we can safely change over to the actual and get rid of the pass-thru // but only if not unsubscribed observerRef.compareAndSet(this, actual); } }
private static final class BufferedObserver<T> extends Subscriber<T> { private final ConcurrentLinkedQueue<Object> buffer = new ConcurrentLinkedQueue<Object>(); private static final NotificationLite<Object> nl = NotificationLite.instance(); @Override public void onCompleted() { buffer.add(nl.completed()); } @Override public void onError(Throwable e) { buffer.add(nl.error(e)); } @Override public void onNext(T t) { buffer.add(nl.next(t)); } }
void error(Throwable e, int id) { List<Object> localQueue; synchronized (guard) { if (id != index) { return; } if (emitting) { if (queue == null) { queue = new ArrayList<Object>(); } queue.add(nl.error(e)); return; } localQueue = queue; queue = null; emitting = true; } drain(localQueue); serializedChild.onError(e); unsubscribe(); }
@Override public void onCompleted() { List<Object> localQueue; synchronized (guard) { mainDone = true; if (active) { return; } if (emitting) { if (queue == null) { queue = new ArrayList<Object>(); } queue.add(nl.completed()); return; } localQueue = queue; queue = null; emitting = true; } drain(localQueue); serializedChild.onCompleted(); unsubscribe(); }
@Override public void onNext(T t) { buffer.add(nl.next(t)); }
@Override public void onError(Throwable e) { buffer.add(nl.error(e)); }
@Override public void onCompleted() { buffer.add(nl.completed()); }
private static final class SwitchSubscriber<T> extends Subscriber<Observable<? extends T>> { final SerializedSubscriber<T> serializedChild; final SerialSubscription ssub; final Object guard = new Object(); final NotificationLite<?> nl = NotificationLite.instance(); final ProducerArbiter arbiter; /** Guarded by guard. */ int index; /** Guarded by guard. */ boolean active; /** Guarded by guard. */ boolean mainDone; /** Guarded by guard. */ List<Object> queue; /** Guarded by guard. */ boolean emitting; /** Guarded by guard. */ InnerSubscriber<T> currentSubscriber; SwitchSubscriber(Subscriber<? super T> child) { serializedChild = new SerializedSubscriber<T>(child); arbiter = new ProducerArbiter(); ssub = new SerialSubscription(); child.add(ssub); child.setProducer( new Producer() { @Override public void request(long n) { if (n > 0) { arbiter.request(n); } } }); } @Override public void onNext(Observable<? extends T> t) { final int id; synchronized (guard) { id = ++index; active = true; currentSubscriber = new InnerSubscriber<T>(id, arbiter, this); } ssub.set(currentSubscriber); t.unsafeSubscribe(currentSubscriber); } @Override public void onError(Throwable e) { serializedChild.onError(e); unsubscribe(); } @Override public void onCompleted() { List<Object> localQueue; synchronized (guard) { mainDone = true; if (active) { return; } if (emitting) { if (queue == null) { queue = new ArrayList<Object>(); } queue.add(nl.completed()); return; } localQueue = queue; queue = null; emitting = true; } drain(localQueue); serializedChild.onCompleted(); unsubscribe(); } void emit(T value, int id, InnerSubscriber<T> innerSubscriber) { List<Object> localQueue; synchronized (guard) { if (id != index) { return; } if (emitting) { if (queue == null) { queue = new ArrayList<Object>(); } queue.add(value); return; } localQueue = queue; queue = null; emitting = true; } boolean once = true; boolean skipFinal = false; try { do { drain(localQueue); if (once) { once = false; serializedChild.onNext(value); arbiter.produced(1); } synchronized (guard) { localQueue = queue; queue = null; if (localQueue == null) { emitting = false; skipFinal = true; break; } } } while (!serializedChild.isUnsubscribed()); } finally { if (!skipFinal) { synchronized (guard) { emitting = false; } } } } void drain(List<Object> localQueue) { if (localQueue == null) { return; } for (Object o : localQueue) { if (nl.isCompleted(o)) { serializedChild.onCompleted(); break; } else if (nl.isError(o)) { serializedChild.onError(nl.getError(o)); break; } else { @SuppressWarnings("unchecked") T t = (T) o; serializedChild.onNext(t); arbiter.produced(1); } } } void error(Throwable e, int id) { List<Object> localQueue; synchronized (guard) { if (id != index) { return; } if (emitting) { if (queue == null) { queue = new ArrayList<Object>(); } queue.add(nl.error(e)); return; } localQueue = queue; queue = null; emitting = true; } drain(localQueue); serializedChild.onError(e); unsubscribe(); } void complete(int id) { List<Object> localQueue; synchronized (guard) { if (id != index) { return; } active = false; if (!mainDone) { return; } if (emitting) { if (queue == null) { queue = new ArrayList<Object>(); } queue.add(nl.completed()); return; } localQueue = queue; queue = null; emitting = true; } drain(localQueue); serializedChild.onCompleted(); unsubscribe(); } }
static final class SourceSubscriber<T> extends Subscriber<Observable<? extends T>> { final NotificationLite<T> nl = NotificationLite.instance(); final int maxConcurrency; final Subscriber<T> s; final CompositeSubscription csub; final Object guard; volatile int wip; @SuppressWarnings("rawtypes") static final AtomicIntegerFieldUpdater<SourceSubscriber> WIP = AtomicIntegerFieldUpdater.newUpdater(SourceSubscriber.class, "wip"); volatile int sourceIndex; @SuppressWarnings("rawtypes") static final AtomicIntegerFieldUpdater<SourceSubscriber> SOURCE_INDEX = AtomicIntegerFieldUpdater.newUpdater(SourceSubscriber.class, "sourceIndex"); /** Guarded by guard. */ int active; /** Guarded by guard. */ final Queue<Observable<? extends T>> queue; /** Indicates the emitting phase. Guarded by this. */ boolean emitting; /** Counts the missed emitting calls. Guarded by this. */ int missedEmitting; /** The last buffer index in the round-robin drain scheme. Accessed while emitting == true. */ int lastIndex; /** Guarded by itself. */ final List<MergeItemSubscriber> subscribers; volatile long requested; @SuppressWarnings("rawtypes") static final AtomicLongFieldUpdater<SourceSubscriber> REQUESTED = AtomicLongFieldUpdater.newUpdater(SourceSubscriber.class, "requested"); public SourceSubscriber(int maxConcurrency, Subscriber<T> s, CompositeSubscription csub) { super(s); this.maxConcurrency = maxConcurrency; this.s = s; this.csub = csub; this.guard = new Object(); this.queue = new ArrayDeque<Observable<? extends T>>(maxConcurrency); this.subscribers = Collections.synchronizedList(new ArrayList<MergeItemSubscriber>()); this.wip = 1; } @Override public void onStart() { request(maxConcurrency); } @Override public void onNext(Observable<? extends T> t) { synchronized (guard) { queue.add(t); } subscribeNext(); } void subscribeNext() { Observable<? extends T> t; synchronized (guard) { t = queue.peek(); if (t == null || active >= maxConcurrency) { return; } active++; queue.poll(); } MergeItemSubscriber itemSub = new MergeItemSubscriber(SOURCE_INDEX.getAndIncrement(this)); subscribers.add(itemSub); csub.add(itemSub); WIP.incrementAndGet(this); t.unsafeSubscribe(itemSub); request(1); } @Override public void onError(Throwable e) { Object[] active; synchronized (subscribers) { active = subscribers.toArray(); subscribers.clear(); } try { s.onError(e); unsubscribe(); } finally { for (Object o : active) { @SuppressWarnings("unchecked") MergeItemSubscriber a = (MergeItemSubscriber) o; a.release(); } } } @Override public void onCompleted() { WIP.decrementAndGet(this); drain(); } protected void downstreamRequest(long n) { for (; ; ) { long r = requested; long u; if (r != Long.MAX_VALUE && n == Long.MAX_VALUE) { u = Long.MAX_VALUE; } else if (r + n < 0) { u = Long.MAX_VALUE; } else { u = r + n; } if (REQUESTED.compareAndSet(this, r, u)) { break; } } drain(); } protected void drain() { synchronized (this) { if (emitting) { missedEmitting++; return; } emitting = true; missedEmitting = 0; } final List<SourceSubscriber<T>.MergeItemSubscriber> subs = subscribers; final Subscriber<T> child = s; Object[] active = new Object[subs.size()]; do { long r; outer: while ((r = requested) > 0) { int idx = lastIndex; synchronized (subs) { if (subs.size() == active.length) { active = subs.toArray(active); } else { active = subs.toArray(); } } int resumeIndex = 0; int j = 0; for (Object o : active) { @SuppressWarnings("unchecked") MergeItemSubscriber e = (MergeItemSubscriber) o; if (e.index == idx) { resumeIndex = j; break; } j++; } int sumConsumed = 0; for (int i = 0; i < active.length; i++) { j = (i + resumeIndex) % active.length; @SuppressWarnings("unchecked") final MergeItemSubscriber e = (MergeItemSubscriber) active[j]; final RxRingBuffer b = e.buffer; lastIndex = e.index; if (!e.once && b.peek() == null) { subs.remove(e); synchronized (guard) { this.active--; } csub.remove(e); e.release(); subscribeNext(); WIP.decrementAndGet(this); continue outer; } int consumed = 0; Object v; while (r > 0 && (v = b.poll()) != null) { nl.accept(child, v); if (child.isUnsubscribed()) { return; } r--; consumed++; } if (consumed > 0) { sumConsumed += consumed; REQUESTED.addAndGet(this, -consumed); e.requestMore(consumed); } if (r == 0) { break outer; } } if (sumConsumed == 0) { break; } } if (active.length == 0) { if (wip == 0) { child.onCompleted(); return; } } synchronized (this) { if (missedEmitting == 0) { emitting = false; break; } missedEmitting = 0; } } while (true); } final class MergeItemSubscriber extends Subscriber<T> { volatile boolean once = true; final int index; final RxRingBuffer buffer; public MergeItemSubscriber(int index) { buffer = RxRingBuffer.getSpmcInstance(); this.index = index; } @Override public void onStart() { request(RxRingBuffer.SIZE); } @Override public void onNext(T t) { try { buffer.onNext(t); } catch (MissingBackpressureException ex) { onError(ex); return; } drain(); } @Override public void onError(Throwable e) { SourceSubscriber.this.onError(e); } @Override public void onCompleted() { if (once) { once = false; drain(); } } /** Request more from upstream. */ void requestMore(long n) { request(n); } void release() { // NO-OP for now buffer.release(); } } }
protected void drain() { synchronized (this) { if (emitting) { missedEmitting++; return; } emitting = true; missedEmitting = 0; } final List<SourceSubscriber<T>.MergeItemSubscriber> subs = subscribers; final Subscriber<T> child = s; Object[] active = new Object[subs.size()]; do { long r; outer: while ((r = requested) > 0) { int idx = lastIndex; synchronized (subs) { if (subs.size() == active.length) { active = subs.toArray(active); } else { active = subs.toArray(); } } int resumeIndex = 0; int j = 0; for (Object o : active) { @SuppressWarnings("unchecked") MergeItemSubscriber e = (MergeItemSubscriber) o; if (e.index == idx) { resumeIndex = j; break; } j++; } int sumConsumed = 0; for (int i = 0; i < active.length; i++) { j = (i + resumeIndex) % active.length; @SuppressWarnings("unchecked") final MergeItemSubscriber e = (MergeItemSubscriber) active[j]; final RxRingBuffer b = e.buffer; lastIndex = e.index; if (!e.once && b.peek() == null) { subs.remove(e); synchronized (guard) { this.active--; } csub.remove(e); e.release(); subscribeNext(); WIP.decrementAndGet(this); continue outer; } int consumed = 0; Object v; while (r > 0 && (v = b.poll()) != null) { nl.accept(child, v); if (child.isUnsubscribed()) { return; } r--; consumed++; } if (consumed > 0) { sumConsumed += consumed; REQUESTED.addAndGet(this, -consumed); e.requestMore(consumed); } if (r == 0) { break outer; } } if (sumConsumed == 0) { break; } } if (active.length == 0) { if (wip == 0) { child.onCompleted(); return; } } synchronized (this) { if (missedEmitting == 0) { emitting = false; break; } missedEmitting = 0; } } while (true); }