reactor.ipc.netty.channel.FluxReceive.java Source code

Java tutorial

Introduction

Here is the source code for reactor.ipc.netty.channel.FluxReceive.java

Source

/*
 * Copyright (c) 2011-2016 Pivotal Software Inc, All Rights Reserved.
 *
 * 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 reactor.ipc.netty.channel;

import java.util.Queue;
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;

import io.netty.channel.Channel;
import io.netty.channel.EventLoop;
import io.netty.util.ReferenceCountUtil;
import org.reactivestreams.Subscriber;
import org.reactivestreams.Subscription;
import reactor.core.Cancellation;
import reactor.core.Exceptions;
import reactor.core.Producer;
import reactor.core.Trackable;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Operators;
import reactor.ipc.netty.NettyContext;
import reactor.util.Logger;
import reactor.util.Loggers;
import reactor.util.concurrent.QueueSupplier;

/**
 * @author Stephane Maldini
 */
final class FluxReceive extends Flux<Object> implements Subscription, Trackable, Producer {

    final Channel channel;
    final ChannelOperations<?, ?> parent;
    final EventLoop eventLoop;

    Subscriber<? super Object> receiver;
    boolean receiverFastpath;
    long receiverDemand;
    Queue<Object> receiverQueue;

    boolean inboundDone;
    Throwable inboundError;

    volatile Cancellation receiverCancel;

    FluxReceive(ChannelOperations<?, ?> parent) {
        this.parent = parent;
        this.channel = parent.channel;
        this.eventLoop = channel.eventLoop();
    }

    @Override
    public void cancel() {
        if (cancelReceiver()) {
            Queue<Object> q = receiverQueue;
            if (q != null) {
                Object o;
                while ((o = q.poll()) != null) {
                    ReferenceCountUtil.release(o);
                }
            }
        }
    }

    @Override
    final public Object downstream() {
        return receiver;
    }

    @Override
    final public Throwable getError() {
        return inboundError;
    }

    @Override
    public long getPending() {
        return receiverQueue != null ? receiverQueue.size() : 0;
    }

    @Override
    final public boolean isStarted() {
        return channel.isActive();
    }

    @Override
    final public boolean isTerminated() {
        return inboundDone;
    }

    @Override
    public boolean isCancelled() {
        return receiverCancel == CANCELLED;
    }

    @Override
    public void request(long n) {
        if (Operators.validate(n)) {
            if (eventLoop.inEventLoop()) {
                this.receiverDemand = Operators.addCap(receiverDemand, n);
                drainReceiver();
            } else {
                eventLoop.execute(() -> {
                    this.receiverDemand = Operators.addCap(receiverDemand, n);
                    drainReceiver();
                });
            }
        }
    }

    @Override
    public long requestedFromDownstream() {
        return receiverDemand;
    }

    @Override
    public void subscribe(Subscriber<? super Object> s) {
        if (s == null) {
            throw Exceptions.argumentIsNullException();
        }
        if (eventLoop.inEventLoop()) {
            startReceiver(s);
        } else {
            eventLoop.execute(() -> startReceiver(s));
        }
    }

    final boolean cancelReceiver() {
        Cancellation c = receiverCancel;
        if (c != CANCELLED) {
            c = CANCEL.getAndSet(this, CANCELLED);
            if (c != CANCELLED) {
                channel.config().setAutoRead(false);
                if (parent.isOutboundDone()) {
                    parent.onHandlerTerminate();
                } else {
                    parent.onInboundCancel();
                }
                if (c != null) {
                    c.dispose();
                    return true;
                }
            }
        }
        return false;
    }

