All Downloads are FREE. Search and download functionalities are using the official Maven repository.

hu.akarnokd.asyncenum.AsyncFromFlowPublisher Maven / Gradle / Ivy

/*
 * Copyright 2017 David Karnok
 *
 * 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 hu.akarnokd.asyncenum;

import java.util.Objects;
import java.util.concurrent.*;
import java.util.concurrent.atomic.*;

final class AsyncFromFlowPublisher implements AsyncEnumerable {

    final Flow.Publisher source;

    AsyncFromFlowPublisher(Flow.Publisher source) {
        this.source = source;
    }

    @Override
    public AsyncEnumerator enumerator() {
        FromFlowPublisherEnumerator subscriber = new FromFlowPublisherEnumerator<>();
        source.subscribe(subscriber);
        return subscriber;
    }

    static final class FromFlowPublisherEnumerator
            extends AtomicInteger
            implements AsyncEnumerator, Flow.Subscriber {

        final AtomicReference upstream;

        final AtomicLong requested;

        volatile T item;
        volatile boolean done;
        Throwable error;

        T current;

        volatile CompletableFuture completable;

        FromFlowPublisherEnumerator() {
            upstream = new AtomicReference<>();
            requested = new AtomicLong();
        }

        @Override
        public CompletionStage moveNext() {
            current = null;
            CompletableFuture cf = new CompletableFuture<>();
            completable = cf;
            deferredRequestOne();
            drain();
            return cf;
        }

        @Override
        public T current() {
            return current;
        }

        @Override
        public void cancel() {
            Flow.Subscription current = upstream.getAndSet(CancelledSubscription.CANCELLED);
            if (current != null && current != CancelledSubscription.CANCELLED) {
                current.cancel();
            }
        }

        void deferredRequestOne() {
            Flow.Subscription current = upstream.get();
            if (current != null) {
                current.request(1);
            } else {
                requested.getAndIncrement();
                current = upstream.get();
                if (current != null) {
                    long r = requested.getAndSet(0L);
                    if (r != 0L) {
                        current.request(r);
                    }
                }
            }
        }

        @Override
        public void onSubscribe(Flow.Subscription subscription) {
            Objects.requireNonNull(subscription, "subscription == null");
            if (upstream.compareAndSet(null, subscription)) {
                long r = requested.getAndSet(0L);
                if (r != 0L) {
                    subscription.request(r);
                }
            } else {
                subscription.cancel();
            }
        }

        @Override
        public void onNext(T item) {
            this.item = item;
            drain();
        }

        @Override
        public void onError(Throwable throwable) {
            error = throwable;
            done = true;
            drain();
        }

        @Override
        public void onComplete() {
            done = true;
            drain();
        }

        void drain() {
            if (getAndIncrement() == 0) {
                do {
                    CompletableFuture cf = completable;
                    if (cf != null) {
                        boolean d = done;
                        T v = item;
                        if (d && v == null) {
                            completable = null;
                            Throwable ex = error;
                            if (ex == null) {
                                cf.complete(false);
                            } else {
                                cf.completeExceptionally(ex);
                            }
                            return;
                        }

                        if (v != null) {
                            current = item;
                            item = null;
                            completable = null;
                            cf.complete(true);
                        }
                    }
                } while (decrementAndGet() != 0);
            }
        }
    }

    enum CancelledSubscription implements Flow.Subscription {
        CANCELLED;

        @Override
        public void request(long n) {
            // Deliberately NO-OP
        }

        @Override
        public void cancel() {
            // Deliberately NO-OP
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy