// 
// Decompiled by Procyon v0.6.0
// 

package io.netty.channel;

import java.util.Objects;
import io.netty.util.internal.SystemPropertyUtil;
import io.netty.util.internal.ObjectPool;
import io.netty.util.Recycler;
import io.netty.util.internal.logging.InternalLoggerFactory;
import io.netty.util.concurrent.AbstractEventExecutor;
import io.netty.util.Attribute;
import io.netty.util.AttributeKey;
import io.netty.util.internal.StringUtil;
import io.netty.util.concurrent.Promise;
import io.netty.util.internal.PromiseNotificationUtil;
import io.netty.util.ReferenceCountUtil;
import java.net.SocketAddress;
import io.netty.buffer.ByteBufAllocator;
import io.netty.util.concurrent.OrderedEventExecutor;
import io.netty.util.internal.ObjectUtil;
import io.netty.util.concurrent.EventExecutor;
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
import io.netty.util.internal.logging.InternalLogger;
import io.netty.util.ResourceLeakHint;

abstract class AbstractChannelHandlerContext implements ChannelHandlerContext, ResourceLeakHint
{
    private static final InternalLogger logger;
    volatile AbstractChannelHandlerContext next;
    volatile AbstractChannelHandlerContext prev;
    private static final AtomicIntegerFieldUpdater<AbstractChannelHandlerContext> HANDLER_STATE_UPDATER;
    private static final int ADD_PENDING = 1;
    private static final int ADD_COMPLETE = 2;
    private static final int REMOVE_COMPLETE = 3;
    private static final int INIT = 0;
    private final DefaultChannelPipeline pipeline;
    private final String name;
    private final boolean ordered;
    private final int executionMask;
    final EventExecutor childExecutor;
    EventExecutor contextExecutor;
    private ChannelFuture succeededFuture;
    private Tasks invokeTasks;
    private volatile int handlerState;
    
    AbstractChannelHandlerContext(final DefaultChannelPipeline pipeline, final EventExecutor executor, final String name, final Class<? extends ChannelHandler> handlerClass) {
        this.handlerState = 0;
        this.name = ObjectUtil.checkNotNull(name, "name");
        this.pipeline = pipeline;
        this.childExecutor = executor;
        this.executionMask = ChannelHandlerMask.mask(handlerClass);
        this.ordered = (executor == null || executor instanceof OrderedEventExecutor);
    }
    
    @Override
    public Channel channel() {
        return this.pipeline.channel();
    }
    
    @Override
    public ChannelPipeline pipeline() {
        return this.pipeline;
    }
    
    @Override
    public ByteBufAllocator alloc() {
        return this.channel().config().getAllocator();
    }
    
    @Override
    public EventExecutor executor() {
        EventExecutor ex = this.contextExecutor;
        if (ex == null) {
            ex = (this.contextExecutor = ((this.childExecutor != null) ? this.childExecutor : this.channel().eventLoop()));
        }
        return ex;
    }
    
    @Override
    public String name() {
        return this.name;
    }
    
    @Override
    public ChannelHandlerContext fireChannelRegistered() {
        final AbstractChannelHandlerContext next = this.findContextInbound(2);
        if (next.executor().inEventLoop()) {
            if (next.invokeHandler()) {
                try {
                    final ChannelHandler handler = next.handler();
                    final DefaultChannelPipeline.HeadContext headContext = this.pipeline.head;
                    if (handler == headContext) {
                        headContext.channelRegistered(next);
                    }
                    else if (handler instanceof ChannelInboundHandlerAdapter) {
                        ((ChannelInboundHandlerAdapter)handler).channelRegistered(next);
                    }
                    else {
                        ((ChannelInboundHandler)handler).channelRegistered(next);
                    }
                }
                catch (final Throwable t) {
                    next.invokeExceptionCaught(t);
                }
            }
            else {
                next.fireChannelRegistered();
            }
        }
        else {
            next.executor().execute(this::fireChannelRegistered);
        }
        return this;
    }
    
    @Override
    public ChannelHandlerContext fireChannelUnregistered() {
        final AbstractChannelHandlerContext next = this.findContextInbound(4);
        if (next.executor().inEventLoop()) {
            if (next.invokeHandler()) {
                try {
                    final ChannelHandler handler = next.handler();
                    final DefaultChannelPipeline.HeadContext headContext = this.pipeline.head;
                    if (handler == headContext) {
                        headContext.channelUnregistered(next);
                    }
                    else if (handler instanceof ChannelInboundHandlerAdapter) {
                        ((ChannelInboundHandlerAdapter)handler).channelUnregistered(next);
                    }
                    else {
                        ((ChannelInboundHandler)handler).channelUnregistered(next);
                    }
                }
                catch (final Throwable t) {
                    next.invokeExceptionCaught(t);
                }
            }
            else {
                next.fireChannelUnregistered();
            }
        }
        else {
            next.executor().execute(this::fireChannelUnregistered);
        }
        return this;
    }
    
