@Test public void testRemoveAndWriteAllReentrance() { EmbeddedChannel channel = new EmbeddedChannel(new ChannelInboundHandlerAdapter()); final PendingWriteQueue queue = new PendingWriteQueue(channel.pipeline().firstContext()); ChannelPromise promise = channel.newPromise(); promise.addListener( new ChannelFutureListener() { @Override public void operationComplete(ChannelFuture future) throws Exception { queue.removeAndWriteAll(); } }); queue.add(1L, promise); ChannelPromise promise2 = channel.newPromise(); queue.add(2L, promise2); queue.removeAndWriteAll(); channel.flush(); assertTrue(promise.isSuccess()); assertTrue(promise2.isSuccess()); assertTrue(channel.finish()); assertEquals(1L, channel.readOutbound()); assertEquals(2L, channel.readOutbound()); assertNull(channel.readOutbound()); assertNull(channel.readInbound()); }
@Override public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception { queue.add(msg, promise); Assert.assertFalse(queue.isEmpty()); Assert.assertEquals(++expectedSize, queue.size()); Assert.assertNotNull(queue.current()); }
@Test public void shouldFireChannelWritabilityChangedAfterRemoval() { final AtomicReference<ChannelHandlerContext> ctxRef = new AtomicReference<ChannelHandlerContext>(); final AtomicReference<PendingWriteQueue> queueRef = new AtomicReference<PendingWriteQueue>(); final ByteBuf msg = Unpooled.copiedBuffer("test", CharsetUtil.US_ASCII); final EmbeddedChannel channel = new EmbeddedChannel( new ChannelHandlerAdapter() { @Override public void handlerAdded(ChannelHandlerContext ctx) throws Exception { ctxRef.set(ctx); queueRef.set(new PendingWriteQueue(ctx)); } @Override public void channelWritabilityChanged(ChannelHandlerContext ctx) throws Exception { final PendingWriteQueue queue = queueRef.get(); final ByteBuf msg = (ByteBuf) queue.current(); if (msg == null) { return; } assertThat(msg.refCnt(), is(1)); // This call will trigger another channelWritabilityChanged() event because the // number of // pending bytes will go below the low watermark. // // If PendingWriteQueue.remove() did not remove the current entry before triggering // channelWritabilityChanged() event, we will end up with attempting to remove the // same // element twice, resulting in the double release. queue.remove(); assertThat(msg.refCnt(), is(0)); } }); channel.config().setWriteBufferLowWaterMark(1); channel.config().setWriteBufferHighWaterMark(3); final PendingWriteQueue queue = queueRef.get(); // Trigger channelWritabilityChanged() by adding a message that's larger than the high // watermark. queue.add(msg, channel.newPromise()); channel.finish(); assertThat(msg.refCnt(), is(0)); }
// See https://github.com/netty/netty/issues/3967 @Test public void testCloseChannelOnCreation() { EmbeddedChannel channel = new EmbeddedChannel(); channel.close().syncUninterruptibly(); final PendingWriteQueue queue = new PendingWriteQueue(channel.pipeline().firstContext()); IllegalStateException ex = new IllegalStateException(); ChannelPromise promise = channel.newPromise(); queue.add(1L, promise); queue.removeAndFailAll(ex); assertSame(ex, promise.cause()); }