void (boolean shutdownEventLoopGroup) { ServerChannel serverChannel = this.serverChannel.get(); if (serverChannel == null) { LOG.assertTrue(clientChannels.isEmpty()); return; } else if (!this.serverChannel.compareAndSet(serverChannel, null)) { return; } EventLoopGroup eventLoopGroup = shutdownEventLoopGroup ? serverChannel.eventLoop().parent() : null; try { long start = System.currentTimeMillis(); Channel[] clientChannels = this.clientChannels.toArray(new Channel[]{}); this.clientChannels.clear(); final CountDownLatch countDown = new CountDownLatch(clientChannels.length + 1); GenericFutureListener<ChannelFuture> listener = new GenericFutureListener<>() { @Override public void operationComplete(@NotNull ChannelFuture future) { try { Throwable cause = future.cause(); if (cause != null) { LOG.warn(cause); } } finally { countDown.countDown(); } } }; addCloseListener(serverChannel, countDown, listener); for (Channel channel : clientChannels) { addCloseListener(channel, countDown, listener); } try { countDown.await(5, TimeUnit.SECONDS); } catch (InterruptedException e) { LOG.warn("Cannot close all channels for 10 seconds, channels: " + Arrays.toString(clientChannels)); } long duration = System.currentTimeMillis() - start; if (duration > 1000) { LOG.info("Close all channels took " + duration + " ms: " + (duration / 60000) + " min " + ((duration % 60000) / 1000) + "sec"); } } finally { if (eventLoopGroup != null) { eventLoopGroup.shutdownGracefully(1, 2, TimeUnit.NANOSECONDS); } } }