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); }