/*
* This is an example implementation made for the sake fo the Reactive Streams project
*/
package co.paralleluniverse.strands.channels.reactivestreams;
import org.reactivestreams.Publisher;
import org.reactivestreams.Subscriber;
import org.reactivestreams.Subscription;
/**
* Implementors must override {@code newSubscription} + {@code ThreadlessSubscription.supply(Subscriber, long)}.
*/
public abstract class ThreadlessPublisher<T> implements Publisher<T> {
@Override
public void subscribe(Subscriber<? super T> s) {
newSubscription(s).start();
}
protected abstract ThreadlessSubscription newSubscription(Subscriber<? super T> s);
protected abstract class ThreadlessSubscription implements Subscription {
private final Subscriber<? super T> sr;
private long pending;
private boolean done;
public ThreadlessSubscription(Subscriber<? super T> subscriber) {
this.sr = new WrappedSubscriber<>(subscriber);
}
private void start() {
sr.onSubscribe(this);
supply();
}
@Override
public final void request(long n) {
if (n <= 0) {
sr.onError(new IllegalArgumentException("Requested number must be positive but was " + n + " (rule 3.9)"));
return;
}
final boolean recursive = pending == 0;
pending += n;
if (pending < 0)
pending = Long.MAX_VALUE;
if (!recursive)
supply();
}
@Override
public void cancel() {
done = true;
}
private void supply() {
while (!done & pending > 0)
pending -= supply(sr, pending);
}
/**
* Needs to supply up to n elements by calling {@code onNext}.
* May freely call {@code onComplete} or {@code onError} as appropriate.
*
* @return the number of elements actually supplied
*/
protected abstract long supply(Subscriber<? super T> subscriber, long n);
private class WrappedSubscriber<R> implements Subscriber<R> {
private final Subscriber<R> sr;
public WrappedSubscriber(Subscriber<R> sr) {
this.sr = sr;
}
@Override
public void onSubscribe(Subscription s) {
sr.onSubscribe(s);
}
@Override
public void onNext(R element) {
if (!done)
sr.onNext(element);
}
@Override
public void onError(Throwable t) {
if (!done) {
done = true;
sr.onError(t);
}
}
@Override
public void onComplete() {
if (!done) {
done = true;
sr.onComplete();
}
}
}
}
}