From 740c0c325c3f12b2028c607620cbb3fcb072a173 Mon Sep 17 00:00:00 2001 From: deanxbox Date: Sun, 16 Aug 2026 23:20:20 +0100 Subject: [PATCH] Fix client hang on shutdown from non-daemon thread pools Cape repo registration and mod resource pack downloading each called Executors.newCachedThreadPool(), which spawns non-daemon threads that linger for a 60s keepalive after their task finishes. Those threads keep the JVM alive past "Stopping!", so ClientShutdownWatchdog eventually fires and reports a crash on quit. Both now use Util.nonCriticalIoPool(), the shared daemon-backed vanilla pool already used by ServerTextureDownloader. This also stops allocating a brand new pool on every call. Also drops two unused imports that were failing checkstyleMain. Fixes #79 --- .../src/main/java/net/frozenblock/lib/cape/api/CapeUtil.java | 4 ++-- .../lib/platform/api/registry/DeferredActivity.java | 1 - .../lib/platform/api/registry/DeferredSimpleParticleType.java | 1 - .../lib/resource/client/api/pack/ModResourcePackApi.java | 4 ++-- 4 files changed, 4 insertions(+), 6 deletions(-) diff --git a/common/src/main/java/net/frozenblock/lib/cape/api/CapeUtil.java b/common/src/main/java/net/frozenblock/lib/cape/api/CapeUtil.java index 6a4373483..6179243ba 100644 --- a/common/src/main/java/net/frozenblock/lib/cape/api/CapeUtil.java +++ b/common/src/main/java/net/frozenblock/lib/cape/api/CapeUtil.java @@ -36,7 +36,6 @@ import java.util.Optional; import java.util.UUID; import java.util.concurrent.CompletableFuture; -import java.util.concurrent.Executors; import net.frozenblock.lib.FrozenLibConstants; import net.frozenblock.lib.cape.client.api.ClientCapeUtil; import net.frozenblock.lib.cape.impl.Cape; @@ -46,6 +45,7 @@ import net.minecraft.network.chat.Component; import net.minecraft.resources.Identifier; import net.minecraft.server.level.ServerPlayer; +import net.minecraft.util.Util; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.Unmodifiable; @@ -125,7 +125,7 @@ public static void registerCapesFromURL(String urlString) { } catch (IOException ignored) {} return Optional.empty(); }, - Executors.newCachedThreadPool() + Util.nonCriticalIoPool().forName("registerCapesFromURL") ).whenComplete((value, throwable) -> { value.ifPresent(string -> CAPE_REPOS.add((String) string)); }); diff --git a/common/src/main/java/net/frozenblock/lib/platform/api/registry/DeferredActivity.java b/common/src/main/java/net/frozenblock/lib/platform/api/registry/DeferredActivity.java index d5c869506..e5e40f5ee 100644 --- a/common/src/main/java/net/frozenblock/lib/platform/api/registry/DeferredActivity.java +++ b/common/src/main/java/net/frozenblock/lib/platform/api/registry/DeferredActivity.java @@ -20,7 +20,6 @@ import net.minecraft.core.Holder; import net.minecraft.resources.Identifier; import net.minecraft.resources.ResourceKey; -import net.minecraft.sounds.SoundEvent; import net.minecraft.world.entity.schedule.Activity; public class DeferredActivity implements DeferredHolder { diff --git a/common/src/main/java/net/frozenblock/lib/platform/api/registry/DeferredSimpleParticleType.java b/common/src/main/java/net/frozenblock/lib/platform/api/registry/DeferredSimpleParticleType.java index 882ed0658..dfc347e75 100644 --- a/common/src/main/java/net/frozenblock/lib/platform/api/registry/DeferredSimpleParticleType.java +++ b/common/src/main/java/net/frozenblock/lib/platform/api/registry/DeferredSimpleParticleType.java @@ -18,7 +18,6 @@ package net.frozenblock.lib.platform.api.registry; import net.minecraft.core.Holder; -import net.minecraft.core.particles.ParticleOptions; import net.minecraft.core.particles.ParticleType; import net.minecraft.core.particles.SimpleParticleType; import net.minecraft.resources.Identifier; diff --git a/common/src/main/java/net/frozenblock/lib/resource/client/api/pack/ModResourcePackApi.java b/common/src/main/java/net/frozenblock/lib/resource/client/api/pack/ModResourcePackApi.java index 7c73f9bc5..a6dc247d5 100644 --- a/common/src/main/java/net/frozenblock/lib/resource/client/api/pack/ModResourcePackApi.java +++ b/common/src/main/java/net/frozenblock/lib/resource/client/api/pack/ModResourcePackApi.java @@ -41,7 +41,6 @@ import java.util.Map; import java.util.Optional; import java.util.concurrent.CompletableFuture; -import java.util.concurrent.Executors; import java.util.concurrent.atomic.AtomicBoolean; import java.util.function.Consumer; import java.util.zip.ZipFile; @@ -58,6 +57,7 @@ import net.minecraft.network.codec.ByteBufCodecs; import net.minecraft.network.codec.StreamCodec; import net.minecraft.util.StringRepresentable; +import net.minecraft.util.Util; import org.apache.commons.io.FileUtils; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.Contract; @@ -230,7 +230,7 @@ public static void downloadResourcePack(PackDownloadInfo downloadInfo, boolean h return failToast; } }, - Executors.newCachedThreadPool() + Util.nonCriticalIoPool().forName("downloadModResourcePack") ).whenComplete((value, throwable) -> { value.ifPresent(failToast -> ((ToastInfo)failToast).addToast()); });