    @Override
    public ChannelHandlerContext fireChannelActive() {
        final AbstractChannelHandlerContext next = this.findContextInbound(8);
        if (next.executor().inEventLoop()) {
            if (next.invokeHandler()) {
                try {
                    final ChannelHandler handler = next.handler();
                    final DefaultChannelPipeline.HeadContext headContext = this.pipeline.head;
                    if (handler == headContext) {
                        headContext.channelActive(next);
                    }
                    else if (handler instanceof ChannelInboundHandlerAdapter) {
                        ((ChannelInboundHandlerAdapter)handler).channelActive(next);
                    }
                    else {
                        ((ChannelInboundHandler)handler).channelActive(next);
                    }
                }
                catch (final Throwable t) {
                    next.invokeExceptionCaught(t);
                }
            }
            else {
                next.fireChannelActive();
            }
        }
        else {
            next.executor().execute(this::fireChannelActive);
        }
        return this;
    }
    
    @Override
    public ChannelHandlerContext fireChannelInactive() {
        final AbstractChannelHandlerContext next = this.findContextInbound(16);
        if (next.executor().inEventLoop()) {
            if (next.invokeHandler()) {
                try {
                    final ChannelHandler handler = next.handler();
                    final DefaultChannelPipeline.HeadContext headContext = this.pipeline.head;
                    if (handler == headContext) {
                        headContext.channelInactive(next);
                    }
                    else if (handler instanceof ChannelInboundHandlerAdapter) {
                        ((ChannelInboundHandlerAdapter)handler).channelInactive(next);
                    }
                    else {
                        ((ChannelInboundHandler)handler).channelInactive(next);
                    }
                }
                catch (final Throwable t) {
                    next.invokeExceptionCaught(t);
                }
            }
            else {
                next.fireChannelInactive();
            }
        }
        else {
            next.executor().execute(this::fireChannelInactive);
        }
        return this;
    }
    
    @Override
    public ChannelHandlerContext fireExceptionCaught(final Throwable cause) {
        final AbstractChannelHandlerContext next = this.findContextInbound(1);
        ObjectUtil.checkNotNull(cause, "cause");
        if (next.executor().inEventLoop()) {
            next.invokeExceptionCaught(cause);
        }
        else {
            try {
                next.executor().execute(() -> next.invokeExceptionCaught(cause));
            }
            catch (final Throwable t) {
                if (AbstractChannelHandlerContext.logger.isWarnEnabled()) {
                    AbstractChannelHandlerContext.logger.warn("Failed to submit an exceptionCaught() event.", t);
                    AbstractChannelHandlerContext.logger.warn("The exceptionCaught() event that was failed to submit was:", cause);
                }
            }
        }
        return this;
    }
    
    private void invokeExceptionCaught(final Throwable cause) {
        if (this.invokeHandler()) {
            try {
                this.handler().exceptionCaught(this, cause);
            }
            catch (final Throwable error) {
                if (AbstractChannelHandlerContext.logger.isDebugEnabled()) {
                    AbstractChannelHandlerContext.logger.debug("An exception was thrown by a user handler's exceptionCaught() method while handling the following exception:", cause);
                }
                else if (AbstractChannelHandlerContext.logger.isWarnEnabled()) {
                    AbstractChannelHandlerContext.logger.warn("An exception '{}' [enable DEBUG level for full stacktrace] was thrown by a user handler's exceptionCaught() method while handling the following exception:", error, cause);
                }
            }
        }
        else {
            this.fireExceptionCaught(cause);
        }
    }
    
    @Override
    public ChannelHandlerContext fireUserEventTriggered(final Object event) {
        ObjectUtil.checkNotNull(event, "event");
        final AbstractChannelHandlerContext next = this.findContextInbound(128);
        if (next.executor().inEventLoop()) {
            if (next.invokeHandler()) {
                try {
                    final ChannelHandler handler = next.handler();
                    final DefaultChannelPipeline.HeadContext headContext = this.pipeline.head;
                    if (handler == headContext) {
                        headContext.userEventTriggered(next, event);
                    }
                    else if (handler instanceof ChannelInboundHandlerAdapter) {
                        ((ChannelInboundHandlerAdapter)handler).userEventTriggered(next, event);
                    }
                    else {
                        ((ChannelInboundHandler)handler).userEventTriggered(next, event);
                    }
                }
                catch (final Throwable t) {
                    next.invokeExceptionCaught(t);
                }
            }
            else {
                next.fireUserEventTriggered(event);
            }
        }
        else {
            next.executor().execute(() -> this.fireUserEventTriggered(event));
        }
        return this;
    }
    
