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

com.alibaba.rocketmq.shade.io.netty.channel.DefaultChannelPipeline Maven / Gradle / Ivy

There is a newer version: 3.6.2.Final
Show newest version
/*
 * Copyright 2012 The Netty Project
 *
 * The Netty Project licenses this file to you 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 io.netty.channel;

import io.netty.channel.Channel.Unsafe;
import io.netty.util.ReferenceCountUtil;
import io.netty.util.concurrent.EventExecutor;
import io.netty.util.concurrent.EventExecutorGroup;
import io.netty.util.concurrent.FastThreadLocal;
import io.netty.util.internal.OneTimeTask;
import io.netty.util.internal.PlatformDependent;
import io.netty.util.internal.StringUtil;
import io.netty.util.internal.logging.InternalLogger;
import io.netty.util.internal.logging.InternalLoggerFactory;

import java.net.SocketAddress;
import java.util.ArrayList;
import java.util.IdentityHashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.WeakHashMap;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.RejectedExecutionException;

/**
 * The default {@link ChannelPipeline} implementation.  It is usually created
 * by a {@link Channel} implementation when the {@link Channel} is created.
 */
final class DefaultChannelPipeline implements ChannelPipeline {

    static final InternalLogger logger = InternalLoggerFactory.getInstance(DefaultChannelPipeline.class);

    private static final FastThreadLocal, String>> nameCaches =
            new FastThreadLocal, String>>() {
        @Override
        protected Map, String> initialValue() throws Exception {
            return new WeakHashMap, String>();
        }
    };

    final AbstractChannel channel;

    final AbstractChannelHandlerContext head;
    final AbstractChannelHandlerContext tail;

    private Map childExecutors;

    /**
     * This is the head of a linked list that is processed by {@link #callHandlerAddedForAllHandlers()} and so process
     * all the pending {@link #callHandlerAdded0(AbstractChannelHandlerContext)}.
     *
     * We only keep the head because it is expected that the list is used infrequently and its size is small.
     * Thus full iterations to do insertions is assumed to be a good compromised to saving memory and tail management
     * complexity.
     */
    private PendingHandlerCallback pendingHandlerCallbackHead;

    /**
     * Set to {@code true} once the {@link AbstractChannel} is registered.Once set to {@code true} the value will never
     * change.
     */
    private boolean registered;

    public DefaultChannelPipeline(AbstractChannel channel) {
        if (channel == null) {
            throw new NullPointerException("channel");
        }
        this.channel = channel;

        tail = new TailContext(this);
        head = new HeadContext(this);

        head.next = tail;
        tail.prev = head;
    }

    private AbstractChannelHandlerContext newContext(EventExecutorGroup group, String name, ChannelHandler handler) {
        return new DefaultChannelHandlerContext(this, childExecutor(group), name, handler);
    }

    private EventExecutor childExecutor(EventExecutorGroup group) {
        if (group == null) {
            return null;
        }
        Map childExecutors = this.childExecutors;
        if (childExecutors == null) {
            // Use size of 4 as most people only use one extra EventExecutor.
            childExecutors = this.childExecutors = new IdentityHashMap(4);
        }
        // Pin one of the child executors once and remember it so that the same child executor
        // is used to fire events for the same channel.
        EventExecutor childExecutor = childExecutors.get(group);
        if (childExecutor == null) {
            childExecutor = group.next();
            childExecutors.put(group, childExecutor);
        }
        return childExecutor;
    }

    @Override
    public Channel channel() {
        return channel;
    }

    @Override
    public ChannelPipeline addFirst(String name, ChannelHandler handler) {
        return addFirst(null, name, handler);
    }

    @Override
    public ChannelPipeline addFirst(EventExecutorGroup group, final String name, ChannelHandler handler) {
        final AbstractChannelHandlerContext newCtx;
        final EventExecutor executor;
        final boolean inEventLoop;
        synchronized (this) {
            checkDuplicateName(name);
            checkMultiplicity(handler);

            newCtx = newContext(group, name, handler);
            executor = executorSafe(newCtx.executor);

            // If the executor is null it means that the channel was not registered on an eventloop yet.
            // In this case we add the context to the pipeline and add a task that will call
            // ChannelHandler.handlerAdded(...) once the channel is registered.
            if (executor == null) {
                addFirst0(newCtx);
                callHandlerCallbackLater(newCtx, true);
                return this;
            }
            inEventLoop = executor.inEventLoop();
            if (inEventLoop) {
                addFirst0(newCtx);
            }
        }

        if (inEventLoop) {
            callHandlerAdded0(newCtx);
        } else {
            waitForFuture(executor.submit(new OneTimeTask() {
                @Override
                public void run() {
                    synchronized (DefaultChannelPipeline.this) {
                        addFirst0(newCtx);
                    }
                    callHandlerAdded0(newCtx);
                }
            }));
        }
        return this;
    }

