Skip to content

Commit 955042a

Browse files
author
Peter Svensson
committed
close threads at server shutdown
To exit normally all non-daemon threads of the java process are supposed to have stopped. There are several cases of EventLoopGroup-created thread pools that are not shut down as a part of the server shutdown. These threads are spawned on the first Bedrock player login. The identified cases are: - The listener threads in GeyserServerInitializer - The player threads in GeyserServerInitializer - The EXECUTOR_SERVICE in the SkinProvider This patch depends on the PR GeyserMC/MCProtocolLib#748 submitted to MCProtocolLib to allow the TcpCLientSession to shut down on process exit. This is a very quickly thrown together proof of concept to verify that the execution thread pools left running were what blocked a clean exit with Geyser active. Nullify EventLoopGroup variables after shutdown to allow them to be restarted.
1 parent bf81fc1 commit 955042a

File tree

3 files changed

+41
-11
lines changed

3 files changed

+41
-11
lines changed

core/src/main/java/org/geysermc/geyser/network/GeyserServerInitializer.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,10 @@ public GeyserServerInitializer(GeyserImpl geyser) {
4747
this.geyser = geyser;
4848
}
4949

50+
public DefaultEventLoopGroup getEventLoopGroup() {
51+
return eventLoopGroup;
52+
}
53+
5054
@Override
5155
public void initSession(@Nonnull BedrockServerSession bedrockServerSession) {
5256
try {
@@ -72,4 +76,4 @@ public void initSession(@Nonnull BedrockServerSession bedrockServerSession) {
7276
protected BedrockPeer createPeer(Channel channel) {
7377
return new GeyserBedrockPeer(channel, this::createSession);
7478
}
75-
}
79+
}

core/src/main/java/org/geysermc/geyser/network/netty/GeyserServer.java

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
package org.geysermc.geyser.network.netty;
2727

2828
import com.github.steveice10.packetlib.helper.TransportHelper;
29+
import com.github.steveice10.packetlib.tcp.TcpClientSession;
2930
import io.netty.bootstrap.ServerBootstrap;
3031
import io.netty.channel.Channel;
3132
import io.netty.channel.ChannelFuture;
@@ -57,6 +58,7 @@
5758
import org.geysermc.geyser.network.netty.proxy.ProxyServerHandler;
5859
import org.geysermc.geyser.ping.GeyserPingInfo;
5960
import org.geysermc.geyser.ping.IGeyserPingPassthrough;
61+
import org.geysermc.geyser.skin.SkinProvider;
6062
import org.geysermc.geyser.text.GeyserLocale;
6163
import org.geysermc.geyser.translator.text.MessageTranslator;
6264

@@ -83,8 +85,9 @@ public final class GeyserServer {
8385
private static final Transport TRANSPORT = compatibleTransport();
8486

8587
private final GeyserImpl geyser;
86-
private final EventLoopGroup group;
88+
private EventLoopGroup group;
8789
private final ServerBootstrap bootstrap;
90+
private EventLoopGroup playerGroup;
8891

8992
@Getter
9093
private final ExpiringMap<InetSocketAddress, InetSocketAddress> proxiedAddresses;
@@ -131,7 +134,15 @@ public CompletableFuture<Void> bind(InetSocketAddress address) {
131134
}
132135

133136
public void shutdown() {
134-
this.group.shutdownGracefully();
137+
try {
138+
this.group.shutdownGracefully().sync();
139+
this.group = null;
140+
this.playerGroup.shutdownGracefully().sync();
141+
this.playerGroup = null;
142+
SkinProvider.shutdown();
143+
} catch (InterruptedException e) {
144+
e.printStackTrace();
145+
}
135146
this.future.channel().closeFuture().syncUninterruptibly();
136147
}
137148

@@ -148,11 +159,13 @@ private ServerBootstrap createBootstrap(EventLoopGroup group) {
148159
}
149160
}
150161

162+
GeyserServerInitializer serverInitializer = new GeyserServerInitializer(this.geyser);
163+
playerGroup = serverInitializer.getEventLoopGroup();
151164
return new ServerBootstrap()
152165
.channelFactory(RakChannelFactory.server(TRANSPORT.datagramChannel()))
153166
.group(group)
154167
.option(RakChannelOption.RAK_HANDLE_PING, true)
155-
.childHandler(new GeyserServerInitializer(this.geyser));
168+
.childHandler(serverInitializer);
156169
}
157170

