| // Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file | |
| // for details. All rights reserved. Use of this source code is governed by a | |
| // BSD-style license that can be found in the LICENSE file. | |
| package flow; | |
| import java.util.concurrent.ExecutorService; | |
| import java.util.concurrent.Flow.Publisher; | |
| import java.util.concurrent.Flow.Subscriber; | |
| import java.util.concurrent.Flow.Subscription; | |
| import java.util.concurrent.ForkJoinPool; | |
| import java.util.concurrent.Future; | |
| import java.util.concurrent.TimeUnit; | |
| import java.util.function.Consumer; | |
| public class FlowExample { | |
| public static void main(String[] args) throws Exception { | |
| OneShotPublisher oneShotPublisher = new OneShotPublisher(); | |
| SampleSubscriber<Object> subscriber = new SampleSubscriber<>(50L, System.out::println); | |
| oneShotPublisher.subscribe(subscriber); | |
| oneShotPublisher.awaitPublishing(); | |
| } | |
| public static class OneShotPublisher implements Publisher<Boolean> { | |
| private final ForkJoinPool executor = new ForkJoinPool(); // daemon-based | |
| private boolean subscribed; // true after first subscribe | |
| public synchronized void subscribe(Subscriber<? super Boolean> subscriber) { | |
| if (subscribed) { | |
| subscriber.onError(new IllegalStateException()); // only one allowed | |
| } else { | |
| subscribed = true; | |
| subscriber.onSubscribe(new OneShotSubscription(subscriber, executor)); | |
| } | |
| } | |
| void awaitPublishing() throws InterruptedException { | |
| // On Android 7 and higher, calling System.exit() terminates all threads without waiting for | |
| // tasks on the fork join pool to be finished. For the test to be completed we need such | |
| // tasks to finish. | |
| int seconds = 60; | |
| executor.awaitTermination(seconds, TimeUnit.SECONDS); | |
| } | |
| static class OneShotSubscription implements Subscription { | |
| private final Subscriber<? super Boolean> subscriber; | |
| private final ExecutorService executor; | |
| private Future<?> future; // to allow cancellation | |
| private boolean completed; | |
| OneShotSubscription(Subscriber<? super Boolean> subscriber, ExecutorService executor) { | |
| this.subscriber = subscriber; | |
| this.executor = executor; | |
| } | |
| public synchronized void request(long n) { | |
| if (n != 0 && !completed) { | |
| completed = true; | |
| if (n < 0) { | |
| IllegalArgumentException ex = new IllegalArgumentException(); | |
| executor.execute(() -> subscriber.onError(ex)); | |
| } else { | |
| future = | |
| executor.submit( | |
| () -> { | |
| subscriber.onNext(Boolean.TRUE); | |
| subscriber.onComplete(); | |
| }); | |
| } | |
| } | |
| } | |
| public synchronized void cancel() { | |
| completed = true; | |
| if (future != null) { | |
| future.cancel(false); | |
| } | |
| } | |
| } | |
| } | |
| static class SampleSubscriber<T> implements Subscriber<T> { | |
| final Consumer<? super T> consumer; | |
| Subscription subscription; | |
| final long bufferSize; | |
| long count; | |
| SampleSubscriber(long bufferSize, Consumer<? super T> consumer) { | |
| this.bufferSize = bufferSize; | |
| this.consumer = consumer; | |
| } | |
| public void onSubscribe(Subscription subscription) { | |
| long initialRequestSize = bufferSize; | |
| count = bufferSize - bufferSize / 2; // re-request when half consumed | |
| (this.subscription = subscription).request(initialRequestSize); | |
| } | |
| public void onNext(T item) { | |
| if (--count <= 0) { | |
| subscription.request(count = bufferSize - bufferSize / 2); | |
| } | |
| consumer.accept(item); | |
| } | |
| public void onError(Throwable ex) { | |
| ex.printStackTrace(); | |
| } | |
| public void onComplete() {} | |
| } | |
| } |