diff --git a/core/src/main/java/com/seibel/lod/core/dataObjects/fullData/AbstractFullDataSourceLoader.java b/core/src/main/java/com/seibel/lod/core/dataObjects/fullData/AbstractFullDataSourceLoader.java index c3d9b4c79..92a526f2b 100644 --- a/core/src/main/java/com/seibel/lod/core/dataObjects/fullData/AbstractFullDataSourceLoader.java +++ b/core/src/main/java/com/seibel/lod/core/dataObjects/fullData/AbstractFullDataSourceLoader.java @@ -1,6 +1,9 @@ package com.seibel.lod.core.dataObjects.fullData; import com.google.common.collect.HashMultimap; +import com.seibel.lod.core.dataObjects.fullData.sources.FullDataSource; +import com.seibel.lod.core.dataObjects.transformers.FullToColumnTransformer; +import com.seibel.lod.core.level.IDhClientLevel; import com.seibel.lod.core.level.IDhLevel; import com.seibel.lod.core.file.fullDatafile.FullDataMetaFile; @@ -52,8 +55,11 @@ public abstract class AbstractFullDataSourceLoader loaderRegistry.put(clazz, this); } - /** Can return null as meaning the requirement is not met */ - public abstract IFullDataSource loadData(FullDataMetaFile dataFile, InputStream data, IDhLevel level) throws IOException; + /** + * Can return null if any of the requirements aren't met. + * @throws InterruptedException if the loader thread is interrupted, generally happens when the level is shutting down + */ + public abstract IFullDataSource loadData(FullDataMetaFile dataFile, InputStream data, IDhLevel level) throws IOException, InterruptedException; diff --git a/core/src/main/java/com/seibel/lod/core/dataObjects/fullData/FullDataLoader.java b/core/src/main/java/com/seibel/lod/core/dataObjects/fullData/FullDataLoader.java index fac28b3e2..ba61dcf67 100644 --- a/core/src/main/java/com/seibel/lod/core/dataObjects/fullData/FullDataLoader.java +++ b/core/src/main/java/com/seibel/lod/core/dataObjects/fullData/FullDataLoader.java @@ -15,7 +15,7 @@ public class FullDataLoader extends AbstractFullDataSourceLoader } @Override - public IFullDataSource loadData(FullDataMetaFile dataFile, InputStream data, IDhLevel level) throws IOException + public IFullDataSource loadData(FullDataMetaFile dataFile, InputStream data, IDhLevel level) throws IOException, InterruptedException { //TODO: Add decompressor here return FullDataSource.loadData(dataFile, data, level); diff --git a/core/src/main/java/com/seibel/lod/core/dataObjects/fullData/FullDataPointIdMap.java b/core/src/main/java/com/seibel/lod/core/dataObjects/fullData/FullDataPointIdMap.java index 7fae98d52..fc882abfa 100644 --- a/core/src/main/java/com/seibel/lod/core/dataObjects/fullData/FullDataPointIdMap.java +++ b/core/src/main/java/com/seibel/lod/core/dataObjects/fullData/FullDataPointIdMap.java @@ -1,5 +1,6 @@ package com.seibel.lod.core.dataObjects.fullData; +import com.seibel.lod.core.dataObjects.transformers.FullToColumnTransformer; import com.seibel.lod.core.dependencyInjection.SingletonInjector; import com.seibel.lod.core.wrapperInterfaces.IWrapperFactory; import com.seibel.lod.core.wrapperInterfaces.block.IBlockStateWrapper; @@ -71,7 +72,7 @@ public class FullDataPointIdMap } /** Creates a new IdBiomeBlockStateMap from the given UTF formatted stream */ - public static FullDataPointIdMap deserialize(InputStream inputStream) throws IOException + public static FullDataPointIdMap deserialize(InputStream inputStream) throws IOException, InterruptedException { DataInputStream dataStream = new DataInputStream(inputStream); // DO NOT CLOSE! It would close all related streams int entityCount = dataStream.readInt(); @@ -136,11 +137,19 @@ public class FullDataPointIdMap public String serialize() { return this.biome.serialize() + " " + this.blockState.serialize(); } - public static Entry deserialize(String str) throws IOException + public static Entry deserialize(String str) throws IOException, InterruptedException { String[] stringArray = str.split(" "); if (stringArray.length != 2) + { throw new IOException("Failed to deserialize BiomeBlockStateEntry"); + } + + // necessary to prevent issues with deserializing objects after the level has been closed + if (Thread.interrupted()) + { + throw new InterruptedException(FullDataPointIdMap.class.getSimpleName()+" task interrupted."); + } IBiomeWrapper biome = WRAPPER_FACTORY.deserializeBiomeWrapper(stringArray[0]); IBlockStateWrapper blockState = WRAPPER_FACTORY.deserializeBlockStateWrapper(stringArray[1]); diff --git a/core/src/main/java/com/seibel/lod/core/dataObjects/fullData/SingleChunkFullDataLoader.java b/core/src/main/java/com/seibel/lod/core/dataObjects/fullData/SingleChunkFullDataLoader.java index d4941a8c0..d4edc7f1f 100644 --- a/core/src/main/java/com/seibel/lod/core/dataObjects/fullData/SingleChunkFullDataLoader.java +++ b/core/src/main/java/com/seibel/lod/core/dataObjects/fullData/SingleChunkFullDataLoader.java @@ -14,7 +14,8 @@ public class SingleChunkFullDataLoader extends AbstractFullDataSourceLoader } @Override - public IFullDataSource loadData(FullDataMetaFile dataFile, InputStream data, IDhLevel level) throws IOException { + public IFullDataSource loadData(FullDataMetaFile dataFile, InputStream data, IDhLevel level) throws IOException, InterruptedException + { return SingleChunkFullDataSource.loadData(dataFile, data, level); } } diff --git a/core/src/main/java/com/seibel/lod/core/dataObjects/fullData/SparseFullDataLoader.java b/core/src/main/java/com/seibel/lod/core/dataObjects/fullData/SparseFullDataLoader.java index 4db0285a6..de9de4305 100644 --- a/core/src/main/java/com/seibel/lod/core/dataObjects/fullData/SparseFullDataLoader.java +++ b/core/src/main/java/com/seibel/lod/core/dataObjects/fullData/SparseFullDataLoader.java @@ -15,7 +15,7 @@ public class SparseFullDataLoader extends AbstractFullDataSourceLoader } @Override - public IFullDataSource loadData(FullDataMetaFile dataFile, InputStream data, IDhLevel level) throws IOException + public IFullDataSource loadData(FullDataMetaFile dataFile, InputStream data, IDhLevel level) throws IOException, InterruptedException { return SparseFullDataSource.loadData(dataFile, data, level); } diff --git a/core/src/main/java/com/seibel/lod/core/dataObjects/fullData/sources/FullDataSource.java b/core/src/main/java/com/seibel/lod/core/dataObjects/fullData/sources/FullDataSource.java index 1eeeb77bb..26d3245cc 100644 --- a/core/src/main/java/com/seibel/lod/core/dataObjects/fullData/sources/FullDataSource.java +++ b/core/src/main/java/com/seibel/lod/core/dataObjects/fullData/sources/FullDataSource.java @@ -178,7 +178,7 @@ public class FullDataSource extends FullArrayView implements IFullDataSource } - public static FullDataSource loadData(FullDataMetaFile dataFile, InputStream dataStream, IDhLevel level) throws IOException + public static FullDataSource loadData(FullDataMetaFile dataFile, InputStream dataStream, IDhLevel level) throws IOException, InterruptedException { DataInputStream dos = new DataInputStream(dataStream); // DO NOT CLOSE { diff --git a/core/src/main/java/com/seibel/lod/core/dataObjects/fullData/sources/SingleChunkFullDataSource.java b/core/src/main/java/com/seibel/lod/core/dataObjects/fullData/sources/SingleChunkFullDataSource.java index cb1187e30..13e976671 100644 --- a/core/src/main/java/com/seibel/lod/core/dataObjects/fullData/sources/SingleChunkFullDataSource.java +++ b/core/src/main/java/com/seibel/lod/core/dataObjects/fullData/sources/SingleChunkFullDataSource.java @@ -119,7 +119,7 @@ public class SingleChunkFullDataSource extends FullArrayView implements IIncompl } - public static SingleChunkFullDataSource loadData(FullDataMetaFile dataFile, InputStream dataStream, IDhLevel level) throws IOException + public static SingleChunkFullDataSource loadData(FullDataMetaFile dataFile, InputStream dataStream, IDhLevel level) throws IOException, InterruptedException { DataInputStream dos = new DataInputStream(dataStream); // DO NOT CLOSE { diff --git a/core/src/main/java/com/seibel/lod/core/dataObjects/fullData/sources/SparseFullDataSource.java b/core/src/main/java/com/seibel/lod/core/dataObjects/fullData/sources/SparseFullDataSource.java index 19d010f07..8c5a002f0 100644 --- a/core/src/main/java/com/seibel/lod/core/dataObjects/fullData/sources/SparseFullDataSource.java +++ b/core/src/main/java/com/seibel/lod/core/dataObjects/fullData/sources/SparseFullDataSource.java @@ -291,7 +291,7 @@ public class SparseFullDataSource implements IIncompleteFullDataSource } } - public static SparseFullDataSource loadData(FullDataMetaFile dataFile, InputStream dataStream, IDhLevel level) throws IOException + public static SparseFullDataSource loadData(FullDataMetaFile dataFile, InputStream dataStream, IDhLevel level) throws IOException, InterruptedException { LodUtil.assertTrue(dataFile.pos.sectionDetailLevel > SPARSE_UNIT_DETAIL); LodUtil.assertTrue(dataFile.pos.sectionDetailLevel <= MAX_SECTION_DETAIL); diff --git a/core/src/main/java/com/seibel/lod/core/dataObjects/render/ColumnRenderLoader.java b/core/src/main/java/com/seibel/lod/core/dataObjects/render/ColumnRenderLoader.java index 8749f8a26..85d8539b6 100644 --- a/core/src/main/java/com/seibel/lod/core/dataObjects/render/ColumnRenderLoader.java +++ b/core/src/main/java/com/seibel/lod/core/dataObjects/render/ColumnRenderLoader.java @@ -57,9 +57,7 @@ public class ColumnRenderLoader } } - /** - * @throws InterruptedException see {@link FullToColumnTransformer#transformFullDataToColumnData(IDhClientLevel, FullDataSource) FullToColumnTransformer#transformFullDataToColumnData} for documentation - */ + /** @throws InterruptedException see {@link FullToColumnTransformer#transformFullDataToColumnData(IDhClientLevel, FullDataSource) FullToColumnTransformer#transformFullDataToColumnData} for documentation */ public ColumnRenderSource createRenderSource(IFullDataSource dataSource, IDhClientLevel level) throws InterruptedException { if (dataSource instanceof FullDataSource) diff --git a/core/src/main/java/com/seibel/lod/core/file/fullDatafile/FullDataMetaFile.java b/core/src/main/java/com/seibel/lod/core/file/fullDatafile/FullDataMetaFile.java index e50bbeff0..6d45ffa14 100644 --- a/core/src/main/java/com/seibel/lod/core/file/fullDatafile/FullDataMetaFile.java +++ b/core/src/main/java/com/seibel/lod/core/file/fullDatafile/FullDataMetaFile.java @@ -227,10 +227,16 @@ public class FullDataMetaFile extends AbstractMetaDataContainerFile { data = this.loader.loadData(this, inputStream, this.level); } - catch (Exception e) + catch (Exception ex) { + if (ex instanceof InterruptedException) + { +// LOGGER.warn(FullDataMetaFile.class.getSimpleName()+" loadOrGetCachedAsync interrupted."); + return null; + } + // can happen if there is a missing file or the file was incorrectly formatted - throw new CompletionException(e); + throw new CompletionException(ex); } // Apply the write queue @@ -241,12 +247,26 @@ public class FullDataMetaFile extends AbstractMetaDataContainerFile data = this.handler.onDataFileLoaded(data, this.metaData, this::saveChanges, this::applyWriteQueue); return data; }, this.handler.getIOExecutor()) - .exceptionally((e) -> + .exceptionally((ex) -> { - LOGGER.error("Error loading file {}: ", this.file, e); + if (ex instanceof InterruptedException) + { + //LOGGER.warn(FullDataMetaFile.class.getSimpleName()+" loadOrGetCachedAsync interrupted."); + //future.completeExceptionally(ex); // this exception can be ignored + return null; + } + else if (ex instanceof RejectedExecutionException) + { + //LOGGER.warn(FullDataMetaFile.class.getSimpleName()+" loadOrGetCachedAsync attempted to use a closed thread pool."); + //future.completeExceptionally(ex); // this exception can be ignored + return null; + } + + + LOGGER.error("Error loading file {}: ", this.file, ex); this.data.set(null); - future.completeExceptionally(e); + future.completeExceptionally(ex); return null; // the return value here doesn't matter }) .whenComplete((dataSource, e) -> diff --git a/core/src/main/java/com/seibel/lod/core/file/metaData/AbstractMetaDataContainerFile.java b/core/src/main/java/com/seibel/lod/core/file/metaData/AbstractMetaDataContainerFile.java index ee6838e10..e04d475d7 100644 --- a/core/src/main/java/com/seibel/lod/core/file/metaData/AbstractMetaDataContainerFile.java +++ b/core/src/main/java/com/seibel/lod/core/file/metaData/AbstractMetaDataContainerFile.java @@ -4,6 +4,7 @@ import java.io.*; import java.nio.ByteBuffer; import java.nio.channels.Channels; import java.nio.channels.ClosedByInterruptException; +import java.nio.channels.ClosedChannelException; import java.nio.channels.FileChannel; import java.nio.file.FileAlreadyExistsException; import java.nio.file.Files; @@ -240,7 +241,7 @@ public abstract class AbstractMetaDataContainerFile //LOGGER.info("replaced file: "+this.file.toPath()); } } - catch (ClosedByInterruptException e) + catch (ClosedChannelException e) // includes ClosedByInterruptException { // expected if the file handler is shut down, the exception can be ignored // LOGGER.warn(AbstractMetaDataContainerFile.class.getSimpleName()+" file writing interrupted."); diff --git a/core/src/main/java/com/seibel/lod/core/file/renderfile/RenderSourceFileHandler.java b/core/src/main/java/com/seibel/lod/core/file/renderfile/RenderSourceFileHandler.java index 56f9f8c2c..68703f5f6 100644 --- a/core/src/main/java/com/seibel/lod/core/file/renderfile/RenderSourceFileHandler.java +++ b/core/src/main/java/com/seibel/lod/core/file/renderfile/RenderSourceFileHandler.java @@ -24,6 +24,7 @@ import java.util.*; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ExecutorService; +import java.util.concurrent.RejectedExecutionException; public class RenderSourceFileHandler implements ILodRenderSourceProvider { @@ -273,16 +274,16 @@ public class RenderSourceFileHandler implements ILodRenderSourceProvider final WeakReference renderSourceReference = new WeakReference<>(renderSource); // TODO why is this a week reference? CompletableFuture fullDataSourceFuture = this.fullDataSourceProvider.read(renderSource.getSectionPos()); - fullDataSourceFuture = fullDataSourceFuture.thenApply((dataSource) -> + fullDataSourceFuture = fullDataSourceFuture.thenApply((fullDataSource) -> { if (renderSourceReference.get() == null) { throw new UncheckedInterruptedException(); } - LodUtil.assertTrue(dataSource != null); - return dataSource; - } - ).exceptionally((ex) -> + + // the fullDataSource can be null if the thread this was running on was interrupted + return fullDataSource; + }).exceptionally((ex) -> { LOGGER.error("Exception when getting data for updateCache()", ex); return null; @@ -296,6 +297,11 @@ public class RenderSourceFileHandler implements ILodRenderSourceProvider if (ex instanceof InterruptedException) { // expected if the transformer is shut down, the exception can be ignored +// LOGGER.warn("RenderSource file transforming interrupted."); + } + else if (ex instanceof RejectedExecutionException || ex.getCause() instanceof RejectedExecutionException) + { + // expected if the transformer was already shut down, the exception can be ignored // LOGGER.warn("RenderSource file transforming interrupted."); } else if (!UncheckedInterruptedException.isThrowableInterruption(ex))