    @Override
    public ChannelHandlerContext fireChannelRead(final Object msg) {
        final AbstractChannelHandlerContext next = this.findContextInbound(32);
        if (next.executor().inEventLoop()) {
            final Object m = this.pipeline.touch(msg, next);
            if (next.invokeHandler()) {
                try {
                    final ChannelHandler handler = next.handler();
                    final DefaultChannelPipeline.HeadContext headContext = this.pipeline.head;
                    if (handler == headContext) {
                        headContext.channelRead(next, m);
                    }
                    else if (handler instanceof ChannelDuplexHandler) {
                        ((ChannelDuplexHandler)handler).channelRead(next, m);
                    }
                    else {
                        ((ChannelInboundHandler)handler).channelRead(next, m);
                    }
                }
                catch (final Throwable t) {
                    next.invokeExceptionCaught(t);
                }
            }
            else {
                next.fireChannelRead(m);
            }
        }
        else {
            next.executor().execute(() -> this.fireChannelRead(msg));
        }
        return this;
    }
    
    @Override
    public ChannelHandlerContext fireChannelReadComplete() {
        final AbstractChannelHandlerContext next = this.findContextInbound(64);
        if (next.executor().inEventLoop()) {
            if (next.invokeHandler()) {
                try {
                    final ChannelHandler handler = next.handler();
                    final DefaultChannelPipeline.HeadContext headContext = this.pipeline.head;
                    if (handler == headContext) {
                        headContext.channelReadComplete(next);
                    }
                    else if (handler instanceof ChannelDuplexHandler) {
                        ((ChannelDuplexHandler)handler).channelReadComplete(next);
                    }
                    else {
                        ((ChannelInboundHandler)handler).channelReadComplete(next);
                    }
                }
                catch (final Throwable t) {
                    next.invokeExceptionCaught(t);
                }
            }
            else {
                next.fireChannelReadComplete();
            }
        }
        else {
            next.executor().execute(this.getInvokeTasks().invokeChannelReadCompleteTask);
        }
        return this;
    }
    
    @Override
    public ChannelHandlerContext fireChannelWritabilityChanged() {
        final AbstractChannelHandlerContext next = this.findContextInbound(256);
        if (next.executor().inEventLoop()) {
            if (next.invokeHandler()) {
                try {
                    final ChannelHandler handler = next.handler();
                    final DefaultChannelPipeline.HeadContext headContext = this.pipeline.head;
                    if (handler == headContext) {
                        headContext.channelWritabilityChanged(next);
                    }
                    else if (handler instanceof ChannelInboundHandlerAdapter) {
                        ((ChannelInboundHandlerAdapter)handler).channelWritabilityChanged(next);
                    }
                    else {
                        ((ChannelInboundHandler)handler).channelWritabilityChanged(next);
                    }
                }
                catch (final Throwable t) {
                    next.invokeExceptionCaught(t);
                }
            }
            else {
                next.fireChannelWritabilityChanged();
            }
        }
        else {
            next.executor().execute(this.getInvokeTasks().invokeChannelWritableStateChangedTask);
        }
        return this;
    }
    
    @Override
    public ChannelFuture bind(final SocketAddress localAddress) {
        return this.bind(localAddress, this.newPromise());
    }
    
    @Override
    public ChannelFuture connect(final SocketAddress remoteAddress) {
        return this.connect(remoteAddress, this.newPromise());
    }
    
    @Override
    public ChannelFuture connect(final SocketAddress remoteAddress, final SocketAddress localAddress) {
        return this.connect(remoteAddress, localAddress, this.newPromise());
    }
    
    @Override
    public ChannelFuture disconnect() {
        return this.disconnect(this.newPromise());
    }
    
    @Override
    public ChannelFuture close() {
        return this.close(this.newPromise());
    }
    
    @Override
    public ChannelFuture deregister() {
        return this.deregister(this.newPromise());
    }
    
