9
0
mirror of https://github.com/VolmitSoftware/Iris.git synced 2025-12-19 15:09:18 +00:00

use coroutines for mantle generation

This commit is contained in:
Julian Krings
2025-10-30 16:40:08 +01:00
parent aadd03990a
commit be35e49302
17 changed files with 305 additions and 277 deletions

View File

@@ -243,6 +243,7 @@ public class IrisSettings {
public int maxBiomeChildDepth = 4;
public boolean preventLeafDecay = true;
public boolean useMulticore = false;
public boolean useMulticoreMantle = false;
public boolean offsetNoiseTypes = false;
public boolean earlyCustomBlocks = false;
}

View File

@@ -216,7 +216,7 @@ public class IrisEngineSVC implements IrisService {
Iris.error("EngineSVC: Failed to unload for " + name);
e.printStackTrace();
}
}, offset + 1000, TRIM_PERIOD, TimeUnit.MILLISECONDS);
}, offset + TRIM_PERIOD / 2, TRIM_PERIOD, TimeUnit.MILLISECONDS);
}
}

View File

@@ -86,11 +86,13 @@ public class IrisEngineMantle implements EngineMantle {
.map(components::get)
.map(components -> {
int radius = components.stream()
.filter(MantleComponent::isEnabled)
.mapToInt(MantleComponent::getRadius)
.max()
.orElse(0);
return new Pair<>(List.copyOf(components), radius);
})
.filter(pair -> !pair.getA().isEmpty())
.toList();
int radius = 0;

View File

@@ -295,7 +295,7 @@ public interface Engine extends DataProvider, Fallible, LootProvider, BlockUpdat
var chunk = mantle.getChunk(c).use();
try {
Semaphore semaphore = new Semaphore(1024);
chunk.raiseFlag(MantleFlag.ETCHED, () -> {
chunk.raiseFlagUnchecked(MantleFlag.ETCHED, () -> {
chunk.raiseFlagUnchecked(MantleFlag.TILE, run(semaphore, () -> {
chunk.iterate(TileWrapper.class, (x, y, z, v) -> {
Block block = c.getBlock(x & 15, y + getWorld().minHeight(), z & 15);

View File

@@ -29,8 +29,6 @@ import com.volmit.iris.engine.mantle.components.MantleObjectComponent;
import com.volmit.iris.engine.object.IrisDimension;
import com.volmit.iris.engine.object.IrisPosition;
import com.volmit.iris.util.collection.KList;
import com.volmit.iris.util.context.ChunkContext;
import com.volmit.iris.util.context.IrisContext;
import com.volmit.iris.util.data.B;
import com.volmit.iris.util.documentation.BlockCoordinates;
import com.volmit.iris.util.documentation.ChunkCoordinates;
@@ -38,7 +36,6 @@ import com.volmit.iris.util.hunk.Hunk;
import com.volmit.iris.util.mantle.Mantle;
import com.volmit.iris.util.mantle.MantleChunk;
import com.volmit.iris.util.mantle.flag.MantleFlag;
import com.volmit.iris.util.math.Position2;
import com.volmit.iris.util.matter.*;
import com.volmit.iris.util.matter.slices.UpdateMatter;
import com.volmit.iris.util.parallel.MultiBurst;
@@ -49,10 +46,7 @@ import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import static com.volmit.iris.util.parallel.StreamUtils.forEach;
import static com.volmit.iris.util.parallel.StreamUtils.streamRadius;
public interface EngineMantle {
public interface EngineMantle extends MatterGenerator {
BlockData AIR = B.get("AIR");
Mantle getMantle();
@@ -180,49 +174,6 @@ public interface EngineMantle {
return getEngine().burst();
}
@ChunkCoordinates
default void generateMatter(int x, int z, boolean multicore, ChunkContext context) {
if (!getEngine().getDimension().isUseMantle() || getMantle().hasFlag(x, z, MantleFlag.PLANNED)) {
return;
}
try (MantleWriter writer = getMantle().write(this, x, z, getRadius() * 2)) {
var iterator = getComponents().iterator();
while (iterator.hasNext()) {
var pair = iterator.next();
int radius = pair.getB();
boolean last = !iterator.hasNext();
forEach(streamRadius(x, z, radius),
pos -> pair.getA()
.stream()
.filter(MantleComponent::isEnabled)
.map(c -> new Pair<>(c, pos)),
p -> {
MantleComponent c = p.getA();
Position2 pos = p.getB();
int xx = pos.getX();
int zz = pos.getZ();
IrisContext.getOr(getEngine()).setChunkContext(context);
generateMantleComponent(writer, xx, zz, c, writer.acquireChunk(xx, zz), context);
},
multicore ? burst() : null
);
if (!last) continue;
forEach(streamRadius(x, z, radius),
p -> writer.acquireChunk(x, z).flag(MantleFlag.PLANNED, true),
multicore ? burst() : null
);
}
}
}
default void generateMantleComponent(MantleWriter writer, int x, int z, MantleComponent c, MantleChunk mc, ChunkContext context) {
mc.raiseFlag(MantleFlag.PLANNED, c.getFlag(), () -> {
if (c.isEnabled()) c.generateLayer(writer, x, z, context);
});
}
@ChunkCoordinates
default <T> void insertMatter(int x, int z, Class<T> t, Hunk<T> blocks, boolean multicore) {
if (!getEngine().getDimension().isUseMantle()) {
@@ -262,9 +213,6 @@ public interface EngineMantle {
default int getLoadedRegionCount() {
return getMantle().getLoadedRegionCount();
}
default long getLastUseMapMemoryUsage(){
return getMantle().LastUseMapMemoryUsage();
}
MantleJigsawComponent getJigsawComponent();
@@ -290,7 +238,7 @@ public interface EngineMantle {
if (!isCovered(x, z)) return;
MantleChunk chunk = getMantle().getChunk(x, z).use();
try {
chunk.raiseFlag(MantleFlag.CLEANED, () -> {
chunk.raiseFlagUnchecked(MantleFlag.CLEANED, () -> {
chunk.deleteSlices(BlockData.class);
chunk.deleteSlices(String.class);
chunk.deleteSlices(MatterCavern.class);

View File

@@ -62,7 +62,8 @@ public class MantleWriter implements IObjectPlacer, AutoCloseable {
public MantleWriter(EngineMantle engineMantle, Mantle mantle, int x, int z, int radius) {
this.engineMantle = engineMantle;
this.mantle = mantle;
this.cachedChunks = new KMap<>();
int d = radius * 2 + 1;
this.cachedChunks = new KMap<>(d * d);
this.radius = radius;
this.x = x;
this.z = z;

View File

@@ -35,7 +35,6 @@ import com.volmit.iris.util.math.Position2;
import com.volmit.iris.util.math.RNG;
import com.volmit.iris.util.matter.slices.container.JigsawStructuresContainer;
import com.volmit.iris.util.noise.CNG;
import lombok.Getter;
import org.jetbrains.annotations.Nullable;
import java.util.List;
@@ -45,7 +44,7 @@ public class MantleJigsawComponent extends IrisMantleComponent {
private final CNG cng;
public MantleJigsawComponent(EngineMantle engineMantle) {
super(engineMantle, ReservedFlag.JIGSAW, 2);
super(engineMantle, ReservedFlag.JIGSAW, 1);
cng = NoiseStyle.STATIC.create(new RNG(jigsaw()));
}

View File

@@ -33,7 +33,11 @@ public class KMap<K, V> extends ConcurrentHashMap<K, V> {
private static final long serialVersionUID = 7288942695300448163L;
public KMap() {
super();
this(16);
}
public KMap(int initialCapacity) {
super(initialCapacity, 0.75f, Runtime.getRuntime().availableProcessors());
}
public KMap(Map<K, V> gMap) {

View File

@@ -1,51 +0,0 @@
package com.volmit.iris.util.context;
import com.volmit.iris.engine.IrisComplex;
import com.volmit.iris.engine.object.IrisBiome;
import com.volmit.iris.engine.object.IrisRegion;
import com.volmit.iris.util.documentation.BlockCoordinates;
import com.volmit.iris.util.parallel.BurstExecutor;
import com.volmit.iris.util.parallel.MultiBurst;
import lombok.Data;
import org.bukkit.block.data.BlockData;
@Data
public class ChunkContext {
private final int x;
private final int z;
private ChunkedDataCache<Double> height;
private ChunkedDataCache<IrisBiome> biome;
private ChunkedDataCache<IrisBiome> cave;
private ChunkedDataCache<BlockData> rock;
private ChunkedDataCache<BlockData> fluid;
private ChunkedDataCache<IrisRegion> region;
@BlockCoordinates
public ChunkContext(int x, int z, IrisComplex c) {
this(x, z, c, true);
}
@BlockCoordinates
public ChunkContext(int x, int z, IrisComplex c, boolean cache) {
this.x = x;
this.z = z;
if (cache) {
BurstExecutor b = MultiBurst.burst.burst();
height = new ChunkedDataCache<>(b, c.getHeightStream(), x, z);
biome = new ChunkedDataCache<>(b, c.getTrueBiomeStream(), x, z);
cave = new ChunkedDataCache<>(b, c.getCaveBiomeStream(), x, z);
rock = new ChunkedDataCache<>(b, c.getRockStream(), x, z);
fluid = new ChunkedDataCache<>(b, c.getFluidStream(), x, z);
region = new ChunkedDataCache<>(b, c.getRegionStream(), x, z);
b.complete();
} else {
height = new ChunkedDataCache<>(null, c.getHeightStream(), x, z, false);
biome = new ChunkedDataCache<>(null, c.getTrueBiomeStream(), x, z, false);
cave = new ChunkedDataCache<>(null, c.getCaveBiomeStream(), x, z, false);
rock = new ChunkedDataCache<>(null, c.getRockStream(), x, z, false);
fluid = new ChunkedDataCache<>(null, c.getFluidStream(), x, z, false);
region = new ChunkedDataCache<>(null, c.getRegionStream(), x, z, false);
}
}
}

View File

@@ -1,60 +0,0 @@
package com.volmit.iris.util.context;
import com.volmit.iris.util.collection.KSet;
import com.volmit.iris.util.documentation.BlockCoordinates;
import com.volmit.iris.util.parallel.BurstExecutor;
import com.volmit.iris.util.stream.ProceduralStream;
import lombok.Data;
@Data
public class ChunkedDataCache<T> {
private final int x;
private final int z;
private final KSet<T> uniques;
private final Object[] data;
private final boolean cache;
private final ProceduralStream<T> stream;
@BlockCoordinates
public ChunkedDataCache(BurstExecutor burst, ProceduralStream<T> stream, int x, int z) {
this(burst, stream, x, z, true);
}
@BlockCoordinates
public ChunkedDataCache(BurstExecutor burst, ProceduralStream<T> stream, int x, int z, boolean cache) {
this.stream = stream;
this.cache = cache;
this.x = x;
this.z = z;
this.uniques = cache ? new KSet<>() : null;
if (cache) {
data = new Object[256];
int i, j;
for (i = 0; i < 16; i++) {
int finalI = i;
for (j = 0; j < 16; j++) {
int finalJ = j;
burst.queue(() -> {
T t = stream.get(x + finalI, z + finalJ);
data[(finalJ * 16) + finalI] = t;
uniques.add(t);
});
}
}
} else {
data = new Object[0];
}
}
@SuppressWarnings("unchecked")
@BlockCoordinates
public T get(int x, int z) {
if (!cache) {
return stream.get(this.x + x, this.z + z);
}
T t = (T) data[(z * 16) + x];
return t == null ? stream.get(this.x + x, this.z + z) : t;
}
}

View File

@@ -406,18 +406,6 @@ public class Mantle {
Iris.debug("The Mantle has Closed " + C.DARK_AQUA + dataFolder.getAbsolutePath());
}
/**
* Estimates the memory usage of the lastUse map.
*
* @return Estimated memory usage in bytes.
*/
public long LastUseMapMemoryUsage() {
long numberOfEntries = lastUse.size();
long bytesPerEntry = Long.BYTES * 2;
return numberOfEntries * bytesPerEntry;
}
/**
* Save & unload regions that have not been used for more than the
* specified amount of milliseconds

View File

@@ -19,16 +19,13 @@
package com.volmit.iris.util.mantle;
import com.volmit.iris.Iris;
import com.volmit.iris.util.data.Varint;
import com.volmit.iris.util.documentation.ChunkCoordinates;
import com.volmit.iris.util.documentation.ChunkRelativeBlockCoordinates;
import com.volmit.iris.util.function.Consumer4;
import com.volmit.iris.util.io.CountingDataInputStream;
import com.volmit.iris.util.mantle.flag.MantleFlag;
import com.volmit.iris.util.matter.IrisMatter;
import com.volmit.iris.util.matter.Matter;
import com.volmit.iris.util.matter.MatterSlice;
import com.volmit.iris.util.parallel.AtomicBooleanArray;
import lombok.Getter;
import lombok.SneakyThrows;
import org.jetbrains.annotations.Nullable;
@@ -44,13 +41,11 @@ import java.util.concurrent.atomic.AtomicReferenceArray;
* Represents a mantle chunk. Mantle chunks contain sections of matter (see matter api)
* Mantle Chunks are fully atomic & thread safe
*/
public class MantleChunk {
public class MantleChunk extends FlaggedChunk {
@Getter
private final int x;
@Getter
private final int z;
private final AtomicBooleanArray flags;
private final Object[] flagLocks;
private final AtomicReferenceArray<Matter> sections;
private final Semaphore ref = new Semaphore(Integer.MAX_VALUE, true);
private final AtomicBoolean closed = new AtomicBoolean(false);
@@ -63,14 +58,8 @@ public class MantleChunk {
@ChunkCoordinates
public MantleChunk(int sectionHeight, int x, int z) {
sections = new AtomicReferenceArray<>(sectionHeight);
flags = new AtomicBooleanArray(MantleFlag.MAX_ORDINAL + 1);
flagLocks = new Object[flags.length()];
this.x = x;
this.z = z;
for (int i = 0; i < flags.length(); i++) {
flagLocks[i] = new Object();
}
}
/**
@@ -84,20 +73,7 @@ public class MantleChunk {
public MantleChunk(int version, int sectionHeight, CountingDataInputStream din) throws IOException {
this(sectionHeight, din.readByte(), din.readByte());
int s = din.readByte();
int l = version < 0 ? 16 : Varint.readUnsignedVarInt(din);
if (version >= 1) {
for (int i = 0; i < l;) {
byte f = din.readByte();
for (int j = 0; j < Byte.SIZE && i < flags.length(); j++, i++) {
flags.set(i, (f & (1 << j)) != 0);
}
}
} else {
for (int i = 0; i < flags.length() && i < l; i++) {
flags.set(i, din.readBoolean());
}
}
readFlags(version, din);
for (int i = 0; i < s; i++) {
Iris.addPanic("read.section", "Section[" + i + "]");
@@ -156,52 +132,10 @@ public class MantleChunk {
public void copyFlags(MantleChunk chunk) {
use();
for (int i = 0; i < flags.length(); i++) {
flags.set(i, chunk.flags.get(i));
}
super.copyFlags(chunk);
release();
}
public void flag(MantleFlag flag, boolean f) {
if (closed.get()) throw new IllegalStateException("Chunk is closed!");
flags.set(flag.ordinal(), f);
}
public void raiseFlag(MantleFlag flag, Runnable r) {
raiseFlag(null, flag, r);
}
public void raiseFlag(@Nullable MantleFlag guard, MantleFlag flag, Runnable r) {
if (closed.get()) throw new IllegalStateException("Chunk is closed!");
if (guard != null && isFlagged(guard)) return;
synchronized (flagLocks[flag.ordinal()]) {
if (flags.compareAndSet(flag.ordinal(), false, true)) {
try {
r.run();
} catch (RuntimeException | Error e) {
flags.set(flag.ordinal(), false);
throw e;
}
}
}
}
public void raiseFlagUnchecked(MantleFlag flag, Runnable r) {
if (closed.get()) throw new IllegalStateException("Chunk is closed!");
if (flags.compareAndSet(flag.ordinal(), false, true)) {
try {
r.run();
} catch (RuntimeException | Error e) {
flags.set(flag.ordinal(), false);
throw e;
}
}
}
public boolean isFlagged(MantleFlag flag) {
return flags.get(flag.ordinal());
}
/**
* Check if a section exists (same as get(section) != null)
*
@@ -283,16 +217,7 @@ public class MantleChunk {
dos.writeByte(x);
dos.writeByte(z);
dos.writeByte(sections.length());
Varint.writeUnsignedVarInt(flags.length(), dos);
int count = flags.length();
for (int i = 0; i < count;) {
int f = 0;
for (int j = 0; j < Byte.SIZE && i < flags.length(); j++, i++) {
f |= flags.get(i) ? (1 << j) : 0;
}
dos.write(f);
}
writeFlags(dos);
var bytes = new ByteArrayOutputStream(8192);
var sub = new DataOutputStream(bytes);
@@ -360,4 +285,9 @@ public class MantleChunk {
}
}
}
@Override
public boolean isClosed() {
return closed.get();
}
}

View File

@@ -23,6 +23,8 @@ import com.volmit.iris.core.IrisSettings;
import com.volmit.iris.util.collection.KList;
import com.volmit.iris.util.math.M;
import com.volmit.iris.util.scheduling.PrecisionStopwatch;
import kotlinx.coroutines.CoroutineDispatcher;
import kotlinx.coroutines.ExecutorsKt;
import org.jetbrains.annotations.NotNull;
import java.util.Collection;
@@ -39,7 +41,9 @@ public class MultiBurst implements ExecutorService {
private final String name;
private final int priority;
private final IntSupplier parallelism;
private ExecutorService service;
private final Object lock = new Object();
private volatile ExecutorService service;
private volatile CoroutineDispatcher dispatcher;
public MultiBurst() {
this("Iris");
@@ -60,9 +64,15 @@ public class MultiBurst implements ExecutorService {
last = new AtomicLong(M.ms());
}
private synchronized ExecutorService getService() {
private ExecutorService getService() {
last.set(M.ms());
if (service == null || service.isShutdown()) {
if (service != null && !service.isShutdown())
return service;
synchronized (lock) {
if (service != null && !service.isShutdown())
return service;
service = new ForkJoinPool(IrisSettings.getThreadCount(parallelism.getAsInt()),
new ForkJoinPool.ForkJoinWorkerThreadFactory() {
int m = 0;
@@ -76,9 +86,14 @@ public class MultiBurst implements ExecutorService {
}
},
(t, e) -> e.printStackTrace(), true);
dispatcher = ExecutorsKt.from(service);
return service;
}
}
return service;
public CoroutineDispatcher getDispatcher() {
getService();
return dispatcher;
}
public void burst(Runnable... r) {

View File

@@ -0,0 +1,68 @@
package com.volmit.iris.engine.mantle
import com.volmit.iris.core.IrisSettings
import com.volmit.iris.core.nms.container.Pair
import com.volmit.iris.engine.framework.Engine
import com.volmit.iris.util.context.ChunkContext
import com.volmit.iris.util.documentation.ChunkCoordinates
import com.volmit.iris.util.mantle.Mantle
import com.volmit.iris.util.mantle.flag.MantleFlag
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.asCoroutineDispatcher
import kotlinx.coroutines.flow.FlowCollector
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import java.util.concurrent.Executors
import kotlin.coroutines.EmptyCoroutineContext
interface MatterGenerator {
val engine: Engine
val mantle: Mantle
val radius: Int
val components: List<Pair<List<MantleComponent>, Int>>
@ChunkCoordinates
fun generateMatter(x: Int, z: Int, multicore: Boolean, context: ChunkContext) {
if (!engine.dimension.isUseMantle || mantle.hasFlag(x, z, MantleFlag.PLANNED))
return
mantle.write(engine.mantle, x, z, radius * 2).use { writer ->
for (pair in components) {
radius(x, z, pair.b, { x, z ->
for (c in pair.a) {
emit(Triple(x, z, c))
}
}, { (x, z, c) -> launch {
writer.acquireChunk(x, z)
.raiseFlagSuspend(MantleFlag.PLANNED, c.flag) {
if (c.isEnabled) c.generateLayer(writer, x, z, context)
}
}})
}
radius(x, z, radius, { x, z ->
emit(Pair(x, z))
}, {
writer.acquireChunk(it.a, it.b)
.flag(MantleFlag.PLANNED, true)
})
}
}
private fun <T> radius(x: Int, z: Int, radius: Int, collector: suspend FlowCollector<T>.(Int, Int) -> Unit, task: suspend CoroutineScope.(T) -> Unit) = runBlocking {
flow {
for (i in -radius..radius) {
for (j in -radius..radius) {
collector(x + i, z + j)
}
}
}.collect { task(it) }
}
companion object {
private val dispatcher = Executors.newVirtualThreadPerTaskExecutor().asCoroutineDispatcher()
private fun CoroutineScope.launch(block: suspend CoroutineScope.() -> Unit) =
launch(if (IrisSettings.get().generator.isUseMulticoreMantle) dispatcher else EmptyCoroutineContext, block = block)
}
}

View File

@@ -0,0 +1,35 @@
package com.volmit.iris.util.context
import com.volmit.iris.engine.IrisComplex
import com.volmit.iris.engine.`object`.IrisBiome
import com.volmit.iris.engine.`object`.IrisRegion
import com.volmit.iris.util.parallel.MultiBurst
import kotlinx.coroutines.*
import org.bukkit.block.data.BlockData
class ChunkContext @JvmOverloads constructor(
val x: Int,
val z: Int,
c: IrisComplex,
cache: Boolean = true,
) {
val height: ChunkedDataCache<Double> = ChunkedDataCache(c.heightStream, x, z, cache)
val biome: ChunkedDataCache<IrisBiome> = ChunkedDataCache(c.trueBiomeStream, x, z, cache)
val cave: ChunkedDataCache<IrisBiome> = ChunkedDataCache(c.caveBiomeStream, x, z, cache)
val rock: ChunkedDataCache<BlockData> = ChunkedDataCache(c.rockStream, x, z, cache)
val fluid: ChunkedDataCache<BlockData> = ChunkedDataCache(c.fluidStream, x, z, cache)
val region: ChunkedDataCache<IrisRegion> = ChunkedDataCache(c.regionStream, x, z, cache)
init {
if (cache) runBlocking {
val dispatcher = MultiBurst.burst.dispatcher
launch { height.fill(dispatcher) }
launch { biome.fill(dispatcher) }
launch { cave.fill(dispatcher) }
launch { rock.fill(dispatcher) }
launch { fluid.fill(dispatcher) }
launch { region.fill(dispatcher) }
}
}
}

View File

@@ -0,0 +1,46 @@
package com.volmit.iris.util.context
import com.volmit.iris.util.documentation.BlockCoordinates
import com.volmit.iris.util.stream.ProceduralStream
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.supervisorScope
import kotlin.coroutines.CoroutineContext
class ChunkedDataCache<T> private constructor(
private val x: Int,
private val z: Int,
private val stream: ProceduralStream<T?>,
private val cache: Boolean
) {
private val data = arrayOfNulls<Any>(if (cache) 256 else 0)
@JvmOverloads
@BlockCoordinates
constructor(stream: ProceduralStream<T?>, x: Int, z: Int, cache: Boolean = true) : this(x, z, stream, cache)
suspend fun fill(context: CoroutineContext = Dispatchers.Default) {
if (!cache) return
supervisorScope {
for (i in 0 until 16) {
for (j in 0 until 16) {
launch(context) {
val t = stream.get((x + i).toDouble(), (z + j).toDouble())
data[(j * 16) + i] = t
}
}
}
}
}
@BlockCoordinates
fun get(x: Int, z: Int): T? {
if (!cache) {
return stream.get((this.x + x).toDouble(), (this.z + z).toDouble())
}
val t = data[(z * 16) + x] as? T
return t ?: stream.get((this.x + x).toDouble(), (this.z + z).toDouble())
}
}

View File

@@ -0,0 +1,102 @@
package com.volmit.iris.util.mantle
import com.volmit.iris.util.data.Varint
import com.volmit.iris.util.mantle.flag.MantleFlag
import com.volmit.iris.util.parallel.AtomicBooleanArray
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import java.io.DataInput
import java.io.DataOutput
import java.io.IOException
import java.lang.Byte
import kotlin.Boolean
import kotlin.IllegalStateException
import kotlin.Int
import kotlin.Throwable
import kotlin.Throws
import kotlin.Unit
abstract class FlaggedChunk() {
private val flags = AtomicBooleanArray(MantleFlag.MAX_ORDINAL + 1)
private val locks = 0.rangeUntil(flags.length()).map { Mutex() }.toTypedArray()
abstract fun isClosed(): Boolean
protected fun copyFlags(other: FlaggedChunk) {
for (i in 0 until flags.length()) {
flags.set(i, other.flags.get(i))
}
}
fun isFlagged(flag: MantleFlag) = flags.get(flag.ordinal())
fun flag(flag: MantleFlag, value: Boolean) {
if (isClosed()) throw IllegalStateException("Chunk is closed!")
flags.set(flag.ordinal(), value)
}
suspend fun raiseFlagSuspend(guard: MantleFlag?, flag: MantleFlag, task: suspend () -> Unit) {
if (isClosed()) throw IllegalStateException("Chunk is closed!")
if (guard != null && isFlagged(guard)) return
locks[flag.ordinal()].withLock {
if (flags.compareAndSet(flag.ordinal(), false, true)) {
try {
task()
} catch (e: Throwable) {
flags.set(flag.ordinal(), false)
throw e
}
}
}
}
fun raiseFlagUnchecked(flag: MantleFlag, task: Runnable) {
if (isClosed()) throw IllegalStateException("Chunk is closed!")
if (flags.compareAndSet(flag.ordinal(), false, true)) {
try {
task.run()
} catch (e: Throwable) {
flags.set(flag.ordinal(), false)
throw e
}
}
}
@Throws(IOException::class)
protected fun readFlags(version: Int, din: DataInput) {
val l = if (version < 0) 16 else Varint.readUnsignedVarInt(din)
if (version >= 1) {
var i = 0
while (i < l) {
val f = din.readByte()
var j = 0
while (j < Byte.SIZE && i < flags.length()) {
flags.set(i, (f.toInt() and (1 shl j)) != 0)
j++
i++
}
}
} else {
for (i in 0 until l) {
flags.set(i, din.readBoolean())
}
}
}
@Throws(IOException::class)
protected fun writeFlags(dos: DataOutput) {
Varint.writeUnsignedVarInt(flags.length(), dos)
val count = flags.length()
var i = 0
while (i < count) {
var f = 0
for (j in 0 until Byte.SIZE) {
if (i >= count) break
f = f or if (flags.get(i)) 1 shl j else 0
i++
}
dos.write(f)
}
}
}