158171
public boolean onConnectionRequest(InetSocketAddress inetSocketAddress) {

core/src/main/java/org/geysermc/geyser/skin/SkinProvider.java

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@
5858

5959
public class SkinProvider {
6060
private static final boolean ALLOW_THIRD_PARTY_CAPES = GeyserImpl.getInstance().getConfig().isAllowThirdPartyCapes();
61-
static final ExecutorService EXECUTOR_SERVICE = Executors.newFixedThreadPool(ALLOW_THIRD_PARTY_CAPES ? 21 : 14);
61+
static ExecutorService EXECUTOR_SERVICE;
6262

6363
static final Skin EMPTY_SKIN;
6464
static final Cape EMPTY_CAPE = new Cape("", "no-cape", ByteArrays.EMPTY_ARRAY, -1, true);
@@ -133,6 +133,19 @@ public class SkinProvider {
133133
WEARING_CUSTOM_SKULL_SLIM = new SkinGeometry("{\"geometry\" :{\"default\" :\"geometry.humanoid.wearingCustomSkullSlim\"}}", wearingCustomSkullSlim, false);
134134
}
135135

136+
private static ExecutorService getExecutorService() {
137+
if (EXECUTOR_SERVICE == null) {
138+
EXECUTOR_SERVICE = Executors.newFixedThreadPool(ALLOW_THIRD_PARTY_CAPES ? 21 : 14);
139+
}
140+
return EXECUTOR_SERVICE;
141+
}
142+
public static void shutdown() {
143+
if (EXECUTOR_SERVICE != null) {
144+
EXECUTOR_SERVICE.shutdown();
145+
EXECUTOR_SERVICE = null;
146+
}
147+
}
148+
136149
public static void registerCacheImageTask(GeyserImpl geyser) {
137150
// Schedule Daily Image Expiry if we are caching them
138151
if (geyser.getConfig().getCacheImages() > 0) {
@@ -302,7 +315,7 @@ private static CompletableFuture<SkinAndCape> requestSkinAndCape(UUID playerId,
302315

303316
GeyserImpl.getInstance().getLogger().debug("Took " + (System.currentTimeMillis() - time) + "ms for " + playerId);
304317
return skinAndCape;
305-
}, EXECUTOR_SERVICE);
318+
}, getExecutorService());
306319
}
307320

308321
static CompletableFuture<Skin> requestSkin(UUID playerId, String textureUrl, boolean newThread) {
@@ -320,7 +333,7 @@ static CompletableFuture<Skin> requestSkin(UUID playerId, String textureUrl, boo
320333

321334
CompletableFuture<Skin> future;
322335
if (newThread) {
323-
future = CompletableFuture.supplyAsync(() -> supplySkin(playerId, textureUrl), EXECUTOR_SERVICE)
336+
future = CompletableFuture.supplyAsync(() -> supplySkin(playerId, textureUrl), getExecutorService())
324337
.whenCompleteAsync((skin, throwable) -> {
325338
skin.updated = true;
326339
CACHED_JAVA_SKINS.put(textureUrl, skin);
@@ -349,7 +362,7 @@ private static CompletableFuture<Cape> requestCape(String capeUrl, CapeProvider
349362

350363
CompletableFuture<Cape> future;
351364
if (newThread) {
352-
future = CompletableFuture.supplyAsync(() -> supplyCape(capeUrl, provider), EXECUTOR_SERVICE)
365+
future = CompletableFuture.supplyAsync(() -> supplyCape(capeUrl, provider), getExecutorService())
353366
.whenCompleteAsync((cape, throwable) -> {
354367
CACHED_JAVA_CAPES.put(capeUrl, cape);
355368
requestedCapes.remove(capeUrl);
@@ -388,7 +401,7 @@ private static CompletableFuture<Skin> requestEars(String earsUrl, boolean newTh
388401

389402
CompletableFuture<Skin> future;
390403
if (newThread) {
391-
future = CompletableFuture.supplyAsync(() -> supplyEars(skin, earsUrl), EXECUTOR_SERVICE)
404+
future = CompletableFuture.supplyAsync(() -> supplyEars(skin, earsUrl), getExecutorService())
392405
.whenCompleteAsync((outSkin, throwable) -> { });
393406
} else {
394407
Skin ears = supplyEars(skin, earsUrl); // blocking
@@ -620,7 +633,7 @@ public static CompletableFuture<String> requestTexturesFromUUID(String uuid) {
620633
}
621634
return null;
622635
}
623-
}, EXECUTOR_SERVICE);
636+
}, getExecutorService());
624637
}
625638

626639
/**
@@ -646,7 +659,7 @@ public static CompletableFuture<String> requestTexturesFromUsername(String usern
646659
}
647660
return null;
648661
}
649-
}, EXECUTOR_SERVICE).thenCompose(uuid -> {
662+
}, getExecutorService()).thenCompose(uuid -> {
650663
if (uuid == null) {
651664
return CompletableFuture.completedFuture(null);
652665
}

0 commit comments

Comments
 (0)