    @Override
    public ChannelFuture bind(final SocketAddress localAddress, final ChannelPromise promise) {
        ObjectUtil.checkNotNull(localAddress, "localAddress");
        if (this.isNotValidPromise(promise, false)) {
            return promise;
        }
        final AbstractChannelHandlerContext next = this.findContextOutbound(512);
        final EventExecutor executor = next.executor();
        if (executor.inEventLoop()) {
            next.invokeBind(localAddress, promise);
        }
        else {
            safeExecute(executor, new Runnable() {
                @Override
                public void run() {
                    AbstractChannelHandlerContext.this.invokeBind(localAddress, promise);
                }
            }, promise, null, false);
        }
        return promise;
    }
    
    private void invokeBind(final SocketAddress localAddress, final ChannelPromise promise) {
        if (this.invokeHandler()) {
            try {
                final ChannelHandler handler = this.handler();
                final DefaultChannelPipeline.HeadContext headContext = this.pipeline.head;
                if (handler == headContext) {
                    headContext.bind(this, localAddress, promise);
                }
                else if (handler instanceof ChannelDuplexHandler) {
                    ((ChannelDuplexHandler)handler).bind(this, localAddress, promise);
                }
                else if (handler instanceof ChannelOutboundHandlerAdapter) {
                    ((ChannelOutboundHandlerAdapter)handler).bind(this, localAddress, promise);
                }
                else {
                    ((ChannelOutboundHandler)handler).bind(this, localAddress, promise);
                }
            }
            catch (final Throwable t) {
                notifyOutboundHandlerException(t, promise);
            }
        }
        else {
            this.bind(localAddress, promise);
        }
    }
    
    @Override
    public ChannelFuture connect(final SocketAddress remoteAddress, final ChannelPromise promise) {
        return this.connect(remoteAddress, null, promise);
    }
    
    @Override
    public ChannelFuture connect(final SocketAddress remoteAddress, final SocketAddress localAddress, final ChannelPromise promise) {
        ObjectUtil.checkNotNull(remoteAddress, "remoteAddress");
        if (this.isNotValidPromise(promise, false)) {
            return promise;
        }
        final AbstractChannelHandlerContext next = this.findContextOutbound(1024);
        final EventExecutor executor = next.executor();
        if (executor.inEventLoop()) {
            next.invokeConnect(remoteAddress, localAddress, promise);
        }
        else {
            safeExecute(executor, new Runnable() {
                @Override
                public void run() {
                    AbstractChannelHandlerContext.this.invokeConnect(remoteAddress, localAddress, promise);
                }
            }, promise, null, false);
        }
        return promise;
    }
    
    private void invokeConnect(final SocketAddress remoteAddress, final SocketAddress localAddress, final ChannelPromise promise) {
        if (this.invokeHandler()) {
            try {
                final ChannelHandler handler = this.handler();
                final DefaultChannelPipeline.HeadContext headContext = this.pipeline.head;
                if (handler == headContext) {
                    headContext.connect(this, remoteAddress, localAddress, promise);
                }
                else if (handler instanceof ChannelDuplexHandler) {
                    ((ChannelDuplexHandler)handler).connect(this, remoteAddress, localAddress, promise);
                }
                else if (handler instanceof ChannelOutboundHandlerAdapter) {
                    ((ChannelOutboundHandlerAdapter)handler).connect(this, remoteAddress, localAddress, promise);
                }
                else {
                    ((ChannelOutboundHandler)handler).connect(this, remoteAddress, localAddress, promise);
                }
            }
            catch (final Throwable t) {
                notifyOutboundHandlerException(t, promise);
            }
        }
        else {
            this.connect(remoteAddress, localAddress, promise);
        }
    }
    
    @Override
    public ChannelFuture disconnect(final ChannelPromise promise) {
        if (!this.channel().metadata().hasDisconnect()) {
            return this.close(promise);
        }
        if (this.isNotValidPromise(promise, false)) {
            return promise;
        }
        final AbstractChannelHandlerContext next = this.findContextOutbound(2048);
        final EventExecutor executor = next.executor();
        if (executor.inEventLoop()) {
            next.invokeDisconnect(promise);
        }
        else {
            safeExecute(executor, new Runnable() {
                @Override
                public void run() {
                    AbstractChannelHandlerContext.this.invokeDisconnect(promise);
                }
            }, promise, null, false);
        }
        return promise;
    }
    
