Abstract file and File handler refactor

This commit is contained in:
James Seibel
2023-07-15 10:26:46 -05:00
parent 693df3f88a
commit f3414ed73d
2 changed files with 71 additions and 32 deletions
@@ -5,10 +5,7 @@ import java.nio.ByteBuffer;
import java.nio.channels.Channels;
import java.nio.channels.ClosedChannelException;
import java.nio.channels.FileChannel;
import java.nio.file.FileAlreadyExistsException;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
import java.nio.file.StandardOpenOption;
import java.nio.file.*;
import java.util.zip.Adler32;
import java.util.zip.CheckedOutputStream;
@@ -253,6 +250,10 @@ public abstract class AbstractMetaDataContainerFile
//LOGGER.info("replaced file: "+this.file.toPath());
}
}
catch (NoSuchFileException e)
{
// can be thrown by the "Files.move" method if the system tries writing to an unloaded level
}
catch (ClosedChannelException e) // includes ClosedByInterruptException
{
// expected if the file handler is shut down, the exception can be ignored
@@ -282,7 +283,7 @@ public abstract class AbstractMetaDataContainerFile
{
LOGGER.error(tempDeleteErrorMessage);
}
DebugThreadCheck = false;
this.DebugThreadCheck = false;
}
}
@@ -76,25 +76,40 @@ public class RenderSourceFileHandler implements ILodRenderSourceProvider
@Override
public void addScannedFile(Collection<File> detectedFiles)
{
if (USE_LAZY_LOADING) {
lazyAddScannedFile(detectedFiles);
if (USE_LAZY_LOADING)
{
this.lazyAddScannedFile(detectedFiles);
}
else {
immediateAddScannedFile(detectedFiles);
else
{
this.immediateAddScannedFile(detectedFiles);
}
}
private void lazyAddScannedFile(Collection<File> detectedFiles) {
for (File file : detectedFiles) {
try {
DhSectionPos pos = decodePositionByFile(file);
if (pos != null) {
unloadedFiles.put(pos, file);
private void lazyAddScannedFile(Collection<File> detectedFiles)
{
for (File file : detectedFiles)
{
if (file == null || !file.exists())
{
// can rarely happen if the user rapidly travels between dimensions
LOGGER.warn("Null or non-existent render file: " + ((file != null) ? file.getPath() : "NULL"));
continue;
}
try
{
DhSectionPos pos = this.decodePositionByFile(file);
if (pos != null)
{
this.unloadedFiles.put(pos, file);
this.topDetailLevel.updateAndGet(v -> Math.max(v, pos.sectionDetailLevel));
}
}
catch (Exception e) {
LOGGER.error("Failed to read data meta file at " + file + ": ", e);
catch (Exception e)
{
LOGGER.error("Failed to read data meta file at "+file+": ", e);
FileUtil.renameCorruptedFile(file);
}
}
@@ -186,33 +201,58 @@ public class RenderSourceFileHandler implements ILodRenderSourceProvider
protected RenderMetaDataFile getLoadOrMakeFile(DhSectionPos pos, boolean allowCreateFile)
{
RenderMetaDataFile metaFile = this.filesBySectionPos.get(pos);
if (metaFile != null) return metaFile;
if (metaFile != null)
{
return metaFile;
}
File fileToLoad = unloadedFiles.get(pos);
File fileToLoad = this.unloadedFiles.get(pos);
if (fileToLoad != null && !fileToLoad.exists())
{
fileToLoad = null;
this.unloadedFiles.remove(pos);
}
// File does exist, but not loaded yet.
if (fileToLoad != null) {
synchronized (this) {
if (fileToLoad != null)
{
synchronized (this)
{
// Double check locking for loading file, as loading file means also loading the metadata, which
// while not... Very expensive, is still better to avoid multiple threads doing it, and dumping the
// duplicated work to the trash. Therefore, eating the overhead of 'synchronized' is worth it.
metaFile = this.filesBySectionPos.get(pos);
if (metaFile != null) return metaFile; // someone else loaded it already.
try {
if (metaFile != null)
{
return metaFile; // someone else loaded it already.
}
try
{
metaFile = RenderMetaDataFile.createFromExistingFile(this, fileToLoad);
this.topDetailLevel.updateAndGet(v -> Math.max(v, pos.sectionDetailLevel));
this.filesBySectionPos.put(pos, metaFile);
return metaFile;
}
catch (IOException e) {
catch (IOException e)
{
LOGGER.error("Failed to read render meta file at " + fileToLoad + ": ", e);
FileUtil.renameCorruptedFile(fileToLoad);
}
finally {
unloadedFiles.remove(pos);
finally
{
this.unloadedFiles.remove(pos);
}
}
}
if (!allowCreateFile) return null;
if (!allowCreateFile)
{
return null;
}
// File does not exist, create it.
// In this case, since 'creating' a file object doesn't actually do anything heavy on IO yet, we use CAS
// to avoid overhead of 'synchronized', and eat the mini-overhead of possibly creating duplicate objects.
@@ -225,6 +265,7 @@ public class RenderSourceFileHandler implements ILodRenderSourceProvider
LOGGER.error("IOException on creating new data file at {}", pos, e);
return null;
}
this.topDetailLevel.updateAndGet(v -> Math.max(v, pos.sectionDetailLevel));
// This is a CAS with expected null value.
RenderMetaDataFile metaFileCas = this.filesBySectionPos.putIfAbsent(pos, metaFile);
@@ -462,16 +503,13 @@ public class RenderSourceFileHandler implements ILodRenderSourceProvider
{
if (!renderFile.delete())
{
LOGGER.error("Unable to delete render file: " + renderFile.getPath());
LOGGER.warn("Unable to delete render file: " + renderFile.getPath());
}
}
}
else {
renderFiles = new File[0];
}
// clear the cached files
this.filesBySectionPos.clear();
addScannedFile(ImmutableList.copyOf(renderFiles));
}