    private void addFirst0(AbstractChannelHandlerContext newCtx) {
        AbstractChannelHandlerContext nextCtx = head.next;
        newCtx.prev = head;
        newCtx.next = nextCtx;
        head.next = newCtx;
        nextCtx.prev = newCtx;
    }

    @Override
    public ChannelPipeline addLast(String name, ChannelHandler handler) {
        return addLast(null, name, handler);
    }

    @Override
    public ChannelPipeline addLast(EventExecutorGroup group, final String name, ChannelHandler handler) {
        final EventExecutor executor;
        final AbstractChannelHandlerContext newCtx;
        final boolean inEventLoop;
        synchronized (this) {
            checkDuplicateName(name);
            checkMultiplicity(handler);

            newCtx = newContext(group, name, handler);
            executor = executorSafe(newCtx.executor);

            // If the executor is null it means that the channel was not registered on an eventloop yet.
            // In this case we add the context to the pipeline and add a task that will call
            // ChannelHandler.handlerAdded(...) once the channel is registered.
            if (executor == null) {
                addLast0(newCtx);
                callHandlerCallbackLater(newCtx, true);
                return this;
            }
            inEventLoop = executor.inEventLoop();
            if (inEventLoop) {
                addLast0(newCtx);
            }
        }
        if (inEventLoop) {
            callHandlerAdded0(newCtx);
        } else {
            waitForFuture(executor.submit(new OneTimeTask() {
                @Override
                public void run() {
                    synchronized (DefaultChannelPipeline.this) {
                        addLast0(newCtx);
                    }
                    callHandlerAdded0(newCtx);
                }
            }));
        }
        return this;
    }

    private void addLast0(AbstractChannelHandlerContext newCtx) {
        AbstractChannelHandlerContext prev = tail.prev;
        newCtx.prev = prev;
        newCtx.next = tail;
        prev.next = newCtx;
        tail.prev = newCtx;
    }

    @Override
    public ChannelPipeline addBefore(String baseName, String name, ChannelHandler handler) {
        return addBefore(null, baseName, name, handler);
    }

    @Override
    public ChannelPipeline addBefore(
            EventExecutorGroup group, String baseName, final String name, ChannelHandler handler) {
        final EventExecutor executor;
        final AbstractChannelHandlerContext newCtx;
        final AbstractChannelHandlerContext ctx;
        final boolean inEventLoop;
        synchronized (this) {
            checkMultiplicity(handler);
            ctx = getContextOrDie(baseName);
            checkDuplicateName(name);

            newCtx = newContext(group, name, handler);
            executor = executorSafe(newCtx.executor);

            // If the executor is null it means that the channel was not registered on an eventloop yet.
            // In this case we add the context to the pipeline and add a task that will call
            // ChannelHandler.handlerAdded(...) once the channel is registered.
            if (executor == null) {
                addBefore0(ctx, newCtx);
                callHandlerCallbackLater(newCtx, true);
                return this;
            }

            inEventLoop = executor.inEventLoop();
            if (inEventLoop) {
                addBefore0(ctx, newCtx);
            }
        }

        if (inEventLoop) {
            callHandlerAdded0(newCtx);
        } else {
            waitForFuture(executor.submit(new OneTimeTask() {
                @Override
                public void run() {
                    synchronized (DefaultChannelPipeline.this) {
                        addBefore0(ctx, newCtx);
                    }
                    callHandlerAdded0(newCtx);
                }
            }));
        }
        return this;
    }

    private static void addBefore0(AbstractChannelHandlerContext ctx, AbstractChannelHandlerContext newCtx) {
        newCtx.prev = ctx.prev;
        newCtx.next = ctx;
        ctx.prev.next = newCtx;
        ctx.prev = newCtx;
    }

    @Override
    public ChannelPipeline addAfter(String baseName, String name, ChannelHandler handler) {
        return addAfter(null, baseName, name, handler);
    }