    private void invokeDisconnect(final ChannelPromise promise) {
        if (this.invokeHandler()) {
            try {
                final ChannelHandler handler = this.handler();
                final DefaultChannelPipeline.HeadContext headContext = this.pipeline.head;
                if (handler == headContext) {
                    headContext.disconnect(this, promise);
                }
                else if (handler instanceof ChannelDuplexHandler) {
                    ((ChannelDuplexHandler)handler).disconnect(this, promise);
                }
                else if (handler instanceof ChannelOutboundHandlerAdapter) {
                    ((ChannelOutboundHandlerAdapter)handler).disconnect(this, promise);
                }
                else {
                    ((ChannelOutboundHandler)handler).disconnect(this, promise);
                }
            }
            catch (final Throwable t) {
                notifyOutboundHandlerException(t, promise);
            }
        }
        else {
            this.disconnect(promise);
        }
    }
    
    @Override
    public ChannelFuture close(final ChannelPromise promise) {
        if (this.isNotValidPromise(promise, false)) {
            return promise;
        }
        final AbstractChannelHandlerContext next = this.findContextOutbound(4096);
        final EventExecutor executor = next.executor();
        if (executor.inEventLoop()) {
            next.invokeClose(promise);
        }
        else {
            safeExecute(executor, new Runnable() {
                @Override
                public void run() {
                    AbstractChannelHandlerContext.this.invokeClose(promise);
                }
            }, promise, null, false);
        }
        return promise;
    }
    
    private void invokeClose(final ChannelPromise promise) {
        if (this.invokeHandler()) {
            try {
                final ChannelHandler handler = this.handler();
                final DefaultChannelPipeline.HeadContext headContext = this.pipeline.head;
                if (handler == headContext) {
                    headContext.close(this, promise);
                }
                else if (handler instanceof ChannelDuplexHandler) {
                    ((ChannelDuplexHandler)handler).close(this, promise);
                }
                else if (handler instanceof ChannelOutboundHandlerAdapter) {
                    ((ChannelOutboundHandlerAdapter)handler).close(this, promise);
                }
                else {
                    ((ChannelOutboundHandler)handler).close(this, promise);
                }
            }
            catch (final Throwable t) {
                notifyOutboundHandlerException(t, promise);
            }
        }
        else {
            this.close(promise);
        }
    }
    
    @Override
    public ChannelFuture deregister(final ChannelPromise promise) {
        if (this.isNotValidPromise(promise, false)) {
            return promise;
        }
        final AbstractChannelHandlerContext next = this.findContextOutbound(8192);
        final EventExecutor executor = next.executor();
        if (executor.inEventLoop()) {
            next.invokeDeregister(promise);
        }
        else {
            safeExecute(executor, new Runnable() {
                @Override
                public void run() {
                    AbstractChannelHandlerContext.this.invokeDeregister(promise);
                }
            }, promise, null, false);
        }
        return promise;
    }
    
    private void invokeDeregister(final ChannelPromise promise) {
        if (this.invokeHandler()) {
            try {
                final ChannelHandler handler = this.handler();
                final DefaultChannelPipeline.HeadContext headContext = this.pipeline.head;
                if (handler == headContext) {
                    headContext.deregister(this, promise);
                }
                else if (handler instanceof ChannelDuplexHandler) {
                    ((ChannelDuplexHandler)handler).deregister(this, promise);
                }
                else if (handler instanceof ChannelOutboundHandlerAdapter) {
                    ((ChannelOutboundHandlerAdapter)handler).deregister(this, promise);
                }
                else {
                    ((ChannelOutboundHandler)handler).deregister(this, promise);
                }
            }
            catch (final Throwable t) {
                notifyOutboundHandlerException(t, promise);
            }
        }
        else {
            this.deregister(promise);
        }
    }
    
    @Override
    public ChannelHandlerContext read() {
        final AbstractChannelHandlerContext next = this.findContextOutbound(16384);
        if (next.executor().inEventLoop()) {
            if (next.invokeHandler()) {
                try {
                    final ChannelHandler handler = next.handler();
                    final DefaultChannelPipeline.HeadContext headContext = this.pipeline.head;
                    if (handler == headContext) {
                        headContext.read(next);
                    }
                    else if (handler instanceof ChannelDuplexHandler) {
                        ((ChannelDuplexHandler)handler).read(next);
                    }
                    else if (handler instanceof ChannelOutboundHandlerAdapter) {
                        ((ChannelOutboundHandlerAdapter)handler).read(next);
                    }
                    else {
                        ((ChannelOutboundHandler)handler).read(next);
                    }
                }
                catch (final Throwable t) {
                    this.invokeExceptionCaught(t);
                }
            }
            else {
                next.read();
            }
        }
        else {
            next.executor().execute(this.getInvokeTasks().invokeReadTask);
        }
        return this;
    }
    
