diff options
| author | shedaniel <daniel@shedaniel.me> | 2024-09-17 22:15:52 +0800 |
|---|---|---|
| committer | shedaniel <daniel@shedaniel.me> | 2024-09-17 22:15:52 +0800 |
| commit | 3251f01cacfaf6dcdfd35ee3eaa5e5bee9d372b7 (patch) | |
| tree | 3b4876ab4de85a4a702300d9bbbb812d94f888e4 /runtime/src/main/java/me/shedaniel/rei/impl/common | |
| parent | dcac5f4891046507047c5951c164fb1c1f08da74 (diff) | |
| parent | 15a46dc3a91cf3f8fdb7a1ddf382ce05bfcf742e (diff) | |
| download | RoughlyEnoughItems-3251f01cacfaf6dcdfd35ee3eaa5e5bee9d372b7.tar.gz RoughlyEnoughItems-3251f01cacfaf6dcdfd35ee3eaa5e5bee9d372b7.tar.bz2 RoughlyEnoughItems-3251f01cacfaf6dcdfd35ee3eaa5e5bee9d372b7.zip | |
Merge remote-tracking branch 'origin/15.x-1.20.5' into 16.x-1.21
# Conflicts:
# runtime/src/main/java/me/shedaniel/rei/RoughlyEnoughItemsCoreClient.java
Diffstat (limited to 'runtime/src/main/java/me/shedaniel/rei/impl/common')
4 files changed, 439 insertions, 101 deletions
diff --git a/runtime/src/main/java/me/shedaniel/rei/impl/common/plugins/PluginManagerImpl.java b/runtime/src/main/java/me/shedaniel/rei/impl/common/plugins/PluginManagerImpl.java index d5140f2c8..b0d7abb52 100644 --- a/runtime/src/main/java/me/shedaniel/rei/impl/common/plugins/PluginManagerImpl.java +++ b/runtime/src/main/java/me/shedaniel/rei/impl/common/plugins/PluginManagerImpl.java @@ -38,12 +38,11 @@ import me.shedaniel.rei.api.common.plugins.REIPlugin; import me.shedaniel.rei.api.common.plugins.REIPluginProvider; import me.shedaniel.rei.api.common.registry.ReloadStage; import me.shedaniel.rei.api.common.registry.Reloadable; -import me.shedaniel.rei.api.common.util.CollectionUtils; import me.shedaniel.rei.impl.common.InternalLogger; import me.shedaniel.rei.impl.common.logging.performance.PerformanceLogger; import net.minecraft.client.Minecraft; import net.minecraft.server.MinecraftServer; -import org.apache.commons.lang3.tuple.MutablePair; +import org.apache.commons.lang3.exception.ExceptionUtils; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.Nullable; @@ -52,7 +51,6 @@ import java.util.*; import java.util.concurrent.ConcurrentHashMap; import java.util.function.BiConsumer; import java.util.function.UnaryOperator; -import java.util.stream.Stream; @ApiStatus.Internal public class PluginManagerImpl<P extends REIPlugin<?>> implements PluginManager<P>, PluginView<P> { @@ -62,7 +60,7 @@ public class PluginManagerImpl<P extends REIPlugin<?>> implements PluginManager< private final UnaryOperator<PluginView<P>> view; @Nullable private ReloadStage reloading = null; - private List<ReloadStage> observedStages = new ArrayList<>(); + private final List<ReloadStage> observedStages = new ArrayList<>(); private final List<REIPluginProvider<P>> plugins = new ArrayList<>(); private final Stopwatch reloadStopwatch = Stopwatch.createUnstarted(); private boolean forcedMainThread; @@ -127,15 +125,7 @@ public class PluginManagerImpl<P extends REIPlugin<?>> implements PluginManager< return FluentIterable.concat(Iterables.transform(plugins, REIPluginProvider::provide)); } - private static class PluginWrapper<P extends REIPlugin<?>> { - private final P plugin; - private final REIPluginProvider<P> provider; - - public PluginWrapper(P plugin, REIPluginProvider<P> provider) { - this.plugin = plugin; - this.provider = provider; - } - + private record PluginWrapper<P extends REIPlugin<?>>(P plugin, REIPluginProvider<P> provider) { public double getPriority() { return plugin.getPriority(); } @@ -143,7 +133,7 @@ public class PluginManagerImpl<P extends REIPlugin<?>> implements PluginManager< public String getPluginProviderName() { String providerName = provider.getPluginProviderName(); - if (provider.provide().size() >= 1) { + if (!provider.provide().isEmpty()) { String pluginName = plugin.getPluginProviderName(); if (!providerName.equals(pluginName)) { @@ -158,32 +148,36 @@ public class PluginManagerImpl<P extends REIPlugin<?>> implements PluginManager< @SuppressWarnings("RedundantTypeArguments") public FluentIterable<PluginWrapper<P>> getPluginWrapped() { return FluentIterable.<PluginWrapper<P>>concat(Iterables.<REIPluginProvider<P>, Iterable<PluginWrapper<P>>>transform(plugins, input -> Iterables.<P, PluginWrapper<P>>transform(input.provide(), - plugin -> new PluginWrapper(plugin, input)))); + plugin -> new PluginWrapper<>(plugin, input)))); } private class SectionClosable implements Closeable { - private ReloadStage stage; - private MutablePair<Stopwatch, String> sectionData; + private final PluginReloadContext context; + private final String section; + private final Stopwatch stopwatch; - public SectionClosable(ReloadStage stage, String section) { - this.stage = stage; - this.sectionData = new MutablePair<>(Stopwatch.createUnstarted(), ""); - sectionData.setRight(section); - InternalLogger.getInstance().trace("[" + name(pluginClass) + " " + stage + "] Reloading Section: \"%s\"", section); - sectionData.getLeft().reset().start(); + public SectionClosable(PluginReloadContext context, String section) { + this.context = context; + this.section = section; + this.stopwatch = Stopwatch.createStarted(); + InternalLogger.getInstance().trace("[" + name(pluginClass) + " " + context.stage() + "] Reloading Section: \"%s\"", section); } @Override public void close() { - sectionData.getLeft().stop(); - String section = sectionData.getRight(); - InternalLogger.getInstance().trace("[" + name(pluginClass) + " " + stage + "] Reloading Section: \"%s\" done in %s", section, sectionData.getLeft().toString()); - sectionData.getLeft().reset(); + this.stopwatch.stop(); + InternalLogger.getInstance().trace("[" + name(pluginClass) + " " + context.stage() + "] Reloading Section: \"%s\" done in %s", this.section, this.stopwatch); + this.stopwatch.reset(); + try { + context.interruptionContext().checkInterrupted(); + } catch (InterruptedException exception) { + ExceptionUtils.rethrow(exception); + } } } - private SectionClosable section(ReloadStage stage, String section) { - return new SectionClosable(stage, section); + private SectionClosable section(PluginReloadContext context, String section) { + return new SectionClosable(context, section); } @FunctionalInterface @@ -191,9 +185,9 @@ public class PluginManagerImpl<P extends REIPlugin<?>> implements PluginManager< void accept(boolean respectMainThread, Runnable task); } - private void pluginSection(ReloadStage stage, String sectionName, List<PluginWrapper<P>> list, @Nullable Reloadable<?> reloadable, BiConsumer<PluginWrapper<P>, SectionPluginSink> consumer) { + private void pluginSection(PluginReloadContext context, String sectionName, List<PluginWrapper<P>> list, @Nullable Reloadable<?> reloadable, BiConsumer<PluginWrapper<P>, SectionPluginSink> consumer) throws InterruptedException { for (PluginWrapper<P> wrapper : list) { - try (SectionClosable section = section(stage, sectionName + wrapper.getPluginProviderName() + "/")) { + try (SectionClosable section = section(context, sectionName + wrapper.getPluginProviderName() + "/")) { consumer.accept(wrapper, (respectMainThread, runnable) -> { if (!respectMainThread || reloadable == null || !wrapper.plugin.shouldBeForcefullyDoneOnMainThread(reloadable)) { runnable.run(); @@ -213,6 +207,7 @@ public class PluginManagerImpl<P extends REIPlugin<?>> implements PluginManager< } }); } catch (Throwable throwable) { + if (throwable instanceof InterruptedException) throw (InterruptedException) throwable; InternalLogger.getInstance().error(wrapper.getPluginProviderName() + " plugin failed to " + sectionName + "!", throwable); } } @@ -230,13 +225,15 @@ public class PluginManagerImpl<P extends REIPlugin<?>> implements PluginManager< } @Override - public void pre(ReloadStage stage) { - this.reloading = stage; + public void pre(PluginReloadContext context0) throws InterruptedException { + this.reloading = context0.stage(); + PluginReloadContext context = PluginReloadContext.of(context0.stage(), context0.interruptionContext().withJob(() -> this.reloading = null)); + List<PluginWrapper<P>> plugins = new ArrayList<>(getPluginWrapped().toList()); plugins.sort(Comparator.comparingDouble(PluginWrapper<P>::getPriority).reversed()); Collections.reverse(plugins); InternalLogger.getInstance().debug("========================================"); - InternalLogger.getInstance().debug(name(pluginClass) + " starting pre-reload for " + stage + "."); + InternalLogger.getInstance().debug(name(pluginClass) + " starting pre-reload for " + context.stage() + "."); InternalLogger.getInstance().debug("Reloadables (%d):".formatted(reloadables.size())); for (Reloadable<P> reloadable : reloadables) { InternalLogger.getInstance().debug(" - " + name(reloadable.getClass())); @@ -250,47 +247,51 @@ public class PluginManagerImpl<P extends REIPlugin<?>> implements PluginManager< this.forceMainThreadStopwatch.reset(); this.reloadStopwatch.reset().start(); this.observedStages.clear(); - this.observedStages.add(stage); - try (SectionClosable preRegister = section(stage, "pre-register/"); + this.observedStages.add(context.stage()); + try (SectionClosable preRegister = section(context, "pre-register/"); PerformanceLogger.Plugin perfLogger = RoughlyEnoughItemsCore.PERFORMANCE_LOGGER.stage("Pre Registration")) { - pluginSection(stage, "pre-register/", plugins, null, (plugin, sink) -> { + pluginSection(context, "pre-register/", plugins, null, (plugin, sink) -> { try (PerformanceLogger.Plugin.Inner inner = perfLogger.plugin(new Pair<>(plugin.provider, plugin.plugin))) { sink.accept(false, () -> { - ((REIPlugin<P>) plugin.plugin).preStage(this, stage); + ((REIPlugin<P>) plugin.plugin).preStage(this, context.stage()); }); } }); + } catch (InterruptedException exception) { + throw exception; } catch (Throwable throwable) { - this.reloading = null; - new RuntimeException("Failed to run pre registration").printStackTrace(); + InternalLogger.getInstance().throwException(new RuntimeException("Failed to run pre registration in stage [" + context.stage() + "]")); } - try (SectionClosable preStageAll = section(stage, "pre-stage/"); - PerformanceLogger.Plugin perfLogger = RoughlyEnoughItemsCore.PERFORMANCE_LOGGER.stage("Pre Stage " + stage.name())) { + try (SectionClosable preStageAll = section(context, "pre-stage/"); + PerformanceLogger.Plugin perfLogger = RoughlyEnoughItemsCore.PERFORMANCE_LOGGER.stage("Pre Stage " + context.stage().name())) { for (Reloadable<P> reloadable : reloadables) { Class<?> reloadableClass = reloadable.getClass(); - try (SectionClosable preStage = section(stage, "pre-stage/" + name(reloadableClass) + "/"); + try (SectionClosable preStage = section(context, "pre-stage/" + name(reloadableClass) + "/"); PerformanceLogger.Plugin.Inner inner = perfLogger.stage(name(reloadableClass))) { - reloadable.preStage(stage); + reloadable.preStage(context.stage()); } catch (Throwable throwable) { - throwable.printStackTrace(); + if (throwable instanceof InterruptedException) throw (InterruptedException) throwable; + InternalLogger.getInstance().error("Failed to run pre registration task for reloadable [" + name(reloadableClass) + "] in stage [" + context.stage() + "]", throwable); } } } this.reloading = null; this.reloadStopwatch.stop(); InternalLogger.getInstance().debug("========================================"); - InternalLogger.getInstance().debug(name(pluginClass) + " finished pre-reload for " + stage + " in " + reloadStopwatch + "."); + InternalLogger.getInstance().debug(name(pluginClass) + " finished pre-reload for " + context.stage() + " in " + reloadStopwatch + "."); InternalLogger.getInstance().debug("========================================"); } @Override - public void post(ReloadStage stage) { - this.reloading = stage; + public void post(PluginReloadContext context0) throws InterruptedException { + this.reloading = context0.stage(); + PluginReloadContext context = PluginReloadContext.of(context0.stage(), context0.interruptionContext().withJob(() -> this.reloading = null)); + List<PluginWrapper<P>> plugins = new ArrayList<>(getPluginWrapped().toList()); plugins.sort(Comparator.comparingDouble(PluginWrapper<P>::getPriority).reversed()); Collections.reverse(plugins); InternalLogger.getInstance().debug("========================================"); - InternalLogger.getInstance().debug(name(pluginClass) + " starting post-reload for " + stage + "."); + InternalLogger.getInstance().debug(name(pluginClass) + " starting post-reload for " + context.stage() + "."); InternalLogger.getInstance().debug("Reloadables (%d):".formatted(reloadables.size())); for (Reloadable<P> reloadable : reloadables) { InternalLogger.getInstance().debug(" - " + name(reloadable.getClass())); @@ -302,28 +303,29 @@ public class PluginManagerImpl<P extends REIPlugin<?>> implements PluginManager< InternalLogger.getInstance().debug("========================================"); this.reloadStopwatch.start(); Stopwatch postStopwatch = Stopwatch.createStarted(); - try (SectionClosable postRegister = section(stage, "post-register/"); + try (SectionClosable postRegister = section(context, "post-register/"); PerformanceLogger.Plugin perfLogger = RoughlyEnoughItemsCore.PERFORMANCE_LOGGER.stage("Post Registration")) { - pluginSection(stage, "post-register/", plugins, null, (plugin, sink) -> { + pluginSection(context, "post-register/", plugins, null, (plugin, sink) -> { try (PerformanceLogger.Plugin.Inner inner = perfLogger.plugin(new Pair<>(plugin.provider, plugin.plugin))) { sink.accept(false, () -> { - ((REIPlugin<P>) plugin.plugin).postStage(this, stage); + ((REIPlugin<P>) plugin.plugin).postStage(this, context.stage()); }); } }); } catch (Throwable throwable) { - this.reloading = null; - new RuntimeException("Failed to run post registration").printStackTrace(); + if (throwable instanceof InterruptedException) throw (InterruptedException) throwable; + InternalLogger.getInstance().throwException(new RuntimeException("Failed to run post registration in stage [" + context.stage() + "]")); } - try (SectionClosable postStageAll = section(stage, "post-stage/"); - PerformanceLogger.Plugin perfLogger = RoughlyEnoughItemsCore.PERFORMANCE_LOGGER.stage("Pre Stage " + stage.name())) { + try (SectionClosable postStageAll = section(context, "post-stage/"); + PerformanceLogger.Plugin perfLogger = RoughlyEnoughItemsCore.PERFORMANCE_LOGGER.stage("Pre Stage " + context.stage().name())) { for (Reloadable<P> reloadable : reloadables) { Class<?> reloadableClass = reloadable.getClass(); - try (SectionClosable postStage = section(stage, "post-stage/" + name(reloadableClass) + "/"); + try (SectionClosable postStage = section(context, "post-stage/" + name(reloadableClass) + "/"); PerformanceLogger.Plugin.Inner inner = perfLogger.stage(name(reloadableClass))) { - reloadable.postStage(stage); + reloadable.postStage(context.stage()); } catch (Throwable throwable) { - throwable.printStackTrace(); + if (throwable instanceof InterruptedException) throw (InterruptedException) throwable; + InternalLogger.getInstance().error("Failed to run post registration task for reloadable [" + name(reloadableClass) + "] in stage [" + context.stage() + "]", throwable); } } } @@ -331,7 +333,7 @@ public class PluginManagerImpl<P extends REIPlugin<?>> implements PluginManager< this.reloadStopwatch.stop(); postStopwatch.stop(); InternalLogger.getInstance().debug("========================================"); - InternalLogger.getInstance().info(name(pluginClass) + " finished post-reload for " + stage + " in " + postStopwatch + ", totaling " + reloadStopwatch + "."); + InternalLogger.getInstance().info(name(pluginClass) + " finished post-reload for " + context.stage() + " in " + postStopwatch + ", totaling " + reloadStopwatch + "."); if (forcedMainThread) { InternalLogger.getInstance().warn("Forcing plugins to run on main thread took " + forceMainThreadStopwatch); } @@ -347,10 +349,20 @@ public class PluginManagerImpl<P extends REIPlugin<?>> implements PluginManager< @Override public void startReload(ReloadStage stage) { try { + reload(PluginReloadContext.of(stage, ReloadInterruptionContext.ofNever())); + } catch (InterruptedException e) { + ExceptionUtils.rethrow(e); + } + } + + @Override + public void reload(PluginReloadContext context0) throws InterruptedException { + try { this.reloadStopwatch.start(); Stopwatch reloadingStopwatch = Stopwatch.createStarted(); - reloading = stage; - + this.reloading = context0.stage(); + PluginReloadContext context = PluginReloadContext.of(context0.stage(), context0.interruptionContext().withJob(() -> this.reloading = null)); + // Sort Plugins List<PluginWrapper<P>> plugins = new ArrayList<>(getPluginWrapped().toList()); plugins.sort(Comparator.comparingDouble(PluginWrapper<P>::getPriority).reversed()); @@ -359,7 +371,7 @@ public class PluginManagerImpl<P extends REIPlugin<?>> implements PluginManager< // Pre Reload String line = new String[]{"*", "=", "#", "@", "%", "~", "O", "-", "+"}[new Random().nextInt(9)].repeat(40); InternalLogger.getInstance().info(line); - InternalLogger.getInstance().info(name(pluginClass) + " starting main-reload for " + stage + "."); + InternalLogger.getInstance().info(name(pluginClass) + " starting main-reload for " + context.stage() + "."); InternalLogger.getInstance().debug("Reloadables (%d):".formatted(reloadables.size())); for (Reloadable<P> reloadable : reloadables) { InternalLogger.getInstance().debug(" - " + name(reloadable.getClass())); @@ -370,57 +382,58 @@ public class PluginManagerImpl<P extends REIPlugin<?>> implements PluginManager< } InternalLogger.getInstance().info(line); - try (SectionClosable startReloadAll = section(stage, "start-reload/"); + try (SectionClosable startReloadAll = section(context, "start-reload/"); PerformanceLogger.Plugin perfLogger = RoughlyEnoughItemsCore.PERFORMANCE_LOGGER.stage("Reload Initialization")) { for (Reloadable<P> reloadable : reloadables) { Class<?> reloadableClass = reloadable.getClass(); - try (SectionClosable startReload = section(stage, "start-reload/" + name(reloadableClass) + "/"); + try (SectionClosable startReload = section(context, "start-reload/" + name(reloadableClass) + "/"); PerformanceLogger.Plugin.Inner inner = perfLogger.stage(name(reloadableClass))) { - reloadable.startReload(stage); + reloadable.startReload(context.stage()); } catch (Throwable throwable) { - throwable.printStackTrace(); + if (throwable instanceof InterruptedException) throw (InterruptedException) throwable; + InternalLogger.getInstance().error("Failed to run start-reload task for reloadable [" + name(reloadableClass) + "] in stage [" + context.stage() + "]", throwable); } } } // Reload InternalLogger.getInstance().debug("========================================"); - InternalLogger.getInstance().debug(name(pluginClass) + " started main-reload for " + stage + "."); + InternalLogger.getInstance().debug(name(pluginClass) + " started main-reload for " + context.stage() + "."); InternalLogger.getInstance().debug("========================================"); for (Reloadable<P> reloadable : getReloadables()) { Class<?> reloadableClass = reloadable.getClass(); - try (SectionClosable reloadablePlugin = section(stage, "reloadable-plugin/" + name(reloadableClass) + "/"); + try (SectionClosable reloadablePlugin = section(context, "reloadable-plugin/" + name(reloadableClass) + "/"); PerformanceLogger.Plugin perfLogger = RoughlyEnoughItemsCore.PERFORMANCE_LOGGER.stage(name(reloadableClass))) { try (PerformanceLogger.Plugin.Inner inner = perfLogger.stage("reloadable-plugin/" + name(reloadableClass) + "/prompt-others-before")) { for (Reloadable<P> listener : reloadables) { try { - listener.beforeReloadable(stage, reloadable); + listener.beforeReloadable(context.stage(), reloadable); } catch (Throwable throwable) { - throwable.printStackTrace(); + InternalLogger.getInstance().error("Failed to prompt others before reloadable [" + name(reloadableClass) + "] in stage [" + context.stage() + "]", throwable); } } } - pluginSection(stage, "reloadable-plugin/" + name(reloadableClass) + "/", plugins, reloadable, (plugin, sink) -> { + pluginSection(context, "reloadable-plugin/" + name(reloadableClass) + "/", plugins, reloadable, (plugin, sink) -> { try (PerformanceLogger.Plugin.Inner inner = perfLogger.plugin(new Pair<>(plugin.provider, plugin.plugin))) { sink.accept(true, () -> { for (Reloadable<P> listener : reloadables) { try { - listener.beforeReloadablePlugin(stage, reloadable, plugin.plugin); + listener.beforeReloadablePlugin(context.stage(), reloadable, plugin.plugin); } catch (Throwable throwable) { - throwable.printStackTrace(); + InternalLogger.getInstance().error("Failed to run pre-reloadable task for " + plugin.getPluginProviderName() + " before reloadable [" + name(reloadableClass) + "] in stage [" + context.stage() + "]", throwable); } } try { - reloadable.acceptPlugin(plugin.plugin, stage); + reloadable.acceptPlugin(plugin.plugin, context.stage()); } finally { for (Reloadable<P> listener : reloadables) { try { - listener.afterReloadablePlugin(stage, reloadable, plugin.plugin); + listener.afterReloadablePlugin(context.stage(), reloadable, plugin.plugin); } catch (Throwable throwable) { - throwable.printStackTrace(); + InternalLogger.getInstance().error("Failed to run post-reloadable task for " + plugin.getPluginProviderName() + " after reloadable [" + name(reloadableClass) + "] in stage [" + context.stage() + "]", throwable); } } } @@ -431,9 +444,9 @@ public class PluginManagerImpl<P extends REIPlugin<?>> implements PluginManager< try (PerformanceLogger.Plugin.Inner inner = perfLogger.stage("reloadable-plugin/" + name(reloadableClass) + "/prompt-others-after")) { for (Reloadable<P> listener : reloadables) { try { - listener.afterReloadable(stage, reloadable); + listener.afterReloadable(context.stage(), reloadable); } catch (Throwable throwable) { - throwable.printStackTrace(); + InternalLogger.getInstance().error("Failed to prompt others after reloadable [" + name(reloadableClass) + "] in stage [" + context.stage() + "]", throwable); } } } @@ -442,28 +455,30 @@ public class PluginManagerImpl<P extends REIPlugin<?>> implements PluginManager< // Post Reload InternalLogger.getInstance().debug("========================================"); - InternalLogger.getInstance().debug(name(pluginClass) + " ending main-reload for " + stage + "."); + InternalLogger.getInstance().debug(name(pluginClass) + " ending main-reload for " + context.stage() + "."); InternalLogger.getInstance().debug("========================================"); - try (SectionClosable endReloadAll = section(stage, "end-reload/"); + try (SectionClosable endReloadAll = section(context, "end-reload/"); PerformanceLogger.Plugin perfLogger = RoughlyEnoughItemsCore.PERFORMANCE_LOGGER.stage("Reload Finalization")) { for (Reloadable<P> reloadable : reloadables) { Class<?> reloadableClass = reloadable.getClass(); - try (SectionClosable endReload = section(stage, "end-reload/" + name(reloadableClass) + "/"); + try (SectionClosable endReload = section(context, "end-reload/" + name(reloadableClass) + "/"); PerformanceLogger.Plugin.Inner inner = perfLogger.stage(name(reloadableClass))) { - reloadable.endReload(stage); + reloadable.endReload(context.stage()); } catch (Throwable throwable) { - throwable.printStackTrace(); + if (throwable instanceof InterruptedException) throw (InterruptedException) throwable; + InternalLogger.getInstance().error("Failed to run end-reload task for reloadable [" + name(reloadableClass) + "] in stage [" + context.stage() + "]", throwable); } } } this.reloadStopwatch.stop(); InternalLogger.getInstance().debug("========================================"); - InternalLogger.getInstance().debug(name(pluginClass) + " ended main-reload for " + stage + " in " + reloadingStopwatch.stop() + "."); + InternalLogger.getInstance().debug(name(pluginClass) + " ended main-reload for " + context.stage() + " in " + reloadingStopwatch.stop() + "."); InternalLogger.getInstance().debug("========================================"); } catch (Throwable throwable) { - throwable.printStackTrace(); + if (throwable instanceof InterruptedException) throw (InterruptedException) throwable; + InternalLogger.getInstance().error("Failed to run reload task in stage [" + context0.stage() + "]", throwable); } finally { reloading = null; } diff --git a/runtime/src/main/java/me/shedaniel/rei/impl/common/plugins/ReloadManagerImpl.java b/runtime/src/main/java/me/shedaniel/rei/impl/common/plugins/ReloadManagerImpl.java new file mode 100644 index 000000000..a9590131f --- /dev/null +++ b/runtime/src/main/java/me/shedaniel/rei/impl/common/plugins/ReloadManagerImpl.java @@ -0,0 +1,211 @@ +/* + * This file is licensed under the MIT License, part of Roughly Enough Items. + * Copyright (c) 2018, 2019, 2020, 2021, 2022, 2023 shedaniel + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package me.shedaniel.rei.impl.common.plugins; + +import com.google.common.base.Stopwatch; +import com.google.common.base.Suppliers; +import dev.architectury.platform.Platform; +import dev.architectury.utils.Env; +import me.shedaniel.rei.RoughlyEnoughItemsCore; +import me.shedaniel.rei.api.client.config.ConfigObject; +import me.shedaniel.rei.api.common.plugins.PluginManager; +import me.shedaniel.rei.api.common.plugins.REIPlugin; +import me.shedaniel.rei.api.common.registry.ReloadStage; +import me.shedaniel.rei.api.common.util.CollectionUtils; +import me.shedaniel.rei.impl.common.InternalLogger; +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.Nullable; + +import java.util.List; +import java.util.concurrent.*; +import java.util.function.Supplier; + +@ApiStatus.Internal +public class ReloadManagerImpl { + private static final Supplier<Executor> RELOAD_PLUGINS = Suppliers.memoize(() -> Executors.newSingleThreadScheduledExecutor(task -> { + Thread thread = new Thread(task, "REI-ReloadPlugins"); + thread.setDaemon(true); + thread.setUncaughtExceptionHandler(($, exception) -> { + if (exception instanceof InterruptedException) { + InternalLogger.getInstance().debug("Interrupted while reloading plugins, could be caused by a new request to reload plugins!", new UncaughtException(exception)); + return; + } + + InternalLogger.getInstance().throwException(new UncaughtException(exception)); + }); + return thread; + })); + + private static final List<Task> RELOAD_TASKS = new CopyOnWriteArrayList<>(); + + private static class Task { + private final Future<?> future; + private boolean interrupted = false; + private boolean completed = false; + + public Task(Future<?> future) { + this.future = future; + } + } + + private static Executor executor() { + if (usesREIThread()) { + return RELOAD_PLUGINS.get(); + } else { + return runnable -> { + try { + runnable.run(); + } catch (Throwable throwable) { + InternalLogger.getInstance().throwException(throwable); + } + }; + } + } + + private static boolean usesREIThread() { + if (Platform.getEnvironment() == Env.CLIENT) { + return usesREIThreadClient(); + } else { + return false; + } + } + + @Environment(EnvType.CLIENT) + private static boolean usesREIThreadClient() { + return ConfigObject.getInstance().doesRegisterRecipesInAnotherThread(); + } + + public static int countRunningReloadTasks() { + return CollectionUtils.sumInt(RELOAD_TASKS, task -> !task.future.isDone() || !task.completed ? 1 : 0); + } + + public static int countUninterruptedRunningReloadTasks() { + return CollectionUtils.sumInt(RELOAD_TASKS, task -> !task.interrupted && (!task.future.isDone() || !task.completed) ? 1 : 0); + } + + public static void reloadPlugins(@Nullable ReloadStage start, ReloadInterruptionContext interruptionContext) { + InternalLogger.getInstance().debug("Starting Reload Plugins of stage " + start, new Throwable()); + if (usesREIThread()) { + if ((start == ReloadStage.START || start == null) && countRunningReloadTasks() > 0) { + InternalLogger.getInstance().warn("Trying to start reload plugins of stage %s but found %d existing reload task(s)!", start, countRunningReloadTasks()); + terminateReloadTasks(); + } + + if (!RELOAD_TASKS.isEmpty()) { + InternalLogger.getInstance().warn("Found %d existing reload task(s) after trying to terminate them!", RELOAD_TASKS.size()); + } + + Task[] task = new Task[1]; + Future<?> future = CompletableFuture.runAsync(() -> reloadPlugins0(start, () -> interruptionContext.isInterrupted() || (task[0] != null && task[0].interrupted)), executor()) + .whenComplete((unused, throwable) -> { + // Remove the future from the list of futures + if (task[0] != null) { + task[0].completed = true; + RELOAD_TASKS.remove(task[0]); + task[0] = null; + } + }); + task[0] = new Task(future); + RELOAD_TASKS.add(task[0]); + } else { + reloadPlugins0(start, interruptionContext); + } + } + + private static void reloadPlugins0(@Nullable ReloadStage stage, ReloadInterruptionContext interruptionContext) { + if (stage == null) { + for (ReloadStage reloadStage : ReloadStage.values()) { + reloadPlugins0(reloadStage, interruptionContext); + } + } else { + reloadPlugins0(PluginReloadContext.of(stage, interruptionContext)); + } + } + + private static void reloadPlugins0(PluginReloadContext context) { + if (context.stage() == ReloadStage.START) RoughlyEnoughItemsCore.PERFORMANCE_LOGGER.clear(); + try { + for (PluginManager<? extends REIPlugin<?>> instance : PluginManager.getActiveInstances()) { + instance.view().pre(context); + } + for (PluginManager<? extends REIPlugin<?>> instance : PluginManager.getActiveInstances()) { + instance.view().reload(context); + } + for (PluginManager<? extends REIPlugin<?>> instance : PluginManager.getActiveInstances()) { + instance.view().post(context); + } + } catch (InterruptedException e) { + InternalLogger.getInstance().debug("Interrupted while reloading plugins, could be caused by a new request to reload plugins!", e); + } catch (Throwable throwable) { + InternalLogger.getInstance().throwException(throwable); + } + } + + public static void terminateReloadTasks() { + if (countUninterruptedRunningReloadTasks() == 0) { + InternalLogger.getInstance().debug("Did not fulfill the request of termination of REI reload tasks because there are no uninterrupted running tasks. This is not an error."); + RELOAD_TASKS.clear(); + return; + } + + InternalLogger.getInstance().debug("Requested the termination of REI reload tasks."); + + for (Task task : RELOAD_TASKS) { + task.interrupted = true; + } + + long startTerminateTime = System.currentTimeMillis(); + Stopwatch stopwatch = Stopwatch.createStarted(); + while (countRunningReloadTasks() > 0) { + try { + Thread.sleep(50); + } catch (InterruptedException e) { + InternalLogger.getInstance().error("Thread interrupted while waiting for reload tasks to terminate!", e); + } + + if (System.currentTimeMillis() - startTerminateTime > 5000) { + InternalLogger.getInstance().error("Took too long to terminate reload tasks (over 5 seconds)! Now forceful |