    @Override
    public ChannelPipeline addAfter(
            EventExecutorGroup group, String baseName, final String name, ChannelHandler handler) {
        final EventExecutor executor;
        final AbstractChannelHandlerContext newCtx;
        final AbstractChannelHandlerContext ctx;
        final boolean inEventLoop;

        synchronized (this) {
            checkMultiplicity(handler);
            ctx = getContextOrDie(baseName);
            checkDuplicateName(name);

            newCtx = newContext(group, name, handler);
            executor = executorSafe(newCtx.executor);

            // If the executor is null it means that the channel was not registered on an eventloop yet.
            // In this case we remove the context from the pipeline and add a task that will call
            // ChannelHandler.handlerRemoved(...) once the channel is registered.
            if (executor == null) {
                addAfter0(ctx, newCtx);
                callHandlerCallbackLater(newCtx, true);
                return this;
            }
            inEventLoop = executor.inEventLoop();
            if (inEventLoop) {
                addAfter0(ctx, newCtx);
            }
        }
        if (inEventLoop) {
            callHandlerAdded0(newCtx);
        } else {
            waitForFuture(executor.submit(new OneTimeTask() {
                @Override
                public void run() {
                    synchronized (DefaultChannelPipeline.this) {
                        addAfter0(ctx, newCtx);
                    }
                    callHandlerAdded0(newCtx);
                }
            }));
        }
        return this;
    }

    private static void addAfter0(AbstractChannelHandlerContext ctx, AbstractChannelHandlerContext newCtx) {
        newCtx.prev = ctx;
        newCtx.next = ctx.next;
        ctx.next.prev = newCtx;
        ctx.next = newCtx;
    }

    @Override
    public ChannelPipeline addFirst(ChannelHandler... handlers) {
        return addFirst(null, handlers);
    }

    @Override
    public ChannelPipeline addFirst(EventExecutorGroup executor, ChannelHandler... handlers) {
        if (handlers == null) {
            throw new NullPointerException("handlers");
        }
        if (handlers.length == 0 || handlers[0] == null) {
            return this;
        }

        int size;
        for (size = 1; size < handlers.length; size ++) {
            if (handlers[size] == null) {
                break;
            }
        }

        for (int i = size - 1; i >= 0; i --) {
            ChannelHandler h = handlers[i];
            addFirst(executor, generateName(h), h);
        }

        return this;
    }

    @Override
    public ChannelPipeline addLast(ChannelHandler... handlers) {
        return addLast(null, handlers);
    }

    @Override
    public ChannelPipeline addLast(EventExecutorGroup executor, ChannelHandler... handlers) {
        if (handlers == null) {
            throw new NullPointerException("handlers");
        }

        for (ChannelHandler h: handlers) {
            if (h == null) {
                break;
            }
            addLast(executor, generateName(h), h);
        }

        return this;
    }

    private String generateName(ChannelHandler handler) {
        Map, String> cache = nameCaches.get();
        Class handlerType = handler.getClass();
        String name = cache.get(handlerType);
        if (name == null) {
            name = generateName0(handlerType);
            cache.put(handlerType, name);
        }

        // It's not very likely for a user to put more than one handler of the same type, but make sure to avoid
        // any name conflicts.  Note that we don't cache the names generated here.
        if (context0(name) != null) {
            String baseName = name.substring(0, name.length() - 1); // Strip the trailing '0'.
            for (int i = 1;; i ++) {
                String newName = baseName + i;
                if (context0(newName) == null) {
                    name = newName;
                    break;
                }
            }
        }
        return name;
    }

    private static String generateName0(Class handlerType) {
        return StringUtil.simpleClassName(handlerType) + "#0";
    }

    @Override
    public ChannelPipeline remove(ChannelHandler handler) {
        remove(getContextOrDie(handler));
        return this;
    }

    @Override
    public ChannelHandler remove(String name) {
        return remove(getContextOrDie(name)).handler();
    }

    @SuppressWarnings("unchecked")
    @Override
    public  T remove(Class handlerType) {
        return (T) remove(getContextOrDie(handlerType)).handler();
    }

    private AbstractChannelHandlerContext remove(final AbstractChannelHandlerContext ctx) {
        assert ctx != head && ctx != tail;

        final EventExecutor executor;
        final boolean inEventLoop;
        synchronized (this) {
            executor = executorSafe(ctx.executor);

            // If the executor is null it means that the channel was not registered on an eventloop yet.
            // In this case we remove the context from the pipeline and add a task that will call
            // ChannelHandler.handlerRemoved(...) once the channel is registered.
            if (executor == null) {
                remove0(ctx);
                callHandlerCallbackLater(ctx, false);
                return ctx;
            }
            inEventLoop = executor.inEventLoop();
            if (inEventLoop) {
                remove0(ctx);
            }
        }
        if (inEventLoop) {
            callHandlerRemoved0(ctx);
        } else {
            waitForFuture(executor.submit(new OneTimeTask() {
                @Override
                public void run() {
                    synchronized (DefaultChannelPipeline.this) {
                        remove0(ctx);
                    }
                    callHandlerRemoved0(ctx);
                }
            }));
        }
        return ctx;
    }

