2013-10-06 15:29:35 +02:00
|
|
|
/*
|
|
|
|
* Copyright 2012 The Netty Project
|
|
|
|
*
|
|
|
|
* The Netty Project licenses this file to you under the Apache License,
|
|
|
|
* version 2.0 (the "License"); you may not use this file except in compliance
|
|
|
|
* with the License. You may obtain a copy of the License at:
|
|
|
|
*
|
|
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
*
|
|
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
|
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
|
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
|
|
* License for the specific language governing permissions and limitations
|
|
|
|
* under the License.
|
|
|
|
*/
|
2014-08-05 14:24:49 +02:00
|
|
|
package io.netty.channel;
|
2013-10-06 15:29:35 +02:00
|
|
|
|
|
|
|
import io.netty.buffer.ByteBuf;
|
|
|
|
import io.netty.buffer.CompositeByteBuf;
|
2014-10-22 10:45:28 +02:00
|
|
|
import io.netty.channel.embedded.EmbeddedChannel;
|
2013-10-06 15:29:35 +02:00
|
|
|
import io.netty.util.CharsetUtil;
|
2018-10-11 18:46:10 +02:00
|
|
|
import io.netty.util.concurrent.DefaultThreadFactory;
|
|
|
|
import io.netty.util.concurrent.RejectedExecutionHandlers;
|
|
|
|
import io.netty.util.concurrent.SingleThreadEventExecutor;
|
2013-10-06 15:29:35 +02:00
|
|
|
import org.junit.Test;
|
|
|
|
|
2014-08-05 14:24:49 +02:00
|
|
|
import java.net.SocketAddress;
|
2013-10-06 15:29:35 +02:00
|
|
|
import java.nio.ByteBuffer;
|
2018-10-11 18:46:10 +02:00
|
|
|
import java.util.Queue;
|
|
|
|
import java.util.concurrent.CountDownLatch;
|
|
|
|
import java.util.concurrent.RejectedExecutionException;
|
2013-10-06 15:29:35 +02:00
|
|
|
|
|
|
|
import static io.netty.buffer.Unpooled.*;
|
2014-10-22 10:45:28 +02:00
|
|
|
import static org.hamcrest.Matchers.*;
|
2013-10-06 15:29:35 +02:00
|
|
|
import static org.junit.Assert.*;
|
|
|
|
|
2014-08-05 14:24:49 +02:00
|
|
|
public class ChannelOutboundBufferTest {
|
2013-10-06 15:29:35 +02:00
|
|
|
|
|
|
|
@Test
|
|
|
|
public void testEmptyNioBuffers() {
|
2014-08-05 14:24:49 +02:00
|
|
|
TestChannel channel = new TestChannel();
|
|
|
|
ChannelOutboundBuffer buffer = new ChannelOutboundBuffer(channel);
|
2013-10-06 15:29:35 +02:00
|
|
|
assertEquals(0, buffer.nioBufferCount());
|
|
|
|
ByteBuffer[] buffers = buffer.nioBuffers();
|
2014-07-22 22:28:02 +02:00
|
|
|
assertNotNull(buffers);
|
2013-10-06 15:29:35 +02:00
|
|
|
for (ByteBuffer b: buffers) {
|
|
|
|
assertNull(b);
|
|
|
|
}
|
|
|
|
assertEquals(0, buffer.nioBufferCount());
|
2013-12-06 12:40:11 +01:00
|
|
|
release(buffer);
|
2013-10-06 15:29:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
@Test
|
|
|
|
public void testNioBuffersSingleBacked() {
|
2014-08-05 14:24:49 +02:00
|
|
|
TestChannel channel = new TestChannel();
|
|
|
|
|
|
|
|
ChannelOutboundBuffer buffer = new ChannelOutboundBuffer(channel);
|
2013-10-06 15:29:35 +02:00
|
|
|
assertEquals(0, buffer.nioBufferCount());
|
|
|
|
|
|
|
|
ByteBuf buf = copiedBuffer("buf1", CharsetUtil.US_ASCII);
|
2017-03-02 18:09:27 +01:00
|
|
|
ByteBuffer nioBuf = buf.internalNioBuffer(buf.readerIndex(), buf.readableBytes());
|
2014-08-05 14:24:49 +02:00
|
|
|
buffer.addMessage(buf, buf.readableBytes(), channel.voidPromise());
|
2013-10-06 15:29:35 +02:00
|
|
|
assertEquals("Should still be 0 as not flushed yet", 0, buffer.nioBufferCount());
|
|
|
|
buffer.addFlush();
|
2014-08-05 14:24:49 +02:00
|
|
|
ByteBuffer[] buffers = buffer.nioBuffers();
|
2014-07-22 22:28:02 +02:00
|
|
|
assertNotNull(buffers);
|
2013-10-06 15:29:35 +02:00
|
|
|
assertEquals("Should still be 0 as not flushed yet", 1, buffer.nioBufferCount());
|
2014-08-05 14:24:49 +02:00
|
|
|
for (int i = 0; i < buffer.nioBufferCount(); i++) {
|
2013-10-06 15:29:35 +02:00
|
|
|
if (i == 0) {
|
2014-08-05 14:24:49 +02:00
|
|
|
assertEquals(buffers[i], nioBuf);
|
2013-10-06 15:29:35 +02:00
|
|
|
} else {
|
|
|
|
assertNull(buffers[i]);
|
|
|
|
}
|
|
|
|
}
|
2013-12-06 12:40:11 +01:00
|
|
|
release(buffer);
|
2013-10-06 15:29:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
@Test
|
|
|
|
public void testNioBuffersExpand() {
|
2014-08-05 14:24:49 +02:00
|
|
|
TestChannel channel = new TestChannel();
|
|
|
|
|
|
|
|
ChannelOutboundBuffer buffer = new ChannelOutboundBuffer(channel);
|
2013-10-06 15:29:35 +02:00
|
|
|
|
2013-10-07 08:07:38 +02:00
|
|
|
ByteBuf buf = directBuffer().writeBytes("buf1".getBytes(CharsetUtil.US_ASCII));
|
2013-10-06 15:29:35 +02:00
|
|
|
for (int i = 0; i < 64; i++) {
|
2014-08-05 14:24:49 +02:00
|
|
|
buffer.addMessage(buf.copy(), buf.readableBytes(), channel.voidPromise());
|
2013-10-06 15:29:35 +02:00
|
|
|
}
|
|
|
|
assertEquals("Should still be 0 as not flushed yet", 0, buffer.nioBufferCount());
|
|
|
|
buffer.addFlush();
|
2014-08-05 14:24:49 +02:00
|
|
|
ByteBuffer[] buffers = buffer.nioBuffers();
|
2013-10-06 15:29:35 +02:00
|
|
|
assertEquals(64, buffer.nioBufferCount());
|
2014-08-05 14:24:49 +02:00
|
|
|
for (int i = 0; i < buffer.nioBufferCount(); i++) {
|
2017-03-02 18:09:27 +01:00
|
|
|
assertEquals(buffers[i], buf.internalNioBuffer(buf.readerIndex(), buf.readableBytes()));
|
2013-10-06 15:29:35 +02:00
|
|
|
}
|
2013-12-06 12:40:11 +01:00
|
|
|
release(buffer);
|
|
|
|
buf.release();
|
2013-10-06 15:29:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
@Test
|
|
|
|
public void testNioBuffersExpand2() {
|
2014-08-05 14:24:49 +02:00
|
|
|
TestChannel channel = new TestChannel();
|
|
|
|
|
|
|
|
ChannelOutboundBuffer buffer = new ChannelOutboundBuffer(channel);
|
2013-10-06 15:29:35 +02:00
|
|
|
|
|
|
|
CompositeByteBuf comp = compositeBuffer(256);
|
2013-10-07 08:07:38 +02:00
|
|
|
ByteBuf buf = directBuffer().writeBytes("buf1".getBytes(CharsetUtil.US_ASCII));
|
2013-10-06 15:29:35 +02:00
|
|
|
for (int i = 0; i < 65; i++) {
|
2016-05-17 14:19:33 +02:00
|
|
|
comp.addComponent(true, buf.copy());
|
2013-10-06 15:29:35 +02:00
|
|
|
}
|
2014-08-05 14:24:49 +02:00
|
|
|
buffer.addMessage(comp, comp.readableBytes(), channel.voidPromise());
|
2013-10-06 15:29:35 +02:00
|
|
|
|
|
|
|
assertEquals("Should still be 0 as not flushed yet", 0, buffer.nioBufferCount());
|
|
|
|
buffer.addFlush();
|
2014-08-05 14:24:49 +02:00
|
|
|
ByteBuffer[] buffers = buffer.nioBuffers();
|
2013-10-06 15:29:35 +02:00
|
|
|
assertEquals(65, buffer.nioBufferCount());
|
2014-08-05 14:24:49 +02:00
|
|
|
for (int i = 0; i < buffer.nioBufferCount(); i++) {
|
2013-10-06 15:29:35 +02:00
|
|
|
if (i < 65) {
|
2017-03-02 18:09:27 +01:00
|
|
|
assertEquals(buffers[i], buf.internalNioBuffer(buf.readerIndex(), buf.readableBytes()));
|
2013-10-06 15:29:35 +02:00
|
|
|
} else {
|
|
|
|
assertNull(buffers[i]);
|
|
|
|
}
|
|
|
|
}
|
2013-12-06 12:40:11 +01:00
|
|
|
release(buffer);
|
|
|
|
buf.release();
|
|
|
|
}
|
|
|
|
|
|
|
|
private static void release(ChannelOutboundBuffer buffer) {
|
2013-10-07 08:07:38 +02:00
|
|
|
for (;;) {
|
|
|
|
if (!buffer.remove()) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2013-10-06 15:29:35 +02:00
|
|
|
}
|
2014-08-05 14:24:49 +02:00
|
|
|
|
|
|
|
private static final class TestChannel extends AbstractChannel {
|
2015-11-25 19:42:50 +01:00
|
|
|
private static final ChannelMetadata TEST_METADATA = new ChannelMetadata(false);
|
2014-08-05 14:24:49 +02:00
|
|
|
private final ChannelConfig config = new DefaultChannelConfig(this);
|
|
|
|
|
|
|
|
TestChannel() {
|
|
|
|
super(null);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
protected AbstractUnsafe newUnsafe() {
|
|
|
|
return new TestUnsafe();
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
protected boolean isCompatible(EventLoop loop) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
protected SocketAddress localAddress0() {
|
|
|
|
throw new UnsupportedOperationException();
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
protected SocketAddress remoteAddress0() {
|
|
|
|
throw new UnsupportedOperationException();
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
protected void doBind(SocketAddress localAddress) throws Exception {
|
|
|
|
throw new UnsupportedOperationException();
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
protected void doDisconnect() throws Exception {
|
|
|
|
throw new UnsupportedOperationException();
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
protected void doClose() throws Exception {
|
|
|
|
throw new UnsupportedOperationException();
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
protected void doBeginRead() throws Exception {
|
|
|
|
throw new UnsupportedOperationException();
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
protected void doWrite(ChannelOutboundBuffer in) throws Exception {
|
|
|
|
throw new UnsupportedOperationException();
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public ChannelConfig config() {
|
|
|
|
return config;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public boolean isOpen() {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public boolean isActive() {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public ChannelMetadata metadata() {
|
2015-11-25 19:42:50 +01:00
|
|
|
return TEST_METADATA;
|
2014-08-05 14:24:49 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
final class TestUnsafe extends AbstractUnsafe {
|
|
|
|
@Override
|
|
|
|
public void connect(SocketAddress remoteAddress, SocketAddress localAddress, ChannelPromise promise) {
|
|
|
|
throw new UnsupportedOperationException();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2014-10-22 10:45:28 +02:00
|
|
|
|
|
|
|
@Test
|
|
|
|
public void testWritability() {
|
|
|
|
final StringBuilder buf = new StringBuilder();
|
2014-10-25 09:57:22 +02:00
|
|
|
EmbeddedChannel ch = new EmbeddedChannel(new ChannelInboundHandlerAdapter() {
|
2014-10-22 10:45:28 +02:00
|
|
|
@Override
|
|
|
|
public void channelWritabilityChanged(ChannelHandlerContext ctx) throws Exception {
|
|
|
|
buf.append(ctx.channel().isWritable());
|
|
|
|
buf.append(' ');
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2016-10-13 14:50:09 +02:00
|
|
|
ch.config().setWriteBufferLowWaterMark(128 + ChannelOutboundBuffer.CHANNEL_OUTBOUND_BUFFER_ENTRY_OVERHEAD);
|
|
|
|
ch.config().setWriteBufferHighWaterMark(256 + ChannelOutboundBuffer.CHANNEL_OUTBOUND_BUFFER_ENTRY_OVERHEAD);
|
2014-10-22 10:45:28 +02:00
|
|
|
|
|
|
|
ch.write(buffer().writeZero(128));
|
2016-08-24 10:56:21 +02:00
|
|
|
// Ensure exceeding the low watermark does not make channel unwritable.
|
|
|
|
ch.write(buffer().writeZero(2));
|
2014-10-22 10:45:28 +02:00
|
|
|
assertThat(buf.toString(), is(""));
|
|
|
|
|
|
|
|
ch.unsafe().outboundBuffer().addFlush();
|
|
|
|
|
|
|
|
// Ensure exceeding the high watermark makes channel unwritable.
|
2016-08-24 10:56:21 +02:00
|
|
|
ch.write(buffer().writeZero(127));
|
2014-10-22 10:45:28 +02:00
|
|
|
assertThat(buf.toString(), is("false "));
|
|
|
|
|
|
|
|
// Ensure going down to the low watermark makes channel writable again by flushing the first write.
|
|
|
|
assertThat(ch.unsafe().outboundBuffer().remove(), is(true));
|
2016-08-24 10:56:21 +02:00
|
|
|
assertThat(ch.unsafe().outboundBuffer().remove(), is(true));
|
2016-10-13 14:50:09 +02:00
|
|
|
assertThat(ch.unsafe().outboundBuffer().totalPendingWriteBytes(),
|
|
|
|
is(127L + ChannelOutboundBuffer.CHANNEL_OUTBOUND_BUFFER_ENTRY_OVERHEAD));
|
2014-10-22 10:45:28 +02:00
|
|
|
assertThat(buf.toString(), is("false true "));
|
|
|
|
|
|
|
|
safeClose(ch);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Test
|
|
|
|
public void testUserDefinedWritability() {
|
|
|
|
final StringBuilder buf = new StringBuilder();
|
2014-10-25 09:57:22 +02:00
|
|
|
EmbeddedChannel ch = new EmbeddedChannel(new ChannelInboundHandlerAdapter() {
|
2014-10-22 10:45:28 +02:00
|
|
|
@Override
|
|
|
|
public void channelWritabilityChanged(ChannelHandlerContext ctx) throws Exception {
|
|
|
|
buf.append(ctx.channel().isWritable());
|
|
|
|
buf.append(' ');
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
ch.config().setWriteBufferLowWaterMark(128);
|
|
|
|
ch.config().setWriteBufferHighWaterMark(256);
|
|
|
|
|
|
|
|
ChannelOutboundBuffer cob = ch.unsafe().outboundBuffer();
|
|
|
|
|
|
|
|
// Ensure that the default value of a user-defined writability flag is true.
|
|
|
|
for (int i = 1; i <= 30; i ++) {
|
|
|
|
assertThat(cob.getUserDefinedWritability(i), is(true));
|
|
|
|
}
|
|
|
|
|
|
|
|
// Ensure that setting a user-defined writability flag to false affects channel.isWritable();
|
|
|
|
cob.setUserDefinedWritability(1, false);
|
2014-12-10 10:36:53 +01:00
|
|
|
ch.runPendingTasks();
|
2014-10-22 10:45:28 +02:00
|
|
|
assertThat(buf.toString(), is("false "));
|
|
|
|
|
|
|
|
// Ensure that setting a user-defined writability flag to true affects channel.isWritable();
|
|
|
|
cob.setUserDefinedWritability(1, true);
|
2014-12-10 10:36:53 +01:00
|
|
|
ch.runPendingTasks();
|
2014-10-22 10:45:28 +02:00
|
|
|
assertThat(buf.toString(), is("false true "));
|
|
|
|
|
|
|
|
safeClose(ch);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Test
|
|
|
|
public void testUserDefinedWritability2() {
|
|
|
|
final StringBuilder buf = new StringBuilder();
|
2014-10-25 09:57:22 +02:00
|
|
|
EmbeddedChannel ch = new EmbeddedChannel(new ChannelInboundHandlerAdapter() {
|
2014-10-22 10:45:28 +02:00
|
|
|
@Override
|
|
|
|
public void channelWritabilityChanged(ChannelHandlerContext ctx) throws Exception {
|
|
|
|
buf.append(ctx.channel().isWritable());
|
|
|
|
buf.append(' ');
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
ch.config().setWriteBufferLowWaterMark(128);
|
|
|
|
ch.config().setWriteBufferHighWaterMark(256);
|
|
|
|
|
|
|
|
ChannelOutboundBuffer cob = ch.unsafe().outboundBuffer();
|
|
|
|
|
|
|
|
// Ensure that setting a user-defined writability flag to false affects channel.isWritable()
|
|
|
|
cob.setUserDefinedWritability(1, false);
|
2014-12-10 10:36:53 +01:00
|
|
|
ch.runPendingTasks();
|
2014-10-22 10:45:28 +02:00
|
|
|
assertThat(buf.toString(), is("false "));
|
|
|
|
|
|
|
|
// Ensure that setting another user-defined writability flag to false does not trigger
|
|
|
|
// channelWritabilityChanged.
|
|
|
|
cob.setUserDefinedWritability(2, false);
|
2014-12-10 10:36:53 +01:00
|
|
|
ch.runPendingTasks();
|
2014-10-22 10:45:28 +02:00
|
|
|
assertThat(buf.toString(), is("false "));
|
|
|
|
|
|
|
|
// Ensure that setting only one user-defined writability flag to true does not affect channel.isWritable()
|
|
|
|
cob.setUserDefinedWritability(1, true);
|
2014-12-10 10:36:53 +01:00
|
|
|
ch.runPendingTasks();
|
2014-10-22 10:45:28 +02:00
|
|
|
assertThat(buf.toString(), is("false "));
|
|
|
|
|
|
|
|
// Ensure that setting all user-defined writability flags to true affects channel.isWritable()
|
|
|
|
cob.setUserDefinedWritability(2, true);
|
2014-12-10 10:36:53 +01:00
|
|
|
ch.runPendingTasks();
|
2014-10-22 10:45:28 +02:00
|
|
|
assertThat(buf.toString(), is("false true "));
|
|
|
|
|
|
|
|
safeClose(ch);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Test
|
|
|
|
public void testMixedWritability() {
|
|
|
|
final StringBuilder buf = new StringBuilder();
|
2014-10-25 09:57:22 +02:00
|
|
|
EmbeddedChannel ch = new EmbeddedChannel(new ChannelInboundHandlerAdapter() {
|
2014-10-22 10:45:28 +02:00
|
|
|
@Override
|
|
|
|
public void channelWritabilityChanged(ChannelHandlerContext ctx) throws Exception {
|
|
|
|
buf.append(ctx.channel().isWritable());
|
|
|
|
buf.append(' ');
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
ch.config().setWriteBufferLowWaterMark(128);
|
|
|
|
ch.config().setWriteBufferHighWaterMark(256);
|
|
|
|
|
|
|
|
ChannelOutboundBuffer cob = ch.unsafe().outboundBuffer();
|
|
|
|
|
|
|
|
// Trigger channelWritabilityChanged() by writing a lot.
|
2016-08-24 10:56:21 +02:00
|
|
|
ch.write(buffer().writeZero(257));
|
2014-10-22 10:45:28 +02:00
|
|
|
assertThat(buf.toString(), is("false "));
|
|
|
|
|
|
|
|
// Ensure that setting a user-defined writability flag to false does not trigger channelWritabilityChanged()
|
|
|
|
cob.setUserDefinedWritability(1, false);
|
2014-12-10 10:36:53 +01:00
|
|
|
ch.runPendingTasks();
|
2014-10-22 10:45:28 +02:00
|
|
|
assertThat(buf.toString(), is("false "));
|
|
|
|
|
2017-04-19 22:37:03 +02:00
|
|
|
// Ensure reducing the totalPendingWriteBytes down to zero does not trigger channelWritabilityChanged()
|
2014-10-22 10:45:28 +02:00
|
|
|
// because of the user-defined writability flag.
|
|
|
|
ch.flush();
|
|
|
|
assertThat(cob.totalPendingWriteBytes(), is(0L));
|
|
|
|
assertThat(buf.toString(), is("false "));
|
|
|
|
|
|
|
|
// Ensure that setting the user-defined writability flag to true triggers channelWritabilityChanged()
|
|
|
|
cob.setUserDefinedWritability(1, true);
|
2014-12-10 10:36:53 +01:00
|
|
|
ch.runPendingTasks();
|
2014-10-22 10:45:28 +02:00
|
|
|
assertThat(buf.toString(), is("false true "));
|
|
|
|
|
|
|
|
safeClose(ch);
|
|
|
|
}
|
|
|
|
|
2018-10-11 18:46:10 +02:00
|
|
|
@Test(timeout = 5000)
|
|
|
|
public void testWriteTaskRejected() throws Exception {
|
|
|
|
final SingleThreadEventExecutor executor = new SingleThreadEventExecutor(
|
|
|
|
null, new DefaultThreadFactory("executorPool"),
|
|
|
|
true, 1, RejectedExecutionHandlers.reject()) {
|
|
|
|
@Override
|
|
|
|
protected void run() {
|
|
|
|
do {
|
|
|
|
Runnable task = takeTask();
|
|
|
|
if (task != null) {
|
|
|
|
task.run();
|
|
|
|
updateLastExecutionTime();
|
|
|
|
}
|
|
|
|
} while (!confirmShutdown());
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
protected Queue<Runnable> newTaskQueue(int maxPendingTasks) {
|
|
|
|
return super.newTaskQueue(1);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
final CountDownLatch handlerAddedLatch = new CountDownLatch(1);
|
2019-01-19 17:17:03 +01:00
|
|
|
final CountDownLatch handlerRemovedLatch = new CountDownLatch(1);
|
2018-10-11 18:46:10 +02:00
|
|
|
EmbeddedChannel ch = new EmbeddedChannel();
|
2019-01-19 17:17:03 +01:00
|
|
|
ch.pipeline().addLast(executor, "handler", new ChannelOutboundHandlerAdapter() {
|
2018-10-11 18:46:10 +02:00
|
|
|
@Override
|
|
|
|
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
|
|
|
|
promise.setFailure(new AssertionError("Should not be called"));
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2019-01-19 17:17:03 +01:00
|
|
|
public void handlerAdded(ChannelHandlerContext ctx) {
|
2018-10-11 18:46:10 +02:00
|
|
|
handlerAddedLatch.countDown();
|
|
|
|
}
|
2019-01-19 17:17:03 +01:00
|
|
|
|
|
|
|
@Override
|
|
|
|
public void handlerRemoved(ChannelHandlerContext ctx) {
|
|
|
|
handlerRemovedLatch.countDown();
|
|
|
|
}
|
2018-10-11 18:46:10 +02:00
|
|
|
});
|
|
|
|
|
|
|
|
// Lets wait until we are sure the handler was added.
|
|
|
|
handlerAddedLatch.await();
|
|
|
|
|
|
|
|
final CountDownLatch executeLatch = new CountDownLatch(1);
|
|
|
|
final CountDownLatch runLatch = new CountDownLatch(1);
|
|
|
|
executor.execute(new Runnable() {
|
|
|
|
@Override
|
|
|
|
public void run() {
|
|
|
|
try {
|
|
|
|
runLatch.countDown();
|
|
|
|
executeLatch.await();
|
|
|
|
} catch (InterruptedException e) {
|
|
|
|
Thread.currentThread().interrupt();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
runLatch.await();
|
|
|
|
|
|
|
|
executor.execute(new Runnable() {
|
|
|
|
@Override
|
|
|
|
public void run() {
|
|
|
|
// Will not be executed but ensure the pending count is 1.
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
assertEquals(1, executor.pendingTasks());
|
|
|
|
assertEquals(0, ch.unsafe().outboundBuffer().totalPendingWriteBytes());
|
|
|
|
|
|
|
|
ByteBuf buffer = buffer(128).writeZero(128);
|
|
|
|
ChannelFuture future = ch.write(buffer);
|
|
|
|
ch.runPendingTasks();
|
|
|
|
|
|
|
|
assertTrue(future.cause() instanceof RejectedExecutionException);
|
|
|
|
assertEquals(0, buffer.refCnt());
|
|
|
|
|
|
|
|
// In case of rejected task we should not have anything pending.
|
|
|
|
assertEquals(0, ch.unsafe().outboundBuffer().totalPendingWriteBytes());
|
|
|
|
executeLatch.countDown();
|
|
|
|
|
2019-01-19 17:17:03 +01:00
|
|
|
while (executor.pendingTasks() != 0) {
|
|
|
|
// Wait until there is no more pending task left.
|
|
|
|
Thread.sleep(10);
|
|
|
|
}
|
|
|
|
|
|
|
|
ch.pipeline().remove("handler");
|
|
|
|
|
|
|
|
// Ensure we do not try to shutdown the executor before we handled everything for the Channel. Otherwise
|
|
|
|
// the Executor may reject when the Channel tries to add a task to it.
|
|
|
|
handlerRemovedLatch.await();
|
|
|
|
|
2018-10-11 18:46:10 +02:00
|
|
|
safeClose(ch);
|
2019-01-19 17:17:03 +01:00
|
|
|
|
2018-10-11 18:46:10 +02:00
|
|
|
executor.shutdownGracefully();
|
|
|
|
}
|
|
|
|
|
2014-10-22 10:45:28 +02:00
|
|
|
private static void safeClose(EmbeddedChannel ch) {
|
|
|
|
ch.finish();
|
|
|
|
for (;;) {
|
|
|
|
ByteBuf m = ch.readOutbound();
|
|
|
|
if (m == null) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
m.release();
|
|
|
|
}
|
|
|
|
}
|
2013-10-06 15:29:35 +02:00
|
|
|
}
|