diff --git a/src/main/java/net/minecraftforge/fml/DeferredWorkQueue.java b/src/main/java/net/minecraftforge/fml/DeferredWorkQueue.java index 705f7c1d2e..ad3f561b95 100644 --- a/src/main/java/net/minecraftforge/fml/DeferredWorkQueue.java +++ b/src/main/java/net/minecraftforge/fml/DeferredWorkQueue.java @@ -31,6 +31,7 @@ import java.util.concurrent.Executor; import java.util.concurrent.TimeUnit; import java.util.function.Consumer; import java.util.function.Supplier; +import java.util.stream.Collectors; import org.apache.commons.lang3.time.StopWatch; import org.apache.logging.log4j.LogManager; @@ -187,21 +188,25 @@ public class DeferredWorkQueue taskQueue.clear(); } - static void runTasks(ModLoadingStage fromStage, Consumer> errorHandler) { + static void runTasks(ModLoadingStage fromStage, Consumer> errorHandler, final Executor executor) { raisedExceptions.clear(); if (taskQueue.isEmpty()) return; // Don't log unnecessarily LOGGER.info(LOADING, "Dispatching synchronous work after {}: {} jobs", fromStage, taskQueue.size()); StopWatch globalTimer = StopWatch.createStarted(); - while (!taskQueue.isEmpty()) { - TaskInfo taskinfo = taskQueue.poll(); - Stopwatch timer = Stopwatch.createStarted(); - taskinfo.task.run(); - timer.stop(); - if (timer.elapsed(TimeUnit.SECONDS) >= 1) { - LOGGER.warn(LOADING, "Mod '{}' took {} to run a deferred task.", taskinfo.owner.getModId(), timer); - } - } + final CompletableFuture tasks = CompletableFuture.allOf(taskQueue.stream().map(ti -> makeRunnable(ti, executor)).toArray(CompletableFuture[]::new)); + tasks.join(); LOGGER.info(LOADING, "Synchronous work queue completed in {}", globalTimer); errorHandler.accept(raisedExceptions); } + + private static CompletableFuture makeRunnable(TaskInfo ti, Executor executor) { + return CompletableFuture.runAsync(() -> { + Stopwatch timer = Stopwatch.createStarted(); + ti.task.run(); + timer.stop(); + if (timer.elapsed(TimeUnit.SECONDS) >= 1) { + LOGGER.warn(LOADING, "Mod '{}' took {} to run a deferred task.", ti.owner.getModId(), timer); + } + }, executor); + } } diff --git a/src/main/java/net/minecraftforge/fml/LifecycleEventProvider.java b/src/main/java/net/minecraftforge/fml/LifecycleEventProvider.java index 3ad7acd5f7..9798c00aad 100644 --- a/src/main/java/net/minecraftforge/fml/LifecycleEventProvider.java +++ b/src/main/java/net/minecraftforge/fml/LifecycleEventProvider.java @@ -23,7 +23,7 @@ import net.minecraftforge.eventbus.api.Event; import net.minecraftforge.forgespi.language.ILifecycleEvent; import java.util.List; -import java.util.function.BiConsumer; +import java.util.concurrent.Executor; import java.util.function.Consumer; import java.util.function.Function; import java.util.function.Supplier; @@ -41,7 +41,7 @@ public enum LifecycleEventProvider GATHERDATA(()->new GatherDataLifecycleEvent(ModLoadingStage.GATHERDATA), ModList.inlineDispatcher); private final Supplier event; - private final BiConsumer>> eventDispatcher; + private final EventHandler>,Executor> eventDispatcher; private Supplier customEventSupplier; private LifecycleEvent.Progression progression = LifecycleEvent.Progression.NEXT; @@ -50,7 +50,7 @@ public enum LifecycleEventProvider this(e, ModList.parallelDispatcher); } - LifecycleEventProvider(Supplier e, BiConsumer>> eventDispatcher) + LifecycleEventProvider(Supplier e, EventHandler>,Executor> eventDispatcher) { this.event = e; this.eventDispatcher = eventDispatcher; @@ -64,11 +64,11 @@ public enum LifecycleEventProvider this.progression = progression; } - public void dispatch(Consumer> errorHandler) { + public void dispatch(Consumer> errorHandler, final Executor executor) { final LifecycleEvent lifecycleEvent = this.event.get(); lifecycleEvent.setCustomEventSupplier(this.customEventSupplier); lifecycleEvent.changeProgression(this.progression); - this.eventDispatcher.accept(lifecycleEvent, errorHandler); + this.eventDispatcher.dispatchEvent(lifecycleEvent, errorHandler, executor); } @@ -147,4 +147,8 @@ public enum LifecycleEventProvider return ModLoadingStage.DONE; } } + + public interface EventHandler>, V extends Executor> { + void dispatchEvent(T event, U exceptionHandler, V executor); + } } diff --git a/src/main/java/net/minecraftforge/fml/ModList.java b/src/main/java/net/minecraftforge/fml/ModList.java index 03a04e4712..8e5f44809a 100644 --- a/src/main/java/net/minecraftforge/fml/ModList.java +++ b/src/main/java/net/minecraftforge/fml/ModList.java @@ -34,6 +34,7 @@ import java.util.Map; import java.util.Objects; import java.util.Optional; import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executor; import java.util.concurrent.ForkJoinPool; import java.util.concurrent.ForkJoinWorkerThread; import java.util.function.BiConsumer; @@ -80,9 +81,9 @@ public class ModList return INSTANCE; } - static BiConsumer>> inlineDispatcher = (event, errors) -> ModList.get().dispatchSynchronousEvent(event, errors); + static LifecycleEventProvider.EventHandler>, Executor> inlineDispatcher = (event, errors, executor) -> ModList.get().dispatchSynchronousEvent(event, errors, executor); - static BiConsumer>> parallelDispatcher = (event, errors) -> ModList.get().dispatchParallelEvent(event, errors); + static LifecycleEventProvider.EventHandler>, Executor> parallelDispatcher = (event, errors, executor) -> ModList.get().dispatchParallelEvent(event, errors, executor); public static ModList get() { return INSTANCE; @@ -106,13 +107,13 @@ public class ModList return this.fileById.get(modid); } - private void dispatchSynchronousEvent(LifecycleEventProvider.LifecycleEvent lifecycleEvent, final Consumer> errorHandler) { + private void dispatchSynchronousEvent(LifecycleEventProvider.LifecycleEvent lifecycleEvent, final Consumer> errorHandler, final Executor executor) { LOGGER.debug(LOADING, "Dispatching synchronous event {}", lifecycleEvent); FMLLoader.getLanguageLoadingProvider().forEach(lp->lp.consumeLifecycleEvent(()->lifecycleEvent)); this.mods.forEach(m->m.transitionState(lifecycleEvent, errorHandler)); FMLLoader.getLanguageLoadingProvider().forEach(lp->lp.consumeLifecycleEvent(()->lifecycleEvent)); } - private void dispatchParallelEvent(LifecycleEventProvider.LifecycleEvent lifecycleEvent, final Consumer> errorHandler) { + private void dispatchParallelEvent(LifecycleEventProvider.LifecycleEvent lifecycleEvent, final Consumer> errorHandler, final Executor executor) { LOGGER.debug(LOADING, "Dispatching parallel event {}", lifecycleEvent); FMLLoader.getLanguageLoadingProvider().forEach(lp->lp.consumeLifecycleEvent(()->lifecycleEvent)); DeferredWorkQueue.clear(); @@ -124,7 +125,7 @@ public class ModList { LOGGER.error(LOADING, "Encountered an exception during parallel processing", e); } - DeferredWorkQueue.runTasks(lifecycleEvent.fromStage(), errorHandler); + DeferredWorkQueue.runTasks(lifecycleEvent.fromStage(), errorHandler, executor); FMLLoader.getLanguageLoadingProvider().forEach(lp->lp.consumeLifecycleEvent(()->lifecycleEvent)); } diff --git a/src/main/java/net/minecraftforge/fml/ModLoader.java b/src/main/java/net/minecraftforge/fml/ModLoader.java index 7dab12a313..c56073d58b 100644 --- a/src/main/java/net/minecraftforge/fml/ModLoader.java +++ b/src/main/java/net/minecraftforge/fml/ModLoader.java @@ -48,6 +48,7 @@ import org.apache.logging.log4j.Logger; import java.nio.file.Path; import java.util.*; +import java.util.concurrent.Executor; import java.util.function.Consumer; import java.util.function.Function; import java.util.stream.Collectors; @@ -126,15 +127,15 @@ public class ModLoader return ()->postEvent(new ModelRegistryEvent()); } - public void loadMods() { + public void loadMods(Executor mainThreadExecutor) { statusConsumer.ifPresent(c->c.accept("Loading mod config")); DistExecutor.runWhenOn(Dist.CLIENT, ()->()-> ConfigTracker.INSTANCE.loadConfigs(ModConfig.Type.CLIENT, FMLPaths.CONFIGDIR.get())); ConfigTracker.INSTANCE.loadConfigs(ModConfig.Type.COMMON, FMLPaths.CONFIGDIR.get()); statusConsumer.ifPresent(c->c.accept("Mod setup: SETUP")); - dispatchAndHandleError(LifecycleEventProvider.SETUP); + dispatchAndHandleError(LifecycleEventProvider.SETUP, mainThreadExecutor); statusConsumer.ifPresent(c->c.accept("Mod setup: SIDED SETUP")); DistExecutor.runWhenOn(Dist.CLIENT, this::fireClientEvents); - dispatchAndHandleError(LifecycleEventProvider.SIDED_SETUP); + dispatchAndHandleError(LifecycleEventProvider.SIDED_SETUP, mainThreadExecutor); statusConsumer.ifPresent(c->c.accept("Mod setup complete")); } @@ -156,21 +157,21 @@ public class ModLoader } modList.setLoadedMods(modContainers); statusConsumer.ifPresent(c->c.accept("Constructing mods")); - dispatchAndHandleError(LifecycleEventProvider.CONSTRUCT); + dispatchAndHandleError(LifecycleEventProvider.CONSTRUCT, Runnable::run); statusConsumer.ifPresent(c->c.accept("Creating registries")); - GameData.fireCreateRegistryEvents(LifecycleEventProvider.CREATE_REGISTRIES, this::dispatchAndHandleError); + GameData.fireCreateRegistryEvents(LifecycleEventProvider.CREATE_REGISTRIES, event -> dispatchAndHandleError(event, Runnable::run)); ObjectHolderRegistry.findObjectHolders(); CapabilityManager.INSTANCE.injectCapabilities(modList.getAllScanData()); statusConsumer.ifPresent(c->c.accept("Populating registries")); - GameData.fireRegistryEvents(rl->true, LifecycleEventProvider.LOAD_REGISTRIES, this::dispatchAndHandleError); + GameData.fireRegistryEvents(rl->true, LifecycleEventProvider.LOAD_REGISTRIES, event -> dispatchAndHandleError(event, Runnable::run)); statusConsumer.ifPresent(c->c.accept("Early mod loading complete")); } - private void dispatchAndHandleError(LifecycleEventProvider event) { + private void dispatchAndHandleError(LifecycleEventProvider event, Executor executor) { if (!loadingExceptions.isEmpty()) { LOGGER.error(LOADING,"Skipping lifecycle event {}, {} errors found.", event, loadingExceptions.size()); } else { - event.dispatch(this::accumulateErrors); + event.dispatch(this::accumulateErrors, executor); } if (!loadingExceptions.isEmpty()) { LOGGER.fatal(LOADING,"Failed to complete lifecycle event {}, {} errors found", event, loadingExceptions.size()); @@ -217,14 +218,14 @@ public class ModLoader ModList.get().forEachModContainer((id, mc) -> mc.acceptEvent(e)); } - public void finishMods() + public void finishMods(Executor mainThreadExecutor) { statusConsumer.ifPresent(c->c.accept("Mod setup: ENQUEUE IMC")); - dispatchAndHandleError(LifecycleEventProvider.ENQUEUE_IMC); + dispatchAndHandleError(LifecycleEventProvider.ENQUEUE_IMC, mainThreadExecutor); statusConsumer.ifPresent(c->c.accept("Mod setup: PROCESS IMC")); - dispatchAndHandleError(LifecycleEventProvider.PROCESS_IMC); + dispatchAndHandleError(LifecycleEventProvider.PROCESS_IMC, mainThreadExecutor); statusConsumer.ifPresent(c->c.accept("Mod setup: Final completion")); - dispatchAndHandleError(LifecycleEventProvider.COMPLETE); + dispatchAndHandleError(LifecycleEventProvider.COMPLETE, mainThreadExecutor); statusConsumer.ifPresent(c->c.accept("Freezing data")); GameData.freezeData(); NetworkRegistry.lock(); @@ -247,7 +248,7 @@ public class ModLoader Bootstrap.register(); dataGeneratorConfig = new GatherDataEvent.DataGeneratorConfig(mods, path, inputs, serverGenerators, clientGenerators, devToolGenerators, reportsGenerator, structureValidator); gatherAndInitializeMods(); - dispatchAndHandleError(LifecycleEventProvider.GATHERDATA); + dispatchAndHandleError(LifecycleEventProvider.GATHERDATA, Runnable::run); dataGeneratorConfig.runAll(); } diff --git a/src/main/java/net/minecraftforge/fml/client/ClientModLoader.java b/src/main/java/net/minecraftforge/fml/client/ClientModLoader.java index 545d56d714..8c08bf73bf 100644 --- a/src/main/java/net/minecraftforge/fml/client/ClientModLoader.java +++ b/src/main/java/net/minecraftforge/fml/client/ClientModLoader.java @@ -67,9 +67,9 @@ public class ClientModLoader } private static CompletableFuture onreload(final IFutureReloadListener.IStage stage, final IResourceManager resourceManager, final IProfiler prepareProfiler, final IProfiler executeProfiler, final Executor asyncExecutor, final Executor syncExecutor) { - return CompletableFuture.runAsync(createRunnableWithCatch(ClientModLoader::startModLoading), syncExecutor). + return CompletableFuture.runAsync(createRunnableWithCatch(() -> startModLoading(syncExecutor)), asyncExecutor). thenCompose(stage::markCompleteAwaitingOthers). - thenRunAsync(ClientModLoader::finishModLoading, syncExecutor); + thenRunAsync(() -> finishModLoading(syncExecutor), asyncExecutor); } private static Runnable createRunnableWithCatch(Runnable r) { @@ -83,15 +83,16 @@ public class ClientModLoader }; } - private static void startModLoading() { + private static void startModLoading(Executor executor) { earlyLoaderGUI.handleElsewhere(); - createRunnableWithCatch(ModLoader.get()::loadMods).run(); + createRunnableWithCatch(() -> ModLoader.get().loadMods(executor)).run(); } - private static void finishModLoading() + private static void finishModLoading(Executor executor) { - createRunnableWithCatch(ModLoader.get()::finishMods).run(); + createRunnableWithCatch(() -> ModLoader.get().finishMods(executor)).run(); loading = false; - mc.gameSettings.loadOptions(); + // reload game settings on main thread + executor.execute(()->mc.gameSettings.loadOptions()); } public static VersionChecker.Status checkForUpdates() diff --git a/src/main/java/net/minecraftforge/fml/server/ServerModLoader.java b/src/main/java/net/minecraftforge/fml/server/ServerModLoader.java index dc58b2973b..25cc0004d2 100644 --- a/src/main/java/net/minecraftforge/fml/server/ServerModLoader.java +++ b/src/main/java/net/minecraftforge/fml/server/ServerModLoader.java @@ -43,11 +43,11 @@ public class ServerModLoader LogicalSidedProvider.setServer(()->dedicatedServer); LanguageHook.loadForgeAndMCLangs(); ModLoader.get().gatherAndInitializeMods(); - ModLoader.get().loadMods(); + ModLoader.get().loadMods(Runnable::run); } public static void end() { - ModLoader.get().finishMods(); + ModLoader.get().finishMods(Runnable::run); List warnings = ModLoader.get().getWarnings(); if (!warnings.isEmpty()) { LOGGER.warn(LOADING, "Mods loaded with {} warnings", warnings.size());