diff --git a/patches/server/0002-Dev-import-deobfuscation-fixes.patch b/patches/server/0002-Dev-import-deobfuscation-fixes.patch index b354d50..330d62b 100644 --- a/patches/server/0002-Dev-import-deobfuscation-fixes.patch +++ b/patches/server/0002-Dev-import-deobfuscation-fixes.patch @@ -92,6 +92,109 @@ index 50a9f33aa31e9273c7c52d4bb2b02f0f884f7ba5..19779298b2b2ecbe30f9308dad1d8037 return this.allInstances.stream().filter(typeClass::isInstance).collect(Collectors.toList()); }); return Collections.unmodifiableCollection(list); +diff --git a/src/main/java/net/minecraft/util/ExtraCodecs.java b/src/main/java/net/minecraft/util/ExtraCodecs.java +index 0e30e20eca4dfd7c9b3f1ec49333cff809ab1b8a..7591002e07271389f9ac8decea25747c0cd8a213 100644 +--- a/src/main/java/net/minecraft/util/ExtraCodecs.java ++++ b/src/main/java/net/minecraft/util/ExtraCodecs.java +@@ -174,7 +174,7 @@ public class ExtraCodecs { + return ImmutableList.of(leftFunction.apply(pair), rightFunction.apply(pair)); + }); + Codec codec3 = RecordCodecBuilder.create((instance) -> { +- return instance.group(codec.fieldOf(leftFieldName).forGetter(Pair::getFirst), codec.fieldOf(rightFieldName).forGetter(Pair::getSecond)).apply(instance, Pair::of); ++ return instance.group(codec.fieldOf(leftFieldName).forGetter(pair -> (P) pair.getFirst()), codec.fieldOf(rightFieldName).forGetter(pair -> (P) pair.getSecond())).apply(instance, Pair::of); // Gale - dev import deobfuscation fixes + }).comapFlatMap((pair) -> { + return combineFunction.apply((P)pair.getFirst(), (P)pair.getSecond()); + }, (pair) -> { +@@ -203,7 +203,7 @@ public class ExtraCodecs { + public DataResult> apply(DynamicOps dynamicOps, T objectx, DataResult> dataResult) { + MutableObject mutableObject = new MutableObject<>(); + Optional> optional = dataResult.resultOrPartial(mutableObject::setValue); +- return optional.isPresent() ? dataResult : DataResult.error("(" + (String)mutableObject.getValue() + " -> using default)", Pair.of(object, object)); ++ return optional.isPresent() ? dataResult : DataResult.error("(" + (String)mutableObject.getValue() + " -> using default)", Pair.of(object, (T) object)); // Gale - dev import deobfuscation fixes + } + + public DataResult coApply(DynamicOps dynamicOps, A objectx, DataResult dataResult) { +@@ -459,7 +459,7 @@ public class ExtraCodecs { + + static record LazyInitializedCodec(Supplier> delegate) implements Codec { + LazyInitializedCodec { +- supplier = Suppliers.memoize(supplier::get); ++ delegate = Suppliers.memoize(delegate::get); // Gale - dev import deobfuscation fixes + } + + public DataResult> decode(DynamicOps dynamicOps, T object) { +diff --git a/src/main/java/net/minecraft/util/datafix/fixes/ChunkProtoTickListFix.java b/src/main/java/net/minecraft/util/datafix/fixes/ChunkProtoTickListFix.java +index db8eed0070a6e9949d73e94e07fea589ace09fd4..d6de68b4b0edabf37ba551b91bbe6682895b2a82 100644 +--- a/src/main/java/net/minecraft/util/datafix/fixes/ChunkProtoTickListFix.java ++++ b/src/main/java/net/minecraft/util/datafix/fixes/ChunkProtoTickListFix.java +@@ -17,6 +17,7 @@ import it.unimi.dsi.fastutil.ints.Int2ObjectArrayMap; + import it.unimi.dsi.fastutil.ints.Int2ObjectMap; + import java.util.Collections; + import java.util.List; ++import java.util.Map; + import java.util.Optional; + import java.util.function.Function; + import java.util.function.Supplier; +@@ -51,23 +52,27 @@ public class ChunkProtoTickListFix extends DataFix { + Dynamic dynamic = typedx.get(DSL.remainderFinder()); + MutableInt mutableInt = new MutableInt(); + Int2ObjectMap> int2ObjectMap = new Int2ObjectArrayMap<>(); +- typedx.getOptionalTyped(opticFinder2).ifPresent((typed) -> { +- typed.getAllTyped(opticFinder3).forEach((typedx) -> { +- Dynamic dynamic = typedx.get(DSL.remainderFinder()); +- int i = dynamic.get("Y").asInt(Integer.MAX_VALUE); ++ // Gale start - dev import deobfuscation fixes ++ typedx.getOptionalTyped(opticFinder2).ifPresent((typed2) -> { ++ typed2.getAllTyped(opticFinder3).forEach((typedx2) -> { ++ Dynamic dynamic2 = typedx2.get(DSL.remainderFinder()); ++ int i = dynamic2.get("Y").asInt(Integer.MAX_VALUE); ++ // Gale end - dev import deobfuscation fixes + if (i != Integer.MAX_VALUE) { +- if (typedx.getOptionalTyped(opticFinder5).isPresent()) { ++ if (typedx2.getOptionalTyped(opticFinder5).isPresent()) { // Gale - dev import deobfuscation fixes + mutableInt.setValue(Math.min(i, mutableInt.getValue())); + } + +- typedx.getOptionalTyped(opticFinder4).ifPresent((typed) -> { ++ typedx2.getOptionalTyped(opticFinder4).ifPresent((typed3) -> { // Gale - dev import deobfuscation fixes + int2ObjectMap.put(i, Suppliers.memoize(() -> { +- List> list = typed.getOptionalTyped(opticFinder6).map((typedx) -> { +- return typedx.write().result().map((dynamic) -> { +- return dynamic.asList(Function.identity()); ++ // Gale start - dev import deobfuscation fixes ++ List> list = typed.getOptionalTyped(opticFinder6).map((typedx3) -> { ++ return typedx3.write().result().map((dynamic3) -> { ++ return dynamic3.asList(Function.identity()); ++ // Gale end - dev import deobfuscation fixes + }).orElse(Collections.emptyList()); + }).orElse(Collections.emptyList()); +- long[] ls = typed.get(DSL.remainderFinder()).get("data").asLongStream().toArray(); ++ long[] ls = typed3.get(DSL.remainderFinder()).get("data").asLongStream().toArray(); // Gale - dev import deobfuscation fixes + return new ChunkProtoTickListFix.PoorMansPalettedContainer(list, ls); + })); + }); +@@ -76,8 +81,10 @@ public class ChunkProtoTickListFix extends DataFix { + }); + byte b = mutableInt.getValue().byteValue(); + typedx = typedx.update(DSL.remainderFinder(), (dynamicx) -> { +- return dynamicx.update("yPos", (dynamic) -> { +- return dynamic.createByte(b); ++ // Gale start - dev import deobfuscation fixes ++ return dynamicx.update("yPos", (dynamic2) -> { ++ return dynamic2.createByte(b); ++ // Gale end - dev import deobfuscation fixes + }); + }); + if (!typedx.getOptionalTyped(opticFinder7).isPresent() && !dynamic.get("fluid_ticks").result().isPresent()) { +@@ -144,7 +151,7 @@ public class ChunkProtoTickListFix extends DataFix { + int n = l >>> 4 & 15; + int o = l >>> 8 & 15; + String string = function.apply(supplier != null ? supplier.get().get(m, n, o) : null); +- return dynamic.createMap(ImmutableMap.builder().put(dynamic.createString("i"), dynamic.createString(string)).put(dynamic.createString("x"), dynamic.createInt(i * 16 + m)).put(dynamic.createString("y"), dynamic.createInt(j * 16 + n)).put(dynamic.createString("z"), dynamic.createInt(k * 16 + o)).put(dynamic.createString("t"), dynamic.createInt(0)).put(dynamic.createString("p"), dynamic.createInt(0)).build()); ++ return dynamic.createMap((Map) ImmutableMap.builder().put(dynamic.createString("i"), dynamic.createString(string)).put(dynamic.createString("x"), dynamic.createInt(i * 16 + m)).put(dynamic.createString("y"), dynamic.createInt(j * 16 + n)).put(dynamic.createString("z"), dynamic.createInt(k * 16 + o)).put(dynamic.createString("t"), dynamic.createInt(0)).put(dynamic.createString("p"), dynamic.createInt(0)).build()); // Gale - dev import deobfuscation fixes + } + + public static final class PoorMansPalettedContainer { diff --git a/src/main/java/net/minecraft/world/entity/monster/Zoglin.java b/src/main/java/net/minecraft/world/entity/monster/Zoglin.java index 5956a7759964f5e4939f062e93714fba64f53141..51ad507a3b625201ecca50bd92f8f089f3b4d60a 100644 --- a/src/main/java/net/minecraft/world/entity/monster/Zoglin.java @@ -118,3 +221,131 @@ index ac75c54e897565e340b66823caeed92ba1d1641a..e44eec821eaa1cd77569814000d4aa36 } @Override +diff --git a/src/main/java/net/minecraft/world/level/levelgen/SurfaceRules.java b/src/main/java/net/minecraft/world/level/levelgen/SurfaceRules.java +index daee1072171769d89783a7d7ef9e5692cb9fbc20..45b0706a48c4bf44923a2590f34913b7373de8f4 100644 +--- a/src/main/java/net/minecraft/world/level/levelgen/SurfaceRules.java ++++ b/src/main/java/net/minecraft/world/level/levelgen/SurfaceRules.java +@@ -154,7 +154,7 @@ public class SurfaceRules { + + @Override + public SurfaceRules.SurfaceRule apply(SurfaceRules.Context context) { +- return context.system::getBand; ++ return (x, y, z) -> context.system.getBand(x, y, z); // Gale - dev import deobfuscation fixes + } + } + +@@ -176,10 +176,10 @@ public class SurfaceRules { + } + + @Override +- public SurfaceRules.Condition apply(final SurfaceRules.Context context) { ++ public SurfaceRules.Condition apply(final SurfaceRules.Context givenContext) { // Gale - dev import deobfuscation fixes + class BiomeCondition extends SurfaceRules.LazyYCondition { + BiomeCondition() { +- super(context); ++ super(givenContext); // Gale - dev import deobfuscation fixes + } + + @Override +@@ -494,12 +494,14 @@ public class SurfaceRules { + } + + @Override +- public SurfaceRules.Condition apply(final SurfaceRules.Context context) { +- final NormalNoise normalNoise = context.randomState.getOrCreateNoise(this.noise); ++ // Gale start - dev import deobfuscation fixes ++ public SurfaceRules.Condition apply(final SurfaceRules.Context givenContext) { ++ final NormalNoise normalNoise = givenContext.randomState.getOrCreateNoise(this.noise); ++ // Gale end - dev import deobfuscation fixes + + class NoiseThresholdCondition extends SurfaceRules.LazyXZCondition { + NoiseThresholdCondition() { +- super(context); ++ super(givenContext); // Gale - dev import deobfuscation fixes + } + + @Override +@@ -622,12 +624,12 @@ public class SurfaceRules { + } + + @Override +- public SurfaceRules.Condition apply(final SurfaceRules.Context context) { ++ public SurfaceRules.Condition apply(final SurfaceRules.Context givenContext) { // Gale - dev import deobfuscation fixes + final boolean bl = this.surfaceType == CaveSurface.CEILING; + + class StoneDepthCondition extends SurfaceRules.LazyYCondition { + StoneDepthCondition() { +- super(context); ++ super(givenContext); // Gale - dev import deobfuscation fixes + } + + @Override +@@ -699,26 +701,32 @@ public class SurfaceRules { + } + + @Override +- public SurfaceRules.Condition apply(final SurfaceRules.Context context) { +- final int i = this.trueAtAndBelow().resolveY(context.context); +- final int j = this.falseAtAndAbove().resolveY(context.context); +- final PositionalRandomFactory positionalRandomFactory = context.randomState.getOrCreateRandomFactory(this.randomName()); ++ // Gale start - dev import deobfuscation fixes ++ public SurfaceRules.Condition apply(final SurfaceRules.Context givenContext) { ++ final int i = this.trueAtAndBelow().resolveY(givenContext.context); ++ final int j = this.falseAtAndAbove().resolveY(givenContext.context); ++ final PositionalRandomFactory positionalRandomFactory = givenContext.randomState.getOrCreateRandomFactory(this.randomName()); ++ // Gale end - dev import deobfuscation fixes + + class VerticalGradientCondition extends SurfaceRules.LazyYCondition { + VerticalGradientCondition() { +- super(context); ++ super(givenContext); // Gale - dev import deobfuscation fixes + } + + @Override + protected boolean compute() { +- int i = this.context.blockY; +- if (i <= i) { ++ // Gale start - dev import deobfuscation fixes ++ int i2 = this.context.blockY; ++ if (i2 <= i) { ++ // Gale end - dev import deobfuscation fixes + return true; +- } else if (i >= j) { ++ } else if (i2 >= j) { // Gale - dev import deobfuscation fixes + return false; + } else { +- double d = Mth.map((double)i, (double)i, (double)j, 1.0D, 0.0D); +- RandomSource randomSource = positionalRandomFactory.at(this.context.blockX, i, this.context.blockZ); ++ // Gale start - dev import deobfuscation fixes ++ double d = Mth.map((double)i2, (double)i, (double)j, 1.0D, 0.0D); ++ RandomSource randomSource = positionalRandomFactory.at(this.context.blockX, i2, this.context.blockZ); ++ // Gale end - dev import deobfuscation fixes + return (double)randomSource.nextFloat() < d; + } + } +@@ -739,10 +747,10 @@ public class SurfaceRules { + } + + @Override +- public SurfaceRules.Condition apply(final SurfaceRules.Context context) { ++ public SurfaceRules.Condition apply(final SurfaceRules.Context givenContext) { // Gale - dev import deobfuscation fixes + class WaterCondition extends SurfaceRules.LazyYCondition { + WaterCondition() { +- super(context); ++ super(givenContext); // Gale - dev import deobfuscation fixes + } + + @Override +@@ -766,10 +774,10 @@ public class SurfaceRules { + } + + @Override +- public SurfaceRules.Condition apply(final SurfaceRules.Context context) { ++ public SurfaceRules.Condition apply(final SurfaceRules.Context givenContext) { // Gale - dev import deobfuscation fixes + class YCondition extends SurfaceRules.LazyYCondition { + YCondition() { +- super(context); ++ super(givenContext); // Gale - dev import deobfuscation fixes + } + + @Override diff --git a/patches/server/0142-Mutex-utility.patch b/patches/server/0142-Mutex-utility.patch index 1c906da..8b49db3 100644 --- a/patches/server/0142-Mutex-utility.patch +++ b/patches/server/0142-Mutex-utility.patch @@ -8,15 +8,16 @@ Gale - https://galemc.org diff --git a/src/main/java/org/galemc/gale/concurrent/Mutex.java b/src/main/java/org/galemc/gale/concurrent/Mutex.java new file mode 100644 -index 0000000000000000000000000000000000000000..e8ab6cd25909a848c1826824a0f04dc593a26be7 +index 0000000000000000000000000000000000000000..56dcc0ec470743ab458f725b8fa7f5b9536102ad --- /dev/null +++ b/src/main/java/org/galemc/gale/concurrent/Mutex.java -@@ -0,0 +1,121 @@ +@@ -0,0 +1,122 @@ +// Gale - mutex utility + +package org.galemc.gale.concurrent; + +import org.galemc.gale.executor.annotation.thread.AnyThreadSafe; ++import org.galemc.gale.executor.lock.CheckableLock; +import org.jetbrains.annotations.NotNull; + +import java.util.concurrent.TimeUnit; @@ -36,7 +37,7 @@ index 0000000000000000000000000000000000000000..e8ab6cd25909a848c1826824a0f04dc5 + * @author Martijn Muijsers + */ +@AnyThreadSafe -+public interface Mutex extends Lock, AutoCloseable { ++public interface Mutex extends CheckableLock, AutoCloseable { + + void acquireUninterruptibly(); + @@ -128,17 +129,17 @@ index 0000000000000000000000000000000000000000..e8ab6cd25909a848c1826824a0f04dc5 + * Instantiates a new {@link Mutex} that we intend to use as a {@link Lock}, with a default implementation + * that should be geared towards performance of the {@link Lock} methods. + */ -+ static @NotNull Lock createLock() { ++ static @NotNull Mutex createLock() { + return create(); + } + +} diff --git a/src/main/java/org/galemc/gale/concurrent/SemaphoreMutex.java b/src/main/java/org/galemc/gale/concurrent/SemaphoreMutex.java new file mode 100644 -index 0000000000000000000000000000000000000000..2e31501d26b141729c80975e97a23b09653ba3bf +index 0000000000000000000000000000000000000000..c310264afea6c81ec575bdf6aa5495ccb34d7ae4 --- /dev/null +++ b/src/main/java/org/galemc/gale/concurrent/SemaphoreMutex.java -@@ -0,0 +1,34 @@ +@@ -0,0 +1,39 @@ +// Gale - mutex utility + +package org.galemc.gale.concurrent; @@ -172,4 +173,9 @@ index 0000000000000000000000000000000000000000..2e31501d26b141729c80975e97a23b09 + throw new UnsupportedOperationException("newCondition() is not implemented for SemaphoreMutex"); + } + ++ @Override ++ public boolean isLocked() { ++ return this.availablePermits() == 0; ++ } ++ +} diff --git a/patches/server/0143-Thread-aware-lock-utility.patch b/patches/server/0143-Thread-aware-lock-utility.patch new file mode 100644 index 0000000..eab237d --- /dev/null +++ b/patches/server/0143-Thread-aware-lock-utility.patch @@ -0,0 +1,123 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Martijn Muijsers +Date: Fri, 3 Feb 2023 23:01:51 +0100 +Subject: [PATCH] Thread-aware lock utility + +License: AGPL-3.0 (https://www.gnu.org/licenses/agpl-3.0.html) +Gale - https://galemc.org + +diff --git a/src/main/java/org/galemc/gale/concurrent/ThreadAwareNonReentrantLock.java b/src/main/java/org/galemc/gale/concurrent/ThreadAwareNonReentrantLock.java +new file mode 100644 +index 0000000000000000000000000000000000000000..1dd9d78f70fe586a9868e046e01ab512af036eeb +--- /dev/null ++++ b/src/main/java/org/galemc/gale/concurrent/ThreadAwareNonReentrantLock.java +@@ -0,0 +1,109 @@ ++// Gale - thread-aware lock utility ++ ++package org.galemc.gale.concurrent; ++ ++import net.minecraft.server.MinecraftServer; ++import org.galemc.gale.executor.lock.CheckableLock; ++import org.jetbrains.annotations.NotNull; ++import org.jetbrains.annotations.Nullable; ++ ++import java.util.concurrent.TimeUnit; ++import java.util.concurrent.locks.Condition; ++import java.util.concurrent.locks.Lock; ++ ++/** ++ * A wrapper for a non-reentrant {@link Lock}, that is aware when the thread that already holds this lock ++ * is trying to acquire it again (for example by calling {@link #tryLock}), and throws an exception in that case. ++ *
++ * This is useful for debugging purposes when a {@link Lock} is not supposed to be reachable ++ * from any code that is executed while the lock is being held. ++ */ ++public class ThreadAwareNonReentrantLock implements CheckableLock { ++ ++ private final CheckableLock innerLock; ++ ++ /** ++ * The {@link Thread} that currently holds this lock, or null if no thread currently holds this lock. ++ */ ++ private volatile @Nullable Thread currentHoldingThread; ++ ++ public ThreadAwareNonReentrantLock(CheckableLock innerLock) { ++ this.innerLock = innerLock; ++ } ++ ++ @Override ++ public void lock() { ++ var currentThread = Thread.currentThread(); ++ if (this.currentHoldingThread == currentThread) { ++ IllegalStateException exception = new IllegalStateException("Called lock() on a " + this.getClass().getSimpleName() + " from the thread (" + currentThread + ") that was already holding it"); ++ MinecraftServer.LOGGER.error(exception.getMessage() + ", at:"); ++ exception.printStackTrace(); ++ throw exception; ++ } ++ this.innerLock.lock(); ++ this.currentHoldingThread = currentThread; ++ } ++ ++ @Override ++ public void lockInterruptibly() throws InterruptedException { ++ var currentThread = Thread.currentThread(); ++ if (this.currentHoldingThread == currentThread) { ++ IllegalStateException exception = new IllegalStateException("Called lockInterruptibly() on a " + this.getClass().getSimpleName() + " from the thread (" + currentThread + ") that was already holding it"); ++ MinecraftServer.LOGGER.error(exception.getMessage() + ", at:"); ++ exception.printStackTrace(); ++ throw exception; ++ } ++ this.innerLock.lockInterruptibly(); ++ this.currentHoldingThread = currentThread; ++ } ++ ++ @Override ++ public boolean tryLock() { ++ var currentThread = Thread.currentThread(); ++ if (this.currentHoldingThread == currentThread) { ++ IllegalStateException exception = new IllegalStateException("Called tryLock() on a " + this.getClass().getSimpleName() + " from the thread (" + currentThread + ") that was already holding it"); ++ MinecraftServer.LOGGER.error(exception.getMessage() + ", at:"); ++ exception.printStackTrace(); ++ throw exception; ++ } ++ if (this.innerLock.tryLock()) { ++ this.currentHoldingThread = currentThread; ++ return true; ++ } ++ return false; ++ } ++ ++ @Override ++ public boolean tryLock(long time, @NotNull TimeUnit unit) throws InterruptedException { ++ var currentThread = Thread.currentThread(); ++ if (this.currentHoldingThread == currentThread) { ++ IllegalStateException exception = new IllegalStateException("Called tryLock() on a " + this.getClass().getSimpleName() + " from the thread (" + currentThread + ") that was already holding it"); ++ MinecraftServer.LOGGER.error(exception.getMessage() + ", at:"); ++ exception.printStackTrace(); ++ throw exception; ++ } ++ if (this.innerLock.tryLock(time, unit)) { ++ this.currentHoldingThread = currentThread; ++ return true; ++ } ++ return false; ++ } ++ ++ @Override ++ public void unlock() { ++ this.innerLock.unlock(); ++ this.currentHoldingThread = null; ++ } ++ ++ @NotNull ++ @Override ++ public Condition newCondition() { ++ return this.innerLock.newCondition(); ++ } ++ ++ @Override ++ public boolean isLocked() { ++ return this.innerLock.isLocked(); ++ } ++ ++} diff --git a/patches/server/0143-Unterminable-executor-utility.patch b/patches/server/0144-Unterminable-executor-utility.patch similarity index 100% rename from patches/server/0143-Unterminable-executor-utility.patch rename to patches/server/0144-Unterminable-executor-utility.patch diff --git a/patches/server/0144-FIFO-concurrent-queue-utility.patch b/patches/server/0145-FIFO-concurrent-queue-utility.patch similarity index 100% rename from patches/server/0144-FIFO-concurrent-queue-utility.patch rename to patches/server/0145-FIFO-concurrent-queue-utility.patch diff --git a/patches/server/0145-Base-thread-pool.patch b/patches/server/0146-Base-thread-pool.patch similarity index 98% rename from patches/server/0145-Base-thread-pool.patch rename to patches/server/0146-Base-thread-pool.patch index 3b9c566..9ef78b4 100644 --- a/patches/server/0145-Base-thread-pool.patch +++ b/patches/server/0146-Base-thread-pool.patch @@ -1851,10 +1851,10 @@ index 78f53ee557276de85f0431ebcb146445b1f4fb92..6176867eea06c53882dcaacfbde0334b return ret; diff --git a/src/main/java/org/galemc/gale/concurrent/Mutex.java b/src/main/java/org/galemc/gale/concurrent/Mutex.java -index e8ab6cd25909a848c1826824a0f04dc593a26be7..fdad4a7a15d4af427e1441dcc3d837faf1189f85 100644 +index 56dcc0ec470743ab458f725b8fa7f5b9536102ad..d01949290487dbe8806427af82c0505dce79f60d 100644 --- a/src/main/java/org/galemc/gale/concurrent/Mutex.java +++ b/src/main/java/org/galemc/gale/concurrent/Mutex.java -@@ -19,7 +19,7 @@ import java.util.concurrent.locks.Lock; +@@ -20,7 +20,7 @@ import java.util.concurrent.locks.Lock; *
* This interface extends {@link AutoCloseable}, where {@link #close()} calls {@link #release()}. * @@ -1862,9 +1862,9 @@ index e8ab6cd25909a848c1826824a0f04dc593a26be7..fdad4a7a15d4af427e1441dcc3d837fa + * @author Martijn Muijsers under AGPL-3.0 */ @AnyThreadSafe - public interface Mutex extends Lock, AutoCloseable { + public interface Mutex extends CheckableLock, AutoCloseable { diff --git a/src/main/java/org/galemc/gale/concurrent/SemaphoreMutex.java b/src/main/java/org/galemc/gale/concurrent/SemaphoreMutex.java -index 2e31501d26b141729c80975e97a23b09653ba3bf..5a454236073dd75ed36d058c0f033c4aada403e3 100644 +index c310264afea6c81ec575bdf6aa5495ccb34d7ae4..d31293a2a2151bc9fbdc6eb2175045b429fb4461 100644 --- a/src/main/java/org/galemc/gale/concurrent/SemaphoreMutex.java +++ b/src/main/java/org/galemc/gale/concurrent/SemaphoreMutex.java @@ -15,7 +15,7 @@ import java.util.concurrent.locks.Lock; @@ -2758,10 +2758,10 @@ index 0000000000000000000000000000000000000000..a248a9ea644a8bb4175da2e1903483ab +} diff --git a/src/main/java/org/galemc/gale/executor/lock/YieldingLock.java b/src/main/java/org/galemc/gale/executor/lock/YieldingLock.java new file mode 100644 -index 0000000000000000000000000000000000000000..ad3a7e94824c32c812e6ca57cc6cea78227eac5f +index 0000000000000000000000000000000000000000..3ec790a7b19790731f70f59cc2bdb1919f26dd2d --- /dev/null +++ b/src/main/java/org/galemc/gale/executor/lock/YieldingLock.java -@@ -0,0 +1,152 @@ +@@ -0,0 +1,163 @@ +// Gale - base thread pool + +package org.galemc.gale.executor.lock; @@ -2788,6 +2788,8 @@ index 0000000000000000000000000000000000000000..ad3a7e94824c32c812e6ca57cc6cea78 + * The lock only be speculatively acquired from any {@link AbstractYieldingThread}. + * Acquiring it on a thread that is not an {@link AbstractYieldingThread} will perform regular locking + * on the underlying controlled lock, which typically blocks the thread. ++ *
++ * A thread cannot acquire a {@link YieldingLock} when it already owns one. + * + * @author Martijn Muijsers under AGPL-3.0 + */ @@ -2840,10 +2842,10 @@ index 0000000000000000000000000000000000000000..ad3a7e94824c32c812e6ca57cc6cea78 + @PotentiallyYielding + @Override + public void lock() { ++ // Find out our current yielding thread, if any ++ @Nullable AbstractYieldingThread yieldingThread = AbstractYieldingThread.currentYieldingThread(); + // Try to acquire the lock straight away + if (!this.innerLock.tryLock()) { -+ // If unsuccessful, we find out our current thread -+ AbstractYieldingThread yieldingThread = AbstractYieldingThread.currentYieldingThread(); + // If we are not on a yielding thread, we wait for the lock instead of yielding + if (yieldingThread == null) { + this.innerLock.lock(); @@ -2852,6 +2854,10 @@ index 0000000000000000000000000000000000000000..ad3a7e94824c32c812e6ca57cc6cea78 + // Otherwise, we yield to other tasks until the lock can be acquired + yieldingThread.yieldUntil(null, this); + } ++ // Increment the YieldingLock count of the current thread ++ if (yieldingThread != null) { ++ yieldingThread.incrementHeldYieldingLockCount(); ++ } + } + + /** @@ -2860,6 +2866,11 @@ index 0000000000000000000000000000000000000000..ad3a7e94824c32c812e6ca57cc6cea78 + @Override + public void unlock() { + this.innerLock.unlock(); ++ // Decrement the YieldingLock count of the current thread ++ @Nullable AbstractYieldingThread yieldingThread = AbstractYieldingThread.currentYieldingThread(); ++ if (yieldingThread != null) { ++ yieldingThread.decrementHeldYieldingLockCount(); ++ } + // Potentially signal a thread that this lock has become available. + // Another thread could also acquire the lock at this moment, so when we signal the thread we obtain below, + // it may already be too late for the polled thread to acquire this lock @@ -4126,17 +4137,19 @@ index 0000000000000000000000000000000000000000..fb4f9c047fc71a9a01aa47871254c6a9 +} diff --git a/src/main/java/org/galemc/gale/executor/thread/AbstractYieldingThread.java b/src/main/java/org/galemc/gale/executor/thread/AbstractYieldingThread.java new file mode 100644 -index 0000000000000000000000000000000000000000..bb2055d8da984671762d9a5f8c4dc12f9353ceeb +index 0000000000000000000000000000000000000000..d1c305735488ee02e3d86c777ada068da955495b --- /dev/null +++ b/src/main/java/org/galemc/gale/executor/thread/AbstractYieldingThread.java -@@ -0,0 +1,43 @@ +@@ -0,0 +1,68 @@ +// Gale - base thread pool + +package org.galemc.gale.executor.thread; + +import org.galemc.gale.executor.annotation.thread.AnyThreadSafe; +import org.galemc.gale.executor.annotation.YieldFree; ++import org.galemc.gale.executor.annotation.thread.ThisThreadOnly; +import org.galemc.gale.executor.lock.YieldingLock; ++import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.function.BooleanSupplier; @@ -4149,13 +4162,36 @@ index 0000000000000000000000000000000000000000..bb2055d8da984671762d9a5f8c4dc12f + */ +public interface AbstractYieldingThread extends SignallableThread { + ++ /** ++ * @return Whether this thread currently holds any {@link YieldingLock}. ++ */ ++ @ThisThreadOnly ++ @YieldFree ++ boolean holdsYieldingLock(); + ++ /** ++ * Increments the number of times this thread is holding a {@link YieldingLock}. ++ * A thread can hold one {@link YieldingLock} multiple times (it can be reentrant). ++ */ ++ @ThisThreadOnly ++ @YieldFree ++ void incrementHeldYieldingLockCount(); ++ ++ /** ++ * Decrements the number of times this thread is holding a {@link YieldingLock}. ++ * ++ * @see #incrementHeldYieldingLockCount() ++ */ ++ @ThisThreadOnly ++ @YieldFree ++ void decrementHeldYieldingLockCount(); ++ ++ @ThisThreadOnly + void yieldUntil(@Nullable BooleanSupplier stopCondition, @Nullable YieldingLock yieldingLock); + -+ ++ @ThisThreadOnly + void runTasksUntil(@Nullable BooleanSupplier stopCondition, @Nullable YieldingLock yieldingLock); + -+ + @AnyThreadSafe + @YieldFree + static @Nullable AbstractYieldingThread currentYieldingThread() { @@ -4259,10 +4295,10 @@ index 0000000000000000000000000000000000000000..a5605765f6be0b75e5df5613e8b393b6 +} diff --git a/src/main/java/org/galemc/gale/executor/thread/BaseThread.java b/src/main/java/org/galemc/gale/executor/thread/BaseThread.java new file mode 100644 -index 0000000000000000000000000000000000000000..7cdd16786a8fba4fbc79b27c0baa9ed10a0bac21 +index 0000000000000000000000000000000000000000..633c1aeddca31f5fe95cc8c9ad54e6a2cf1b4ac1 --- /dev/null +++ b/src/main/java/org/galemc/gale/executor/thread/BaseThread.java -@@ -0,0 +1,683 @@ +@@ -0,0 +1,721 @@ +// Gale - base thread pool + +package org.galemc.gale.executor.thread; @@ -4326,6 +4362,15 @@ index 0000000000000000000000000000000000000000..7cdd16786a8fba4fbc79b27c0baa9ed1 + public final int maximumYieldDepth; + + /** ++ * The number of times this thread holds a {@link YieldingLock}, ++ * used in {@link #holdsYieldingLock()}. ++ * ++ * @see AbstractYieldingThread#incrementHeldYieldingLockCount() ++ */ ++ @ThisThreadOnly ++ public int heldYieldingLockCount = 0; ++ ++ /** + * The current yield depth of this thread. + */ + @AnyThreadSafe(Access.READ) @ThisThreadOnly(Access.WRITE) @@ -4335,7 +4380,8 @@ index 0000000000000000000000000000000000000000..7cdd16786a8fba4fbc79b27c0baa9ed1 + * Whether this thread can currently start yielding tasks with respect to being restricted + * due to {@link #yieldDepth} being at least {@link #maximumYieldDepth}. + *
-+ * This is updated after {@link #yieldDepth} is updated. ++ * This is updated using {@link #updateCanStartYieldingTasks()} ++ * after {@link #yieldDepth} or {@link #heldYieldingLockCount} is changed. + */ + @AnyThreadSafe(Access.READ) @ThisThreadOnly(Access.WRITE) + public volatile boolean canStartYieldingTasks = true; @@ -4437,6 +4483,34 @@ index 0000000000000000000000000000000000000000..7cdd16786a8fba4fbc79b27c0baa9ed1 + this.maximumYieldDepth = maximumYieldDepth; + } + ++ @Override ++ public boolean holdsYieldingLock() { ++ return this.heldYieldingLockCount > 0; ++ } ++ ++ @Override ++ public void incrementHeldYieldingLockCount() { ++ this.heldYieldingLockCount++; ++ if (this.heldYieldingLockCount == 1) { ++ this.updateCanStartYieldingTasks(); ++ } ++ } ++ ++ @Override ++ public void decrementHeldYieldingLockCount() { ++ this.heldYieldingLockCount--; ++ if (this.heldYieldingLockCount == 0) { ++ this.updateCanStartYieldingTasks(); ++ } ++ } ++ ++ /** ++ * Updates {@link #canStartYieldingTasks} according to {@link #yieldDepth} and {@link #heldYieldingLockCount}. ++ */ ++ private void updateCanStartYieldingTasks() { ++ this.canStartYieldingTasks = this.heldYieldingLockCount == 0 && this.yieldDepth < this.maximumYieldDepth; ++ } ++ + /** + * This method is based on {@link #signal}, and must be so, or {@link BaseThreadActivation} will get stuck + * while choosing a thread to activate. @@ -4475,16 +4549,16 @@ index 0000000000000000000000000000000000000000..7cdd16786a8fba4fbc79b27c0baa9ed1 + @ThisThreadOnly + @PotentiallyYielding("this method is meant to yield") + public final void yieldUntil(@Nullable BooleanSupplier stopCondition, @Nullable YieldingLock yieldingLock) { -+ //noinspection NonAtomicOperationOnVolatileField -+ this.yieldDepth++; -+ if (this.canStartYieldingTasks && this.yieldDepth >= this.maximumYieldDepth) { -+ this.canStartYieldingTasks = false; ++ int oldYieldDepth = this.yieldDepth; ++ int newYieldDepth = oldYieldDepth + 1; ++ this.yieldDepth = newYieldDepth; ++ if (newYieldDepth == maximumYieldDepth) { ++ this.updateCanStartYieldingTasks(); + } + this.runTasksUntil(stopCondition, yieldingLock); -+ //noinspection NonAtomicOperationOnVolatileField -+ this.yieldDepth--; -+ if (!this.canStartYieldingTasks && this.yieldDepth < this.maximumYieldDepth) { -+ this.canStartYieldingTasks = true; ++ this.yieldDepth = oldYieldDepth; ++ if (newYieldDepth == maximumYieldDepth) { ++ this.updateCanStartYieldingTasks(); + } + } + @@ -5419,10 +5493,10 @@ index 0000000000000000000000000000000000000000..77fe10e51b00115da520cfc211bf84ba +} diff --git a/src/main/java/org/galemc/gale/executor/thread/pool/BaseThreadActivation.java b/src/main/java/org/galemc/gale/executor/thread/pool/BaseThreadActivation.java new file mode 100644 -index 0000000000000000000000000000000000000000..1bcb4e58a1c3951c30ea4e6c12b877f3a10b6ca6 +index 0000000000000000000000000000000000000000..ac1f646ed846ed6067f77e7526e2bd0a43bf6677 --- /dev/null +++ b/src/main/java/org/galemc/gale/executor/thread/pool/BaseThreadActivation.java -@@ -0,0 +1,514 @@ +@@ -0,0 +1,518 @@ +// Gale - base thread pool + +package org.galemc.gale.executor.thread.pool; @@ -5608,8 +5682,9 @@ index 0000000000000000000000000000000000000000..1bcb4e58a1c3951c30ea4e6c12b877f3 + * {@link BaseThread#skipNextWait}, which are set at similar times as {@link BaseThread#isWaiting}. + * + *
  • -+ * {@link BaseThread#yieldDepth} and {@link BaseThread#canStartYieldingTasks}, -+ * which are always updated in tandem. ++ * {@link BaseThread#canStartYieldingTasks} and the values ++ * {@link BaseThread#yieldDepth} and {@link BaseThread#heldYieldingLockCount} it depends on. ++ * //TODO Gale We currently do not call callForUpdate just due to changes in heldYieldingLockCount, do we really have to? That would cause a lot of calls. + *
  • + * + * This specifically does not include: @@ -5855,6 +5930,7 @@ index 0000000000000000000000000000000000000000..1bcb4e58a1c3951c30ea4e6c12b877f3 + int threadIToUpdate = -1; + boolean threadIToUpdateIsWaitingForAvailableYieldingLock = false; + int threadIToUpdateYieldDepth = 0; ++ int threadIToUpdateYieldPotential = 0; + int threadIToUpdateTierOrdinalOrLength = 0; + for (int threadI = tryThreadsStart; threadI < tryThreadsEnd; threadI++) { + BaseThread thread = threads[threadI]; @@ -5878,6 +5954,7 @@ index 0000000000000000000000000000000000000000..1bcb4e58a1c3951c30ea4e6c12b877f3 + if (isThreadWaitingForAvailableYieldingLock || highestTierOfTaskOnStack == null || highestTierOfTaskOnStack.ordinal >= tierI) { + boolean isBestChoice = false; + int yieldDepth = thread.yieldDepth; ++ int yieldPotential = thread.maximumYieldDepth - yieldDepth; + if (threadIToUpdate == -1) { + isBestChoice = true; + } else if (isThreadWaitingForAvailableYieldingLock != threadIToUpdateIsWaitingForAvailableYieldingLock) { @@ -5885,7 +5962,7 @@ index 0000000000000000000000000000000000000000..1bcb4e58a1c3951c30ea4e6c12b877f3 + } else if (threadIToUpdateYieldDepth == 0 && yieldDepth != 0) { + isBestChoice = true; + } else if (yieldDepth != 0) { -+ if (yieldDepth < threadIToUpdateYieldDepth) { ++ if (yieldPotential > threadIToUpdateYieldPotential) { + isBestChoice = true; + } else if (highestTierOfTaskOnStackOrdinalOrLength > threadIToUpdateTierOrdinalOrLength) { + isBestChoice = true; @@ -5895,6 +5972,7 @@ index 0000000000000000000000000000000000000000..1bcb4e58a1c3951c30ea4e6c12b877f3 + threadIToUpdate = threadI; + threadIToUpdateIsWaitingForAvailableYieldingLock = isThreadWaitingForAvailableYieldingLock; + threadIToUpdateYieldDepth = yieldDepth; ++ threadIToUpdateYieldPotential = yieldPotential; + threadIToUpdateTierOrdinalOrLength = highestTierOfTaskOnStackOrdinalOrLength; + } + } diff --git a/patches/server/0151-Non-blocking-PooledObjects.patch b/patches/server/0147-Non-blocking-PooledObjects.patch similarity index 100% rename from patches/server/0151-Non-blocking-PooledObjects.patch rename to patches/server/0147-Non-blocking-PooledObjects.patch diff --git a/patches/server/0148-Yielding-memoized-Supplier.patch b/patches/server/0148-Yielding-memoized-Supplier.patch new file mode 100644 index 0000000..061d622 --- /dev/null +++ b/patches/server/0148-Yielding-memoized-Supplier.patch @@ -0,0 +1,427 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Martijn Muijsers +Date: Fri, 3 Feb 2023 23:17:33 +0100 +Subject: [PATCH] Yielding memoized Supplier + +License: AGPL-3.0 (https://www.gnu.org/licenses/agpl-3.0.html) +Gale - https://galemc.org + +diff --git a/src/main/java/io/papermc/paper/configuration/PaperConfigurations.java b/src/main/java/io/papermc/paper/configuration/PaperConfigurations.java +index 47a3580caef45ffe71446c247d4e06e332b2fda2..0b7d6bdd9d0543a4b37abfdd67c9fda4688134de 100644 +--- a/src/main/java/io/papermc/paper/configuration/PaperConfigurations.java ++++ b/src/main/java/io/papermc/paper/configuration/PaperConfigurations.java +@@ -41,6 +41,7 @@ import net.minecraft.world.level.levelgen.feature.ConfiguredFeature; + import org.apache.commons.lang3.RandomStringUtils; + import org.bukkit.configuration.ConfigurationSection; + import org.bukkit.configuration.file.YamlConfiguration; ++import org.galemc.gale.executor.lock.YieldingMemoizedSupplier; + import org.jetbrains.annotations.VisibleForTesting; + import org.slf4j.Logger; + import org.spigotmc.SpigotConfig; +@@ -127,7 +128,7 @@ public class PaperConfigurations extends Configurations SPIGOT_WORLD_DEFAULTS = Suppliers.memoize(() -> new SpigotWorldConfig(RandomStringUtils.randomAlphabetic(255)) { // Gale - Gale configuration ++ public static final Supplier SPIGOT_WORLD_DEFAULTS = new YieldingMemoizedSupplier<>(() -> new SpigotWorldConfig(RandomStringUtils.randomAlphabetic(255)) { // Gale - Gale configuration, yielding memoized Supplier + @Override // override to ensure "verbose" is false + public void init() { + SpigotConfig.readConfig(SpigotWorldConfig.class, this); +diff --git a/src/main/java/io/papermc/paper/console/BrigadierCommandCompleter.java b/src/main/java/io/papermc/paper/console/BrigadierCommandCompleter.java +index 0627c98cae0b5ebdd71a849ae1299d7d3d581850..b0ea72d8a186a8e2758d0b2e3204cc81463085d4 100644 +--- a/src/main/java/io/papermc/paper/console/BrigadierCommandCompleter.java ++++ b/src/main/java/io/papermc/paper/console/BrigadierCommandCompleter.java +@@ -15,6 +15,7 @@ import net.minecraft.commands.CommandSourceStack; + import net.minecraft.network.chat.ComponentUtils; + import net.minecraft.server.dedicated.DedicatedServer; + import org.checkerframework.checker.nullness.qual.NonNull; ++import org.galemc.gale.executor.lock.YieldingMemoizedSupplier; + import org.jline.reader.Candidate; + import org.jline.reader.LineReader; + import org.jline.reader.ParsedLine; +@@ -27,7 +28,7 @@ public final class BrigadierCommandCompleter { + + public BrigadierCommandCompleter(final @NonNull DedicatedServer server) { + this.server = server; +- this.commandSourceStack = Suppliers.memoize(this.server::createCommandSourceStack); ++ this.commandSourceStack = new YieldingMemoizedSupplier<>(this.server::createCommandSourceStack); // Gale - yielding memoized Supplier + } + + public void complete(final @NonNull LineReader reader, final @NonNull ParsedLine line, final @NonNull List candidates, final @NonNull List existing) { +diff --git a/src/main/java/io/papermc/paper/console/BrigadierCommandHighlighter.java b/src/main/java/io/papermc/paper/console/BrigadierCommandHighlighter.java +index dd9d77d7c7f1a5a130a1f4c15e5b1e68ae3753e1..c3661b25ae8b4dc5e9d7e9bb0e14f6d2b314f309 100644 +--- a/src/main/java/io/papermc/paper/console/BrigadierCommandHighlighter.java ++++ b/src/main/java/io/papermc/paper/console/BrigadierCommandHighlighter.java +@@ -9,6 +9,7 @@ import java.util.regex.Pattern; + import net.minecraft.commands.CommandSourceStack; + import net.minecraft.server.dedicated.DedicatedServer; + import org.checkerframework.checker.nullness.qual.NonNull; ++import org.galemc.gale.executor.lock.YieldingMemoizedSupplier; + import org.jline.reader.Highlighter; + import org.jline.reader.LineReader; + import org.jline.utils.AttributedString; +@@ -22,7 +23,7 @@ public final class BrigadierCommandHighlighter implements Highlighter { + + public BrigadierCommandHighlighter(final @NonNull DedicatedServer server) { + this.server = server; +- this.commandSourceStack = Suppliers.memoize(this.server::createCommandSourceStack); ++ this.commandSourceStack = new YieldingMemoizedSupplier<>(this.server::createCommandSourceStack); // Gale - yielding memoized Supplier + } + + @Override +diff --git a/src/main/java/io/papermc/paper/registry/PaperRegistry.java b/src/main/java/io/papermc/paper/registry/PaperRegistry.java +index 7c265d27da034986be73921d35bf08ae250b42f3..89c4b9e4feb6e7720efbb91cf8ba380a89f34d6e 100644 +--- a/src/main/java/io/papermc/paper/registry/PaperRegistry.java ++++ b/src/main/java/io/papermc/paper/registry/PaperRegistry.java +@@ -14,6 +14,7 @@ import org.bukkit.craftbukkit.util.CraftNamespacedKey; + import org.checkerframework.checker.nullness.qual.NonNull; + import org.checkerframework.checker.nullness.qual.Nullable; + import org.checkerframework.framework.qual.DefaultQualifier; ++import org.galemc.gale.executor.lock.YieldingMemoizedSupplier; + + import java.util.Collections; + import java.util.HashMap; +@@ -29,7 +30,7 @@ import java.util.function.Supplier; + public abstract class PaperRegistry implements org.bukkit.Registry { + + @SuppressWarnings("FieldMayBeFinal") // non-final for testing +- private static Supplier REGISTRY_ACCESS = Suppliers.memoize(() -> MinecraftServer.getServer().registryAccess()); ++ private static Supplier REGISTRY_ACCESS = new YieldingMemoizedSupplier<>(() -> MinecraftServer.getServer().registryAccess()); // Gale - yielding memoized Supplier + private static final Map, PaperRegistry> INTERNAL_REGISTRIES = new HashMap<>(); + public static final Map, PaperRegistry> REGISTRIES = Collections.unmodifiableMap(INTERNAL_REGISTRIES); + private static final Map, PaperRegistry> REGISTRY_BY_API_CLASS = new HashMap<>(); +@@ -43,7 +44,7 @@ public abstract class PaperRegistry implements org + + public PaperRegistry(RegistryKey registryKey) { + this.registryKey = registryKey; +- this.registry = Suppliers.memoize(() -> REGISTRY_ACCESS.get().registryOrThrow(this.registryKey.resourceKey())); ++ this.registry = new YieldingMemoizedSupplier<>(() -> REGISTRY_ACCESS.get().registryOrThrow(this.registryKey.resourceKey())); // Gale - yielding memoized Supplier + } + + @Override +@@ -138,7 +139,7 @@ public abstract class PaperRegistry implements org + } + + protected static Supplier> registryFor(ResourceKey> registryKey) { +- return Suppliers.memoize(() -> REGISTRY_ACCESS.get().registryOrThrow(registryKey)); ++ return new YieldingMemoizedSupplier<>(() -> REGISTRY_ACCESS.get().registryOrThrow(registryKey)); // Gale - yielding memoized Supplier + } + + public static void clearCaches() { +diff --git a/src/main/java/net/minecraft/util/ExtraCodecs.java b/src/main/java/net/minecraft/util/ExtraCodecs.java +index 7591002e07271389f9ac8decea25747c0cd8a213..506e8b0b06aca455ea608b6cee2292f120ae5e90 100644 +--- a/src/main/java/net/minecraft/util/ExtraCodecs.java ++++ b/src/main/java/net/minecraft/util/ExtraCodecs.java +@@ -1,7 +1,6 @@ + package net.minecraft.util; + + import com.google.common.annotations.VisibleForTesting; +-import com.google.common.base.Suppliers; + import com.google.common.collect.ImmutableList; + import com.google.gson.JsonElement; + import com.google.gson.JsonParseException; +@@ -47,6 +46,7 @@ import net.minecraft.core.UUIDUtil; + import net.minecraft.network.chat.Component; + import net.minecraft.resources.ResourceLocation; + import org.apache.commons.lang3.mutable.MutableObject; ++import org.galemc.gale.executor.lock.YieldingMemoizedSupplier; + import org.joml.Vector3f; + + public class ExtraCodecs { +@@ -459,7 +459,7 @@ public class ExtraCodecs { + + static record LazyInitializedCodec
    (Supplier> delegate) implements Codec { + LazyInitializedCodec { +- delegate = Suppliers.memoize(delegate::get); // Gale - dev import deobfuscation fixes ++ delegate = new YieldingMemoizedSupplier<>(delegate::get); // Gale - dev import deobfuscation fixes, yielding memoized Supplier + } + + public DataResult> decode(DynamicOps dynamicOps, T object) { +diff --git a/src/main/java/net/minecraft/util/LazyLoadedValue.java b/src/main/java/net/minecraft/util/LazyLoadedValue.java +index 906d4bca809fe2ae6b45e7212be8547b81422b01..3ceff89ef71768826be1156538dc70f25d68488a 100644 +--- a/src/main/java/net/minecraft/util/LazyLoadedValue.java ++++ b/src/main/java/net/minecraft/util/LazyLoadedValue.java +@@ -1,6 +1,7 @@ + package net.minecraft.util; + +-import com.google.common.base.Suppliers; ++import org.galemc.gale.executor.lock.YieldingMemoizedSupplier; ++ + import java.util.function.Supplier; + + /** @deprecated */ +@@ -9,7 +10,7 @@ public class LazyLoadedValue { + private final Supplier factory; + + public LazyLoadedValue(Supplier delegate) { +- this.factory = Suppliers.memoize(delegate::get); ++ this.factory = new YieldingMemoizedSupplier<>(delegate); // Gale - yielding memoized Supplier + } + + public T get() { +diff --git a/src/main/java/net/minecraft/util/datafix/fixes/ChunkProtoTickListFix.java b/src/main/java/net/minecraft/util/datafix/fixes/ChunkProtoTickListFix.java +index d6de68b4b0edabf37ba551b91bbe6682895b2a82..b4cf65c1b84a4ebcf4a362ec614f5fd7a585ab2e 100644 +--- a/src/main/java/net/minecraft/util/datafix/fixes/ChunkProtoTickListFix.java ++++ b/src/main/java/net/minecraft/util/datafix/fixes/ChunkProtoTickListFix.java +@@ -1,6 +1,5 @@ + package net.minecraft.util.datafix.fixes; + +-import com.google.common.base.Suppliers; + import com.google.common.collect.ImmutableMap; + import com.google.common.collect.ImmutableSet; + import com.mojang.datafixers.DSL; +@@ -24,6 +23,7 @@ import java.util.function.Supplier; + import java.util.stream.Stream; + import javax.annotation.Nullable; + import org.apache.commons.lang3.mutable.MutableInt; ++import org.galemc.gale.executor.lock.YieldingMemoizedSupplier; + + public class ChunkProtoTickListFix extends DataFix { + private static final int SECTION_WIDTH = 16; +@@ -64,7 +64,7 @@ public class ChunkProtoTickListFix extends DataFix { + } + + typedx2.getOptionalTyped(opticFinder4).ifPresent((typed3) -> { // Gale - dev import deobfuscation fixes +- int2ObjectMap.put(i, Suppliers.memoize(() -> { ++ int2ObjectMap.put(i, new YieldingMemoizedSupplier<>(() -> { // Gale - yielding memoized Supplier + // Gale start - dev import deobfuscation fixes + List> list = typed.getOptionalTyped(opticFinder6).map((typedx3) -> { + return typedx3.write().result().map((dynamic3) -> { +diff --git a/src/main/java/net/minecraft/world/inventory/AbstractContainerMenu.java b/src/main/java/net/minecraft/world/inventory/AbstractContainerMenu.java +index 95ba37458e8154dbce6a8590508840d694fcbed1..24af3be284d5ed693ec932e53935a777d0a8d266 100644 +--- a/src/main/java/net/minecraft/world/inventory/AbstractContainerMenu.java ++++ b/src/main/java/net/minecraft/world/inventory/AbstractContainerMenu.java +@@ -33,6 +33,7 @@ import net.minecraft.world.flag.FeatureFlagSet; + import net.minecraft.world.item.ItemStack; + import net.minecraft.world.level.block.Block; + import net.minecraft.world.level.block.entity.BlockEntity; ++import org.galemc.gale.executor.lock.YieldingMemoizedSupplier; + import org.slf4j.Logger; + + // CraftBukkit start +@@ -238,7 +239,7 @@ public abstract class AbstractContainerMenu { + ItemStack itemstack = ((Slot) this.slots.get(i)).getItem(); + + Objects.requireNonNull(itemstack); +- Supplier supplier = Suppliers.memoize(itemstack::copy); ++ Supplier supplier = new YieldingMemoizedSupplier<>(itemstack::copy); // Gale - yielding memoized Supplier + + this.triggerSlotListeners(i, itemstack, supplier); + this.synchronizeSlotToRemote(i, itemstack, supplier); +diff --git a/src/main/java/net/minecraft/world/item/HoneycombItem.java b/src/main/java/net/minecraft/world/item/HoneycombItem.java +index 1f8b7b50c6aa24778d87821ae2ff4d019d176082..d63da08e75e25860e5ae54674d32f5b3ff421e22 100644 +--- a/src/main/java/net/minecraft/world/item/HoneycombItem.java ++++ b/src/main/java/net/minecraft/world/item/HoneycombItem.java +@@ -16,12 +16,13 @@ import net.minecraft.world.level.block.Block; + import net.minecraft.world.level.block.Blocks; + import net.minecraft.world.level.block.state.BlockState; + import net.minecraft.world.level.gameevent.GameEvent; ++import org.galemc.gale.executor.lock.YieldingMemoizedSupplier; + + public class HoneycombItem extends Item { +- public static final Supplier> WAXABLES = Suppliers.memoize(() -> { ++ public static final Supplier> WAXABLES = new YieldingMemoizedSupplier<>(() -> { // Gale - yielding memoized Supplier + return ImmutableBiMap.builder().put(Blocks.COPPER_BLOCK, Blocks.WAXED_COPPER_BLOCK).put(Blocks.EXPOSED_COPPER, Blocks.WAXED_EXPOSED_COPPER).put(Blocks.WEATHERED_COPPER, Blocks.WAXED_WEATHERED_COPPER).put(Blocks.OXIDIZED_COPPER, Blocks.WAXED_OXIDIZED_COPPER).put(Blocks.CUT_COPPER, Blocks.WAXED_CUT_COPPER).put(Blocks.EXPOSED_CUT_COPPER, Blocks.WAXED_EXPOSED_CUT_COPPER).put(Blocks.WEATHERED_CUT_COPPER, Blocks.WAXED_WEATHERED_CUT_COPPER).put(Blocks.OXIDIZED_CUT_COPPER, Blocks.WAXED_OXIDIZED_CUT_COPPER).put(Blocks.CUT_COPPER_SLAB, Blocks.WAXED_CUT_COPPER_SLAB).put(Blocks.EXPOSED_CUT_COPPER_SLAB, Blocks.WAXED_EXPOSED_CUT_COPPER_SLAB).put(Blocks.WEATHERED_CUT_COPPER_SLAB, Blocks.WAXED_WEATHERED_CUT_COPPER_SLAB).put(Blocks.OXIDIZED_CUT_COPPER_SLAB, Blocks.WAXED_OXIDIZED_CUT_COPPER_SLAB).put(Blocks.CUT_COPPER_STAIRS, Blocks.WAXED_CUT_COPPER_STAIRS).put(Blocks.EXPOSED_CUT_COPPER_STAIRS, Blocks.WAXED_EXPOSED_CUT_COPPER_STAIRS).put(Blocks.WEATHERED_CUT_COPPER_STAIRS, Blocks.WAXED_WEATHERED_CUT_COPPER_STAIRS).put(Blocks.OXIDIZED_CUT_COPPER_STAIRS, Blocks.WAXED_OXIDIZED_CUT_COPPER_STAIRS).build(); + }); +- public static final Supplier> WAX_OFF_BY_BLOCK = Suppliers.memoize(() -> { ++ public static final Supplier> WAX_OFF_BY_BLOCK = new YieldingMemoizedSupplier<>(() -> { // Gale - yielding memoized Supplier + return WAXABLES.get().inverse(); + }); + +diff --git a/src/main/java/net/minecraft/world/level/PathNavigationRegion.java b/src/main/java/net/minecraft/world/level/PathNavigationRegion.java +index efe922810507c96183a56a5e81a7b14214d8747b..b87c9a6189161e776fa488c1ac6215edca596184 100644 +--- a/src/main/java/net/minecraft/world/level/PathNavigationRegion.java ++++ b/src/main/java/net/minecraft/world/level/PathNavigationRegion.java +@@ -24,6 +24,7 @@ import net.minecraft.world.level.material.FluidState; + import net.minecraft.world.level.material.Fluids; + import net.minecraft.world.phys.AABB; + import net.minecraft.world.phys.shapes.VoxelShape; ++import org.galemc.gale.executor.lock.YieldingMemoizedSupplier; + + public class PathNavigationRegion implements BlockGetter, CollisionGetter { + protected final int centerX; +@@ -35,7 +36,7 @@ public class PathNavigationRegion implements BlockGetter, CollisionGetter { + + public PathNavigationRegion(Level world, BlockPos minPos, BlockPos maxPos) { + this.level = world; +- this.plains = Suppliers.memoize(() -> { ++ this.plains = new YieldingMemoizedSupplier<>(() -> { // Gale - yielding memoized Supplier + return world.registryAccess().registryOrThrow(Registries.BIOME).getHolderOrThrow(Biomes.PLAINS); + }); + this.centerX = SectionPos.blockToSectionCoord(minPos.getX()); +diff --git a/src/main/java/net/minecraft/world/level/biome/BiomeGenerationSettings.java b/src/main/java/net/minecraft/world/level/biome/BiomeGenerationSettings.java +index 3f9628ec396a11ffd49270be7996d13f606366b6..3f7e8bb767ba4e886549cf487a20f62c3519012a 100644 +--- a/src/main/java/net/minecraft/world/level/biome/BiomeGenerationSettings.java ++++ b/src/main/java/net/minecraft/world/level/biome/BiomeGenerationSettings.java +@@ -26,6 +26,7 @@ import net.minecraft.world.level.levelgen.carver.ConfiguredWorldCarver; + import net.minecraft.world.level.levelgen.feature.ConfiguredFeature; + import net.minecraft.world.level.levelgen.feature.Feature; + import net.minecraft.world.level.levelgen.placement.PlacedFeature; ++import org.galemc.gale.executor.lock.YieldingMemoizedSupplier; + import org.slf4j.Logger; + + public class BiomeGenerationSettings { +@@ -46,12 +47,12 @@ public class BiomeGenerationSettings { + BiomeGenerationSettings(Map>> carvers, List> features) { + this.carvers = carvers; + this.features = features; +- this.flowerFeatures = Suppliers.memoize(() -> { ++ this.flowerFeatures = new YieldingMemoizedSupplier<>(() -> { // Gale - yielding memoized Supplier + return features.stream().flatMap(HolderSet::stream).map(Holder::value).flatMap(PlacedFeature::getFeatures).filter((feature) -> { + return feature.feature() == Feature.FLOWER; + }).collect(ImmutableList.toImmutableList()); + }); +- this.featureSet = Suppliers.memoize(() -> { ++ this.featureSet = new YieldingMemoizedSupplier<>(() -> { // Gale - yielding memoized Supplier + return features.stream().flatMap(HolderSet::stream).map(Holder::value).collect(Collectors.toSet()); + }); + } +diff --git a/src/main/java/net/minecraft/world/level/block/WeatheringCopper.java b/src/main/java/net/minecraft/world/level/block/WeatheringCopper.java +index 16bd93606ac4a9c34108a92dd4b98cb4600221d9..f4aeb2a4cafe7fe0782660c0e7fd94ae216a8940 100644 +--- a/src/main/java/net/minecraft/world/level/block/WeatheringCopper.java ++++ b/src/main/java/net/minecraft/world/level/block/WeatheringCopper.java +@@ -6,12 +6,13 @@ import com.google.common.collect.ImmutableBiMap; + import java.util.Optional; + import java.util.function.Supplier; + import net.minecraft.world.level.block.state.BlockState; ++import org.galemc.gale.executor.lock.YieldingMemoizedSupplier; + + public interface WeatheringCopper extends ChangeOverTimeBlock { +- Supplier> NEXT_BY_BLOCK = Suppliers.memoize(() -> { ++ Supplier> NEXT_BY_BLOCK = new YieldingMemoizedSupplier<>(() -> { // Gale - yielding memoized Supplier + return ImmutableBiMap.builder().put(Blocks.COPPER_BLOCK, Blocks.EXPOSED_COPPER).put(Blocks.EXPOSED_COPPER, Blocks.WEATHERED_COPPER).put(Blocks.WEATHERED_COPPER, Blocks.OXIDIZED_COPPER).put(Blocks.CUT_COPPER, Blocks.EXPOSED_CUT_COPPER).put(Blocks.EXPOSED_CUT_COPPER, Blocks.WEATHERED_CUT_COPPER).put(Blocks.WEATHERED_CUT_COPPER, Blocks.OXIDIZED_CUT_COPPER).put(Blocks.CUT_COPPER_SLAB, Blocks.EXPOSED_CUT_COPPER_SLAB).put(Blocks.EXPOSED_CUT_COPPER_SLAB, Blocks.WEATHERED_CUT_COPPER_SLAB).put(Blocks.WEATHERED_CUT_COPPER_SLAB, Blocks.OXIDIZED_CUT_COPPER_SLAB).put(Blocks.CUT_COPPER_STAIRS, Blocks.EXPOSED_CUT_COPPER_STAIRS).put(Blocks.EXPOSED_CUT_COPPER_STAIRS, Blocks.WEATHERED_CUT_COPPER_STAIRS).put(Blocks.WEATHERED_CUT_COPPER_STAIRS, Blocks.OXIDIZED_CUT_COPPER_STAIRS).build(); + }); +- Supplier> PREVIOUS_BY_BLOCK = Suppliers.memoize(() -> { ++ Supplier> PREVIOUS_BY_BLOCK = new YieldingMemoizedSupplier<>(() -> { // Gale - yielding memoized Supplier + return NEXT_BY_BLOCK.get().inverse(); + }); + +diff --git a/src/main/java/net/minecraft/world/level/chunk/ChunkGenerator.java b/src/main/java/net/minecraft/world/level/chunk/ChunkGenerator.java +index 7e9c388179c75a233d9b179ea1e00428ac65ee99..9f0a04b8aad7dc14f87dc48b70968b55d84951b8 100644 +--- a/src/main/java/net/minecraft/world/level/chunk/ChunkGenerator.java ++++ b/src/main/java/net/minecraft/world/level/chunk/ChunkGenerator.java +@@ -76,6 +76,7 @@ import net.minecraft.world.level.levelgen.structure.placement.RandomSpreadStruct + import net.minecraft.world.level.levelgen.structure.placement.StructurePlacement; + import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplateManager; + import org.apache.commons.lang3.mutable.MutableBoolean; ++import org.galemc.gale.executor.lock.YieldingMemoizedSupplier; + + public abstract class ChunkGenerator { + +@@ -93,7 +94,7 @@ public abstract class ChunkGenerator { + public ChunkGenerator(BiomeSource biomeSource, Function, BiomeGenerationSettings> generationSettingsGetter) { + this.biomeSource = biomeSource; + this.generationSettingsGetter = generationSettingsGetter; +- this.featuresPerStep = Suppliers.memoize(() -> { ++ this.featuresPerStep = new YieldingMemoizedSupplier<>(() -> { // Gale - yielding memoized Supplier + return FeatureSorter.buildFeaturesPerStep(List.copyOf(biomeSource.possibleBiomes()), (holder) -> { + return ((BiomeGenerationSettings) generationSettingsGetter.apply(holder)).features(); + }, true); +diff --git a/src/main/java/net/minecraft/world/level/levelgen/NoiseBasedChunkGenerator.java b/src/main/java/net/minecraft/world/level/levelgen/NoiseBasedChunkGenerator.java +index 6c93e621d584927076b886c719e93039019e32be..4b8bf8f150030c014ccff1290d0b996441a29adf 100644 +--- a/src/main/java/net/minecraft/world/level/levelgen/NoiseBasedChunkGenerator.java ++++ b/src/main/java/net/minecraft/world/level/levelgen/NoiseBasedChunkGenerator.java +@@ -49,6 +49,7 @@ import net.minecraft.world.level.levelgen.blending.Blender; + import net.minecraft.world.level.levelgen.carver.CarvingContext; + import net.minecraft.world.level.levelgen.carver.ConfiguredWorldCarver; + import org.apache.commons.lang3.mutable.MutableObject; ++import org.galemc.gale.executor.lock.YieldingMemoizedSupplier; + + public final class NoiseBasedChunkGenerator extends ChunkGenerator { + +@@ -67,7 +68,7 @@ public final class NoiseBasedChunkGenerator extends ChunkGenerator { + public NoiseBasedChunkGenerator(BiomeSource biomeSource, Holder settings) { + super(biomeSource); + this.settings = settings; +- this.globalFluidPicker = Suppliers.memoize(() -> { ++ this.globalFluidPicker = new YieldingMemoizedSupplier<>(() -> { // Gale - yielding memoized Supplier + // Gale start - Lithium - cache world generator sea level + var fluidPicker = NoiseBasedChunkGenerator.createFluidPicker((NoiseGeneratorSettings) settings.value()); + this.cachedSeaLevel = settings.value().seaLevel(); +diff --git a/src/main/java/net/minecraft/world/level/levelgen/SurfaceRules.java b/src/main/java/net/minecraft/world/level/levelgen/SurfaceRules.java +index 45b0706a48c4bf44923a2590f34913b7373de8f4..5990287d2ffaa32accb2444d6627ed29dc65d60d 100644 +--- a/src/main/java/net/minecraft/world/level/levelgen/SurfaceRules.java ++++ b/src/main/java/net/minecraft/world/level/levelgen/SurfaceRules.java +@@ -28,6 +28,7 @@ import net.minecraft.world.level.block.state.BlockState; + import net.minecraft.world.level.chunk.ChunkAccess; + import net.minecraft.world.level.levelgen.placement.CaveSurface; + import net.minecraft.world.level.levelgen.synth.NormalNoise; ++import org.galemc.gale.executor.lock.YieldingMemoizedSupplier; + + public class SurfaceRules { + public static final SurfaceRules.ConditionSource ON_FLOOR = stoneDepthCheck(0, false, CaveSurface.FLOOR); +@@ -310,7 +311,7 @@ public class SurfaceRules { + + protected void updateY(int stoneDepthAbove, int stoneDepthBelow, int fluidHeight, int blockX, int blockY, int blockZ) { + ++this.lastUpdateY; +- this.biome = Suppliers.memoize(() -> { ++ this.biome = new YieldingMemoizedSupplier<>(() -> { // Gale - yielding memoized Supplier + return this.biomeGetter.apply(this.pos.set(blockX, blockY, blockZ)); + }); + this.blockY = blockY; +diff --git a/src/main/java/org/galemc/gale/executor/lock/YieldingMemoizedSupplier.java b/src/main/java/org/galemc/gale/executor/lock/YieldingMemoizedSupplier.java +new file mode 100644 +index 0000000000000000000000000000000000000000..8732ea0a07a88117e0e6a2fe073a48bad4ff8b19 +--- /dev/null ++++ b/src/main/java/org/galemc/gale/executor/lock/YieldingMemoizedSupplier.java +@@ -0,0 +1,60 @@ ++// Gale - yielding memoized Supplier ++ ++package org.galemc.gale.executor.lock; ++ ++import com.google.common.base.Suppliers; ++import org.galemc.gale.concurrent.Mutex; ++import org.galemc.gale.concurrent.ThreadAwareNonReentrantLock; ++import org.jetbrains.annotations.Nullable; ++ ++import java.util.function.Supplier; ++ ++/** ++ * A replacement for {@link Suppliers#memoize} that provides thread-safety ++ * with a {@link YieldingLock} instead of by a synchronized block. ++ *
    ++ * The {@link #get()} method of this supplier must not be reachable from the body of the original supplier. ++ * To assure this, the lock used internally is a {@link ThreadAwareNonReentrantLock}. ++ *
    ++ * The original {@link Supplier} will be de-referenced once the value has been provided once, ++ * so that it may be garbage collected. ++ */ ++public class YieldingMemoizedSupplier implements Supplier { ++ ++ private volatile @Nullable Supplier delegate; ++ private volatile boolean initialized; ++ @Nullable T value; ++ private final YieldingLock lock = new MultipleWaitingBaseThreadsYieldingLock(new ThreadAwareNonReentrantLock(Mutex.createLock())); ++ ++ public YieldingMemoizedSupplier(Supplier delegate) { ++ this.delegate = delegate; ++ } ++ ++ @Override ++ public T get() { ++ if (!this.initialized) { ++ this.lock.lock(); ++ try { ++ if (!this.initialized) { ++ T t = this.delegate.get(); ++ this.value = t; ++ this.initialized = true; ++ this.delegate = null; ++ return t; ++ } ++ } finally { ++ this.lock.unlock(); ++ } ++ } ++ return value; ++ } ++ ++ @Override ++ public String toString() { ++ var delegate = this.delegate; ++ return this.getClass().getSimpleName() + "(" ++ + (delegate == null ? "" : delegate) ++ + ")"; ++ } ++ ++} diff --git a/patches/server/0146-Run-async-executor-tasks-on-base-thread-pool.patch b/patches/server/0149-Run-async-executor-tasks-on-base-thread-pool.patch similarity index 100% rename from patches/server/0146-Run-async-executor-tasks-on-base-thread-pool.patch rename to patches/server/0149-Run-async-executor-tasks-on-base-thread-pool.patch diff --git a/patches/server/0147-Run-background-executor-tasks-on-base-thread-pool.patch b/patches/server/0150-Run-background-executor-tasks-on-base-thread-pool.patch similarity index 100% rename from patches/server/0147-Run-background-executor-tasks-on-base-thread-pool.patch rename to patches/server/0150-Run-background-executor-tasks-on-base-thread-pool.patch diff --git a/patches/server/0148-Run-world-upgrade-tasks-on-base-thread-pool.patch b/patches/server/0151-Run-world-upgrade-tasks-on-base-thread-pool.patch similarity index 100% rename from patches/server/0148-Run-world-upgrade-tasks-on-base-thread-pool.patch rename to patches/server/0151-Run-world-upgrade-tasks-on-base-thread-pool.patch diff --git a/patches/server/0149-Run-tab-completion-tasks-on-base-thread-pool.patch b/patches/server/0152-Run-tab-completion-tasks-on-base-thread-pool.patch similarity index 100% rename from patches/server/0149-Run-tab-completion-tasks-on-base-thread-pool.patch rename to patches/server/0152-Run-tab-completion-tasks-on-base-thread-pool.patch diff --git a/patches/server/0150-Run-text-filter-tasks-on-base-thread-pool.patch b/patches/server/0153-Run-text-filter-tasks-on-base-thread-pool.patch similarity index 100% rename from patches/server/0150-Run-text-filter-tasks-on-base-thread-pool.patch rename to patches/server/0153-Run-text-filter-tasks-on-base-thread-pool.patch diff --git a/patches/server/0152-Run-cleaner-tasks-on-base-thread-pool.patch b/patches/server/0154-Run-cleaner-tasks-on-base-thread-pool.patch similarity index 100% rename from patches/server/0152-Run-cleaner-tasks-on-base-thread-pool.patch rename to patches/server/0154-Run-cleaner-tasks-on-base-thread-pool.patch diff --git a/patches/server/0153-Run-chunk-cache-tasks-on-base-thread-pool.patch b/patches/server/0155-Run-chunk-cache-tasks-on-base-thread-pool.patch similarity index 100% rename from patches/server/0153-Run-chunk-cache-tasks-on-base-thread-pool.patch rename to patches/server/0155-Run-chunk-cache-tasks-on-base-thread-pool.patch diff --git a/patches/server/0154-Run-TickThread-chunk-tasks-on-base-thread-pool.patch b/patches/server/0156-Run-TickThread-chunk-tasks-on-base-thread-pool.patch similarity index 100% rename from patches/server/0154-Run-TickThread-chunk-tasks-on-base-thread-pool.patch rename to patches/server/0156-Run-TickThread-chunk-tasks-on-base-thread-pool.patch diff --git a/patches/server/0155-BaseThread-PrioritisedQueueExecutorThread-agent-util.patch b/patches/server/0157-BaseThread-PrioritisedQueueExecutorThread-agent-util.patch similarity index 100% rename from patches/server/0155-BaseThread-PrioritisedQueueExecutorThread-agent-util.patch rename to patches/server/0157-BaseThread-PrioritisedQueueExecutorThread-agent-util.patch diff --git a/patches/server/0156-Run-chunk-worker-tasks-on-base-thread-pool.patch b/patches/server/0158-Run-chunk-worker-tasks-on-base-thread-pool.patch similarity index 98% rename from patches/server/0156-Run-chunk-worker-tasks-on-base-thread-pool.patch rename to patches/server/0158-Run-chunk-worker-tasks-on-base-thread-pool.patch index de917be..1968096 100644 --- a/patches/server/0156-Run-chunk-worker-tasks-on-base-thread-pool.patch +++ b/patches/server/0158-Run-chunk-worker-tasks-on-base-thread-pool.patch @@ -545,10 +545,10 @@ index ed3ccf2e64539363a7be2d507c68c40b5913f75c..a12250e5aaed02995b7bf09a8018a93f } diff --git a/src/main/java/org/galemc/gale/executor/queue/ChunkWorkerTaskQueue.java b/src/main/java/org/galemc/gale/executor/queue/ChunkWorkerTaskQueue.java new file mode 100644 -index 0000000000000000000000000000000000000000..3f7095f502f883715799958f36980bbfa8ccf581 +index 0000000000000000000000000000000000000000..6f78603c87517bb681ae473d0c72b75e23db6689 --- /dev/null +++ b/src/main/java/org/galemc/gale/executor/queue/ChunkWorkerTaskQueue.java -@@ -0,0 +1,104 @@ +@@ -0,0 +1,102 @@ +// Gale - base thread pool - chunk worker task queue + +package org.galemc.gale.executor.queue; @@ -566,9 +566,7 @@ index 0000000000000000000000000000000000000000..3f7095f502f883715799958f36980bbf + * This class provides access to, but does not store, the tasks related to chunk loading and chunk generation, + * that are scheduled and normally polled by the chunk system's {@link PrioritisedQueueExecutorThread}s. + *
    -+ * All tasks provided by this queue must be yield-free. // TODO Gale replace potential used synchronized blocks etc. by spin locks -+ * Since multiple chunk system tasks may be executed in one base thread pool task, -+ * and individual tasks may be quite big, these tasks are not {@link TaskSpan#TINY}. ++ * Tasks provided by this queue are {@link TaskSpan#YIELDING}. // TODO Gale replace potential used synchronized blocks etc. by spin locks or yielding locks + * + * @author Martijn Muijsers under AGPL-3.0 + */ @@ -606,12 +604,12 @@ index 0000000000000000000000000000000000000000..3f7095f502f883715799958f36980bbf + + @Override + public boolean hasTasks(TaskSpan span) { -+ return span == TaskSpan.FREE && this.hasTasks(); ++ return span == TaskSpan.YIELDING && this.hasTasks(); + } + + @Override + public boolean canHaveTasks(TaskSpan span) { -+ return span == TaskSpan.FREE; ++ return span == TaskSpan.YIELDING; + } + + @Override @@ -641,7 +639,7 @@ index 0000000000000000000000000000000000000000..3f7095f502f883715799958f36980bbf + * To be called when a new task has been added to the underlying storage of this queue. + */ + public void newTaskWasAdded() { -+ BaseThreadActivation.newTaskWasAdded(this.tier, TaskSpan.FREE, true); ++ BaseThreadActivation.newTaskWasAdded(this.tier, TaskSpan.YIELDING, true); + } + + @Override @@ -654,7 +652,7 @@ index 0000000000000000000000000000000000000000..3f7095f502f883715799958f36980bbf + +} diff --git a/src/main/java/org/galemc/gale/executor/thread/BaseThread.java b/src/main/java/org/galemc/gale/executor/thread/BaseThread.java -index 7cdd16786a8fba4fbc79b27c0baa9ed10a0bac21..e20e3cb98ab828c4dc7187f3c63e51e7512d3a32 100644 +index 633c1aeddca31f5fe95cc8c9ad54e6a2cf1b4ac1..81e244e94c7422de589e91db44b8e493e1daf5e7 100644 --- a/src/main/java/org/galemc/gale/executor/thread/BaseThread.java +++ b/src/main/java/org/galemc/gale/executor/thread/BaseThread.java @@ -2,6 +2,8 @@ @@ -674,7 +672,7 @@ index 7cdd16786a8fba4fbc79b27c0baa9ed10a0bac21..e20e3cb98ab828c4dc7187f3c63e51e7 import org.jetbrains.annotations.Nullable; import java.util.concurrent.TimeUnit; -@@ -660,6 +663,20 @@ public abstract class BaseThread extends Thread implements AbstractYieldingThrea +@@ -698,6 +701,20 @@ public abstract class BaseThread extends Thread implements AbstractYieldingThrea } } diff --git a/patches/server/0157-Split-tick-steps.patch b/patches/server/0159-Split-tick-steps.patch similarity index 99% rename from patches/server/0157-Split-tick-steps.patch rename to patches/server/0159-Split-tick-steps.patch index d3fc169..a7f0594 100644 --- a/patches/server/0157-Split-tick-steps.patch +++ b/patches/server/0159-Split-tick-steps.patch @@ -307,7 +307,7 @@ index ba352013692b987518dd200d376fb6cf2c90da19..99fe51c3654bf5f978b6576290504c7b private void tickChunks() { diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java -index e7747b19685fd943d7fbefbfef656f8bb7c359f1..8dfbd2f0ad7d767f2bbe2de235fef0e04daaf5c8 100644 +index e7747b19685fd943d7fbefbfef656f8bb7c359f1..5472fac17f78f59c3e97ecac42f29092ce364e42 100644 --- a/src/main/java/net/minecraft/server/level/ServerLevel.java +++ b/src/main/java/net/minecraft/server/level/ServerLevel.java @@ -23,7 +23,6 @@ import java.nio.file.Files; @@ -475,7 +475,7 @@ index e7747b19685fd943d7fbefbfef656f8bb7c359f1..8dfbd2f0ad7d767f2bbe2de235fef0e0 int i = this.getGameRules().getInt(GameRules.RULE_PLAYERS_SLEEPING_PERCENTAGE); long j; -@@ -667,40 +782,156 @@ public class ServerLevel extends Level implements WorldGenLevel { +@@ -667,40 +782,155 @@ public class ServerLevel extends Level implements WorldGenLevel { this.resetWeatherCycle(); } } @@ -519,7 +519,7 @@ index e7747b19685fd943d7fbefbfef656f8bb7c359f1..8dfbd2f0ad7d767f2bbe2de235fef0e0 + this.tick_scheduledBlocksGameTime = this.getGameTime(); + }); + } - ++ + private Runnable tickStep_setIsDebug_create() { + return this.wrapScheduledBlocksTickStep(() -> { + this.tick_isDebug = this.isDebug(); @@ -539,7 +539,7 @@ index e7747b19685fd943d7fbefbfef656f8bb7c359f1..8dfbd2f0ad7d767f2bbe2de235fef0e0 + this.blockTicks.tick(this.tick_scheduledBlocksGameTime, 65536, this::tickBlock); + }); + } -+ + + private Runnable tickStep_tickFluids_create() { + return this.wrapScheduledBlocksTickStep(() -> { + this.fluidTicks.tick(this.tick_scheduledBlocksGameTime, 65536, this::tickFluid); @@ -609,7 +609,7 @@ index e7747b19685fd943d7fbefbfef656f8bb7c359f1..8dfbd2f0ad7d767f2bbe2de235fef0e0 + private Runnable wrapTickEntitiesTickStep(Runnable runnable) { + return this.wrapDoEntityAndBlockEntityTickTickStep(() -> { + // Gale end - split tick steps -+ timings.tickEntities.startTiming(); // Spigot + timings.tickEntities.startTiming(); // Spigot + // Gale start - split tick steps + runnable.run(); + timings.tickEntities.stopTiming(); // Spigot @@ -619,7 +619,6 @@ index e7747b19685fd943d7fbefbfef656f8bb7c359f1..8dfbd2f0ad7d767f2bbe2de235fef0e0 + private Runnable tickStep_tickDragonFight_create() { + return this.wrapTickEntitiesTickStep(() -> { + // Gale end - split tick steps - timings.tickEntities.startTiming(); // Spigot if (this.dragonFight != null) { this.dragonFight.tick(); } @@ -641,7 +640,7 @@ index e7747b19685fd943d7fbefbfef656f8bb7c359f1..8dfbd2f0ad7d767f2bbe2de235fef0e0 timings.entityTick.startTiming(); // Spigot this.entityTickList.forEach((entity) -> { if (!entity.isRemoved()) { -@@ -738,11 +969,15 @@ public class ServerLevel extends Level implements WorldGenLevel { +@@ -738,11 +968,15 @@ public class ServerLevel extends Level implements WorldGenLevel { } }); timings.entityTick.stopTiming(); // Spigot diff --git a/patches/server/0158-Multithreaded-ticking.patch b/patches/server/0160-Multithreaded-ticking.patch similarity index 91% rename from patches/server/0158-Multithreaded-ticking.patch rename to patches/server/0160-Multithreaded-ticking.patch index d2a52f5..405f50a 100644 --- a/patches/server/0158-Multithreaded-ticking.patch +++ b/patches/server/0160-Multithreaded-ticking.patch @@ -7,10 +7,10 @@ License: AGPL-3.0 (https://www.gnu.org/licenses/agpl-3.0.html) Gale - https://galemc.org diff --git a/src/main/java/org/galemc/gale/configuration/GaleGlobalConfiguration.java b/src/main/java/org/galemc/gale/configuration/GaleGlobalConfiguration.java -index 39c4fe8fdd806b4b5bc3cb2dfdde9a29b11b386e..6188bfcffeded21563aa8eae0e3d135cc497e386 100644 +index 39c4fe8fdd806b4b5bc3cb2dfdde9a29b11b386e..231b38b1af45c9260a43e23745efb163de06d3f0 100644 --- a/src/main/java/org/galemc/gale/configuration/GaleGlobalConfiguration.java +++ b/src/main/java/org/galemc/gale/configuration/GaleGlobalConfiguration.java -@@ -432,4 +432,20 @@ public class GaleGlobalConfiguration extends ConfigurationPart { +@@ -432,4 +432,39 @@ public class GaleGlobalConfiguration extends ConfigurationPart { } @@ -19,6 +19,25 @@ index 39c4fe8fdd806b4b5bc3cb2dfdde9a29b11b386e..6188bfcffeded21563aa8eae0e3d135c + public class Multithreading extends ConfigurationPart { + + // Gale start - multithreaded ticking ++ /** ++ * If multithreading is enabled for any ticking steps, the order of the individual ticking steps for which ++ * multithreading is disabled may still be different from vanilla. ++ *
    ++ * For example, instead of the following vanilla sequence: ++ *
      ++ *
    • Tick the entities in world A
    • ++ *
    • Tick the block entities in world A
    • ++ *
    • Tick the entities in world B
    • ++ * Tick the block entities in world B ++ *
    ++ * the following sequence may happen: ++ *
      ++ *
    • Tick the entities in world A
    • ++ *
    • Tick the entities in world B
    • ++ *
    • Tick the block entities in world A
    • ++ *
    • Tick the block entities in world B
    • ++ *
    ++ */ + public Ticking ticking; + public class Ticking extends ConfigurationPart { + @@ -185,18 +204,14 @@ index 0000000000000000000000000000000000000000..9408c7c6e6b60bd9d5153912a2d85585 +} diff --git a/src/main/java/org/galemc/gale/tick/step/ServerTickStep.java b/src/main/java/org/galemc/gale/tick/step/ServerTickStep.java new file mode 100644 -index 0000000000000000000000000000000000000000..538dad9bfa0eb982ef0d0ffb96bb8a6ef7d4ca38 +index 0000000000000000000000000000000000000000..f6a4f0ef64cf39e69a55a231c8a1cecaf930a40a --- /dev/null +++ b/src/main/java/org/galemc/gale/tick/step/ServerTickStep.java -@@ -0,0 +1,26 @@ +@@ -0,0 +1,22 @@ +// Gale - multithreaded ticking + +package org.galemc.gale.tick.step; + -+import net.minecraft.server.level.ServerLevel; -+ -+import java.util.function.Consumer; -+ +/** + * A {@link TickStep} that applies to the whole server (i.e. is run once per tick). + *