    private static void remove0(AbstractChannelHandlerContext ctx) {
        AbstractChannelHandlerContext prev = ctx.prev;
        AbstractChannelHandlerContext next = ctx.next;
        prev.next = next;
        next.prev = prev;
    }

    @Override
    public ChannelHandler removeFirst() {
        if (head.next == tail) {
            throw new NoSuchElementException();
        }
        return remove(head.next).handler();
    }

    @Override
    public ChannelHandler removeLast() {
        if (head.next == tail) {
            throw new NoSuchElementException();
        }
        return remove(tail.prev).handler();
    }

    @Override
    public ChannelPipeline replace(ChannelHandler oldHandler, String newName, ChannelHandler newHandler) {
        replace(getContextOrDie(oldHandler), newName, newHandler);
        return this;
    }

    @Override
    public ChannelHandler replace(String oldName, String newName, ChannelHandler newHandler) {
        return replace(getContextOrDie(oldName), newName, newHandler);
    }

    @Override
    @SuppressWarnings("unchecked")
    public  T replace(
            Class oldHandlerType, String newName, ChannelHandler newHandler) {
        return (T) replace(getContextOrDie(oldHandlerType), newName, newHandler);
    }

    private ChannelHandler replace(
            final AbstractChannelHandlerContext ctx, final String newName, ChannelHandler newHandler) {
        assert ctx != head && ctx != tail;

        final AbstractChannelHandlerContext newCtx;
        final EventExecutor executor;
        final boolean inEventLoop;
        synchronized (this) {
            checkMultiplicity(newHandler);
            boolean sameName = ctx.name().equals(newName);
            if (!sameName) {
                checkDuplicateName(newName);
            }

            newCtx = newContext(ctx.executor, newName, newHandler);
            executor = executorSafe(ctx.executor);

            // If the executor is null it means that the channel was not registered on an eventloop yet.
            // In this case we replace the context in the pipeline
            // and add a task that will call ChannelHandler.handlerAdded(...) and
            // ChannelHandler.handlerRemoved(...) once the channel is registered.
            if (executor == null) {
                replace0(ctx, newCtx);
                callHandlerCallbackLater(newCtx, true);
                callHandlerCallbackLater(ctx, false);
                return ctx.handler();
            }
            inEventLoop = executor.inEventLoop();
            if (inEventLoop) {
                replace0(ctx, newCtx);
            }
        }
        if (inEventLoop) {
            // Invoke newHandler.handlerAdded() first (i.e. before oldHandler.handlerRemoved() is invoked)
            // because callHandlerRemoved() will trigger channelRead() or flush() on newHandler and those
            // event handlers must be called after handlerAdded().
            callHandlerAdded0(newCtx);
            callHandlerRemoved0(ctx);
        } else {
            waitForFuture(executor.submit(new OneTimeTask() {
                @Override
                public void run() {
                    synchronized (DefaultChannelPipeline.this) {
                        replace0(ctx, newCtx);
                    }
                    // Invoke newHandler.handlerAdded() first (i.e. before oldHandler.handlerRemoved() is invoked)
                    // because callHandlerRemoved() will trigger channelRead() or flush() on newHandler and
                    // those event handlers must be called after handlerAdded().
                    callHandlerAdded0(newCtx);
                    callHandlerRemoved0(ctx);
                }
            }));
        }
        return ctx.handler();
    }

    private static void replace0(AbstractChannelHandlerContext oldCtx, AbstractChannelHandlerContext newCtx) {
        AbstractChannelHandlerContext prev = oldCtx.prev;
        AbstractChannelHandlerContext next = oldCtx.next;
        newCtx.prev = prev;
        newCtx.next = next;

        // Finish the replacement of oldCtx with newCtx in the linked list.
        // Note that this doesn't mean events will be sent to the new handler immediately
        // because we are currently at the event handler thread and no more than one handler methods can be invoked
        // at the same time (we ensured that in replace().)
        prev.next = newCtx;
        next.prev = newCtx;

        // update the reference to the replacement so forward of buffered content will work correctly
        oldCtx.prev = newCtx;
        oldCtx.next = newCtx;
    }

