From 7e869105cbeeb319bd8bd574da2148ace6ea45ff Mon Sep 17 00:00:00 2001 From: James Seibel Date: Mon, 20 Nov 2023 07:35:10 -0600 Subject: [PATCH] Fix null pointers when moving between multiverse levels --- .../core/api/internal/SharedApi.java | 5 +++++ .../ColumnRenderBufferBuilder.java | 16 ++++++++++++++-- .../transformers/ChunkToLodBuilder.java | 8 +++++++- .../core/file/fullDatafile/FullDataMetaFile.java | 4 ++-- .../file/renderfile/RenderSourceFileHandler.java | 7 +++++-- .../core/generation/WorldGenerationQueue.java | 3 ++- .../core/render/LodRenderSection.java | 8 ++++---- .../core/util/threading/ThreadPools.java | 7 +++++++ 8 files changed, 46 insertions(+), 12 deletions(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/api/internal/SharedApi.java b/core/src/main/java/com/seibel/distanthorizons/core/api/internal/SharedApi.java index 9691c82a4..2fb441c5d 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/api/internal/SharedApi.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/api/internal/SharedApi.java @@ -232,6 +232,11 @@ public class SharedApi // lighting the chunk needs to be done on a separate thread to prevent lagging any of the event threads ThreadPoolExecutor executor = ThreadPools.getLightPopulatorExecutor(); + if (executor == null) + { + return; + } + executor.execute(() -> { LOGGER.trace(chunkWrapper.getChunkPos() + " " + executor.getActiveCount() + " / " + executor.getQueue().size() + " - " + executor.getCompletedTaskCount()); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/bufferBuilding/ColumnRenderBufferBuilder.java b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/bufferBuilding/ColumnRenderBufferBuilder.java index dcdd65be7..046b2c8af 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/bufferBuilding/ColumnRenderBufferBuilder.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/bufferBuilding/ColumnRenderBufferBuilder.java @@ -39,6 +39,7 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ThreadPoolExecutor; /** * Used to populate the buffers in a {@link ColumnRenderSource} object. @@ -66,6 +67,17 @@ public class ColumnRenderBufferBuilder IDhClientLevel clientLevel, Reference renderBufferRef, ColumnRenderSource renderSource, ColumnRenderSource[] adjData) { + ThreadPoolExecutor bufferBuilderExecutor = ThreadPools.getBufferBuilderExecutor(); + ThreadPoolExecutor bufferUploaderExecutor = ThreadPools.getBufferUploaderExecutor(); + if ((bufferBuilderExecutor == null || bufferBuilderExecutor.isTerminated()) || + (bufferUploaderExecutor == null || bufferUploaderExecutor.isTerminated())) + { + // one or more of the thread pools has been shut down + CompletableFuture future = new CompletableFuture<>(); + future.cancel(true); + return future; + } + //LOGGER.info("RenderRegion startBuild @ "+renderSource.sectionPos); return CompletableFuture.supplyAsync(() -> { @@ -101,7 +113,7 @@ public class ColumnRenderBufferBuilder LOGGER.error("\"LodNodeBufferBuilder\" was unable to build quads: ", e3); throw e3; } - }, ThreadPools.getBufferBuilderExecutor()) + }, bufferBuilderExecutor) .thenApplyAsync((quadBuilder) -> { try @@ -136,7 +148,7 @@ public class ColumnRenderBufferBuilder LOGGER.error("\"LodNodeBufferBuilder\" was unable to upload buffer: ", e3); throw e3; } - }, ThreadPools.getBufferUploaderExecutor()) + }, bufferUploaderExecutor) .handle((columnRenderBuffer, ex) -> { //LOGGER.info("RenderRegion endBuild @ {}", renderSource.sectionPos); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/transformers/ChunkToLodBuilder.java b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/transformers/ChunkToLodBuilder.java index 19d78f4d0..3fd433a62 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/transformers/ChunkToLodBuilder.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/transformers/ChunkToLodBuilder.java @@ -102,6 +102,12 @@ public class ChunkToLodBuilder implements AutoCloseable return; } + ThreadPoolExecutor lodBuilderExecutor = ThreadPools.getChunkToLodBuilderExecutor(); + if (lodBuilderExecutor == null) + { + return; + } + for (int i = 0; i < threadCount; i++) { @@ -116,7 +122,7 @@ public class ChunkToLodBuilder implements AutoCloseable { this.runningCount.decrementAndGet(); } - }, ThreadPools.getChunkToLodBuilderExecutor()); + }, lodBuilderExecutor); } } private void tickThreadTask() diff --git a/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/FullDataMetaFile.java b/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/FullDataMetaFile.java index c13f21890..3c09bfa5b 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/FullDataMetaFile.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/FullDataMetaFile.java @@ -247,7 +247,7 @@ public class FullDataMetaFile extends AbstractMetaDataContainerFile implements I ThreadPoolExecutor executor = ThreadPools.getFileHandlerExecutor(); - if (!executor.isTerminated()) + if (executor != null && !executor.isTerminated()) { // load the data source @@ -344,7 +344,7 @@ public class FullDataMetaFile extends AbstractMetaDataContainerFile implements I else { ThreadPoolExecutor executor = ThreadPools.getFileHandlerExecutor(); - if (!executor.isTerminated()) + if (executor != null && !executor.isTerminated()) { // wait for the update to finish before returning the data source diff --git a/core/src/main/java/com/seibel/distanthorizons/core/file/renderfile/RenderSourceFileHandler.java b/core/src/main/java/com/seibel/distanthorizons/core/file/renderfile/RenderSourceFileHandler.java index 6396ba783..d417929e6 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/file/renderfile/RenderSourceFileHandler.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/file/renderfile/RenderSourceFileHandler.java @@ -104,7 +104,7 @@ public class RenderSourceFileHandler implements IRenderSourceProvider { // don't continue if the handler has been shut down ThreadPoolExecutor executor = ThreadPools.getFileHandlerExecutor(); - if (executor.isTerminated()) + if (executor != null && executor.isTerminated()) { return CompletableFuture.completedFuture(null); } @@ -266,11 +266,14 @@ public class RenderSourceFileHandler implements IRenderSourceProvider private String[] f3Log() { ThreadPoolExecutor executor = ThreadPools.getFileHandlerExecutor(); + String queueSize = executor != null ? executor.getQueue().size()+"" : "-"; + String completedTaskSize = executor != null ? executor.getCompletedTaskCount()+"" : "-"; + ArrayList lines = new ArrayList<>(); lines.add("Render Source File Handler [" + this.clientLevel.getClientLevelWrapper().getDimensionType().getDimensionName() + "]"); lines.add(" Loaded files: " + this.loadedMetaFileBySectionPos.size()); - lines.add(" Thread pool tasks: " + executor.getQueue().size() + " (completed: " + executor.getCompletedTaskCount() + ")"); + lines.add(" Thread pool tasks: " + queueSize + " (completed: " + completedTaskSize + ")"); int totalFutures = this.taskTracker.size(); EnumMap tasksOutstanding = new EnumMap<>(ETaskType.class); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldGenerationQueue.java b/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldGenerationQueue.java index 0fd8cafe4..3a4510aed 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldGenerationQueue.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldGenerationQueue.java @@ -518,7 +518,8 @@ public class WorldGenerationQueue implements IWorldGenerationQueue, IDebugRender try { int waitTimeInSeconds = 3; - if (!ThreadPools.getWorldGenExecutor().awaitTermination(waitTimeInSeconds, TimeUnit.SECONDS)) + ThreadPoolExecutor executor = ThreadPools.getWorldGenExecutor(); + if (executor != null && !executor.awaitTermination(waitTimeInSeconds, TimeUnit.SECONDS)) { LOGGER.warn("World generator thread pool shutdown didn't complete after [" + waitTimeInSeconds + "] seconds. Some world generator requests may still be running."); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/render/LodRenderSection.java b/core/src/main/java/com/seibel/distanthorizons/core/render/LodRenderSection.java index 25c09c34b..dbb6358cc 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/render/LodRenderSection.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/render/LodRenderSection.java @@ -324,10 +324,10 @@ public class LodRenderSection implements IDebugRenderable if (showRenderSectionStatus && this.pos.getDetailLevel() == DhSectionPos.SECTION_MINIMUM_DETAIL_LEVEL) { DebugRenderer.makeParticle( - new DebugRenderer.BoxParticle( - new DebugRenderer.Box(this.pos, 32f, 64f, 0.2f, Color.yellow), - 0.5, 16f - ) + new DebugRenderer.BoxParticle( + new DebugRenderer.Box(this.pos, 32f, 64f, 0.2f, Color.yellow), + 0.5, 16f + ) ); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/util/threading/ThreadPools.java b/core/src/main/java/com/seibel/distanthorizons/core/util/threading/ThreadPools.java index a4f851b7c..1084c4d8d 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/util/threading/ThreadPools.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/util/threading/ThreadPools.java @@ -22,6 +22,7 @@ package com.seibel.distanthorizons.core.util.threading; import com.seibel.distanthorizons.core.config.Config; import com.seibel.distanthorizons.core.config.listeners.ConfigChangeListener; import com.seibel.distanthorizons.core.util.ThreadUtil; +import org.jetbrains.annotations.Nullable; import java.util.concurrent.Semaphore; import java.util.concurrent.ThreadPoolExecutor; @@ -42,13 +43,16 @@ public class ThreadPools public static final DhThreadFactory FILE_HANDLER_THREAD_FACTORY = new DhThreadFactory("File Handler", Thread.MIN_PRIORITY); private static ConfigThreadPool fileHandlerThreadPool; + @Nullable public static ThreadPoolExecutor getFileHandlerExecutor() { return fileHandlerThreadPool.executor; } public static final DhThreadFactory WORLD_GEN_THREAD_FACTORY = new DhThreadFactory("World Gen", Thread.MIN_PRIORITY); private static ConfigThreadPool worldGenThreadPool; + @Nullable public static ThreadPoolExecutor getWorldGenExecutor() { return worldGenThreadPool.executor; } private static ThreadPoolExecutor bufferUploaderThreadPool; + @Nullable public static ThreadPoolExecutor getBufferUploaderExecutor() { return bufferUploaderThreadPool; } @@ -63,14 +67,17 @@ public class ThreadPools public static final DhThreadFactory LIGHT_POPULATOR_THREAD_FACTORY = new DhThreadFactory("LOD Builder - Light Populator", Thread.MIN_PRIORITY); private static ConfigThreadPool lightPopulatorThreadPool; + @Nullable public static ThreadPoolExecutor getLightPopulatorExecutor() { return lightPopulatorThreadPool.executor; } public static final DhThreadFactory CHUNK_TO_LOD_BUILDER_THREAD_FACTORY = new DhThreadFactory("LOD Builder - Chunk to Lod Builder", Thread.MIN_PRIORITY); private static ConfigThreadPool chunkToLodBuilderThreadPool; + @Nullable public static ThreadPoolExecutor getChunkToLodBuilderExecutor() { return chunkToLodBuilderThreadPool.executor; } public static final DhThreadFactory BUFFER_BUILDER_THREAD_FACTORY = new DhThreadFactory("LOD Builder - Buffer Builder", Thread.MIN_PRIORITY); private static ConfigThreadPool bufferBuilderThreadPool; + @Nullable public static ThreadPoolExecutor getBufferBuilderExecutor() { return bufferBuilderThreadPool.executor; }