    @Override
    public ChannelFuture write(final Object msg) {
        final ChannelPromise promise = this.newPromise();
        this.write(msg, false, promise);
        return promise;
    }
    
    @Override
    public ChannelFuture write(final Object msg, final ChannelPromise promise) {
        this.write(msg, false, promise);
        return promise;
    }
    
    @Override
    public ChannelHandlerContext flush() {
        final AbstractChannelHandlerContext next = this.findContextOutbound(65536);
        final EventExecutor executor = next.executor();
        if (executor.inEventLoop()) {
            next.invokeFlush();
        }
        else {
            Tasks tasks = next.invokeTasks;
            if (tasks == null) {
                tasks = (next.invokeTasks = new Tasks(next));
            }
            safeExecute(executor, tasks.invokeFlushTask, this.channel().voidPromise(), null, false);
        }
        return this;
    }
    
    private void invokeFlush() {
        if (this.invokeHandler()) {
            this.invokeFlush0();
        }
        else {
            this.flush();
        }
    }
    
    private void invokeFlush0() {
        try {
            final ChannelHandler handler = this.handler();
            final DefaultChannelPipeline.HeadContext headContext = this.pipeline.head;
            if (handler == headContext) {
                headContext.flush(this);
            }
            else if (handler instanceof ChannelDuplexHandler) {
                ((ChannelDuplexHandler)handler).flush(this);
            }
            else if (handler instanceof ChannelOutboundHandlerAdapter) {
                ((ChannelOutboundHandlerAdapter)handler).flush(this);
            }
            else {
                ((ChannelOutboundHandler)handler).flush(this);
            }
        }
        catch (final Throwable t) {
            this.invokeExceptionCaught(t);
        }
    }
    
    @Override
    public ChannelFuture writeAndFlush(final Object msg, final ChannelPromise promise) {
        this.write(msg, true, promise);
        return promise;
    }
    
    void write(final Object msg, final boolean flush, final ChannelPromise promise) {
        if (this.validateWrite(msg, promise)) {
            final AbstractChannelHandlerContext next = this.findContextOutbound(flush ? 98304 : 32768);
            final Object m = this.pipeline.touch(msg, next);
            final EventExecutor executor = next.executor();
            if (executor.inEventLoop()) {
                if (next.invokeHandler()) {
                    try {
                        final ChannelHandler handler = next.handler();
                        final DefaultChannelPipeline.HeadContext headContext = this.pipeline.head;
                        if (handler == headContext) {
                            headContext.write(next, msg, promise);
                        }
                        else if (handler instanceof ChannelDuplexHandler) {
                            ((ChannelDuplexHandler)handler).write(next, msg, promise);
                        }
                        else if (handler instanceof ChannelOutboundHandlerAdapter) {
                            ((ChannelOutboundHandlerAdapter)handler).write(next, msg, promise);
                        }
                        else {
                            ((ChannelOutboundHandler)handler).write(next, msg, promise);
                        }
                    }
                    catch (final Throwable t) {
                        notifyOutboundHandlerException(t, promise);
                    }
                    if (flush) {
                        next.invokeFlush0();
                    }
                }
                else {
                    next.write(msg, flush, promise);
                }
            }
            else {
                final WriteTask task = WriteTask.newInstance(this, m, promise, flush);
                if (!safeExecute(executor, task, promise, m, !flush)) {
                    task.cancel();
                }
            }
        }
    }
    
    private boolean validateWrite(final Object msg, final ChannelPromise promise) {
        ObjectUtil.checkNotNull(msg, "msg");
        try {
            if (this.isNotValidPromise(promise, true)) {
                ReferenceCountUtil.release(msg);
                return false;
            }
        }
        catch (final RuntimeException e) {
            ReferenceCountUtil.release(msg);
            throw e;
        }
        return true;
    }
    
    @Override
    public ChannelFuture writeAndFlush(final Object msg) {
        return this.writeAndFlush(msg, this.newPromise());
    }
    
    private static void notifyOutboundHandlerException(final Throwable cause, final ChannelPromise promise) {
        PromiseNotificationUtil.tryFailure(promise, cause, (promise instanceof VoidChannelPromise) ? null : AbstractChannelHandlerContext.logger);
    }
    
    @Override
    public ChannelPromise newPromise() {
        return new DefaultChannelPromise(this.channel(), this.executor());
    }
    
    @Override
    public ChannelProgressivePromise newProgressivePromise() {
        return new DefaultChannelProgressivePromise(this.channel(), this.executor());
    }
    
