Java 9 reactive flows: does one Subscriber belong to one Publisher

Peter G. Horvath :

I am wondering if a reactive flows Publisher can safely assume subscriptions belong to it only and call java.util.concurrent.Flow.Subscriber#onComplete on it, if the Publisher is going out of commission (e.g. being shut down). The code sample below demonstrates the dilemma (obviously it's just some synthetic code to demonstrate the question):

import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.Flow;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class TimePublisher implements Flow.Publisher<Long> {

    private final ScheduledExecutorService scheduledExecutorService = new ScheduledThreadPoolExecutor(1);

    private final ConcurrentLinkedQueue<Flow.Subscriber<? super Long>> subscribersList = new ConcurrentLinkedQueue<>();


    private TimePublisher() {

    }

    public static TimePublisher newInstance() {
        TimePublisher timePublisher = new TimePublisher();
        timePublisher.startTickScheduler();

        return timePublisher;
    }

    private void startTickScheduler() {
        scheduledExecutorService.scheduleAtFixedRate(() -> {
            // does not make too much sense: just for the sake of the example
            final long currentTimeMillis = System.currentTimeMillis();
            subscribersList.forEach(sub -> sub.onNext(currentTimeMillis));
        }, 1, 1, TimeUnit.SECONDS);
    }

    @Override
    public void subscribe(Flow.Subscriber<? super Long> subscriber) {

        subscribersList.add(subscriber);

        subscriber.onSubscribe(new Flow.Subscription() {
            @Override
            public void request(long n) {
                // no-op in this sample
            }

            @Override
            public void cancel() {
                subscribersList.remove(subscriber);
            }
        });
    }

    public void stop() {
        // the publisher can be stopped from the outside: after that it will
        // definitely not emit any next items.
        scheduledExecutorService.shutdown();

        // QUESTION: can we assume that a Subscriber is subscribed to only this Publisher?
        // if it is subscribed to another publisher, the following is illegal, as onNext
        // could potentially be called by another Publisher...
        subscribersList.forEach(Flow.Subscriber::onComplete);

        subscribersList.clear();
    }
}
  • when the TimePublisher#stop is called, this particular Publisher will absolutely not emit any onNext calls, hence calling onComplete seems to be a legitimate choice
  • however, if the Subscriber is subscribed to another Publisher as well, then calling onComplete might be illegal, as the another Publisher could still be emitting items.
Alexey Romanov :

The documentation for Subscriber says

The methods in this interface are invoked in strict sequential order for each Flow.Subscription.

onComplete in particular:

Method invoked when it is known that no additional Subscriber method invocations will occur for a Subscription that is not already terminated by error, after which no other Subscriber methods are invoked by the Subscription. If this method throws an exception, resulting behavior is undefined.

So it's legal for other Subscriptions to continue invoking methods.

Flow documentation says having multiple Subscriptions in a Subscriber implementation is possible but not recommended:

Because Subscriber method invocations for a given Flow.Subscription are strictly ordered, there is no need for these methods to use locks or volatiles unless a Subscriber maintains multiple Subscriptions (in which case it is better to instead define multiple Subscribers, each with its own Subscription).

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=144648&siteId=1
one
ONE