    private static void checkMultiplicity(ChannelHandler handler) {
        if (handler instanceof ChannelHandlerAdapter) {
            ChannelHandlerAdapter h = (ChannelHandlerAdapter) handler;
            if (!h.isSharable() && h.added) {
                throw new ChannelPipelineException(
                        h.getClass().getName() +
                        " is not a @Sharable handler, so can't be added or removed multiple times.");
            }
            h.added = true;
        }
    }

    private void callHandlerAdded0(final AbstractChannelHandlerContext ctx) {
        try {
            ctx.handler().handlerAdded(ctx);
        } catch (Throwable t) {
            boolean removed = false;
            try {
                remove0(ctx);
                try {
                    ctx.handler().handlerRemoved(ctx);
                } finally {
                    ctx.setRemoved();
                }
                removed = true;
            } catch (Throwable t2) {
                if (logger.isWarnEnabled()) {
                    logger.warn("Failed to remove a handler: " + ctx.name(), t2);
                }
            }

            if (removed) {
                fireExceptionCaught(new ChannelPipelineException(
                        ctx.handler().getClass().getName() +
                        ".handlerAdded() has thrown an exception; removed.", t));
            } else {
                fireExceptionCaught(new ChannelPipelineException(
                        ctx.handler().getClass().getName() +
                        ".handlerAdded() has thrown an exception; also failed to remove.", t));
            }
        }
    }

    private void callHandlerRemoved0(final AbstractChannelHandlerContext ctx) {
        // Notify the complete removal.
        try {
            try {
                ctx.handler().handlerRemoved(ctx);
            } finally {
                ctx.setRemoved();
            }
        } catch (Throwable t) {
            fireExceptionCaught(new ChannelPipelineException(
                    ctx.handler().getClass().getName() + ".handlerRemoved() has thrown an exception.", t));
        }
    }

