diff --git a/transport/src/main/java/io/netty/channel/local/LocalChannel.java b/transport/src/main/java/io/netty/channel/local/LocalChannel.java index 24eb2d7851..3aba344be4 100644 --- a/transport/src/main/java/io/netty/channel/local/LocalChannel.java +++ b/transport/src/main/java/io/netty/channel/local/LocalChannel.java @@ -153,7 +153,12 @@ public class LocalChannel extends AbstractChannel { @Override protected void doRegister() throws Exception { - if (peer != null) { + // Check if both peer and parent are non-null because this channel was created by a LocalServerChannel. + // This is needed as a peer may not be null also if a LocalChannel was connected before and + // deregistered / registered later again. + // + // See https://github.com/netty/netty/issues/2400 + if (peer != null && parent() != null) { // Store the peer in a local variable as it may be set to null if doClose() is called. // Because of this we also set registerInProgress to true as we check for this in doClose() and make sure // we delay the fireChannelInactive() to be fired after the fireChannelActive() and so keep the correct @@ -235,9 +240,7 @@ public class LocalChannel extends AbstractChannel { @Override protected void doDeregister() throws Exception { - if (isOpen()) { - unsafe().close(unsafe().voidPromise()); - } + // Just remove the shutdownHook as this Channel may be closed later or registered to another EventLoop ((SingleThreadEventExecutor) eventLoop()).removeShutdownHook(shutdownHook); } diff --git a/transport/src/test/java/io/netty/channel/local/LocalChannelTest.java b/transport/src/test/java/io/netty/channel/local/LocalChannelTest.java index adcec5f99d..d72d9d3197 100644 --- a/transport/src/test/java/io/netty/channel/local/LocalChannelTest.java +++ b/transport/src/test/java/io/netty/channel/local/LocalChannelTest.java @@ -29,7 +29,6 @@ import io.netty.channel.SingleThreadEventLoop; import io.netty.util.concurrent.EventExecutor; import io.netty.util.internal.logging.InternalLogger; import io.netty.util.internal.logging.InternalLoggerFactory; -import org.junit.Assert; import org.junit.Test; import java.nio.channels.ClosedChannelException; @@ -248,6 +247,39 @@ public class LocalChannelTest { } } + @Test + public void testReRegister() { + EventLoopGroup group1 = new LocalEventLoopGroup(); + EventLoopGroup group2 = new LocalEventLoopGroup(); + LocalAddress addr = new LocalAddress(LOCAL_ADDR_ID); + Bootstrap cb = new Bootstrap(); + ServerBootstrap sb = new ServerBootstrap(); + + cb.group(group1) + .channel(LocalChannel.class) + .handler(new TestHandler()); + + sb.group(group2) + .channel(LocalServerChannel.class) + .childHandler(new ChannelInitializer() { + @Override + public void initChannel(LocalChannel ch) throws Exception { + ch.pipeline().addLast(new TestHandler()); + } + }); + + // Start server + final Channel sc = sb.bind(addr).syncUninterruptibly().channel(); + + // Connect to the server + final Channel cc = cb.connect(addr).syncUninterruptibly().channel(); + + cc.deregister().syncUninterruptibly(); + // Change event loop group. + group2.register(cc).syncUninterruptibly(); + cc.close().syncUninterruptibly(); + sc.close().syncUninterruptibly(); + } static class TestHandler extends ChannelInboundHandlerAdapter { @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {