Repository: netty/netty
File: codec-base/src/main/java/io/netty/handler/codec/ByteToMessageDecoder.java (Java)

Code before:
            // Directly set this to null, so we are sure we not access it in any other method here anymore.
            cumulation = null;
            numReads = 0;
            int readable = buf.readableBytes();
            if (readable > 0) {
                ctx.fireChannelRead(buf);
                ctx.fireChannelReadComplete();
            } else {
                buf.release();
            }
        }
        handlerRemoved0(ctx);
    }

    /**
     * Gets called after the {@link ByteToMessageDecoder} was removed from the actual context and it doesn't handle
     * events anymore.
     */
    protected void handlerRemoved0(ChannelHandlerContext ctx) throws Exception { }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object input) throws Exception {
        if (decodeState == STATE_INIT) {
            do {
                channelReadOne(ctx, input);
            } while (!inputMessages.isEmpty() && (input = inputMessages.pollFirst()) != null);
        } else {
            // Reentrant call. Bail out here and let original call process our message.
            inputMessages.offerLast(input);
        }
    }

    private void channelReadOne(ChannelHandlerContext ctx, Object msg) {
        if (msg instanceof ByteBuf) {
            selfFiredChannelRead = true;
            CodecOutputList out = CodecOutputList.newInstance();
            try {
                first = cumulation == null;
                cumulation = cumulator.cumulate(ctx.alloc(),
                        first ? Unpooled.EMPTY_BUFFER : cumulation, (ByteBuf) msg);
                callDecode(ctx, cumulation, out);
            } catch (DecoderException e) {
                throw e;
            } catch (Exception e) {
                throw new DecoderException(e);
            } finally {
                try {
                    if (cumulation != null && !cumulation.isReadable()) {
                        numReads = 0;
                        try {
                            cumulation.release();

Review comment (suggestion):
Just remove the `isEmpty()` check... there is no real benefit imho.
```suggestion
            } while ((input = inputMessages.pollFirst()) != null);
```

Code after:
            // Directly set this to null, so we are sure we not access it in any other method here anymore.
            cumulation = null;
            numReads = 0;
            int readable = buf.readableBytes();
            if (readable > 0) {
                ctx.fireChannelRead(buf);
                ctx.fireChannelReadComplete();
            } else {
                buf.release();
            }
        }
        handlerRemoved0(ctx);
    }

    /**
     * Gets called after the {@link ByteToMessageDecoder} was removed from the actual context and it doesn't handle
     * events anymore.
     */
    protected void handlerRemoved0(ChannelHandlerContext ctx) throws Exception { }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object input) throws Exception {
        if (decodeState == STATE_INIT) {
            do {
                if (input instanceof ByteBuf) {
                    selfFiredChannelRead = true;
                    CodecOutputList out = CodecOutputList.newInstance();
                    try {
                        first = cumulation == null;
                        cumulation = cumulator.cumulate(ctx.alloc(),
                                first ? EMPTY_BUFFER : cumulation, (ByteBuf) input);
                        callDecode(ctx, cumulation, out);
                    } catch (DecoderException e) {
                        throw e;
                    } catch (Exception e) {
                        throw new DecoderException(e);
                    } finally {
                        try {
                            if (cumulation != null && !cumulation.isReadable()) {
                                numReads = 0;
                                try {
                                    cumulation.release();
                                } catch (IllegalReferenceCountException e) {
                                    //noinspection ThrowFromFinallyBlock
                                    throw new IllegalReferenceCountException(
                                            getClass().getSimpleName() +
                                                    "#decode() might have released its input buffer, " +
                                                    "or passed it down the pipeline without a retain() call, " +
                                                    "which is not allowed.", e);
                                }
                                cumulation = null;