fix more unsafe mantle chunk accesses

This commit is contained in:
Julian Krings 2025-07-25 12:44:01 +02:00
parent 16a4d20c90
commit 556bef6e43
No known key found for this signature in database
GPG Key ID: 208C6E08C3B718D2
2 changed files with 54 additions and 44 deletions

View File

@ -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<Long, Long> lastUse;
private final Map<Long, TectonicPlate> loadedRegions;
private final KMap<Long, Long> lastUse;
private final KMap<Long, TectonicPlate> 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<Long> 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<TectonicPlate> 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;
}));
}

View File

@ -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<MantleChunk> 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)
*