    final boolean drainReceiver() {

        final Queue<Object> q = receiverQueue;
        final Subscriber<? super Object> a = receiver;

        if (a == null) {
            if (inboundDone) {
                cancelReceiver();
            }
            return false;
        }

        long r = receiverDemand;
        long e = 0L;

        while (e != r) {
            if (isCancelled()) {
                return false;
            }

            boolean d = inboundDone;
            Object v = q != null ? q.poll() : null;
            boolean empty = v == null;

            if (d && empty) {
                terminateReceiver(q, a);
                return false;
            }

            if (empty) {
                break;
            }

            try {
                a.onNext(v);
            } finally {
                ReferenceCountUtil.release(v);
            }

            e++;
        }

        if (isCancelled()) {
            return false;
        }

        if (inboundDone && (q == null || q.isEmpty())) {
            terminateReceiver(q, a);
            return false;
        }

        if (r == Long.MAX_VALUE) {
            channel.config().setAutoRead(true);
            channel.read();
            return true;
        }

        if ((receiverDemand -= e) > 0L || e > 0L) {
            channel.read();
        }

        return false;
    }

    final void startReceiver(Subscriber<? super Object> s) {
        if (receiver == null) {
            if (log.isDebugEnabled()) {
                log.debug("Subscribing inbound receiver [pending: " + "" + getPending() + ", inboundDone: {}]",
                        inboundDone);
            }
            if (inboundDone && getPending() == 0) {
                if (inboundError != null) {
                    Operators.error(s, inboundError);
                    return;
                }

                Operators.complete(s);
                return;
            }

            receiver = s;
            CANCEL.lazySet(this, () -> {
                if (eventLoop.inEventLoop()) {
                    unsubscribeReceiver();
                } else {
                    eventLoop.execute(this::unsubscribeReceiver);
                }
            });
            s.onSubscribe(this);
        } else {
            Operators.error(s, new IllegalStateException("Only one connection receive subscriber allowed."));
        }
    }

    final boolean markInboundDone() {
        if (inboundDone) {
            Queue<?> q = receiverQueue;
            Subscriber<?> receiver = this.receiver;
            if (receiver == null && (q == null || q.isEmpty())) {
                cancel();
                return true;
            }
            return false;
        }
        inboundDone = true;
        return false;
    }

    final void onInboundNext(Object msg) {
        if (inboundDone) {
            if (log.isDebugEnabled()) {
                log.debug("Dropping frame {}", msg);
            }
            return;
        }
        ReferenceCountUtil.retain(msg);
        if (receiverFastpath && receiver != null) {
            try {
                receiver.onNext(msg);
            } finally {
                ReferenceCountUtil.release(msg);
            }

        } else {
            Queue<Object> q = receiverQueue;
            if (q == null) {
                q = QueueSupplier.unbounded().get();
                receiverQueue = q;
            }
            q.offer(msg);
            if (drainReceiver()) {
                receiverFastpath = true;
            }
        }
    }

    final boolean onInboundComplete() {
        if (inboundDone) {
            return false;
        }
        inboundDone = true;
        Subscriber<?> receiver = this.receiver;
        if (receiverFastpath && receiver != null) {
            receiver.onComplete();
            cancelReceiver();
            return true;
        }
        drainReceiver();
        return false;
    }

    final boolean onInboundError(Throwable err) {
        if (isCancelled() || inboundDone) {
            Operators.onErrorDropped(err);
            return false;
        }
        inboundDone = true;
        Subscriber<?> receiver = this.receiver;
        this.inboundError = err;
        if (receiverFastpath && receiver != null) {
            cancelReceiver();
            receiver.onError(err);
            return true;
        } else {
            drainReceiver();
        }
        return false;
    }

    final void terminateReceiver(Queue<?> q, Subscriber<?> a) {
        cancelReceiver();
        if (q != null) {
            q.clear();
        }
        Throwable ex = inboundError;
        if (ex != null) {
            a.onError(ex);
        } else {
            a.onComplete();
        }
        parent.context.fireContextActive(parent);
    }

    final void unsubscribeReceiver() {
        receiverDemand = 0L;
        receiver = null;
    }

    @SuppressWarnings("rawtypes")
    static final AtomicReferenceFieldUpdater<FluxReceive, Cancellation> CANCEL = AtomicReferenceFieldUpdater
            .newUpdater(FluxReceive.class, Cancellation.class, "receiverCancel");

    static final Cancellation CANCELLED = () -> {
    };

    static final Logger log = Loggers.getLogger(FluxReceive.class);
}