Process the main modloading work on the async thread, but still

do deferred work on the main thread by passing in the executor.

Signed-off-by: cpw <cpw+github@weeksfamily.ca>
This commit is contained in:
cpw 2019-06-23 12:26:25 -04:00
parent 359be1a880
commit 9067dbf6a0
No known key found for this signature in database
GPG Key ID: 8EB3DF749553B1B7
6 changed files with 54 additions and 42 deletions

View File

@ -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<List<ModLoadingException>> errorHandler) {
static void runTasks(ModLoadingStage fromStage, Consumer<List<ModLoadingException>> 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<Void> 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);
}
}

View File

@ -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<? extends LifecycleEvent> event;
private final BiConsumer<LifecycleEvent, Consumer<List<ModLoadingException>>> eventDispatcher;
private final EventHandler<LifecycleEvent, Consumer<List<ModLoadingException>>,Executor> eventDispatcher;
private Supplier<Event> customEventSupplier;
private LifecycleEvent.Progression progression = LifecycleEvent.Progression.NEXT;
@ -50,7 +50,7 @@ public enum LifecycleEventProvider
this(e, ModList.parallelDispatcher);
}
LifecycleEventProvider(Supplier<? extends LifecycleEvent> e, BiConsumer<LifecycleEvent, Consumer<List<ModLoadingException>>> eventDispatcher)
LifecycleEventProvider(Supplier<? extends LifecycleEvent> e, EventHandler<LifecycleEvent, Consumer<List<ModLoadingException>>,Executor> eventDispatcher)
{
this.event = e;
this.eventDispatcher = eventDispatcher;
@ -64,11 +64,11 @@ public enum LifecycleEventProvider
this.progression = progression;
}
public void dispatch(Consumer<List<ModLoadingException>> errorHandler) {
public void dispatch(Consumer<List<ModLoadingException>> 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<T extends LifecycleEvent, U extends Consumer<? extends List<? super ModLoadingException>>, V extends Executor> {
void dispatchEvent(T event, U exceptionHandler, V executor);
}
}

View File

@ -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<LifecycleEventProvider.LifecycleEvent, Consumer<List<ModLoadingException>>> inlineDispatcher = (event, errors) -> ModList.get().dispatchSynchronousEvent(event, errors);
static LifecycleEventProvider.EventHandler<LifecycleEventProvider.LifecycleEvent, Consumer<List<ModLoadingException>>, Executor> inlineDispatcher = (event, errors, executor) -> ModList.get().dispatchSynchronousEvent(event, errors, executor);
static BiConsumer<LifecycleEventProvider.LifecycleEvent, Consumer<List<ModLoadingException>>> parallelDispatcher = (event, errors) -> ModList.get().dispatchParallelEvent(event, errors);
static LifecycleEventProvider.EventHandler<LifecycleEventProvider.LifecycleEvent, Consumer<List<ModLoadingException>>, 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<List<ModLoadingException>> errorHandler) {
private void dispatchSynchronousEvent(LifecycleEventProvider.LifecycleEvent lifecycleEvent, final Consumer<List<ModLoadingException>> 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<List<ModLoadingException>> errorHandler) {
private void dispatchParallelEvent(LifecycleEventProvider.LifecycleEvent lifecycleEvent, final Consumer<List<ModLoadingException>> 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));
}

View File

@ -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();
}

View File

@ -67,9 +67,9 @@ public class ClientModLoader
}
private static CompletableFuture<Void> 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()

View File

@ -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<ModLoadingWarning> warnings = ModLoader.get().getWarnings();
if (!warnings.isEmpty()) {
LOGGER.warn(LOADING, "Mods loaded with {} warnings", warnings.size());