From 556bef6e4309440da14ca18a47537009920794f8 Mon Sep 17 00:00:00 2001 From: Julian Krings Date: Fri, 25 Jul 2025 12:44:01 +0200 Subject: [PATCH] fix more unsafe mantle chunk accesses --- .../com/volmit/iris/util/mantle/Mantle.java | 90 ++++++++++--------- .../iris/util/mantle/TectonicPlate.java | 8 ++ 2 files changed, 54 insertions(+), 44 deletions(-) diff --git a/core/src/main/java/com/volmit/iris/util/mantle/Mantle.java b/core/src/main/java/com/volmit/iris/util/mantle/Mantle.java index 6851c4cea..c48c43bd8 100644 --- a/core/src/main/java/com/volmit/iris/util/mantle/Mantle.java +++ b/core/src/main/java/com/volmit/iris/util/mantle/Mantle.java @@ -47,7 +47,6 @@ import org.bukkit.Chunk; import java.io.EOFException; import java.io.File; import java.io.IOException; -import java.util.*; import java.util.concurrent.*; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; @@ -58,16 +57,18 @@ import java.util.concurrent.atomic.AtomicInteger; */ public class Mantle { + private static final int LOCK_SIZE = Short.MAX_VALUE; + private final File dataFolder; @Getter private final int worldHeight; - private final Map lastUse; - private final Map loadedRegions; + private final KMap lastUse; + private final KMap loadedRegions; private final HyperLock hyperLock; private final AtomicBoolean closed; private final MultiBurst ioBurst; - private final AtomicBoolean ioTrim; - private final AtomicBoolean ioTectonicUnload; + private final Semaphore ioTrim; + private final Semaphore ioTectonicUnload; private final AtomicDouble adjustedIdleDuration; private final KSet toUnload; @@ -83,8 +84,8 @@ public class Mantle { this.closed = new AtomicBoolean(false); this.dataFolder = dataFolder; this.worldHeight = worldHeight; - this.ioTrim = new AtomicBoolean(false); - this.ioTectonicUnload = new AtomicBoolean(false); + this.ioTrim = new Semaphore(LOCK_SIZE, true); + this.ioTectonicUnload = new Semaphore(LOCK_SIZE, true); loadedRegions = new KMap<>(); lastUse = new KMap<>(); ioBurst = MultiBurst.burst; @@ -428,7 +429,7 @@ public class Mantle { } adjustedIdleDuration.set(idleDuration); - ioTrim.set(true); + ioTrim.acquireUninterruptibly(LOCK_SIZE); try { Iris.debug("Trimming Tectonic Plates older than " + Form.duration(idleDuration, 0)); @@ -446,7 +447,7 @@ public class Mantle { } catch (Throwable e) { Iris.reportError(e); } finally { - ioTrim.set(false); + ioTrim.release(LOCK_SIZE); } } @@ -459,7 +460,7 @@ public class Mantle { BurstExecutor burst = ioBurst.burst(toUnload.size()); burst.setMulticore(toUnload.size() > tectonicLimit); - ioTectonicUnload.set(true); + ioTectonicUnload.acquireUninterruptibly(LOCK_SIZE); try { for (long id : toUnload) { double unloadTime = M.ms() - adjustedIdleDuration.get(); @@ -478,15 +479,14 @@ public class Mantle { if (m.inUse()) { Iris.debug("Tectonic Plate was added to unload while in use " + C.DARK_GREEN + m.getX() + " " + m.getZ()); - lastUse.put(id, M.ms()); - toUnload.remove(id); + use(id); return; } try { m.write(fileForRegion(dataFolder, id, false)); oldFileForRegion(dataFolder, id).delete(); - loadedRegions.remove(id); + loadedRegions.remove(id, m); lastUse.remove(id); toUnload.remove(id); i.incrementAndGet(); @@ -502,7 +502,7 @@ public class Mantle { e.printStackTrace(); burst.complete(); } finally { - ioTectonicUnload.set(false); + ioTectonicUnload.release(LOCK_SIZE); } return i.get(); } @@ -518,32 +518,39 @@ public class Mantle { */ @RegionCoordinates private TectonicPlate get(int x, int z) { - if (ioTrim.get() || ioTectonicUnload.get()) { + boolean trim = ioTrim.tryAcquire(); + boolean unload = ioTectonicUnload.tryAcquire(); + try { + if (!trim || !unload) { + try { + return getSafe(x, z).get(); + } catch (InterruptedException e) { + e.printStackTrace(); + } catch (ExecutionException e) { + e.printStackTrace(); + } + } + + Long key = key(x, z); + TectonicPlate p = loadedRegions.get(key); + + if (p != null && !p.isClosed()) { + use(key); + return p; + } + try { return getSafe(x, z).get(); } catch (InterruptedException e) { - e.printStackTrace(); + Iris.warn("Failed to get Tectonic Plate " + x + " " + z + " Due to a thread intterruption (hotload?)"); + Iris.reportError(e); } catch (ExecutionException e) { - e.printStackTrace(); + Iris.warn("Failed to get Tectonic Plate " + x + " " + z + " Due to a thread execution exception (engine close?)"); + Iris.reportError(e); } - } - - Long key = key(x, z); - TectonicPlate p = loadedRegions.get(key); - - if (p != null) { - use(key); - return p; - } - - try { - return getSafe(x, z).get(); - } catch (InterruptedException e) { - Iris.warn("Failed to get Tectonic Plate " + x + " " + z + " Due to a thread intterruption (hotload?)"); - Iris.reportError(e); - } catch (ExecutionException e) { - Iris.warn("Failed to get Tectonic Plate " + x + " " + z + " Due to a thread execution exception (engine close?)"); - Iris.reportError(e); + } finally { + if (trim) ioTrim.release(); + if (unload) ioTectonicUnload.release(); } Iris.warn("Retrying to get " + x + " " + z + " Mantle Region"); @@ -560,19 +567,12 @@ public class Mantle { */ @RegionCoordinates private Future getSafe(int x, int z) { - Long k = key(x, z); - TectonicPlate p = loadedRegions.get(k); - - if (p != null) { - use(k); - return CompletableFuture.completedFuture(p); - } - return ioBurst.completeValue(() -> hyperLock.withResult(x, z, () -> { + Long k = key(x, z); use(k); TectonicPlate region = loadedRegions.get(k); - if (region != null) { + if (region != null && !region.isClosed()) { return region; } @@ -600,12 +600,14 @@ public class Mantle { Iris.debug("Created new Tectonic Plate (Due to Load Failure) " + C.DARK_GREEN + x + " " + z); } + use(k); return region; } region = new TectonicPlate(worldHeight, x, z); loadedRegions.put(k, region); Iris.debug("Created new Tectonic Plate " + C.DARK_GREEN + x + " " + z); + use(k); return region; })); } diff --git a/core/src/main/java/com/volmit/iris/util/mantle/TectonicPlate.java b/core/src/main/java/com/volmit/iris/util/mantle/TectonicPlate.java index 101019023..181ce9cd2 100644 --- a/core/src/main/java/com/volmit/iris/util/mantle/TectonicPlate.java +++ b/core/src/main/java/com/volmit/iris/util/mantle/TectonicPlate.java @@ -39,6 +39,7 @@ import java.nio.channels.FileChannel; import java.nio.file.Files; import java.nio.file.StandardCopyOption; import java.nio.file.StandardOpenOption; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReferenceArray; /** @@ -52,6 +53,7 @@ public class TectonicPlate { private final int sectionHeight; private final AtomicReferenceArray chunks; + private final AtomicBoolean closed; @Getter private final int x; @@ -67,6 +69,7 @@ public class TectonicPlate { public TectonicPlate(int worldHeight, int x, int z) { this.sectionHeight = worldHeight >> 4; this.chunks = new AtomicReferenceArray<>(1024); + this.closed = new AtomicBoolean(false); this.x = x; this.z = z; } @@ -143,6 +146,7 @@ public class TectonicPlate { } public void close() throws InterruptedException { + closed.set(true); for (int i = 0; i < chunks.length(); i++) { MantleChunk chunk = chunks.get(i); if (chunk != null) { @@ -151,6 +155,10 @@ public class TectonicPlate { } } + public boolean isClosed() { + return closed.get(); + } + /** * Check if a chunk exists in this plate or not (same as get(x, z) != null) *