Skip to content

Commit

Permalink
use daemon threads to allow shut down of application
Browse files Browse the repository at this point in the history
To exit normally all non-daemon threads of the java process are supposed
to have stopped. TcpClientSession creates an static class member
EventLoopGroup which in turn instantiates worker threads. These are
changed to daemon threads to not get in the way of the shutdown. This
is similar to the default in Netty 5.

To ensure work is finishd the Runtime shutdown event is hooked to
initiate a time-limited graceful shutdown of the event loops. Normally
an application should shut down communication before the process
shutdown is inititated.
  • Loading branch information
Peter Svensson committed Sep 22, 2023
1 parent bfb6430 commit 46595e2
Show file tree
Hide file tree
Showing 2 changed files with 24 additions and 6 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -45,13 +45,16 @@
import io.netty.incubator.channel.uring.IOUringSocketChannel;
import io.netty.resolver.dns.DnsNameResolver;
import io.netty.resolver.dns.DnsNameResolverBuilder;
import io.netty.util.concurrent.DefaultThreadFactory;
import java.net.*;
import java.util.concurrent.ThreadFactory;

public class TcpClientSession extends TcpSession {
private static final String IP_REGEX = "\\b\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\b";
private static Class<? extends Channel> CHANNEL_CLASS;
private static Class<? extends DatagramChannel> DATAGRAM_CHANNEL_CLASS;
private static EventLoopGroup EVENT_LOOP_GROUP;
private static final int WAIT_FOR_SHUTDOWN_IN_MS = 2000;

private final String bindAddress;
private final int bindPort;
Expand Down Expand Up @@ -295,27 +298,37 @@ private static void createTcpEventLoopGroup() {

switch (TransportHelper.determineTransportMethod()) {
case IO_URING:
EVENT_LOOP_GROUP = new IOUringEventLoopGroup();
EVENT_LOOP_GROUP = new IOUringEventLoopGroup(newThreadFactory());
CHANNEL_CLASS = IOUringSocketChannel.class;
DATAGRAM_CHANNEL_CLASS = IOUringDatagramChannel.class;
break;
case EPOLL:
EVENT_LOOP_GROUP = new EpollEventLoopGroup();
EVENT_LOOP_GROUP = new EpollEventLoopGroup(newThreadFactory());
CHANNEL_CLASS = EpollSocketChannel.class;
DATAGRAM_CHANNEL_CLASS = EpollDatagramChannel.class;
break;
case KQUEUE:
EVENT_LOOP_GROUP = new KQueueEventLoopGroup();
EVENT_LOOP_GROUP = new KQueueEventLoopGroup(newThreadFactory());
CHANNEL_CLASS = KQueueSocketChannel.class;
DATAGRAM_CHANNEL_CLASS = KQueueDatagramChannel.class;
break;
case NIO:
EVENT_LOOP_GROUP = new NioEventLoopGroup();
EVENT_LOOP_GROUP = new NioEventLoopGroup(newThreadFactory());
CHANNEL_CLASS = NioSocketChannel.class;
DATAGRAM_CHANNEL_CLASS = NioDatagramChannel.class;
break;
}

Runtime.getRuntime().addShutdownHook(new Thread(() -> EVENT_LOOP_GROUP.shutdownGracefully()));
Runtime.getRuntime().addShutdownHook(new Thread(() -> EVENT_LOOP_GROUP.shutdownGracefully().awaitUninterruptibly(WAIT_FOR_SHUTDOWN_IN_MS)));
}

protected static ThreadFactory newThreadFactory() {
// Create a new daemon thread. When the last non daemon thread ends
// the runtime environment will call the shutdown hooks. One of the
// hooks will try to shut down the event loop group which will
// normally lead to the thread exiting. If not, it will be forcably
// killed after WAIT_FOR_SHUTDOWN_IN_MS ms along with the other
// daemon threads as the runtime exits.
return new DefaultThreadFactory(TcpClientSession.class, true);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import io.netty.handler.timeout.ReadTimeoutHandler;
import io.netty.handler.timeout.WriteTimeoutException;
import io.netty.handler.timeout.WriteTimeoutHandler;
import io.netty.util.concurrent.DefaultThreadFactory;
import net.kyori.adventure.text.Component;

import javax.annotation.Nullable;
Expand All @@ -32,6 +33,7 @@ public abstract class TcpSession extends SimpleChannelInboundHandler<Packet> imp
*/
public static boolean USE_EVENT_LOOP_FOR_PACKETS = true;
private static EventLoopGroup PACKET_EVENT_LOOP;
private static final int WAIT_FOR_SHUTDOWN_IN_MS = 2000;

protected String host;
protected int port;
Expand Down Expand Up @@ -296,7 +298,10 @@ public void disconnect(final Component reason, final Throwable cause) {
}

if (PACKET_EVENT_LOOP == null) {
PACKET_EVENT_LOOP = new DefaultEventLoopGroup();
// See TcpClientSession.newThreadFactory() for details on
// daemon threads and their interaction with the runtime.
PACKET_EVENT_LOOP = new DefaultEventLoopGroup(new DefaultThreadFactory(this.getClass(), true));
Runtime.getRuntime().addShutdownHook(new Thread(() -> PACKET_EVENT_LOOP.shutdownGracefully().awaitUninterruptibly(WAIT_FOR_SHUTDOWN_IN_MS)));
}
return PACKET_EVENT_LOOP.next();
}
Expand Down

0 comments on commit 46595e2

Please sign in to comment.