/** * Copyright 2014 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.operators; import java.util.LinkedList; import java.util.Queue; import java.util.concurrent.atomic.AtomicIntegerFieldUpdater; import rx.Observable; import rx.Observable.Operator; import rx.Subscriber; import rx.observers.SerializedSubscriber; import rx.subscriptions.CompositeSubscription; /** * Flattens a list of Observables into one Observable sequence, without any transformation. * <p> * <img width="640" src="https://github.com/Netflix/RxJava/wiki/images/rx-operators/merge.png" alt=""> * <p> * You can combine the items emitted by multiple Observables so that they act like a single * Observable, by using the merge operation. * * @param <T> the emitted value type */ public final class OperatorMergeMaxConcurrent<T> implements Operator<T, Observable<? extends T>> { final int maxConcurrency; public OperatorMergeMaxConcurrent(int maxConcurrency) { this.maxConcurrency = maxConcurrency; } @Override public Subscriber<? super Observable<? extends T>> call(Subscriber<? super T> child) { final SerializedSubscriber<T> s = new SerializedSubscriber<T>(child); final CompositeSubscription csub = new CompositeSubscription(); child.add(csub); return new SourceSubscriber<T>(maxConcurrency, s, csub); } static final class SourceSubscriber<T> extends Subscriber<Observable<? extends T>> { final int maxConcurrency; final Subscriber<T> s; final CompositeSubscription csub; final Object guard; volatile int wip; @SuppressWarnings("rawtypes") static final AtomicIntegerFieldUpdater<SourceSubscriber> WIP_UPDATER = AtomicIntegerFieldUpdater.newUpdater(SourceSubscriber.class, "wip"); /** Guarded by guard. */ int active; /** Guarded by guard. */ final Queue<Observable<? extends T>> queue; 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 LinkedList<Observable<? extends T>>(); this.wip = 1; } @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(); } Subscriber<T> itemSub = new Subscriber<T>() { boolean once = true; @Override public void onNext(T t) { s.onNext(t); } @Override public void onError(Throwable e) { SourceSubscriber.this.onError(e); } @Override public void onCompleted() { if (once) { once = false; synchronized (guard) { active--; } csub.remove(this); subscribeNext(); SourceSubscriber.this.onCompleted(); } } }; csub.add(itemSub); WIP_UPDATER.incrementAndGet(this); t.unsafeSubscribe(itemSub); } @Override public void onError(Throwable e) { s.onError(e); unsubscribe(); } @Override public void onCompleted() { if (WIP_UPDATER.decrementAndGet(this) == 0) { s.onCompleted(); } } } }