    /**
     * Waits for a future to finish.  If the task is interrupted, then the current thread will be interrupted.
     * It is expected that the task performs any appropriate locking.
     * 

* If the internal call throws a {@link Throwable}, but it is not an instance of {@link Error} or * {@link RuntimeException}, then it is wrapped inside a {@link ChannelPipelineException} and that is * thrown instead.

* * @param future wait for this future * @see Future#get() * @throws Error if the task threw this. * @throws RuntimeException if the task threw this. * @throws ChannelPipelineException with a {@link Throwable} as a cause, if the task threw another type of * {@link Throwable}. */ private static void waitForFuture(Future future) { try { future.get(); } catch (ExecutionException ex) { // In the arbitrary case, we can throw Error, RuntimeException, and Exception PlatformDependent.throwException(ex.getCause()); } catch (InterruptedException ex) { // Interrupt the calling thread (note that this method is not called from the event loop) Thread.currentThread().interrupt(); } } @Override public ChannelHandler first() { ChannelHandlerContext first = firstContext(); if (first == null) { return null; } return first.handler(); } @Override public ChannelHandlerContext firstContext() { AbstractChannelHandlerContext first = head.next; if (first == tail) { return null; } return head.next; } @Override public ChannelHandler last() { AbstractChannelHandlerContext last = tail.prev; if (last == head) { return null; } return last.handler(); } @Override public ChannelHandlerContext lastContext() { AbstractChannelHandlerContext last = tail.prev; if (last == head) { return null; } return last; } @Override public ChannelHandler get(String name) { ChannelHandlerContext ctx = context(name); if (ctx == null) { return null; } else { return ctx.handler(); } } @SuppressWarnings("unchecked") @Override public T get(Class handlerType) { ChannelHandlerContext ctx = context(handlerType); if (ctx == null) { return null; } else { return (T) ctx.handler(); } } @Override public ChannelHandlerContext context(String name) { if (name == null) { throw new NullPointerException("name"); } return context0(name); } @Override public ChannelHandlerContext context(ChannelHandler handler) { if (handler == null) { throw new NullPointerException("handler"); } AbstractChannelHandlerContext ctx = head.next; for (;;) { if (ctx == null) { return null; } if (ctx.handler() == handler) { return ctx; } ctx = ctx.next; } } @Override public ChannelHandlerContext context(Class handlerType) { if (handlerType == null) { throw new NullPointerException("handlerType"); } AbstractChannelHandlerContext ctx = head.next; for (;;) { if (ctx == null) { return null; } if (handlerType.isAssignableFrom(ctx.handler().getClass())) { return ctx; } ctx = ctx.next; } } @Override public List names() { List list = new ArrayList(); AbstractChannelHandlerContext ctx = head.next; for (;;) { if (ctx == null) { return list; } list.add(ctx.name()); ctx = ctx.next; } } @Override public Map toMap() { Map map = new LinkedHashMap(); AbstractChannelHandlerContext ctx = head.next; for (;;) { if (ctx == tail) { return map; } map.put(ctx.name(), ctx.handler()); ctx = ctx.next; } } @Override public Iterator> iterator() { return toMap().entrySet().iterator(); } /** * Returns the {@link String} representation of this pipeline. */ @Override public String toString() { StringBuilder buf = new StringBuilder() .append(StringUtil.simpleClassName(this)) .append('{'); AbstractChannelHandlerContext ctx = head.next; for (;;) { if (ctx == tail) { break; } buf.append('(') .append(ctx.name()) .append(" = ") .append(ctx.handler().getClass().getName()) .append(')'); ctx = ctx.next; if (ctx == tail) { break; } buf.append(", "); } buf.append('}'); return buf.toString(); } @Override public ChannelPipeline fireChannelRegistered() { head.fireChannelRegistered(); return this; } @Override public ChannelPipeline fireChannelUnregistered() { head.fireChannelUnregistered(); // Remove all handlers sequentially if channel is closed and unregistered. if (!channel.isOpen()) { destroy(); } return this; } /** * Removes all handlers from the pipeline one by one from tail (exclusive) to head (exclusive) to trigger * handlerRemoved(). * * Note that we traverse up the pipeline ({@link #destroyUp(AbstractChannelHandlerContext, boolean)}) * before traversing down ({@link #destroyDown(Thread, AbstractChannelHandlerContext, boolean)}) so that * the handlers are removed after all events are handled. * * See: https://github.com/netty/netty/issues/3156 */ private synchronized void destroy() { destroyUp(head.next, false); } private void destroyUp(AbstractChannelHandlerContext ctx, boolean inEventLoop) { final Thread currentThread = Thread.currentThread(); final AbstractChannelHandlerContext tail = this.tail; for (;;) { if (ctx == tail) { destroyDown(currentThread, tail.prev, inEventLoop); break; } final EventExecutor executor = ctx.executor(); if (!inEventLoop && !executor.inEventLoop(currentThread)) { final AbstractChannelHandlerContext finalCtx = ctx; executor.execute(new OneTimeTask() { @Override public void run() { destroyUp(finalCtx, true); } }); break; } ctx = ctx.next; inEventLoop = false; } } private void destroyDown(Thread currentThread, AbstractChannelHandlerContext ctx, boolean inEventLoop) { // We have reached at tail; now traverse backwards. final AbstractChannelHandlerContext head = this.head; for (;;) { if (ctx == head) { break; } final EventExecutor executor = ctx.executor(); if (inEventLoop || executor.inEventLoop(currentThread)) { synchronized (this) { remove0(ctx); callHandlerRemoved0(ctx); } } else { final AbstractChannelHandlerContext finalCtx = ctx; executor.execute(new OneTimeTask() { @Override public void run() { destroyDown(Thread.currentThread(), finalCtx, true); } }); break; } ctx = ctx.prev; inEventLoop = false; } } @Override public ChannelPipeline fireChannelActive() { head.fireChannelActive(); if (channel.config().isAutoRead()) { channel.read(); } return this; } @Override public ChannelPipeline fireChannelInactive() { head.fireChannelInactive(); return this; } @Override public ChannelPipeline fireExceptionCaught(Throwable cause) { head.fireExceptionCaught(cause); return this; } @Override public ChannelPipeline fireUserEventTriggered(Object event) { head.fireUserEventTriggered(event); return this; } @Override public ChannelPipeline fireChannelRead(Object msg) { head.fireChannelRead(msg); return this; } @Override public ChannelPipeline fireChannelReadComplete() { head.fireChannelReadComplete(); if (channel.config().isAutoRead()) { read(); } return this; } @Override public ChannelPipeline fireChannelWritabilityChanged() { head.fireChannelWritabilityChanged(); return this; } @Override public ChannelFuture bind(SocketAddress localAddress) { return tail.bind(localAddress); } @Override public ChannelFuture connect(SocketAddress remoteAddress) { return tail.connect(remoteAddress); } @Override public ChannelFuture connect(SocketAddress remoteAddress, SocketAddress localAddress) { return tail.connect(remoteAddress, localAddress); } @Override public ChannelFuture disconnect() { return tail.disconnect(); } @Override public ChannelFuture close() { return tail.close(); } @Override public ChannelFuture deregister() { return tail.deregister(); } @Override public ChannelPipeline flush() { tail.flush(); return this; } @Override public ChannelFuture bind(SocketAddress localAddress, ChannelPromise promise) { return tail.bind(localAddress, promise); } @Override public ChannelFuture connect(SocketAddress remoteAddress, ChannelPromise promise) { return tail.connect(remoteAddress, promise); } @Override public ChannelFuture connect(SocketAddress remoteAddress, SocketAddress localAddress, ChannelPromise promise) { return tail.connect(remoteAddress, localAddress, promise); } @Override public ChannelFuture disconnect(ChannelPromise promise) { return tail.disconnect(promise); } @Override public ChannelFuture close(ChannelPromise promise) { return tail.close(promise); } @Override public ChannelFuture deregister(final ChannelPromise promise) { return tail.deregister(promise); } @Override public ChannelPipeline read() { tail.read(); return this; } @Override public ChannelFuture write(Object msg) { return tail.write(msg); } @Override public ChannelFuture write(Object msg, ChannelPromise promise) { return tail.write(msg, promise); } @Override public ChannelFuture writeAndFlush(Object msg, ChannelPromise promise) { return tail.writeAndFlush(msg, promise); } @Override public ChannelFuture writeAndFlush(Object msg) { return tail.writeAndFlush(msg); } private void checkDuplicateName(String name) { if (context0(name) != null) { throw new IllegalArgumentException("Duplicate handler name: " + name); } } private AbstractChannelHandlerContext context0(String name) { AbstractChannelHandlerContext context = head.next; while (context != tail) { if (context.name().equals(name)) { return context; } context = context.next; } return null; } private AbstractChannelHandlerContext getContextOrDie(String name) { AbstractChannelHandlerContext ctx = (AbstractChannelHandlerContext) context(name); if (ctx == null) { throw new NoSuchElementException(name); } else { return ctx; } } private AbstractChannelHandlerContext getContextOrDie(ChannelHandler handler) { AbstractChannelHandlerContext ctx = (AbstractChannelHandlerContext) context(handler); if (ctx == null) { throw new NoSuchElementException(handler.getClass().getName()); } else { return ctx; } } private AbstractChannelHandlerContext getContextOrDie(Class handlerType) { AbstractChannelHandlerContext ctx = (AbstractChannelHandlerContext) context(handlerType); if (ctx == null) { throw new NoSuchElementException(handlerType.getName()); } else { return ctx; } } /** * Should be called before {@link #fireChannelRegistered()} is called the first time. */ void callHandlerAddedForAllHandlers() { // This should only called from within the EventLoop. assert channel.eventLoop().inEventLoop(); final PendingHandlerCallback pendingHandlerCallbackHead; synchronized (this) { assert !registered; // This Channel itself was registered. registered = true; pendingHandlerCallbackHead = this.pendingHandlerCallbackHead; // Null out so it can be GC'ed. this.pendingHandlerCallbackHead = null; } // This must happen outside of the synchronized(...) block as otherwise handlerAdded(...) may be called while // holding the lock and so produce a deadlock if handlerAdded(...) will try to add another handler from outside // the EventLoop. PendingHandlerCallback task = pendingHandlerCallbackHead; while (task != null) { task.execute(); task = task.next; } } private void callHandlerCallbackLater(AbstractChannelHandlerContext ctx, boolean added) { assert !registered; PendingHandlerCallback task = added ? new PendingHandlerAddedTask(ctx) : new PendingHandlerRemovedTask(ctx); PendingHandlerCallback pending = pendingHandlerCallbackHead; if (pending == null) { pendingHandlerCallbackHead = task; } else { // Find the tail of the linked-list. while (pending.next != null) { pending = pending.next; } pending.next = task; } } private EventExecutor executorSafe(EventExecutor eventExecutor) { if (eventExecutor == null) { // We check for channel().isRegistered and handlerAdded because even if isRegistered() is false we // can safely access the eventLoop() if handlerAdded is true. This is because in this case the Channel // was previously registered and so we can still access the old EventLoop to dispatch things. return channel.isRegistered() || registered ? channel.eventLoop() : null; } return eventExecutor; } // A special catch-all handler that handles both bytes and messages. static final class TailContext extends AbstractChannelHandlerContext implements ChannelInboundHandler { private static final String TAIL_NAME = generateName0(TailContext.class); TailContext(DefaultChannelPipeline pipeline) { super(pipeline, null, TAIL_NAME, true, false); } @Override public ChannelHandler handler() { return this; } @Override public void channelRegistered(ChannelHandlerContext ctx) throws Exception { } @Override public void channelUnregistered(ChannelHandlerContext ctx) throws Exception { } @Override public void channelActive(ChannelHandlerContext ctx) throws Exception { } @Override public void channelInactive(ChannelHandlerContext ctx) throws Exception { } @Override public void channelWritabilityChanged(ChannelHandlerContext ctx) throws Exception { } @Override public void handlerAdded(ChannelHandlerContext ctx) throws Exception { } @Override public void handlerRemoved(ChannelHandlerContext ctx) throws Exception { } @Override public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { // This may not be a configuration error and so don't log anything. // The event may be superfluous for the current pipeline configuration. ReferenceCountUtil.release(evt); } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { try { logger.warn( "An exceptionCaught() event was fired, and it reached at the tail of the pipeline. " + "It usually means the last handler in the pipeline did not handle the exception.", cause); } finally { ReferenceCountUtil.release(cause); } } @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { try { logger.debug( "Discarded inbound message {} that reached at the tail of the pipeline. " + "Please check your pipeline configuration.", msg); } finally { ReferenceCountUtil.release(msg); } } @Override public void channelReadComplete(ChannelHandlerContext ctx) throws Exception { } } static final class HeadContext extends AbstractChannelHandlerContext implements ChannelOutboundHandler { private static final String HEAD_NAME = generateName0(HeadContext.class); protected final Unsafe unsafe; HeadContext(DefaultChannelPipeline pipeline) { super(pipeline, null, HEAD_NAME, false, true); unsafe = pipeline.channel().unsafe(); } @Override public ChannelHandler handler() { return this; } @Override public void handlerAdded(ChannelHandlerContext ctx) throws Exception { // NOOP } @Override public void handlerRemoved(ChannelHandlerContext ctx) throws Exception { // NOOP } @Override public void bind( ChannelHandlerContext ctx, SocketAddress localAddress, ChannelPromise promise) throws Exception { unsafe.bind(localAddress, promise); } @Override public void connect( ChannelHandlerContext ctx, SocketAddress remoteAddress, SocketAddress localAddress, ChannelPromise promise) throws Exception { unsafe.connect(remoteAddress, localAddress, promise); } @Override public void disconnect(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception { unsafe.disconnect(promise); } @Override public void close(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception { unsafe.close(promise); } @Override public void deregister(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception { unsafe.deregister(promise); } @Override public void read(ChannelHandlerContext ctx) { unsafe.beginRead(); } @Override public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception { unsafe.write(msg, promise); } @Override public void flush(ChannelHandlerContext ctx) throws Exception { unsafe.flush(); } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { ctx.fireExceptionCaught(cause); } } private abstract static class PendingHandlerCallback extends OneTimeTask { final AbstractChannelHandlerContext ctx; PendingHandlerCallback next; PendingHandlerCallback(AbstractChannelHandlerContext ctx) { this.ctx = ctx; } abstract void execute(); } private final class PendingHandlerAddedTask extends PendingHandlerCallback { PendingHandlerAddedTask(AbstractChannelHandlerContext ctx) { super(ctx); } @Override public void run() { callHandlerAdded0(ctx); } @Override void execute() { EventExecutor executor = ctx.executor(); if (executor.inEventLoop()) { callHandlerAdded0(ctx); } else { try { executor.execute(this); } catch (RejectedExecutionException e) { if (logger.isWarnEnabled()) { logger.warn( "Can't invoke handlerAdded() as the EventExecutor {} rejected it, removing handler {}.", executor, ctx.name(), e); } remove0(ctx); ctx.setRemoved(); } } } } private final class PendingHandlerRemovedTask extends PendingHandlerCallback { PendingHandlerRemovedTask(AbstractChannelHandlerContext ctx) { super(ctx); } @Override public void run() { callHandlerRemoved0(ctx); } @Override void execute() { EventExecutor executor = ctx.executor(); if (executor.inEventLoop()) { callHandlerRemoved0(ctx); } else { try { executor.execute(this); } catch (RejectedExecutionException e) { if (logger.isWarnEnabled()) { logger.warn( "Can't invoke handlerRemoved() as the EventExecutor {} rejected it," + " removing handler {}.", executor, ctx.name(), e); } // remove0(...) was call before so just call AbstractChannelHandlerContext.setRemoved(). ctx.setRemoved(); } } } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy