Fix file loaders not getting interrupted on level close

This commit is contained in:
James Seibel
2023-03-07 20:39:07 -06:00
parent 380f92e105
commit f691f016bc
12 changed files with 65 additions and 24 deletions
@@ -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;
@@ -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);
@@ -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]);
@@ -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);
}
}
@@ -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);
}
@@ -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
{
@@ -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
{
@@ -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);
@@ -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)
@@ -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) ->
@@ -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.");
@@ -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<ColumnRenderSource> renderSourceReference = new WeakReference<>(renderSource); // TODO why is this a week reference?
CompletableFuture<IFullDataSource> 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))