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

Code before:
        buffer.retain(); // buffer is allocated from the pool - reusing same buffer, the pool would call retain()
        buffer.writeByte('w').writeByte('o').writeByte('r').writeByte('l').writeByte('d');
        assertTrue(channel.writeInbound(buffer));
        assertFalse(channel.inboundMessages().isEmpty(), "Message should be received");
        assertEquals("hello world", channel.inboundMessages().poll(), "Message should be received correctly");
        assertTrue(channel.inboundMessages().isEmpty(), "Only a single message should be received");
        assertFalse(buffer.isReadable(), "Buffer should not have remaining data after reading complete message");

        buffer.release(); // we are done with the buffer - release it from the pool
        assertEquals(0, buffer.refCnt(), "Buffer should be released");
        assertFalse(channel.finish());
    }

    @Test
    void reentrantReadSafety() throws Exception {
        EmbeddedChannel channel = new EmbeddedChannel();
        ByteToMessageDecoder decoder = new ByteToMessageDecoder() {
            int reentrancy;

            @Override
            protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
                reentrancy++;
                if (reentrancy == 1) {
                    ByteBuf buf2 = channel.alloc().buffer();
                    buf2.writeLong(42); // Adding 8 bytes.
                    channel.writeInbound(buf2); // Reentrant call back into ByteToMessageDecoder
                    ctx.read(); // With LocalChannel this would be a reentrant call. For EmbeddedChannel it's harmless.
                }
                int bytes = in.readableBytes();
                out.add(bytes);
                in.skipBytes(bytes);
            }
        };
        channel.pipeline().addLast(decoder);
        ByteBuf buf1 = channel.alloc().buffer();
        buf1.writeInt(42); // Adding 4 bytes.
        channel.writeInbound(buf1);
        Integer first = channel.readInbound();
        Integer second = channel.readInbound();
        assertEquals(4, first);
        assertEquals(8, second);
        channel.finishAndReleaseAll();
    }
}


Review comment (suggestion):
```suggestion
                    assertFalse(channel.writeInbound(buf2)); // Reentrant call back into ByteToMessageDecoder
```

Code after:
        buffer.retain(); // buffer is allocated from the pool - reusing same buffer, the pool would call retain()
        buffer.writeByte('w').writeByte('o').writeByte('r').writeByte('l').writeByte('d');
        assertTrue(channel.writeInbound(buffer));
        assertFalse(channel.inboundMessages().isEmpty(), "Message should be received");
        assertEquals("hello world", channel.inboundMessages().poll(), "Message should be received correctly");
        assertTrue(channel.inboundMessages().isEmpty(), "Only a single message should be received");
        assertFalse(buffer.isReadable(), "Buffer should not have remaining data after reading complete message");

        buffer.release(); // we are done with the buffer - release it from the pool
        assertEquals(0, buffer.refCnt(), "Buffer should be released");
        assertFalse(channel.finish());
    }

    @Test
    void reentrantReadSafety() throws Exception {
        EmbeddedChannel channel = new EmbeddedChannel();
        ByteToMessageDecoder decoder = new ByteToMessageDecoder() {
            int reentrancy;

            @Override
            protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
                reentrancy++;
                if (reentrancy == 1) {
                    ByteBuf buf2 = channel.alloc().buffer();
                    buf2.writeLong(42); // Adding 8 bytes.
                    assertFalse(channel.writeInbound(buf2)); // Reentrant call back into ByteToMessageDecoder
                    ctx.read();
                }
                int bytes = in.readableBytes();
                out.add(bytes);
                in.skipBytes(bytes);
            }
        };
        channel.pipeline().addLast(decoder);
        ByteBuf buf1 = channel.alloc().buffer();
        buf1.writeInt(42); // Adding 4 bytes.
        assertTrue(channel.writeInbound(buf1));
        Integer first = channel.readInbound();
        Integer second = channel.readInbound();
        assertEquals(4, first);
        assertEquals(8, second);
        assertFalse(channel.finishAndReleaseAll());
    }

    @Test
    void reentrantReadThenRemoveSafety() throws Exception {
        EmbeddedChannel channel = new EmbeddedChannel();
        ByteToMessageDecoder decoder = new ByteToMessageDecoder() {
            boolean removed;
            int reentrancy;