    @Override
    public ChannelFuture newSucceededFuture() {
        ChannelFuture succeededFuture = this.succeededFuture;
        if (succeededFuture == null) {
            succeededFuture = (this.succeededFuture = new SucceededChannelFuture(this.channel(), this.executor()));
        }
        return succeededFuture;
    }
    
    @Override
    public ChannelFuture newFailedFuture(final Throwable cause) {
        return new FailedChannelFuture(this.channel(), this.executor(), cause);
    }
    
    private boolean isNotValidPromise(final ChannelPromise promise, final boolean allowVoidPromise) {
        ObjectUtil.checkNotNull(promise, "promise");
        if (promise.isDone()) {
            if (promise.isCancelled()) {
                return true;
            }
            throw new IllegalArgumentException("promise already done: " + promise);
        }
        else {
            if (promise.channel() != this.channel()) {
                throw new IllegalArgumentException(String.format("promise.channel does not match: %s (expected: %s)", promise.channel(), this.channel()));
            }
            if (promise.getClass() == DefaultChannelPromise.class) {
                return false;
            }
            if (!allowVoidPromise && promise instanceof VoidChannelPromise) {
                throw new IllegalArgumentException(StringUtil.simpleClassName(VoidChannelPromise.class) + " not allowed for this operation");
            }
            if (promise instanceof AbstractChannel.CloseFuture) {
                throw new IllegalArgumentException(StringUtil.simpleClassName(AbstractChannel.CloseFuture.class) + " not allowed in a pipeline");
            }
            return false;
        }
    }
    
    private AbstractChannelHandlerContext findContextInbound(final int mask) {
        AbstractChannelHandlerContext ctx = this;
        final EventExecutor currentExecutor = this.executor();
        do {
            ctx = ctx.next;
        } while (skipContext(ctx, currentExecutor, mask, 510));
        return ctx;
    }
    
    private AbstractChannelHandlerContext findContextOutbound(final int mask) {
        AbstractChannelHandlerContext ctx = this;
        final EventExecutor currentExecutor = this.executor();
        do {
            ctx = ctx.prev;
        } while (skipContext(ctx, currentExecutor, mask, 130560));
        return ctx;
    }
    
    private static boolean skipContext(final AbstractChannelHandlerContext ctx, final EventExecutor currentExecutor, final int mask, final int onlyMask) {
        return (ctx.executionMask & (onlyMask | mask)) == 0x0 || (ctx.executor() == currentExecutor && (ctx.executionMask & mask) == 0x0);
    }
    
    @Override
    public ChannelPromise voidPromise() {
        return this.channel().voidPromise();
    }
    
    final void setRemoved() {
        this.handlerState = 3;
    }
    
    final boolean setAddComplete() {
        while (true) {
            final int oldState = this.handlerState;
            if (oldState == 3) {
                return false;
            }
            if (AbstractChannelHandlerContext.HANDLER_STATE_UPDATER.compareAndSet(this, oldState, 2)) {
                return true;
            }
        }
    }
    
    final void setAddPending() {
        final boolean updated = AbstractChannelHandlerContext.HANDLER_STATE_UPDATER.compareAndSet(this, 0, 1);
        assert updated;
    }
    
    final void callHandlerAdded() throws Exception {
        if (this.setAddComplete()) {
            this.handler().handlerAdded(this);
        }
    }
    
    final void callHandlerRemoved() throws Exception {
        try {
            if (this.handlerState == 2) {
                this.handler().handlerRemoved(this);
            }
        }
        finally {
            this.setRemoved();
        }
    }
    
    boolean invokeHandler() {
        final int handlerState = this.handlerState;
        return handlerState == 2 || (!this.ordered && handlerState == 1);
    }
    
    @Override
    public boolean isRemoved() {
        return this.handlerState == 3;
    }
    
    @Override
    public <T> Attribute<T> attr(final AttributeKey<T> key) {
        return this.channel().attr(key);
    }
    
    @Override
    public <T> boolean hasAttr(final AttributeKey<T> key) {
        return this.channel().hasAttr(key);
    }
    
    private static boolean safeExecute(final EventExecutor executor, final Runnable runnable, final ChannelPromise promise, final Object msg, final boolean lazy) {
        try {
            if (lazy && executor instanceof AbstractEventExecutor) {
                ((AbstractEventExecutor)executor).lazyExecute(runnable);
            }
            else {
                executor.execute(runnable);
            }
            return true;
        }
        catch (final Throwable cause) {
            try {
                if (msg != null) {
                    ReferenceCountUtil.release(msg);
                }
            }
            finally {
                promise.setFailure(cause);
            }
            return false;
        }
    }
    
