protected void decodeLast(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
        if (in.isReadable()) {
            // Only call decode() if there is something left in the buffer to decode.
            // See https://github.com/netty/netty/issues/4386
            decodeRemovalReentryProtection(ctx, in, out);
        }
    }
@Test
    public void testDecodeLast() {
        final AtomicBoolean removeHandler = new AtomicBoolean();
        EmbeddedChannel channel = new EmbeddedChannel(new ByteToMessageDecoder() {

            @Override
            protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) {
                if (removeHandler.get()) {
                    ctx.pipeline().remove(this);
                }
            }
        });
        byte[] bytes = new byte[1024];
        PlatformDependent.threadLocalRandom().nextBytes(bytes);

        assertFalse(channel.writeInbound(Unpooled.copiedBuffer(bytes)));
        assertNull(channel.readInbound());
        removeHandler.set(true);
        // This should trigger channelInputClosed(...)
        channel.pipeline().fireUserEventTriggered(ChannelInputShutdownEvent.INSTANCE);

        assertTrue(channel.finish());
        assertBuffer(Unpooled.wrappedBuffer(bytes), (ByteBuf) channel.readInbound());
        assertNull(channel.readInbound());
    }