Move the world gen thread pool into the WorldGenQueue

We want Core to handle the world gen threads, not the individual world generators
This commit is contained in:
James Seibel
2023-06-05 19:50:32 -05:00
parent bb4497dd80
commit 005f1ed8a0
3 changed files with 43 additions and 79 deletions
@@ -41,7 +41,6 @@ import java.io.IOException;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Consumer;
@@ -64,18 +63,14 @@ import net.minecraft.world.level.chunk.ChunkStatus;
import net.minecraft.world.level.chunk.LevelChunk;
import net.minecraft.world.level.chunk.ProtoChunk;
import net.minecraft.world.level.chunk.UpgradeData;
import net.minecraft.world.level.chunk.storage.RegionFile;
import net.minecraft.world.level.chunk.storage.RegionFileStorage;
import net.minecraft.world.level.levelgen.DebugLevelSource;
import net.minecraft.world.level.levelgen.FlatLevelSource;
import net.minecraft.world.level.levelgen.NoiseBasedChunkGenerator;
import net.minecraft.core.Registry;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.world.level.lighting.LevelLightEngine;
import org.apache.logging.log4j.LogManager;
import javax.annotation.Nullable;
/*
Total: 3.135214124s
=====================================
@@ -169,39 +164,32 @@ public final class BatchGenerationEnvironment extends AbstractBatchGenerationEnv
public final StepFeatures stepFeatures = new StepFeatures(this);
public final StepLight stepLight = new StepLight(this);
public boolean unsafeThreadingRecorded = false;
//public boolean safeMode = false;
//private static final IMinecraftClientWrapper MC = SingletonHandler.get(IMinecraftClientWrapper.class);
public static final long EXCEPTION_TIMER_RESET_TIME = TimeUnit.NANOSECONDS.convert(1, TimeUnit.SECONDS);
public static final int EXCEPTION_COUNTER_TRIGGER = 20;
public static final int RANGE_TO_RANGE_EMPTY_EXTENSION = 1;
public int unknownExceptionCount = 0;
public long lastExceptionTriggerTime = 0;
private AtomicReference<RegionFileStorageExternalCache> regionFileStorageCache = new AtomicReference<>();
private AtomicReference<RegionFileStorageExternalCache> regionFileStorageCacheRef = new AtomicReference<>();
public RegionFileStorageExternalCache getOrCreateRegionFileCache(RegionFileStorage storage)
{
RegionFileStorageExternalCache cache = regionFileStorageCache.get();
RegionFileStorageExternalCache cache = regionFileStorageCacheRef.get();
if (cache == null)
{
cache = new RegionFileStorageExternalCache(storage);
if (!regionFileStorageCache.compareAndSet(null, cache))
if (!regionFileStorageCacheRef.compareAndSet(null, cache))
{
cache = regionFileStorageCache.get();
cache = regionFileStorageCacheRef.get();
}
}
return cache;
}
public static final LodThreadFactory threadFactory = new LodThreadFactory("DH-Gen-Worker-Thread", Thread.MIN_PRIORITY);
public static ThreadLocal<Boolean> isDistantGeneratorThread = new ThreadLocal<>();
public static boolean isCurrentThreadDistantGeneratorThread() { return (isDistantGeneratorThread.get() != null); }
public ExecutorService executorService = Executors.newFixedThreadPool(
Math.max(Config.Client.Advanced.Threading.numberOfWorldGenerationThreads.get().intValue(), 1),
threadFactory);
public static final LodThreadFactory threadFactory = new LodThreadFactory("DH-Gen-Worker-Thread", Thread.MIN_PRIORITY);
@@ -227,16 +215,16 @@ public final class BatchGenerationEnvironment extends AbstractBatchGenerationEnv
if (generator.getClass().toString().equals("class com.terraforged.mod.chunk.TFChunkGenerator"))
{
EVENT_LOGGER.info("TerraForge Chunk Generator detected: ["+generator.getClass()+"], Distant Generation will try its best to support it.");
EVENT_LOGGER.info("If it does crash, turn Distant Generation off or set it to to "+EDhApiWorldGenerationStep.EMPTY+".");
EVENT_LOGGER.info("If it does crash, turn Distant Generation off or set it to to ["+EDhApiWorldGenerationStep.EMPTY+"].");
}
else
{
EVENT_LOGGER.warn("Unknown Chunk Generator detected: ["+generator.getClass()+"], Distant Generation May Fail!");
EVENT_LOGGER.warn("If it does crash, set Distant Generation to OFF or Generation Mode to None.");
EVENT_LOGGER.warn("If it does crash, disable Distant Generation or set the Generation Mode to ["+EDhApiWorldGenerationStep.EMPTY+"].");
}
}
params = new GlobalParameters(serverlevel);
this.params = new GlobalParameters(serverlevel);
}
@@ -254,8 +242,6 @@ public final class BatchGenerationEnvironment extends AbstractBatchGenerationEnv
return future.join();
}
public void resizeThreadPool(int newThreadCount) { executorService = Executors.newFixedThreadPool(newThreadCount, new LodThreadFactory("DH-Gen-Worker-Thread", Thread.MIN_PRIORITY)); }
public void updateAllFutures()
{
if (unknownExceptionCount > 0)
@@ -599,12 +585,11 @@ public final class BatchGenerationEnvironment extends AbstractBatchGenerationEnv
public int getEventCount() { return this.generationEventList.size(); }
@Override
public void stop(boolean blocking)
public void stop()
{
EVENT_LOGGER.info(BatchGenerationEnvironment.class.getSimpleName()+" shutting down...");
EVENT_LOGGER.info("Canceling futures...");
this.executorService.shutdownNow();
EVENT_LOGGER.info("Canceling in progress generation event futures...");
Iterator<GenerationEvent> iter = this.generationEventList.iterator();
while (iter.hasNext())
{
@@ -613,52 +598,40 @@ public final class BatchGenerationEnvironment extends AbstractBatchGenerationEnv
iter.remove();
}
EVENT_LOGGER.info("Awaiting termination...");
if (blocking)
// clear the chunk cache
var regionStorage = this.regionFileStorageCacheRef.get();
if (regionStorage != null)
{
try
{
int waitTimeInSeconds = 3;
if (!this.executorService.awaitTermination(waitTimeInSeconds, TimeUnit.SECONDS))
{
EVENT_LOGGER.error("Batch Chunk Generator shutdown didn't complete after ["+waitTimeInSeconds+"] seconds! Ignoring child threads...");
}
}
catch (InterruptedException e)
{
EVENT_LOGGER.error("Batch Chunk Generator shutdown interrupted! Ignoring child threads...", e);
}
}
var r = regionFileStorageCache.get();
if (r != null)
{
try
{
r.close();
regionStorage.close();
}
catch (IOException e)
{
EVENT_LOGGER.error("Failed to close region file storage cache!", e);
}
}
EVENT_LOGGER.info(BatchGenerationEnvironment.class.getSimpleName()+" shutdown complete.");
}
@Override
public CompletableFuture<Void> generateChunks(int minX, int minZ, int genSize, EDhApiWorldGenerationStep targetStep, double runTimeRatio, Consumer<IChunkWrapper> resultConsumer)
public CompletableFuture<Void> generateChunks(
int minX, int minZ, int genSize, EDhApiWorldGenerationStep targetStep,
ExecutorService worldGeneratorThreadPool, Consumer<IChunkWrapper> resultConsumer)
{
//System.out.println("GenerationEvent: "+genSize+"@"+minX+","+minZ+" "+targetStep);
// TODO: Check event overlap via e.tooClose()
GenerationEvent genEvent = GenerationEvent.startEvent(new DhChunkPos(minX, minZ), genSize, this, targetStep, runTimeRatio, resultConsumer);
generationEventList.add(genEvent);
GenerationEvent genEvent = GenerationEvent.startEvent(new DhChunkPos(minX, minZ), genSize, this, targetStep, resultConsumer, worldGeneratorThreadPool);
this.generationEventList.add(genEvent);
return genEvent.future;
}
/**
* Called before code that may run for an extended period of time. <br>
* This is necessary to allow canceling world gen since waiting
* for them to cancel when leaving a world can take a while.
* for some world gen requests to finish can take a while.
*/
private static void throwIfThreadInterrupted() throws InterruptedException
{
@@ -21,6 +21,7 @@ package com.seibel.lod.common.wrappers.worldGeneration;
import java.lang.invoke.MethodHandles;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
@@ -40,23 +41,22 @@ public final class GenerationEvent
private static final Logger LOGGER = DhLoggerBuilder.getLogger(MethodHandles.lookup().lookupClass().getSimpleName());
private static int generationFutureDebugIDs = 0;
final int id;
final ThreadedParameters threadedParam;
final DhChunkPos minPos;
final int size;
final EDhApiWorldGenerationStep targetGenerationStep;
final ELightGenerationMode lightMode;
final double runTimeRatio;
EventTimer timer = null;
long inQueueTime;
long timeoutTime = -1;
public final int id;
public final ThreadedParameters threadedParam;
public final DhChunkPos minPos;
public final int size;
public final EDhApiWorldGenerationStep targetGenerationStep;
public final ELightGenerationMode lightMode;
public EventTimer timer = null;
public long inQueueTime;
public long timeoutTime = -1;
public CompletableFuture<Void> future = null;
final Consumer<IChunkWrapper> resultConsumer;
public final Consumer<IChunkWrapper> resultConsumer;
public GenerationEvent(DhChunkPos minPos, int size, BatchGenerationEnvironment generationGroup,
EDhApiWorldGenerationStep targetGenerationStep, double runTimeRatio, Consumer<IChunkWrapper> resultConsumer)
EDhApiWorldGenerationStep targetGenerationStep, Consumer<IChunkWrapper> resultConsumer)
{
this.inQueueTime = System.nanoTime();
this.id = generationFutureDebugIDs++;
@@ -65,51 +65,42 @@ public final class GenerationEvent
this.targetGenerationStep = targetGenerationStep;
this.threadedParam = ThreadedParameters.getOrMake(generationGroup.params);
this.lightMode = Config.Client.WorldGenerator.lightGenerationMode.get();
this.runTimeRatio = runTimeRatio;
this.resultConsumer = resultConsumer;
}
public static GenerationEvent startEvent(DhChunkPos minPos, int size, BatchGenerationEnvironment generationGroup,
EDhApiWorldGenerationStep target, double runTimeRatio, Consumer<IChunkWrapper> resultConsumer)
public static GenerationEvent startEvent(
DhChunkPos minPos, int size, BatchGenerationEnvironment genEnvironment,
EDhApiWorldGenerationStep target, Consumer<IChunkWrapper> resultConsumer,
ExecutorService worldGeneratorThreadPool)
{
if (size % 2 == 0)
{
size += 1; // size must be odd for vanilla world gen regions to work
}
GenerationEvent generationEvent = new GenerationEvent(minPos, size, generationGroup, target, runTimeRatio, resultConsumer);
GenerationEvent generationEvent = new GenerationEvent(minPos, size, genEnvironment, target, resultConsumer);
generationEvent.future = CompletableFuture.runAsync(() ->
{
long runStartTime = System.nanoTime();
generationEvent.timeoutTime = runStartTime;
generationEvent.inQueueTime = runStartTime - generationEvent.inQueueTime;
generationEvent.timer = new EventTimer("setup");
BatchGenerationEnvironment.isDistantGeneratorThread.set(true);
try
{
//LOGGER.info("generating [{}]", event.minPos);
generationGroup.generateLodFromList(generationEvent);
genEnvironment.generateLodFromList(generationEvent);
}
catch (InterruptedException ignored) { }
finally
{
BatchGenerationEnvironment.isDistantGeneratorThread.remove();
if (!Thread.interrupted() && runTimeRatio < 1.0)
{
long endTime = System.nanoTime();
try
{
long deltaMs = TimeUnit.NANOSECONDS.toMillis(endTime - runStartTime);
Thread.sleep((long) (deltaMs / runTimeRatio - deltaMs));
}
catch (InterruptedException ignored)
{
}
}
}
}, generationGroup.executorService);
}, worldGeneratorThreadPool);
return generationEvent;
}