    @Override
    public String toHintString() {
        return '\'' + this.name + "' will handle the message from this point.";
    }
    
    @Override
    public String toString() {
        return StringUtil.simpleClassName(ChannelHandlerContext.class) + '(' + this.name + ", " + this.channel() + ')';
    }
    
    Tasks getInvokeTasks() {
        Tasks tasks = this.invokeTasks;
        if (tasks == null) {
            tasks = (this.invokeTasks = new Tasks(this));
        }
        return tasks;
    }
    
    static {
        logger = InternalLoggerFactory.getInstance(AbstractChannelHandlerContext.class);
        HANDLER_STATE_UPDATER = AtomicIntegerFieldUpdater.newUpdater(AbstractChannelHandlerContext.class, "handlerState");
    }
    
    static final class WriteTask implements Runnable
    {
        private static final Recycler<WriteTask> RECYCLER;
        private static final boolean ESTIMATE_TASK_SIZE_ON_SUBMIT;
        private static final int WRITE_TASK_OVERHEAD;
        private final ObjectPool.Handle<WriteTask> handle;
        private AbstractChannelHandlerContext ctx;
        private Object msg;
        private ChannelPromise promise;
        private int size;
        
        static WriteTask newInstance(final AbstractChannelHandlerContext ctx, final Object msg, final ChannelPromise promise, final boolean flush) {
            final WriteTask task = WriteTask.RECYCLER.get();
            init(task, ctx, msg, promise, flush);
            return task;
        }
        
        private WriteTask(final ObjectPool.Handle<WriteTask> handle) {
            this.handle = handle;
        }
        
        static void init(final WriteTask task, final AbstractChannelHandlerContext ctx, final Object msg, final ChannelPromise promise, final boolean flush) {
            task.ctx = ctx;
            task.msg = msg;
            task.promise = promise;
            if (WriteTask.ESTIMATE_TASK_SIZE_ON_SUBMIT) {
                task.size = ctx.pipeline.estimatorHandle().size(msg) + WriteTask.WRITE_TASK_OVERHEAD;
                ctx.pipeline.incrementPendingOutboundBytes(task.size);
            }
            else {
                task.size = 0;
            }
            if (flush) {
                task.size |= Integer.MIN_VALUE;
            }
        }
        
        @Override
        public void run() {
            try {
                this.decrementPendingOutboundBytes();
                this.ctx.write(this.msg, this.size < 0, this.promise);
            }
            finally {
                this.recycle();
            }
        }
        
        void cancel() {
            try {
                this.decrementPendingOutboundBytes();
            }
            finally {
                this.recycle();
            }
        }
        
        private void decrementPendingOutboundBytes() {
            if (WriteTask.ESTIMATE_TASK_SIZE_ON_SUBMIT) {
                this.ctx.pipeline.decrementPendingOutboundBytes(this.size & Integer.MAX_VALUE);
            }
        }
        
        private void recycle() {
            this.ctx = null;
            this.msg = null;
            this.promise = null;
            this.handle.recycle(this);
        }
        
        static {
            RECYCLER = new Recycler<WriteTask>() {
                @Override
                protected WriteTask newObject(final Handle<WriteTask> handle) {
                    return new WriteTask((ObjectPool.Handle)handle);
                }
            };
            ESTIMATE_TASK_SIZE_ON_SUBMIT = SystemPropertyUtil.getBoolean("io.netty.transport.estimateSizeOnSubmit", true);
            WRITE_TASK_OVERHEAD = SystemPropertyUtil.getInt("io.netty.transport.writeTaskSizeOverhead", 32);
        }
    }
    
    static final class Tasks
    {
        final Runnable invokeChannelReadCompleteTask;
        private final Runnable invokeReadTask;
        private final Runnable invokeChannelWritableStateChangedTask;
        private final Runnable invokeFlushTask;
        
        Tasks(final AbstractChannelHandlerContext ctx) {
            Objects.requireNonNull(ctx);
            this.invokeChannelReadCompleteTask = ctx::fireChannelReadComplete;
            Objects.requireNonNull(ctx);
            this.invokeReadTask = ctx::read;
            Objects.requireNonNull(ctx);
            this.invokeChannelWritableStateChangedTask = ctx::fireChannelWritabilityChanged;
            Objects.requireNonNull(ctx);
            this.invokeFlushTask = (() -> rec$.invokeFlush());
        }
    }
}
