Skip to content

NioEventLoopGroup deadlocks 1 second on close when using a DnsAddressResolverGroup #5308

@vietj

Description

@vietj

With Netty 4.1.0.CR7 (probably also with Final)

The NioEventLoop deadlocks 1 second in NioEventLoop#select, at:

int selectedKeys = selector.select(timeoutMillis);

where timeoutMillis is 1000.

The deadlock seem to ahppen when using the DnsAddressResolverGroup opens indirectly an NioDatagramChannel that remains connected since it is closed in a promise of the NioEventLoop.shutdown() - hence the deadlock.

Reproducer:

    ServerBootstrap bs = new ServerBootstrap();

    bs.channel(NioServerSocketChannel.class);
    NioEventLoopGroup group = new NioEventLoopGroup();
    bs.group(group);
    bs.childHandler(new ChannelInitializer<Channel>() {
      @Override
      protected void initChannel(Channel ch) throws Exception {
        ch.pipeline().addLast(new ChannelDuplexHandler(
        ));
      }
    });

    ChannelFuture server = bs.bind("localhost", 8080);
    server.sync();

    DnsAddressResolverGroup resolverGroup = new DnsAddressResolverGroup(NioDatagramChannel.class, DnsServerAddresses.defaultAddresses());

    Bootstrap bootstrap = new Bootstrap();
    bootstrap.resolver(resolverGroup);
    bootstrap.channel(NioSocketChannel.class);
    bootstrap.group(group);
    bootstrap.handler(new ChannelInitializer<Channel>() {
      @Override
      protected void initChannel(Channel ch) throws Exception {
        ch.pipeline().addLast(new ChannelDuplexHandler(
        ));
      }
    });
    ChannelFuture client = bootstrap.connect("localhost", 8080);
    client.sync();

    client.channel().close().sync();
    server.channel().close().sync();

    // Try close
    resolverGroup.close();

    long now = System.currentTimeMillis();
    group.shutdownGracefully(0, 10, TimeUnit.SECONDS).addListener(v -> {
      long after = System.currentTimeMillis();
      System.out.println("stopped " + (after - now));
    }).sync();

There is a work around for this:

    ServerBootstrap bs = new ServerBootstrap();

    bs.channel(NioServerSocketChannel.class);
    NioEventLoopGroup group = new NioEventLoopGroup();
    bs.group(group);
    bs.childHandler(new ChannelInitializer<Channel>() {
      @Override
      protected void initChannel(Channel ch) throws Exception {
        ch.pipeline().addLast(new ChannelDuplexHandler(
        ));
      }
    });

    ChannelFuture server = bs.bind("localhost", 8080);
    server.sync();

    DnsAddressResolverGroup resolverGroup = new DnsAddressResolverGroup(NioDatagramChannel.class, DnsServerAddresses.defaultAddresses()) {
      @Override
      protected AddressResolver<InetSocketAddress> newResolver(EventLoop eventLoop, ChannelFactory<? extends DatagramChannel> channelFactory, InetSocketAddress localAddress, DnsServerAddresses nameServerAddresses) throws Exception {

        DnsNameResolver resolver = new DnsNameResolverBuilder(eventLoop)
            .channelFactory(channelFactory)
            .localAddress(localAddress)
            .nameServerAddresses(nameServerAddresses)
            .build();

        return new InetSocketAddressResolver(eventLoop, resolver) {
          @Override
          public void close() {
            resolver.close();
          }
        };
      }
    };

    Bootstrap bootstrap = new Bootstrap();
    bootstrap.resolver(resolverGroup);
    bootstrap.channel(NioSocketChannel.class);
    bootstrap.group(group);
    bootstrap.handler(new ChannelInitializer<Channel>() {
      @Override
      protected void initChannel(Channel ch) throws Exception {
        ch.pipeline().addLast(new ChannelDuplexHandler(
        ));
      }
    });
    ChannelFuture client = bootstrap.connect("localhost", 8080);
    client.sync();

    client.channel().close().sync();
    server.channel().close().sync();

    // Try close
    resolverGroup.close();

    long now = System.currentTimeMillis();
    group.shutdownGracefully(0, 10, TimeUnit.SECONDS).addListener(v -> {
      long after = System.currentTimeMillis();
      System.out.println("stopped " + (after - now));
    }).sync();

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions