package com.github.davidmoten.rx.internal.operators;
import java.util.concurrent.atomic.AtomicLong;
import com.github.davidmoten.rx.Actions;
import com.github.davidmoten.rx.Transformers;
import com.github.davidmoten.rx.util.BackpressureUtils;
import rx.Observable;
import rx.Observable.Operator;
import rx.Observable.Transformer;
import rx.Subscriber;
import rx.functions.Action0;
import rx.functions.Action1;
import rx.functions.Func0;
import rx.schedulers.Schedulers;
public final class TransformerOnBackpressureBufferRequestLimiting<T> implements Transformer<T, T> {
private static final TransformerOnBackpressureBufferRequestLimiting<Object> instance = new TransformerOnBackpressureBufferRequestLimiting<Object>();
@SuppressWarnings("unchecked")
public static final <T> TransformerOnBackpressureBufferRequestLimiting<T> instance() {
return (TransformerOnBackpressureBufferRequestLimiting<T>) instance;
}
@Override
public Observable<T> call(final Observable<T> o) {
return Observable.defer(new Func0<Observable<T>>() {
@Override
public Observable<T> call() {
final OperatorPassThroughAdjustedRequest<T> op = new OperatorPassThroughAdjustedRequest<T>();
return o.lift(op).onBackpressureBuffer().doOnRequest(new Action1<Long>() {
@Override
public void call(Long n) {
op.requestMore(n);
}
});
}
});
}
/**
* Only used with an immediate downstream operator that requests
* {@code Long.MAX_VALUE} and should only be subscribed to once (use it
* within a {@code defer} block}.
*
* @param <T>
* stream item type
*/
private static final class OperatorPassThroughAdjustedRequest<T> implements Operator<T, T> {
private volatile ParentSubscriber<T> parent;
private final AtomicLong requested = new AtomicLong();
private final Object lock = new Object();
@Override
public Subscriber<? super T> call(Subscriber<? super T> child) {
// this method should only be called once for this instance
// assume child requests MAX_VALUE
ParentSubscriber<T> p = new ParentSubscriber<T>(child);
synchronized (lock) {
parent = p;
}
p.requestMore(requested.get());
child.add(p);
return p;
}
public void requestMore(long n) {
ParentSubscriber<T> p = parent;
if (p != null) {
p.requestMore(n);
} else {
synchronized (lock) {
ParentSubscriber<T> par = parent;
if (par == null) {
BackpressureUtils.getAndAddRequest(requested, n);
} else {
par.requestMore(n);
}
}
}
}
}
private static final class ParentSubscriber<T> extends Subscriber<T> {
private final Subscriber<? super T> child;
private final AtomicLong expected = new AtomicLong();
public ParentSubscriber(Subscriber<? super T> child) {
this.child = child;
request(0);
}
public void requestMore(long n) {
if (n <= 0) {
return;
}
long r = expected.get();
if (r == Long.MAX_VALUE) {
return;
} else {
long u = r;
while (true) {
long sum = u + n;
final long v;
if (sum < 0) {
v = Long.MAX_VALUE;
} else {
v = sum;
}
if (expected.compareAndSet(u, v)) {
// if v negative (more have arrived than requested)
long diff = Math.max(0, v);
long req = Math.min(n, diff);
if (req > 0) {
request(req);
}
return;
} else {
u = expected.get();
}
}
}
}
@Override
public void onCompleted() {
child.onCompleted();
}
@Override
public void onError(Throwable e) {
child.onError(e);
}
@Override
public void onNext(T t) {
expected.decrementAndGet();
child.onNext(t);
}
}
public static void main(String[] args) throws InterruptedException {
Observable.range(1, 10000) //
.doOnRequest(new Action1<Long>() {
@Override
public void call(Long n) {
System.out.println("requested " + n);
}
}).doOnUnsubscribe(new Action0() {
@Override
public void call() {
System.out.println("unsubscribed");
}
}) //
.compose(Transformers.<Integer> onBackpressureBufferRequestLimiting()) //
.take(10) //
.subscribeOn(Schedulers.io()) //
.doOnNext(Actions.println()) //
.count().toBlocking().single();
Thread.sleep(2000);
}
}