mirror of
https://github.com/Dreeam-qwq/Gale.git
synced 2025-12-22 08:19:31 +00:00
Yielding ChunkTaskScheduler
This commit is contained in:
@@ -25,7 +25,7 @@ index 01780a73ee9cc602951bdf568c17673c4c639f79..4b279948e82a6dfd2f471ba698e361dc
|
||||
// Gale end - hide irrelevant compilation warnings
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java
|
||||
index 9c3ccbbd657d1605b8fabb6e01c11ff31f39a17e..1d1db89362b78ac34d46cdd71b9ab27404b98e48 100644
|
||||
index 9c3ccbbd657d1605b8fabb6e01c11ff31f39a17e..20ae254b1cd3d75781d1ea4b9859bf0ef92f173d 100644
|
||||
--- a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java
|
||||
+++ b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java
|
||||
@@ -14,6 +14,8 @@ import java.util.Locale;
|
||||
@@ -45,7 +45,7 @@ index 9c3ccbbd657d1605b8fabb6e01c11ff31f39a17e..1d1db89362b78ac34d46cdd71b9ab274
|
||||
import org.slf4j.Logger;
|
||||
|
||||
// CraftBukkit start
|
||||
@@ -223,6 +226,21 @@ public class DedicatedServer extends MinecraftServer implements ServerInterface
|
||||
@@ -223,6 +226,20 @@ public class DedicatedServer extends MinecraftServer implements ServerInterface
|
||||
io.papermc.paper.brigadier.PaperBrigadierProviderImpl.INSTANCE.getClass(); // init PaperBrigadierProvider
|
||||
// Paper end
|
||||
|
||||
@@ -59,7 +59,6 @@ index 9c3ccbbd657d1605b8fabb6e01c11ff31f39a17e..1d1db89362b78ac34d46cdd71b9ab274
|
||||
+ LOGGER.warn("SIMD operations are available for your server, but are not configured!");
|
||||
+ LOGGER.warn("To enable additional optimizations, add \"--add-modules=jdk.incubator.vector\" to your startup flags, BEFORE the \"-jar\".");
|
||||
+ LOGGER.warn("If you have already added this flag, then SIMD operations are not supported on your JVM or CPU.");
|
||||
+ LOGGER.warn("Debug: Java: " + System.getProperty("java.version") + ", test run: " + SIMDDetection.testRun);
|
||||
+ LOGGER.warn("If you would like to disable this message, set simd.warn-if-disabled to false in gale-global.yml");
|
||||
+ }
|
||||
+ // Gale start - Pufferfish - SIMD support
|
||||
|
||||
@@ -41,10 +41,10 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java
|
||||
index 9302502f59243fa254c88a7a8d513a2a65c17979..f5ed3fa20097bdd43a25c76b38353a23743bc9e5 100644
|
||||
index 70d375b9803b5b7a33aa02cc1554f6b857aa6613..e4284c16512bbabd25c538eb88bf81d7c5bef9ba 100644
|
||||
--- a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java
|
||||
+++ b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java
|
||||
@@ -293,7 +293,7 @@ public class DedicatedServer extends MinecraftServer implements ServerInterface
|
||||
@@ -292,7 +292,7 @@ public class DedicatedServer extends MinecraftServer implements ServerInterface
|
||||
server.enablePlugins(org.bukkit.plugin.PluginLoadOrder.STARTUP);
|
||||
// CraftBukkit end
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ Gale - https://galemc.org
|
||||
|
||||
diff --git a/src/main/java/org/galemc/gale/executor/annotation/Access.java b/src/main/java/org/galemc/gale/executor/annotation/Access.java
|
||||
new file mode 100644
|
||||
index 0000000000000000000000000000000000000000..d07f68ff73a368c8f0da56152021a95474a601ca
|
||||
index 0000000000000000000000000000000000000000..50541414e1d91ff06d108d9b3fe64dcb4ad09668
|
||||
--- /dev/null
|
||||
+++ b/src/main/java/org/galemc/gale/executor/annotation/Access.java
|
||||
@@ -0,0 +1,39 @@
|
||||
@@ -41,22 +41,22 @@ index 0000000000000000000000000000000000000000..d07f68ff73a368c8f0da56152021a954
|
||||
+ WRITE,
|
||||
+ /**
|
||||
+ * Both {@link #READ} and {@link #WRITE}: if the annotation is applied to a field, it holds for both access to
|
||||
+ * the field's value, as well as modifications made to the field.
|
||||
+ * the field's value, and for modifications made to the field.
|
||||
+ * <br>
|
||||
+ * This may or may not extend to conceptual access and/or modifications.
|
||||
+ *
|
||||
+ * @see #READ
|
||||
+ * @see #WRITE
|
||||
+ */
|
||||
+ READ_WRITE;
|
||||
+ READ_WRITE
|
||||
+
|
||||
+}
|
||||
diff --git a/src/main/java/org/galemc/gale/executor/annotation/Guarded.java b/src/main/java/org/galemc/gale/executor/annotation/Guarded.java
|
||||
new file mode 100644
|
||||
index 0000000000000000000000000000000000000000..84a0bac98a382550c826e6adbecec1fe7be974a1
|
||||
index 0000000000000000000000000000000000000000..6f1d1960953daf7f6f61643f5165e9a0760a647e
|
||||
--- /dev/null
|
||||
+++ b/src/main/java/org/galemc/gale/executor/annotation/Guarded.java
|
||||
@@ -0,0 +1,47 @@
|
||||
@@ -0,0 +1,55 @@
|
||||
+// Gale - thread-safety annotations
|
||||
+
|
||||
+package org.galemc.gale.executor.annotation;
|
||||
@@ -83,7 +83,7 @@ index 0000000000000000000000000000000000000000..84a0bac98a382550c826e6adbecec1fe
|
||||
+ * @author Martijn Muijsers under AGPL-3.0
|
||||
+ */
|
||||
+@Documented
|
||||
+@Repeatable
|
||||
+@Repeatable(Guarded.Container.class)
|
||||
+@Target({ElementType.FIELD, ElementType.METHOD, ElementType.TYPE})
|
||||
+public @interface Guarded {
|
||||
+
|
||||
@@ -103,19 +103,25 @@ index 0000000000000000000000000000000000000000..84a0bac98a382550c826e6adbecec1fe
|
||||
+ */
|
||||
+ String except() default "";
|
||||
+
|
||||
+ @Documented
|
||||
+ @Target(ElementType.ANNOTATION_TYPE)
|
||||
+ @interface Container {
|
||||
+
|
||||
+ Guarded[] value();
|
||||
+
|
||||
+ }
|
||||
+
|
||||
+}
|
||||
diff --git a/src/main/java/org/galemc/gale/executor/annotation/PotentiallyBlocking.java b/src/main/java/org/galemc/gale/executor/annotation/PotentiallyBlocking.java
|
||||
new file mode 100644
|
||||
index 0000000000000000000000000000000000000000..71f26852c96dea34ea07efe07f834f8262509957
|
||||
index 0000000000000000000000000000000000000000..a4dc0ebe48fdd352387f06be42ff46fc11ee5822
|
||||
--- /dev/null
|
||||
+++ b/src/main/java/org/galemc/gale/executor/annotation/PotentiallyBlocking.java
|
||||
@@ -0,0 +1,39 @@
|
||||
@@ -0,0 +1,34 @@
|
||||
+// Gale - thread-safety annotations
|
||||
+
|
||||
+package org.galemc.gale.executor.annotation;
|
||||
+
|
||||
+import org.galemc.gale.executor.thread.BaseThread;
|
||||
+
|
||||
+import java.lang.annotation.Documented;
|
||||
+import java.lang.annotation.ElementType;
|
||||
+import java.lang.annotation.Target;
|
||||
@@ -129,9 +135,6 @@ index 0000000000000000000000000000000000000000..71f26852c96dea34ea07efe07f834f82
|
||||
+ * <br>
|
||||
+ * In a method annotated with {@link PotentiallyBlocking}, fields and methods annotated with
|
||||
+ * {@link PotentiallyBlocking}, {@link PotentiallyYielding} or {@link YieldFree} may all be used.
|
||||
+ * <br>
|
||||
+ * Methods that are potentially blocking, including those annotated with {@link PotentiallyBlocking}, must never
|
||||
+ * be called on a {@link BaseThread}.
|
||||
+ *
|
||||
+ * @author Martijn Muijsers under AGPL-3.0
|
||||
+ */
|
||||
@@ -151,7 +154,7 @@ index 0000000000000000000000000000000000000000..71f26852c96dea34ea07efe07f834f82
|
||||
+}
|
||||
diff --git a/src/main/java/org/galemc/gale/executor/annotation/PotentiallyYielding.java b/src/main/java/org/galemc/gale/executor/annotation/PotentiallyYielding.java
|
||||
new file mode 100644
|
||||
index 0000000000000000000000000000000000000000..e87ee2612348fc559b21256cc7cadfc684f01f8e
|
||||
index 0000000000000000000000000000000000000000..44b70d68ba6823ab72ea9af4b7774051785c0a2b
|
||||
--- /dev/null
|
||||
+++ b/src/main/java/org/galemc/gale/executor/annotation/PotentiallyYielding.java
|
||||
@@ -0,0 +1,35 @@
|
||||
@@ -165,7 +168,7 @@ index 0000000000000000000000000000000000000000..e87ee2612348fc559b21256cc7cadfc6
|
||||
+
|
||||
+/**
|
||||
+ * An annotation primarily for methods, identifying methods that do not block, but may yield to other tasks
|
||||
+ * under certain circumstances, such as when attempting to acquire a {@link YieldingLock}.
|
||||
+ * under certain circumstances.
|
||||
+ * <br>
|
||||
+ * When applied to a class, this annotation indicates it holds for all methods, both instance and static,
|
||||
+ * belonging to the class, or any superclass thereof, or any inner or statically nested class of the class or
|
||||
|
||||
42
patches/server/0143-CheckableLock-utility.patch
Normal file
42
patches/server/0143-CheckableLock-utility.patch
Normal file
@@ -0,0 +1,42 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Martijn Muijsers <martijnmuijsers@live.nl>
|
||||
Date: Sun, 5 Feb 2023 19:11:39 +0100
|
||||
Subject: [PATCH] CheckableLock 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/CheckableLock.java b/src/main/java/org/galemc/gale/concurrent/CheckableLock.java
|
||||
new file mode 100644
|
||||
index 0000000000000000000000000000000000000000..749d8d8e2038554a21c2d879349d5189d86a8481
|
||||
--- /dev/null
|
||||
+++ b/src/main/java/org/galemc/gale/concurrent/CheckableLock.java
|
||||
@@ -0,0 +1,28 @@
|
||||
+// Gale - CheckableLock utility
|
||||
+
|
||||
+package org.galemc.gale.concurrent;
|
||||
+
|
||||
+import org.galemc.gale.executor.annotation.YieldFree;
|
||||
+
|
||||
+import java.util.concurrent.locks.Lock;
|
||||
+
|
||||
+/**
|
||||
+ * A {@link Lock} that also provides an {@link #isLocked()} method.
|
||||
+ *
|
||||
+ * @author Martijn Muijsers under AGPL-3.0
|
||||
+ */
|
||||
+public interface CheckableLock extends Lock {
|
||||
+
|
||||
+ /**
|
||||
+ * @return Whether this lock is currently held.
|
||||
+ */
|
||||
+ @YieldFree
|
||||
+ boolean isLocked();
|
||||
+
|
||||
+ /**
|
||||
+ * @return Whether this lock is currently held by the {@link Thread#currentThread}.
|
||||
+ */
|
||||
+ @YieldFree
|
||||
+ boolean isHeldByCurrentThread();
|
||||
+
|
||||
+}
|
||||
@@ -8,16 +8,15 @@ 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..f7bedd5cbe9b48ac94f8cc228a17c8a54db7d7e9
|
||||
index 0000000000000000000000000000000000000000..649d2cfc1d73699302b4e5e64e9110e7681ae09c
|
||||
--- /dev/null
|
||||
+++ b/src/main/java/org/galemc/gale/concurrent/Mutex.java
|
||||
@@ -0,0 +1,123 @@
|
||||
@@ -0,0 +1,127 @@
|
||||
+// 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;
|
||||
@@ -34,7 +33,7 @@ index 0000000000000000000000000000000000000000..f7bedd5cbe9b48ac94f8cc228a17c8a5
|
||||
+ * <br>
|
||||
+ * This interface extends {@link AutoCloseable}, where {@link #close()} calls {@link #release()}.
|
||||
+ *
|
||||
+ * @author Martijn Muijsers
|
||||
+ * @author Martijn Muijsers under AGPL-3.0
|
||||
+ */
|
||||
+@AnyThreadSafe
|
||||
+public interface Mutex extends CheckableLock, AutoCloseable {
|
||||
@@ -134,10 +133,15 @@ index 0000000000000000000000000000000000000000..f7bedd5cbe9b48ac94f8cc228a17c8a5
|
||||
+ return create();
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ default boolean isHeldByCurrentThread() {
|
||||
+ throw new UnsupportedOperationException("isHeldByCurrentThread() is not supported for Mutex, and is not implemented by the implementation " + this.getClass().getName() + " either");
|
||||
+ }
|
||||
+
|
||||
+}
|
||||
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..c310264afea6c81ec575bdf6aa5495ccb34d7ae4
|
||||
index 0000000000000000000000000000000000000000..d31293a2a2151bc9fbdc6eb2175045b429fb4461
|
||||
--- /dev/null
|
||||
+++ b/src/main/java/org/galemc/gale/concurrent/SemaphoreMutex.java
|
||||
@@ -0,0 +1,39 @@
|
||||
@@ -158,7 +162,7 @@ index 0000000000000000000000000000000000000000..c310264afea6c81ec575bdf6aa5495cc
|
||||
+ * and throws {@link UnsupportedOperationException} for all {@link Lock} methods that do not have a default
|
||||
+ * implementation in {@link Mutex}.
|
||||
+ *
|
||||
+ * @author Martijn Muijsers
|
||||
+ * @author Martijn Muijsers under AGPL-3.0
|
||||
+ */
|
||||
+@AnyThreadSafe
|
||||
+@YieldFree
|
||||
@@ -8,16 +8,15 @@ 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
|
||||
index 0000000000000000000000000000000000000000..8c14d90d1a907adf994070cbe5d62f1fbfd8d9c8
|
||||
--- /dev/null
|
||||
+++ b/src/main/java/org/galemc/gale/concurrent/ThreadAwareNonReentrantLock.java
|
||||
@@ -0,0 +1,109 @@
|
||||
@@ -0,0 +1,113 @@
|
||||
+// 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;
|
||||
+
|
||||
@@ -120,4 +119,9 @@ index 0000000000000000000000000000000000000000..1dd9d78f70fe586a9868e046e01ab512
|
||||
+ return this.innerLock.isLocked();
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public boolean isHeldByCurrentThread() {
|
||||
+ return this.innerLock.isHeldByCurrentThread();
|
||||
+ }
|
||||
+
|
||||
+}
|
||||
@@ -1352,7 +1352,7 @@ index f0a7a8df3caa2ea765bb0a87cfede71d0995d276..16f3475b059d2b6b85d2b342e84ab32d
|
||||
// CraftBukkit start
|
||||
TimeSkipEvent event = new TimeSkipEvent(worldserver.getWorld(), TimeSkipEvent.SkipReason.COMMAND, time);
|
||||
diff --git a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java
|
||||
index eed9f125df46b616b7234a2d669971bc51bc231b..3056d66779d236071d52d731a1c4e56aad0746c5 100644
|
||||
index 5292616c846d495b1d7be040cecf47fb33412191..b49b2fe152b70c8020ba2edc48d46cc4fe8d525e 100644
|
||||
--- a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java
|
||||
+++ b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java
|
||||
@@ -49,6 +49,7 @@ import net.minecraft.world.level.block.entity.SkullBlockEntity;
|
||||
@@ -1372,6 +1372,20 @@ index eed9f125df46b616b7234a2d669971bc51bc231b..3056d66779d236071d52d731a1c4e56a
|
||||
super(options, worldLoader, thread, convertable_conversionsession, resourcepackrepository, worldstem, Proxy.NO_PROXY, datafixer, services, worldloadlistenerfactory);
|
||||
// CraftBukkit end
|
||||
this.settings = dedicatedserversettings;
|
||||
diff --git a/src/main/java/net/minecraft/server/level/ServerChunkCache.java b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
|
||||
index df4db98618c6c9261b4ec8e2987c4ed26af4bd4b..83a57b9bc59063ed8299f98bc33e14b57f2ea0de 100644
|
||||
--- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java
|
||||
+++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
|
||||
@@ -48,6 +48,9 @@ import net.minecraft.world.level.storage.DimensionDataStorage;
|
||||
import net.minecraft.world.level.storage.LevelData;
|
||||
import net.minecraft.world.level.storage.LevelStorageSource;
|
||||
import it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet; // Paper
|
||||
+import org.galemc.gale.executor.lock.YieldingLock;
|
||||
+import org.galemc.gale.executor.queue.BaseTaskQueues;
|
||||
+import org.galemc.gale.executor.thread.AbstractYieldingThread;
|
||||
|
||||
public class ServerChunkCache extends ChunkSource {
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java
|
||||
index 37e0b6212fec71ec9662e6be3b1e8bea487eb4a6..e7747b19685fd943d7fbefbfef656f8bb7c359f1 100644
|
||||
--- a/src/main/java/net/minecraft/server/level/ServerLevel.java
|
||||
@@ -1850,32 +1864,6 @@ 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 f7bedd5cbe9b48ac94f8cc228a17c8a54db7d7e9..129ac4f70f2e7f176517af1e46ea36c5144473a0 100644
|
||||
--- a/src/main/java/org/galemc/gale/concurrent/Mutex.java
|
||||
+++ b/src/main/java/org/galemc/gale/concurrent/Mutex.java
|
||||
@@ -20,7 +20,7 @@ import java.util.concurrent.locks.Lock;
|
||||
* <br>
|
||||
* This interface extends {@link AutoCloseable}, where {@link #close()} calls {@link #release()}.
|
||||
*
|
||||
- * @author Martijn Muijsers
|
||||
+ * @author Martijn Muijsers under AGPL-3.0
|
||||
*/
|
||||
@AnyThreadSafe
|
||||
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 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;
|
||||
* and throws {@link UnsupportedOperationException} for all {@link Lock} methods that do not have a default
|
||||
* implementation in {@link Mutex}.
|
||||
*
|
||||
- * @author Martijn Muijsers
|
||||
+ * @author Martijn Muijsers under AGPL-3.0
|
||||
*/
|
||||
@AnyThreadSafe
|
||||
@YieldFree
|
||||
diff --git a/src/main/java/org/galemc/gale/configuration/GaleConfigurations.java b/src/main/java/org/galemc/gale/configuration/GaleConfigurations.java
|
||||
index 9571aae593999d11b3908856b0295a7d6b588007..ed2841d3a6c6d90ad02266f38c0821bca4f549f1 100644
|
||||
--- a/src/main/java/org/galemc/gale/configuration/GaleConfigurations.java
|
||||
@@ -2420,79 +2408,34 @@ index 0000000000000000000000000000000000000000..1d5bb1ba545200f954c886a2afb9d8ee
|
||||
+ public static final int length = VALUES.length;
|
||||
+
|
||||
+}
|
||||
diff --git a/src/main/java/org/galemc/gale/executor/annotation/Access.java b/src/main/java/org/galemc/gale/executor/annotation/Access.java
|
||||
index d07f68ff73a368c8f0da56152021a95474a601ca..50541414e1d91ff06d108d9b3fe64dcb4ad09668 100644
|
||||
--- a/src/main/java/org/galemc/gale/executor/annotation/Access.java
|
||||
+++ b/src/main/java/org/galemc/gale/executor/annotation/Access.java
|
||||
@@ -27,13 +27,13 @@ public enum Access {
|
||||
WRITE,
|
||||
/**
|
||||
* Both {@link #READ} and {@link #WRITE}: if the annotation is applied to a field, it holds for both access to
|
||||
- * the field's value, as well as modifications made to the field.
|
||||
+ * the field's value, and for modifications made to the field.
|
||||
* <br>
|
||||
* This may or may not extend to conceptual access and/or modifications.
|
||||
*
|
||||
* @see #READ
|
||||
* @see #WRITE
|
||||
*/
|
||||
- READ_WRITE;
|
||||
+ READ_WRITE
|
||||
|
||||
}
|
||||
diff --git a/src/main/java/org/galemc/gale/executor/annotation/Guarded.java b/src/main/java/org/galemc/gale/executor/annotation/Guarded.java
|
||||
index 84a0bac98a382550c826e6adbecec1fe7be974a1..6f1d1960953daf7f6f61643f5165e9a0760a647e 100644
|
||||
--- a/src/main/java/org/galemc/gale/executor/annotation/Guarded.java
|
||||
+++ b/src/main/java/org/galemc/gale/executor/annotation/Guarded.java
|
||||
@@ -24,7 +24,7 @@ import java.lang.annotation.Target;
|
||||
* @author Martijn Muijsers under AGPL-3.0
|
||||
*/
|
||||
@Documented
|
||||
-@Repeatable
|
||||
+@Repeatable(Guarded.Container.class)
|
||||
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.TYPE})
|
||||
public @interface Guarded {
|
||||
|
||||
@@ -44,4 +44,12 @@ public @interface Guarded {
|
||||
*/
|
||||
String except() default "";
|
||||
|
||||
+ @Documented
|
||||
+ @Target(ElementType.ANNOTATION_TYPE)
|
||||
+ @interface Container {
|
||||
+
|
||||
+ Guarded[] value();
|
||||
+
|
||||
+ }
|
||||
+
|
||||
}
|
||||
diff --git a/src/main/java/org/galemc/gale/executor/annotation/PotentiallyBlocking.java b/src/main/java/org/galemc/gale/executor/annotation/PotentiallyBlocking.java
|
||||
index 71f26852c96dea34ea07efe07f834f8262509957..d324c303245bcbedaaaab573803d73caff941901 100644
|
||||
index a4dc0ebe48fdd352387f06be42ff46fc11ee5822..d324c303245bcbedaaaab573803d73caff941901 100644
|
||||
--- a/src/main/java/org/galemc/gale/executor/annotation/PotentiallyBlocking.java
|
||||
+++ b/src/main/java/org/galemc/gale/executor/annotation/PotentiallyBlocking.java
|
||||
@@ -2,7 +2,7 @@
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
package org.galemc.gale.executor.annotation;
|
||||
|
||||
-import org.galemc.gale.executor.thread.BaseThread;
|
||||
+import org.galemc.gale.executor.thread.AbstractYieldingThread;
|
||||
|
||||
+
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
@@ -19,7 +19,7 @@ import java.lang.annotation.Target;
|
||||
* {@link PotentiallyBlocking}, {@link PotentiallyYielding} or {@link YieldFree} may all be used.
|
||||
import java.lang.annotation.Target;
|
||||
@@ -15,6 +17,9 @@ import java.lang.annotation.Target;
|
||||
* <br>
|
||||
* Methods that are potentially blocking, including those annotated with {@link PotentiallyBlocking}, must never
|
||||
- * be called on a {@link BaseThread}.
|
||||
* In a method annotated with {@link PotentiallyBlocking}, fields and methods annotated with
|
||||
* {@link PotentiallyBlocking}, {@link PotentiallyYielding} or {@link YieldFree} may all be used.
|
||||
+ * <br>
|
||||
+ * Methods that are potentially blocking, including those annotated with {@link PotentiallyBlocking}, must never
|
||||
+ * be called on an {@link AbstractYieldingThread}.
|
||||
*
|
||||
* @author Martijn Muijsers under AGPL-3.0
|
||||
*/
|
||||
diff --git a/src/main/java/org/galemc/gale/executor/annotation/PotentiallyYielding.java b/src/main/java/org/galemc/gale/executor/annotation/PotentiallyYielding.java
|
||||
index e87ee2612348fc559b21256cc7cadfc684f01f8e..7ff4e4ab43d316e319efb33b2dd365d679a58118 100644
|
||||
index 44b70d68ba6823ab72ea9af4b7774051785c0a2b..7ff4e4ab43d316e319efb33b2dd365d679a58118 100644
|
||||
--- a/src/main/java/org/galemc/gale/executor/annotation/PotentiallyYielding.java
|
||||
+++ b/src/main/java/org/galemc/gale/executor/annotation/PotentiallyYielding.java
|
||||
@@ -2,6 +2,9 @@
|
||||
@@ -2,13 +2,16 @@
|
||||
|
||||
package org.galemc.gale.executor.annotation;
|
||||
|
||||
@@ -2502,6 +2445,14 @@ index e87ee2612348fc559b21256cc7cadfc684f01f8e..7ff4e4ab43d316e319efb33b2dd365d6
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* An annotation primarily for methods, identifying methods that do not block, but may yield to other tasks
|
||||
- * under certain circumstances.
|
||||
+ * under certain circumstances, such as when attempting to acquire a {@link YieldingLock}.
|
||||
* <br>
|
||||
* When applied to a class, this annotation indicates it holds for all methods, both instance and static,
|
||||
* belonging to the class, or any superclass thereof, or any inner or statically nested class of the class or
|
||||
@@ -16,6 +19,9 @@ import java.lang.annotation.Target;
|
||||
* <br>
|
||||
* In a method annotated with {@link PotentiallyYielding}, the only methods that can be called are those
|
||||
@@ -2685,32 +2636,6 @@ index 0000000000000000000000000000000000000000..d27ee27a65635d0136c5c9e33925b640
|
||||
+ Access value() default Access.READ_WRITE;
|
||||
+
|
||||
+}
|
||||
diff --git a/src/main/java/org/galemc/gale/executor/lock/CheckableLock.java b/src/main/java/org/galemc/gale/executor/lock/CheckableLock.java
|
||||
new file mode 100644
|
||||
index 0000000000000000000000000000000000000000..995746e7f89481f885ea6e3965fc9a2f3f9e9498
|
||||
--- /dev/null
|
||||
+++ b/src/main/java/org/galemc/gale/executor/lock/CheckableLock.java
|
||||
@@ -0,0 +1,20 @@
|
||||
+package org.galemc.gale.executor.lock;
|
||||
+
|
||||
+import org.galemc.gale.executor.annotation.YieldFree;
|
||||
+
|
||||
+import java.util.concurrent.locks.Lock;
|
||||
+
|
||||
+/**
|
||||
+ * A {@link Lock} that also provides an {@link #isLocked()} method.
|
||||
+ *
|
||||
+ * @author Martijn Muijsers under AGPL-3.0
|
||||
+ */
|
||||
+public interface CheckableLock extends Lock {
|
||||
+
|
||||
+ /**
|
||||
+ * @return Whether this lock is currently held.
|
||||
+ */
|
||||
+ @YieldFree
|
||||
+ boolean isLocked();
|
||||
+
|
||||
+}
|
||||
diff --git a/src/main/java/org/galemc/gale/executor/lock/MultipleWaitingBaseThreadsYieldingLock.java b/src/main/java/org/galemc/gale/executor/lock/MultipleWaitingBaseThreadsYieldingLock.java
|
||||
new file mode 100644
|
||||
index 0000000000000000000000000000000000000000..a248a9ea644a8bb4175da2e1903483ab6866bc48
|
||||
@@ -2756,16 +2681,62 @@ 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
|
||||
diff --git a/src/main/java/org/galemc/gale/executor/lock/SingleWaitingBaseThreadYieldingLock.java b/src/main/java/org/galemc/gale/executor/lock/SingleWaitingBaseThreadYieldingLock.java
|
||||
new file mode 100644
|
||||
index 0000000000000000000000000000000000000000..3ec790a7b19790731f70f59cc2bdb1919f26dd2d
|
||||
index 0000000000000000000000000000000000000000..686e16da8372085196d8f92adb881f82dd5c2947
|
||||
--- /dev/null
|
||||
+++ b/src/main/java/org/galemc/gale/executor/lock/YieldingLock.java
|
||||
@@ -0,0 +1,163 @@
|
||||
+++ b/src/main/java/org/galemc/gale/executor/lock/SingleWaitingBaseThreadYieldingLock.java
|
||||
@@ -0,0 +1,39 @@
|
||||
+// Gale - base thread pool
|
||||
+
|
||||
+package org.galemc.gale.executor.lock;
|
||||
+
|
||||
+import org.galemc.gale.executor.thread.BaseThread;
|
||||
+
|
||||
+import java.util.concurrent.atomic.AtomicInteger;
|
||||
+import java.util.concurrent.locks.Lock;
|
||||
+
|
||||
+/**
|
||||
+ * A {@link YieldingLock} for which one {@link BaseThread} may be waiting at any time.
|
||||
+ *
|
||||
+ * @author Martijn Muijsers under AGPL-3.0
|
||||
+ */
|
||||
+@SuppressWarnings("unused")
|
||||
+public class SingleWaitingBaseThreadYieldingLock extends YieldingLock {
|
||||
+
|
||||
+ private volatile boolean hasWaitingThread = false;
|
||||
+
|
||||
+ public SingleWaitingBaseThreadYieldingLock(Lock innerLock) {
|
||||
+ super(innerLock);
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public void incrementWaitingThreads() {
|
||||
+ hasWaitingThread = true;
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public void decrementWaitingThreads() {
|
||||
+ hasWaitingThread = false;
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ protected boolean hasWaitingThreads() {
|
||||
+ return this.hasWaitingThread;
|
||||
+ }
|
||||
+
|
||||
+}
|
||||
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..799608befb739d051153b5321598ea87b50fd6bd
|
||||
--- /dev/null
|
||||
+++ b/src/main/java/org/galemc/gale/executor/lock/YieldingLock.java
|
||||
@@ -0,0 +1,202 @@
|
||||
+// Gale - base thread pool
|
||||
+
|
||||
+package org.galemc.gale.executor.lock;
|
||||
+
|
||||
+import org.galemc.gale.concurrent.CheckableLock;
|
||||
+import org.galemc.gale.executor.annotation.thread.AnyThreadSafe;
|
||||
+import org.galemc.gale.executor.annotation.PotentiallyYielding;
|
||||
+import org.galemc.gale.executor.annotation.YieldFree;
|
||||
@@ -2808,6 +2779,13 @@ index 0000000000000000000000000000000000000000..3ec790a7b19790731f70f59cc2bdb191
|
||||
+ */
|
||||
+ private final @Nullable ReentrantLock innerReentrantLock;
|
||||
+
|
||||
+ /**
|
||||
+ * Whether the {@link #innerLock} can be truly 'held' as lock.
|
||||
+ * Can be set to false via {@link #withCanNotBeHeld()} for locks that are not real, but
|
||||
+ * merely use the {@link Lock} interface allow base threads to wait for them.
|
||||
+ */
|
||||
+ private boolean canBeHeld;
|
||||
+
|
||||
+ public YieldingLock(Lock innerLock) {
|
||||
+ this.innerLock = innerLock;
|
||||
+ if (innerLock instanceof CheckableLock checkableLock) {
|
||||
@@ -2829,7 +2807,17 @@ index 0000000000000000000000000000000000000000..3ec790a7b19790731f70f59cc2bdb191
|
||||
+ @YieldFree
|
||||
+ @Override
|
||||
+ public boolean tryLock() {
|
||||
+ return innerLock.tryLock();
|
||||
+ if (innerLock.tryLock()) {
|
||||
+ // Increment the YieldingLock count of the current thread
|
||||
+ if (this.canBeHeld) {
|
||||
+ @Nullable AbstractYieldingThread yieldingThread = AbstractYieldingThread.currentYieldingThread();
|
||||
+ if (yieldingThread != null) {
|
||||
+ yieldingThread.incrementHeldYieldingLockCount();
|
||||
+ }
|
||||
+ }
|
||||
+ return true;
|
||||
+ }
|
||||
+ return false;
|
||||
+ }
|
||||
+
|
||||
+ /**
|
||||
@@ -2855,8 +2843,10 @@ index 0000000000000000000000000000000000000000..3ec790a7b19790731f70f59cc2bdb191
|
||||
+ yieldingThread.yieldUntil(null, this);
|
||||
+ }
|
||||
+ // Increment the YieldingLock count of the current thread
|
||||
+ if (yieldingThread != null) {
|
||||
+ yieldingThread.incrementHeldYieldingLockCount();
|
||||
+ if (this.canBeHeld) {
|
||||
+ if (yieldingThread != null) {
|
||||
+ yieldingThread.incrementHeldYieldingLockCount();
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
@@ -2867,9 +2857,11 @@ index 0000000000000000000000000000000000000000..3ec790a7b19790731f70f59cc2bdb191
|
||||
+ public void unlock() {
|
||||
+ this.innerLock.unlock();
|
||||
+ // Decrement the YieldingLock count of the current thread
|
||||
+ @Nullable AbstractYieldingThread yieldingThread = AbstractYieldingThread.currentYieldingThread();
|
||||
+ if (yieldingThread != null) {
|
||||
+ yieldingThread.decrementHeldYieldingLockCount();
|
||||
+ if (this.canBeHeld) {
|
||||
+ @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,
|
||||
@@ -2906,6 +2898,11 @@ index 0000000000000000000000000000000000000000..3ec790a7b19790731f70f59cc2bdb191
|
||||
+ return this.innerCheckableLock != null ? this.innerCheckableLock.isLocked() : this.innerReentrantLock.isLocked();
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public boolean isHeldByCurrentThread() {
|
||||
+ return this.innerCheckableLock != null ? this.innerCheckableLock.isHeldByCurrentThread() : this.innerReentrantLock.isHeldByCurrentThread();
|
||||
+ }
|
||||
+
|
||||
+ /**
|
||||
+ * Increments the number of threads waiting for this lock to be released.
|
||||
+ */
|
||||
@@ -2924,6 +2921,18 @@ index 0000000000000000000000000000000000000000..3ec790a7b19790731f70f59cc2bdb191
|
||||
+ @YieldFree
|
||||
+ protected abstract boolean hasWaitingThreads();
|
||||
+
|
||||
+ /**
|
||||
+ * Sets {@link #canBeHeld} to false.
|
||||
+ * This should be called immediately after construction of this {@link YieldingLock}.
|
||||
+ *
|
||||
+ * @return This instance.
|
||||
+ */
|
||||
+ @YieldFree
|
||||
+ public @NotNull YieldingLock withCanNotBeHeld() {
|
||||
+ this.canBeHeld = false;
|
||||
+ return this;
|
||||
+ }
|
||||
+
|
||||
+}
|
||||
diff --git a/src/main/java/org/galemc/gale/executor/queue/AbstractTaskQueue.java b/src/main/java/org/galemc/gale/executor/queue/AbstractTaskQueue.java
|
||||
new file mode 100644
|
||||
@@ -3026,10 +3035,10 @@ index 0000000000000000000000000000000000000000..552e82a33c59261b06911b479400a7b1
|
||||
+}
|
||||
diff --git a/src/main/java/org/galemc/gale/executor/queue/AllLevelsScheduledTaskQueue.java b/src/main/java/org/galemc/gale/executor/queue/AllLevelsScheduledTaskQueue.java
|
||||
new file mode 100644
|
||||
index 0000000000000000000000000000000000000000..bbe92d92f58d484084114da73003c345d00aac7e
|
||||
index 0000000000000000000000000000000000000000..2eb121798b2feb2a5ce5ebb60316660dbff87de3
|
||||
--- /dev/null
|
||||
+++ b/src/main/java/org/galemc/gale/executor/queue/AllLevelsScheduledTaskQueue.java
|
||||
@@ -0,0 +1,127 @@
|
||||
@@ -0,0 +1,130 @@
|
||||
+// Gale - base thread pool
|
||||
+
|
||||
+package org.galemc.gale.executor.queue;
|
||||
@@ -3086,6 +3095,9 @@ index 0000000000000000000000000000000000000000..bbe92d92f58d484084114da73003c345
|
||||
+
|
||||
+ @Override
|
||||
+ public boolean hasTasks() {
|
||||
+ if (MinecraftServer.SERVER == null) {
|
||||
+ return false;
|
||||
+ }
|
||||
+ for (ServerLevel level : MinecraftServer.SERVER.getAllLevels()) {
|
||||
+ if (this.hasLevelTasks(level)) {
|
||||
+ return true;
|
||||
@@ -3192,10 +3204,10 @@ index 0000000000000000000000000000000000000000..690979cb9b7ec3dedbd7d0c45d0c183a
|
||||
+}
|
||||
diff --git a/src/main/java/org/galemc/gale/executor/queue/BaseTaskQueueTier.java b/src/main/java/org/galemc/gale/executor/queue/BaseTaskQueueTier.java
|
||||
new file mode 100644
|
||||
index 0000000000000000000000000000000000000000..cc5373e78657d04a43cb844c4fcd5f0f9cacf187
|
||||
index 0000000000000000000000000000000000000000..e4a0b3085cb22f25246010c43919129283a3b872
|
||||
--- /dev/null
|
||||
+++ b/src/main/java/org/galemc/gale/executor/queue/BaseTaskQueueTier.java
|
||||
@@ -0,0 +1,115 @@
|
||||
@@ -0,0 +1,125 @@
|
||||
+// Gale - base thread pool
|
||||
+
|
||||
+package org.galemc.gale.executor.queue;
|
||||
@@ -3204,6 +3216,7 @@ index 0000000000000000000000000000000000000000..cc5373e78657d04a43cb844c4fcd5f0f
|
||||
+import net.minecraft.server.MinecraftServer;
|
||||
+import net.minecraft.server.level.ServerLevel;
|
||||
+import net.minecraft.world.entity.Entity;
|
||||
+import org.galemc.gale.executor.TaskSpan;
|
||||
+import org.galemc.gale.executor.thread.AssistThread;
|
||||
+import org.galemc.gale.executor.thread.ServerThread;
|
||||
+
|
||||
@@ -3243,6 +3256,15 @@ index 0000000000000000000000000000000000000000..cc5373e78657d04a43cb844c4fcd5f0f
|
||||
+ * on any ticking thread, and additional queues would need to be added concerning a specific
|
||||
+ * subject (like an entity or chunk) with tasks that will be run on whichever ticking thread is the
|
||||
+ * ticking thread for that subject at the time of polling.
|
||||
+ * <br>
|
||||
+ * Note that a {@link ServerThread} can only yield to {@link TaskSpan#TINY} tasks
|
||||
+ * (since there are no higher tiers, and threads can only yield to lower tiers when
|
||||
+ * the task yielded to is{@link TaskSpan#TINY}.
|
||||
+ * Yielding to other tasks in this same tier is somewhat risky, since this means that the tasks that were
|
||||
+ * yielded to must assume that although they are running on the server thread, they may be running at
|
||||
+ * some unknown point in execution of the main thread. Therefore, scheduling any {@link TaskSpan#TINY} tasks to
|
||||
+ * a queue in this tier must be done with the utmost care that the task cannot disrupt, or be disrupted by,
|
||||
+ * the surrounding code that yields to it.
|
||||
+ */
|
||||
+ SERVER(new AbstractTaskQueue[]{
|
||||
+ BaseTaskQueues.deferredToServerThread,
|
||||
@@ -4141,22 +4163,30 @@ 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..d1c305735488ee02e3d86c777ada068da955495b
|
||||
index 0000000000000000000000000000000000000000..24a30760982244fb0d3fa1933751a8286c27014c
|
||||
--- /dev/null
|
||||
+++ b/src/main/java/org/galemc/gale/executor/thread/AbstractYieldingThread.java
|
||||
@@ -0,0 +1,68 @@
|
||||
@@ -0,0 +1,147 @@
|
||||
+// Gale - base thread pool
|
||||
+
|
||||
+package org.galemc.gale.executor.thread;
|
||||
+
|
||||
+import net.minecraft.server.MinecraftServer;
|
||||
+import org.galemc.gale.concurrent.CheckableLock;
|
||||
+import org.galemc.gale.executor.annotation.PotentiallyYielding;
|
||||
+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.SingleWaitingBaseThreadYieldingLock;
|
||||
+import org.galemc.gale.executor.lock.YieldingLock;
|
||||
+import org.jetbrains.annotations.NotNull;
|
||||
+import org.jetbrains.annotations.Nullable;
|
||||
+
|
||||
+import java.util.concurrent.CompletableFuture;
|
||||
+import java.util.concurrent.TimeUnit;
|
||||
+import java.util.concurrent.locks.Condition;
|
||||
+import java.util.function.BooleanSupplier;
|
||||
+import java.util.function.Consumer;
|
||||
+
|
||||
+/**
|
||||
+ * An interface for threads that can yield to other tasks, for example upon encountering a {@link YieldingLock},
|
||||
@@ -4191,8 +4221,79 @@ index 0000000000000000000000000000000000000000..d1c305735488ee02e3d86c777ada068d
|
||||
+ void decrementHeldYieldingLockCount();
|
||||
+
|
||||
+ @ThisThreadOnly
|
||||
+ @PotentiallyYielding("this method is meant to yield")
|
||||
+ void yieldUntil(@Nullable BooleanSupplier stopCondition, @Nullable YieldingLock yieldingLock);
|
||||
+
|
||||
+ /**
|
||||
+ * Calls {@link #yieldUntil(BooleanSupplier, YieldingLock)}, but creates a {@link YieldingLock}
|
||||
+ * based on the given {@code future}, that causes this thread to be notified when the future is completed.
|
||||
+ *
|
||||
+ * @param autoCompletingLockConsumer An optional consumer that will be applied to the created {@link YieldingLock},
|
||||
+ * so that {@link YieldingLock#unlock} can be called on it
|
||||
+ * when the {@code stopCondition} becomes true.
|
||||
+ */
|
||||
+ @ThisThreadOnly
|
||||
+ @PotentiallyYielding("this method is meant to yield")
|
||||
+ default void yieldUntilFuture(@Nullable BooleanSupplier stopCondition, @NotNull CompletableFuture<?> future, @Nullable Consumer<YieldingLock> autoCompletingLockConsumer) {
|
||||
+
|
||||
+ /*
|
||||
+ Here, we abuse the Lock interface to create a YieldingLock
|
||||
+ that can be speculatively locked only while tasks for this blockable event loop are available,
|
||||
+ or when the future has completed.
|
||||
+ */
|
||||
+ YieldingLock autoCompletingLock = new SingleWaitingBaseThreadYieldingLock(new CheckableLock() {
|
||||
+
|
||||
+ @Override
|
||||
+ public boolean isLocked() {
|
||||
+ return !stopCondition.getAsBoolean() && !future.isDone();
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public boolean isHeldByCurrentThread() {
|
||||
+ throw new UnsupportedOperationException("isHeldByCurrentThread() is not supported for a yieldUntilFuture lock");
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public void lock() {
|
||||
+ throw new UnsupportedOperationException("lock() is not supported for a yieldUntilFuture lock");
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public void lockInterruptibly() {
|
||||
+ throw new UnsupportedOperationException("lockInterruptibly() is not supported for a yieldUntilFuture lock");
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public boolean tryLock() {
|
||||
+ return !this.isLocked();
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public boolean tryLock(long time, @NotNull TimeUnit unit) throws InterruptedException {
|
||||
+ throw new UnsupportedOperationException("tryLock(long, TimeUnit) is not supported for a yieldUntilFuture lock");
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public void unlock() {}
|
||||
+
|
||||
+ @NotNull
|
||||
+ @Override
|
||||
+ public Condition newCondition() {
|
||||
+ throw new UnsupportedOperationException("newCondition() is not supported for a yieldUntilFuture lock");
|
||||
+ }
|
||||
+
|
||||
+ }).withCanNotBeHeld();
|
||||
+ if (autoCompletingLockConsumer != null) {
|
||||
+ autoCompletingLockConsumer.accept(autoCompletingLock);
|
||||
+ }
|
||||
+ // Be properly notified when the future completes
|
||||
+ future.thenRun(autoCompletingLock::unlock);
|
||||
+
|
||||
+ // Yield while necessary
|
||||
+ this.yieldUntil(null, autoCompletingLock);
|
||||
+
|
||||
+ }
|
||||
+
|
||||
+ @ThisThreadOnly
|
||||
+ void runTasksUntil(@Nullable BooleanSupplier stopCondition, @Nullable YieldingLock yieldingLock);
|
||||
+
|
||||
@@ -4299,10 +4400,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..633c1aeddca31f5fe95cc8c9ad54e6a2cf1b4ac1
|
||||
index 0000000000000000000000000000000000000000..1c7275dd53e29594d25c63d3df54311a300a08fb
|
||||
--- /dev/null
|
||||
+++ b/src/main/java/org/galemc/gale/executor/thread/BaseThread.java
|
||||
@@ -0,0 +1,721 @@
|
||||
@@ -0,0 +1,719 @@
|
||||
+// Gale - base thread pool
|
||||
+
|
||||
+package org.galemc.gale.executor.thread;
|
||||
@@ -4516,8 +4617,9 @@ index 0000000000000000000000000000000000000000..633c1aeddca31f5fe95cc8c9ad54e6a2
|
||||
+ }
|
||||
+
|
||||
+ /**
|
||||
+ * This method is based on {@link #signal}, and must be so, or {@link BaseThreadActivation} will get stuck
|
||||
+ * while choosing a thread to activate.
|
||||
+ * This method is based on {@link #signal}.
|
||||
+ * {@link #signal} must always return true if this method returns true;
|
||||
+ * otherwise {@link BaseThreadActivation} will get stuck while choosing a thread to activate.
|
||||
+ *
|
||||
+ * @see #signal
|
||||
+ */
|
||||
@@ -4533,10 +4635,6 @@ index 0000000000000000000000000000000000000000..633c1aeddca31f5fe95cc8c9ad54e6a2
|
||||
+ if (!this.mayBeStillWaitingButHasBeenSignalled) {
|
||||
+ return true;
|
||||
+ }
|
||||
+ } else if (this.isPollingTaskOrCheckingStopCondition) {
|
||||
+ if (!this.skipNextWait) {
|
||||
+ return true;
|
||||
+ }
|
||||
+ }
|
||||
+ return false;
|
||||
+ }
|
||||
@@ -4550,8 +4648,6 @@ index 0000000000000000000000000000000000000000..633c1aeddca31f5fe95cc8c9ad54e6a2
|
||||
+ * <br>
|
||||
+ * Exactly one of {@code stopCondition} and {@code yieldingLock} must be non-null.
|
||||
+ */
|
||||
+ @ThisThreadOnly
|
||||
+ @PotentiallyYielding("this method is meant to yield")
|
||||
+ public final void yieldUntil(@Nullable BooleanSupplier stopCondition, @Nullable YieldingLock yieldingLock) {
|
||||
+ int oldYieldDepth = this.yieldDepth;
|
||||
+ int newYieldDepth = oldYieldDepth + 1;
|
||||
@@ -4574,6 +4670,8 @@ index 0000000000000000000000000000000000000000..633c1aeddca31f5fe95cc8c9ad54e6a2
|
||||
+ * The above is the same as {@link #yieldUntil}, except it may be called in situations that is not 'yielding',
|
||||
+ * for instance the endless loop polling tasks performed by a n{@link AssistThread}. The difference with
|
||||
+ * {@link #yieldUntil} is that this method does not increment or decrement things the yield depth of this thread.
|
||||
+ * <br>
|
||||
+ * Exactly one of {@code stopCondition} or {@code yieldingLock} must be non-null.
|
||||
+ *
|
||||
+ * @see #yieldUntil
|
||||
+ */
|
||||
@@ -4699,37 +4797,37 @@ index 0000000000000000000000000000000000000000..633c1aeddca31f5fe95cc8c9ad54e6a2
|
||||
+ for (var queue : tier.taskQueues) {
|
||||
+ Runnable task = tinyOnly ? queue.pollTiny(this) : queue.poll(this);
|
||||
+ if (task != null) {
|
||||
+ /*
|
||||
+ Check if the tier has run out of tasks for a span,
|
||||
+ in order to update BaseThreadActivation#thereMayBeTasks.
|
||||
+ */
|
||||
+ for (int spanI = 0; spanI < TaskSpan.length; spanI++) {
|
||||
+ TaskSpan span = TaskSpan.VALUES[spanI];
|
||||
+ if (queue.canHaveTasks(span)) {
|
||||
+ int oldTasks = BaseThreadActivation.thereMayBeTasks[tier.ordinal][spanI].get();
|
||||
+ if (oldTasks > 0) {
|
||||
+ if (!queue.hasTasks(span)) {
|
||||
+ boolean tierHasNoTasksForSpan = true;
|
||||
+ for (AbstractTaskQueue otherTierQueue : tier.taskQueues) {
|
||||
+ // We already know there are no tasks in this queue
|
||||
+ if (otherTierQueue == queue) {
|
||||
+ continue;
|
||||
+ }
|
||||
+ if (otherTierQueue.hasTasks(span)) {
|
||||
+ tierHasNoTasksForSpan = false;
|
||||
+ break;
|
||||
+ }
|
||||
+ this.lastPolledTaskTier = tier;
|
||||
+ return task;
|
||||
+ }
|
||||
+ /*
|
||||
+ Check if the tier has run out of tasks for a span,
|
||||
+ in order to update BaseThreadActivation#thereMayBeTasks.
|
||||
+ */
|
||||
+ for (int spanI = 0; spanI < TaskSpan.length; spanI++) {
|
||||
+ TaskSpan span = TaskSpan.VALUES[spanI];
|
||||
+ if (queue.canHaveTasks(span)) {
|
||||
+ int oldTasks = BaseThreadActivation.thereMayBeTasks[tier.ordinal][spanI].get();
|
||||
+ if (oldTasks > 0) {
|
||||
+ if (!queue.hasTasks(span)) {
|
||||
+ boolean tierHasNoTasksForSpan = true;
|
||||
+ for (AbstractTaskQueue otherTierQueue : tier.taskQueues) {
|
||||
+ // We already know there are no tasks in this queue
|
||||
+ if (otherTierQueue == queue) {
|
||||
+ continue;
|
||||
+ }
|
||||
+ if (tierHasNoTasksForSpan) {
|
||||
+ // Set thereMayBeTasks to false, but only if it did not change in the meantime
|
||||
+ BaseThreadActivation.thereMayBeTasks[tier.ordinal][spanI].compareAndSet(oldTasks, 0);
|
||||
+ if (otherTierQueue.hasTasks(span)) {
|
||||
+ tierHasNoTasksForSpan = false;
|
||||
+ break;
|
||||
+ }
|
||||
+ }
|
||||
+ if (tierHasNoTasksForSpan) {
|
||||
+ // Set thereMayBeTasks to false, but only if it did not change in the meantime
|
||||
+ BaseThreadActivation.thereMayBeTasks[tier.ordinal][spanI].compareAndSet(oldTasks, 0);
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ this.lastPolledTaskTier = tier;
|
||||
+ return task;
|
||||
+ }
|
||||
+ }
|
||||
+ return null;
|
||||
@@ -4972,8 +5070,9 @@ index 0000000000000000000000000000000000000000..633c1aeddca31f5fe95cc8c9ad54e6a2
|
||||
+ @AnyThreadSafe
|
||||
+ @YieldFree
|
||||
+ public final boolean signal(@Nullable SignalReason reason) {
|
||||
+ //noinspection StatementWithEmptyBody
|
||||
+ while (!this.waitLock.tryLock()); // TODO Gale use a wait-free system here by using a sort of leave-a-message-at-the-door Atomic class system
|
||||
+ while (!this.waitLock.tryLock()) { // TODO Gale use a wait-free system here by using a sort of leave-a-message-at-the-door Atomic class system
|
||||
+ Thread.onSpinWait();
|
||||
+ }
|
||||
+ try {
|
||||
+ if (this.isWaiting) {
|
||||
+ if (this.isNotActuallyWaitingYet) {
|
||||
@@ -5497,16 +5596,18 @@ 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..ac1f646ed846ed6067f77e7526e2bd0a43bf6677
|
||||
index 0000000000000000000000000000000000000000..a22b63a15fcc737494454c0d91c35eef5bb21d9e
|
||||
--- /dev/null
|
||||
+++ b/src/main/java/org/galemc/gale/executor/thread/pool/BaseThreadActivation.java
|
||||
@@ -0,0 +1,518 @@
|
||||
@@ -0,0 +1,552 @@
|
||||
+// Gale - base thread pool
|
||||
+
|
||||
+package org.galemc.gale.executor.thread.pool;
|
||||
+
|
||||
+import net.minecraft.server.MinecraftServer;
|
||||
+import org.galemc.gale.executor.TaskSpan;
|
||||
+import org.galemc.gale.executor.annotation.YieldFree;
|
||||
+import org.galemc.gale.executor.annotation.thread.AnyThreadSafe;
|
||||
+import org.galemc.gale.executor.lock.YieldingLock;
|
||||
+import org.galemc.gale.executor.queue.BaseTaskQueueTier;
|
||||
+import org.galemc.gale.executor.thread.BaseThread;
|
||||
@@ -5527,6 +5628,8 @@ index 0000000000000000000000000000000000000000..ac1f646ed846ed6067f77e7526e2bd0a
|
||||
+ *
|
||||
+ * @author Martijn Muijsers under AGPL-3.0
|
||||
+ */
|
||||
+@AnyThreadSafe
|
||||
+@YieldFree
|
||||
+public final class BaseThreadActivation {
|
||||
+
|
||||
+ private BaseThreadActivation() {}
|
||||
@@ -5563,10 +5666,10 @@ index 0000000000000000000000000000000000000000..ac1f646ed846ed6067f77e7526e2bd0a
|
||||
+ * A re-usable array for use inside {@link #update()}.
|
||||
+ */
|
||||
+ @SuppressWarnings("unchecked")
|
||||
+ private static final List<BaseThread>[] threadsWaitingForLockForTier = new List[BaseTaskQueueTier.length];
|
||||
+ private static final List<BaseThread>[] threadsWaitingForUnlockedLockForTier = new List[BaseTaskQueueTier.length];
|
||||
+ static {
|
||||
+ for (int tierI = 0; tierI < BaseTaskQueueTier.length; tierI++) {
|
||||
+ threadsWaitingForLockForTier[tierI] = new ArrayList<>();
|
||||
+ threadsWaitingForUnlockedLockForTier[tierI] = new ArrayList<>();
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
@@ -5705,7 +5808,7 @@ index 0000000000000000000000000000000000000000..ac1f646ed846ed6067f77e7526e2bd0a
|
||||
+ * The following values that are never changed outside of {@link #update()}:
|
||||
+ * <ul>
|
||||
+ * <li>{@link #numberOfThreadsActiveForTier}</li>
|
||||
+ * <li>{@link #threadsWaitingForLockForTier}</li>
|
||||
+ * <li>{@link #threadsWaitingForUnlockedLockForTier}</li>
|
||||
+ * <li>{@link #numberOfThreadsActiveForLowerThanTier}</li>
|
||||
+ * <li>{@link #numberOfThreadsActiveForHigherThanTier}</li>
|
||||
+ * <li>{@link #numberOfThreadsIntendedToBeActiveForTier}</li>
|
||||
@@ -5805,32 +5908,48 @@ index 0000000000000000000000000000000000000000..ac1f646ed846ed6067f77e7526e2bd0a
|
||||
+
|
||||
+ /*
|
||||
+ Compute for each tier, for how many threads
|
||||
+ the highest tier of any task on their stack is that tier.
|
||||
+ the highest tier of any task on their stack is that tier,
|
||||
+ and that are not waiting.
|
||||
+ Additionally, compute the threads for each tier that are waiting for some YieldingLock
|
||||
+ (threads with no tasks on their stack cannot be waiting for a YieldingLock).
|
||||
+ (threads with no tasks on their stack cannot be waiting for a YieldingLock)
|
||||
+ that can be unlocked.
|
||||
+ Additionally, compute the number of threads that are active (i.e. not waiting)
|
||||
+ but that are not executing a task (i.e. do not have any tasks on their stack).
|
||||
+ */
|
||||
+ Arrays.fill(numberOfThreadsActiveForTier, 0);
|
||||
+ for (int tierI = 0; tierI < BaseTaskQueueTier.length; tierI++) {
|
||||
+ threadsWaitingForLockForTier[tierI].clear();
|
||||
+ threadsWaitingForUnlockedLockForTier[tierI].clear();
|
||||
+ }
|
||||
+ int activeAssistThreadsWithoutTask = 0;
|
||||
+ for (BaseThread thread : threads) {
|
||||
+ if (thread != null) {
|
||||
+ BaseTaskQueueTier tier = thread.highestTierOfTaskOnStack;
|
||||
+ if (tier == null) {
|
||||
+ // The thread is doing nothing
|
||||
+ if (thread.baseThreadIndex > 0 && !thread.isWaitingAndNeedsSignal()) {
|
||||
+ // If it is doing nothing but not waiting, it is available to do anything
|
||||
+ activeAssistThreadsWithoutTask++;
|
||||
+ // This value will be unused if tier is null, so we can safely use some dummy value like 1
|
||||
+ int tierOrdinal = thread.baseThreadIndex > 0 ? (tier == null ? -1 : tier.ordinal) : BaseTaskQueueTier.SERVER.ordinal;
|
||||
+ if (thread.isWaitingAndNeedsSignal()) {
|
||||
+ var lockWaitingFor = thread.lockWaitingFor;
|
||||
+ if (lockWaitingFor != null && !lockWaitingFor.isLocked()) {
|
||||
+ threadsWaitingForUnlockedLockForTier[tierOrdinal].add(thread);
|
||||
+ }
|
||||
+ } else {
|
||||
+ numberOfThreadsActiveForTier[tier.ordinal]++;
|
||||
+ if (thread.lockWaitingFor != null) {
|
||||
+ threadsWaitingForLockForTier[tier.ordinal].add(thread);
|
||||
+ if (tier == null && thread.baseThreadIndex > 0) {
|
||||
+ /*
|
||||
+ ^ Note that assist threads are never waiting for a YieldingLock while they are doing nothing,
|
||||
+ so we can safely use 'else' below, but the server thread may be waiting for a YieldingLock
|
||||
+ while it has no highestTierOfTaskOnStack, because it reached a YieldingLock during its
|
||||
+ normal outer execution.
|
||||
+ */
|
||||
+ /*
|
||||
+ The thread is doing nothing:
|
||||
+ if it is also not waiting, it is available to start anything.
|
||||
+ */
|
||||
+ activeAssistThreadsWithoutTask++;
|
||||
+ } else {
|
||||
+ numberOfThreadsActiveForTier[tierOrdinal]++;
|
||||
+ }
|
||||
+ }
|
||||
+ } else {
|
||||
+ numberOfThreadsActiveForTier[BaseTaskQueueTier.SERVER.ordinal]++;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
@@ -5858,11 +5977,16 @@ index 0000000000000000000000000000000000000000..ac1f646ed846ed6067f77e7526e2bd0a
|
||||
+ For each tier, compute the number of threads that should be active if there were tasks.
|
||||
+ This can then later be compared to the actual number of active threads for that tier.
|
||||
+ */
|
||||
+ for (int tierI = 0; tierI < BaseTaskQueueTier.length; tierI++) {
|
||||
+ numberOfThreadsIntendedToBeActiveForTier[tierI] = BaseThreadPool.targetParallelism - (tierI == 0 ? 0 : activeAssistThreadsWithoutTask) - numberOfThreadsActiveForHigherThanTier[tierI] - Math.min(numberOfThreadsActiveForLowerThanTier[tierI], BaseThreadPool.maxUndisturbedLowerTierThreadCount);
|
||||
+ for (int tierI = 1; tierI < BaseTaskQueueTier.length; tierI++) {
|
||||
+ numberOfThreadsIntendedToBeActiveForTier[tierI] = BaseThreadPool.targetParallelism - activeAssistThreadsWithoutTask - numberOfThreadsActiveForHigherThanTier[tierI] - Math.min(numberOfThreadsActiveForLowerThanTier[tierI], BaseThreadPool.maxUndisturbedLowerTierThreadCount);
|
||||
+ }
|
||||
+ // There can only be one active server thread
|
||||
+ numberOfThreadsIntendedToBeActiveForTier[0] = Math.min(numberOfThreadsIntendedToBeActiveForTier[0], 1);
|
||||
+ /*
|
||||
+ There must always be an attempt to have one active server thread.
|
||||
+ The above computation would sometimes give 0 due to not wanting to disturb lower tiers,
|
||||
+ which we don't care about for the server thread because it is always more important.
|
||||
+ The above computation would also sometimes give a number higher than 1 which makes no sense.
|
||||
+ */
|
||||
+ numberOfThreadsIntendedToBeActiveForTier[0] = 1;
|
||||
+
|
||||
+ {
|
||||
+ final int finalActiveAssistThreadsWithoutTask = activeAssistThreadsWithoutTask;
|
||||
@@ -5900,7 +6024,7 @@ index 0000000000000000000000000000000000000000..ac1f646ed846ed6067f77e7526e2bd0a
|
||||
+ break;
|
||||
+ }
|
||||
+ }
|
||||
+ if (thereAreTasks || !threadsWaitingForLockForTier[tierI].isEmpty()) {
|
||||
+ if (thereAreTasks || !threadsWaitingForUnlockedLockForTier[tierI].isEmpty()) {
|
||||
+
|
||||
+ /*
|
||||
+ * We attempt to wake up a thread that is sleeping,
|
||||
@@ -5939,7 +6063,16 @@ index 0000000000000000000000000000000000000000..ac1f646ed846ed6067f77e7526e2bd0a
|
||||
+ for (int threadI = tryThreadsStart; threadI < tryThreadsEnd; threadI++) {
|
||||
+ BaseThread thread = threads[threadI];
|
||||
+ if (thread != null) {
|
||||
+ if (thread.isWaitingAndNeedsSignal() && thread.canStartYieldingTasks) {
|
||||
+ /*
|
||||
+ Note that we only activate threads that can start yielding tasks
|
||||
+ (it seems wasteful to take the effort to activate threads that can not).
|
||||
+ or threads that are waiting for a lock that is not currently locked.
|
||||
+ Note that for the server thread, if it cannot start yielding tasks,
|
||||
+ there is never an alternative thread that can, so we also allow it,
|
||||
+ as a special case.
|
||||
+ */
|
||||
+ @Nullable YieldingLock lockWaitingFor = thread.lockWaitingFor;
|
||||
+ if (thread.isWaitingAndNeedsSignal() && (tierI == 0 || (lockWaitingFor != null && !lockWaitingFor.isLocked()) || thread.canStartYieldingTasks)) {
|
||||
+ /*
|
||||
+ Tasks of a certain tier may yield to tasks of the same or a higher
|
||||
+ tier, and they may also yield to tiny tasks of a lower tier.
|
||||
@@ -5953,7 +6086,6 @@ index 0000000000000000000000000000000000000000..ac1f646ed846ed6067f77e7526e2bd0a
|
||||
+ */
|
||||
+ var highestTierOfTaskOnStack = thread.highestTierOfTaskOnStack;
|
||||
+ var highestTierOfTaskOnStackOrdinalOrLength = highestTierOfTaskOnStack == null ? BaseTaskQueueTier.length : highestTierOfTaskOnStack.ordinal;
|
||||
+ @Nullable YieldingLock lockWaitingFor = thread.lockWaitingFor;
|
||||
+ boolean isThreadWaitingForAvailableYieldingLock = lockWaitingFor != null && !lockWaitingFor.isLocked();
|
||||
+ if (isThreadWaitingForAvailableYieldingLock || highestTierOfTaskOnStack == null || highestTierOfTaskOnStack.ordinal >= tierI) {
|
||||
+ boolean isBestChoice = false;
|
||||
@@ -5989,7 +6121,8 @@ index 0000000000000000000000000000000000000000..ac1f646ed846ed6067f77e7526e2bd0a
|
||||
+ }
|
||||
+ // Check if the thread still seems valid and attempt to activate it
|
||||
+ BaseThread thread = threads[threadIToUpdate];
|
||||
+ if (thread.isWaitingAndNeedsSignal() && thread.canStartYieldingTasks) {
|
||||
+ @Nullable YieldingLock lockWaitingFor = thread.lockWaitingFor;
|
||||
+ if (thread.isWaitingAndNeedsSignal() && (tierI == 0 || (lockWaitingFor != null && !lockWaitingFor.isLocked()) || thread.canStartYieldingTasks)) {
|
||||
+ // Wake up the thread
|
||||
+ if (thread.signal(thereAreTasks ? SignalReason.TASK : SignalReason.YIELDING_LOCK)) {
|
||||
+ // Do another update
|
||||
@@ -32,10 +32,10 @@ index 4b279948e82a6dfd2f471ba698e361dcfe4499de..438e7d8ce4e1df13a6a5cc45075996be
|
||||
|
||||
doFirst {
|
||||
diff --git a/src/main/java/org/bukkit/craftbukkit/Main.java b/src/main/java/org/bukkit/craftbukkit/Main.java
|
||||
index fab5d1c1531fa89113bef6d17df8437b0aec4582..7ec31bf30db664ade4ee4eaf22dccb4177f9262f 100644
|
||||
index fab5d1c1531fa89113bef6d17df8437b0aec4582..95d8715928a0ddccdabcb76090a3b5bdd143c5a5 100644
|
||||
--- a/src/main/java/org/bukkit/craftbukkit/Main.java
|
||||
+++ b/src/main/java/org/bukkit/craftbukkit/Main.java
|
||||
@@ -4,18 +4,19 @@ import java.io.File;
|
||||
@@ -4,18 +4,20 @@ import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.text.DateFormat;
|
||||
import java.text.SimpleDateFormat;
|
||||
@@ -55,11 +55,12 @@ index fab5d1c1531fa89113bef6d17df8437b0aec4582..7ec31bf30db664ade4ee4eaf22dccb41
|
||||
import net.minecraft.world.level.lighting.LayerLightEventListener;
|
||||
import net.minecrell.terminalconsole.TerminalConsoleAppender; // Paper
|
||||
+import org.galemc.gale.executor.thread.BaseThread;
|
||||
+import org.spigotmc.WatchdogThread;
|
||||
+import reactor.blockhound.BlockHound;
|
||||
|
||||
public class Main {
|
||||
public static boolean useJline = true;
|
||||
@@ -48,6 +49,43 @@ public class Main {
|
||||
@@ -48,6 +50,49 @@ public class Main {
|
||||
// Gale end - include time in startup logs
|
||||
|
||||
public static void main(String[] args) {
|
||||
@@ -67,11 +68,15 @@ index fab5d1c1531fa89113bef6d17df8437b0aec4582..7ec31bf30db664ade4ee4eaf22dccb41
|
||||
+ if (Boolean.getBoolean("gale.detect.thread.blocks")) {
|
||||
+ printlnStartupInfoToSystemOut("Initializing blocking base thread detection...");
|
||||
+ try {
|
||||
+ var builder = BlockHound.builder()
|
||||
+ // Mark base threads as intended to be non-blocking
|
||||
+ builder.nonBlockingThreadPredicate(currentPredicate -> currentPredicate.or(thread -> thread instanceof BaseThread))
|
||||
+ var builder = BlockHound.builder();
|
||||
+ // Mark base threads as intended to be non-blocking (except the WatchdogThread, which obviously sleeps until needed)
|
||||
+ builder.nonBlockingThreadPredicate(currentPredicate -> currentPredicate.or(thread -> thread instanceof BaseThread && !(thread instanceof WatchdogThread)));
|
||||
+ // Set the callback when a base thread blocks
|
||||
+ builder.blockingMethodCallback(blockingMethod -> {
|
||||
+ if (MinecraftServer.SERVER == null) {
|
||||
+ // Allow blocking before the server has finished initializing
|
||||
+ return;
|
||||
+ }
|
||||
+ String message = "A base thread (" + Thread.currentThread() + ") started blocking:";
|
||||
+ if (MinecraftServer.LOGGER != null) {
|
||||
+ MinecraftServer.LOGGER.error(message);
|
||||
@@ -88,7 +93,9 @@ index fab5d1c1531fa89113bef6d17df8437b0aec4582..7ec31bf30db664ade4ee4eaf22dccb41
|
||||
+ threadBlockingMethods.remove("onSpinWait");
|
||||
+ threadBlockingMethods.remove("yield");
|
||||
+ // Allow base threads to block in the intended way
|
||||
+ builder.allowBlockingCallsInside("org.galemc.gale.executor.thread.BaseThread", "waitUntilSignalled")
|
||||
+ builder.allowBlockingCallsInside("org.galemc.gale.executor.thread.BaseThread", "waitUntilSignalled");
|
||||
+ // Allow the ServerThread to block during initialization
|
||||
+ builder.allowBlockingCallsInside("net.minecraft.server.dedicated.DedicatedServer", "initServer");
|
||||
+ // Install BlockHound
|
||||
+ builder.install();
|
||||
+ printlnStartupInfoToSystemOut("Blocking base thread detection is enabled.");
|
||||
@@ -54,10 +54,10 @@ index ac12cabaf15bc3520ff74d09faa48a135c63f23c..0019e5eefc4b638526a75dd3706a5403
|
||||
LOGGER.info("Closing Server");
|
||||
try {
|
||||
diff --git a/src/main/java/org/galemc/gale/executor/queue/BaseTaskQueueTier.java b/src/main/java/org/galemc/gale/executor/queue/BaseTaskQueueTier.java
|
||||
index cc5373e78657d04a43cb844c4fcd5f0f9cacf187..43cb69a7fc55f6b5db05158b5dba3637a664bd19 100644
|
||||
index e4a0b3085cb22f25246010c43919129283a3b872..52c413990fd8aaca72e371c3bc8f1f145a172abc 100644
|
||||
--- a/src/main/java/org/galemc/gale/executor/queue/BaseTaskQueueTier.java
|
||||
+++ b/src/main/java/org/galemc/gale/executor/queue/BaseTaskQueueTier.java
|
||||
@@ -63,7 +63,9 @@ public enum BaseTaskQueueTier {
|
||||
@@ -73,7 +73,9 @@ public enum BaseTaskQueueTier {
|
||||
* asynchronously with respect to the {@link ServerThread} and the ticking of the server.
|
||||
* Execution of
|
||||
*/
|
||||
@@ -77,10 +77,10 @@ index 80f9e70d5c4330e079feccc9a4b1b5957c79ef45..e4955e8d04735b74007aae0bf3230281
|
||||
public static final long INVALID_CHUNK_KEY = getCoordinateKey(Integer.MAX_VALUE, Integer.MAX_VALUE);
|
||||
|
||||
diff --git a/src/main/java/org/galemc/gale/executor/queue/BaseTaskQueueTier.java b/src/main/java/org/galemc/gale/executor/queue/BaseTaskQueueTier.java
|
||||
index 43cb69a7fc55f6b5db05158b5dba3637a664bd19..c5e05b115eb3f8b2d55c5aa2ffce8d86822becf2 100644
|
||||
index 52c413990fd8aaca72e371c3bc8f1f145a172abc..85f467b3cedef57b6b51f04eb34316d43e192d87 100644
|
||||
--- a/src/main/java/org/galemc/gale/executor/queue/BaseTaskQueueTier.java
|
||||
+++ b/src/main/java/org/galemc/gale/executor/queue/BaseTaskQueueTier.java
|
||||
@@ -64,6 +64,8 @@ public enum BaseTaskQueueTier {
|
||||
@@ -74,6 +74,8 @@ public enum BaseTaskQueueTier {
|
||||
* Execution of
|
||||
*/
|
||||
ASYNC(new AbstractTaskQueue[]{
|
||||
@@ -7,7 +7,7 @@ License: AGPL-3.0 (https://www.gnu.org/licenses/agpl-3.0.html)
|
||||
Gale - https://galemc.org
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/server/level/ServerChunkCache.java b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
|
||||
index df4db98618c6c9261b4ec8e2987c4ed26af4bd4b..ba352013692b987518dd200d376fb6cf2c90da19 100644
|
||||
index 83a57b9bc59063ed8299f98bc33e14b57f2ea0de..2a0cbe5146eb444a8cb7ab4960904143af4456a7 100644
|
||||
--- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java
|
||||
+++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
|
||||
@@ -11,7 +11,6 @@ import java.util.Collections;
|
||||
@@ -26,16 +26,34 @@ index df4db98618c6c9261b4ec8e2987c4ed26af4bd4b..ba352013692b987518dd200d376fb6cf
|
||||
import net.minecraft.server.level.progress.ChunkProgressListener;
|
||||
import net.minecraft.util.VisibleForDebug;
|
||||
import net.minecraft.util.thread.BlockableEventLoop;
|
||||
@@ -48,6 +48,8 @@ import net.minecraft.world.level.storage.DimensionDataStorage;
|
||||
import net.minecraft.world.level.storage.LevelData;
|
||||
import net.minecraft.world.level.storage.LevelStorageSource;
|
||||
import it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet; // Paper
|
||||
+import org.galemc.gale.executor.queue.BaseTaskQueues;
|
||||
+import org.galemc.gale.executor.thread.pool.BaseThreadActivation;
|
||||
@@ -446,7 +446,7 @@ public class ServerChunkCache extends ChunkSource {
|
||||
// Paper end
|
||||
com.destroystokyo.paper.io.SyncLoadFinder.logSyncLoad(this.level, x1, z1); // Paper - sync load info
|
||||
this.level.timings.syncChunkLoad.startTiming(); // Paper
|
||||
- chunkproviderserver_b.managedBlock(completablefuture::isDone);
|
||||
+ chunkproviderserver_b.managedYield(completablefuture); // Gale - base thread pool
|
||||
io.papermc.paper.chunk.system.scheduling.ChunkTaskScheduler.popChunkWait(); // Paper - async chunk debug // Paper - rewrite chunk system
|
||||
this.level.timings.syncChunkLoad.stopTiming(); // Paper
|
||||
} // Paper
|
||||
@@ -489,7 +489,7 @@ public class ServerChunkCache extends ChunkSource {
|
||||
ServerChunkCache.MainThreadExecutor chunkproviderserver_b = this.mainThreadProcessor;
|
||||
|
||||
public class ServerChunkCache extends ChunkSource {
|
||||
Objects.requireNonNull(completablefuture);
|
||||
- chunkproviderserver_b.managedBlock(completablefuture::isDone);
|
||||
+ chunkproviderserver_b.managedYield(completablefuture); // Gale - base thread pool
|
||||
} else {
|
||||
completablefuture = CompletableFuture.supplyAsync(() -> {
|
||||
return this.getChunkFutureMainThread(chunkX, chunkZ, leastStatus, create);
|
||||
@@ -973,6 +973,8 @@ public class ServerChunkCache extends ChunkSource {
|
||||
|
||||
@@ -999,6 +1001,14 @@ public class ServerChunkCache extends ChunkSource {
|
||||
public final class MainThreadExecutor extends BlockableEventLoop<Runnable> {
|
||||
|
||||
+ private @Nullable YieldingLock yieldingLockToNotifyOnNewTasks = null; // Gale - base thread pool
|
||||
+
|
||||
MainThreadExecutor(Level world) {
|
||||
super("Chunk source main thread executor for " + world.dimension().location());
|
||||
}
|
||||
@@ -1002,6 +1004,39 @@ public class ServerChunkCache extends ChunkSource {
|
||||
super.doRunTask(task);
|
||||
}
|
||||
|
||||
@@ -45,11 +63,62 @@ index df4db98618c6c9261b4ec8e2987c4ed26af4bd4b..ba352013692b987518dd200d376fb6cf
|
||||
+ super.tell(runnable);
|
||||
+ MinecraftServer.nextTimeAssumeWeMayHaveDelayedTasks = true;
|
||||
+ BaseTaskQueues.allLevelsScheduledChunkCache.newTaskWasAdded();
|
||||
+ if (this.yieldingLockToNotifyOnNewTasks != null) {
|
||||
+ this.yieldingLockToNotifyOnNewTasks.unlock();
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ public void managedBlock(BooleanSupplier stopCondition) {
|
||||
+ throw new UnsupportedOperationException("Cannot call " + this.getClass().getName() + ".managedBlock(BooleanSupplier), call managedYield(CompletableFuture) instead");
|
||||
+ }
|
||||
+
|
||||
+ public void managedYield(CompletableFuture<?> future) {
|
||||
+ if (!future.isDone()) {
|
||||
+ ++this.blockingCount;
|
||||
+ try {
|
||||
+ var currentThread = AbstractYieldingThread.currentYieldingThread();
|
||||
+ while (!future.isDone()) {
|
||||
+ if (!this.pollTask()) {
|
||||
+ currentThread.yieldUntilFuture(this::hasPendingTasks, future, autoCompletingLock -> this.yieldingLockToNotifyOnNewTasks = autoCompletingLock);
|
||||
+ }
|
||||
+ }
|
||||
+ this.yieldingLockToNotifyOnNewTasks = null;
|
||||
+ } finally {
|
||||
+ --this.blockingCount;
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ // Gale end - base thread pool
|
||||
+
|
||||
@Override
|
||||
// CraftBukkit start - process pending Chunk loadCallback() and unloadCallback() after each run task
|
||||
public boolean pollTask() {
|
||||
diff --git a/src/main/java/net/minecraft/util/thread/BlockableEventLoop.java b/src/main/java/net/minecraft/util/thread/BlockableEventLoop.java
|
||||
index 392e7b4a89669f16b32043b65b69e6593d17f10e..6328c0254b585b3bc169dd2b5d0f25bfd67566d2 100644
|
||||
--- a/src/main/java/net/minecraft/util/thread/BlockableEventLoop.java
|
||||
+++ b/src/main/java/net/minecraft/util/thread/BlockableEventLoop.java
|
||||
@@ -21,7 +21,7 @@ public abstract class BlockableEventLoop<R extends Runnable> implements Profiler
|
||||
private final String name;
|
||||
private static final Logger LOGGER = LogUtils.getLogger();
|
||||
private final Queue<R> pendingRunnables = Queues.newConcurrentLinkedQueue();
|
||||
- private int blockingCount;
|
||||
+ protected int blockingCount; // Gale - base thread pool
|
||||
|
||||
protected BlockableEventLoop(String name) {
|
||||
this.name = name;
|
||||
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
|
||||
index 949feba1264bcafb8dc2dcecd0a566fea80a2ba0..9eae3862abb5f1d7755a8e777fd4bf9a6f8e321d 100644
|
||||
--- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
|
||||
+++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
|
||||
@@ -470,7 +470,7 @@ public class CraftWorld extends CraftRegionAccessor implements World {
|
||||
list,
|
||||
true
|
||||
);
|
||||
- serverChunkCache.mainThreadProcessor.managedBlock(future::isDone);
|
||||
+ serverChunkCache.mainThreadProcessor.managedYield(future); // Gale - base thread pool
|
||||
if (chunkStatus == ChunkStatus.NOISE) {
|
||||
future.join().left().ifPresent(chunk -> net.minecraft.world.level.levelgen.Heightmap.primeHeightmaps(chunk, ChunkStatus.POST_FEATURES));
|
||||
}
|
||||
diff --git a/src/main/java/org/galemc/gale/executor/queue/AllLevelsScheduledChunkCacheTaskQueue.java b/src/main/java/org/galemc/gale/executor/queue/AllLevelsScheduledChunkCacheTaskQueue.java
|
||||
new file mode 100644
|
||||
index 0000000000000000000000000000000000000000..60a3b6e935fcea6cf27c31e2b967bf3758283274
|
||||
@@ -109,7 +178,7 @@ index 0000000000000000000000000000000000000000..60a3b6e935fcea6cf27c31e2b967bf37
|
||||
+
|
||||
+}
|
||||
diff --git a/src/main/java/org/galemc/gale/executor/queue/AllLevelsScheduledTaskQueue.java b/src/main/java/org/galemc/gale/executor/queue/AllLevelsScheduledTaskQueue.java
|
||||
index bbe92d92f58d484084114da73003c345d00aac7e..2a42fa59fb09196959be66d58b67862902a5e17f 100644
|
||||
index 2eb121798b2feb2a5ce5ebb60316660dbff87de3..c9ded5f30e5465c8e15719ac785797cf476474f1 100644
|
||||
--- a/src/main/java/org/galemc/gale/executor/queue/AllLevelsScheduledTaskQueue.java
|
||||
+++ b/src/main/java/org/galemc/gale/executor/queue/AllLevelsScheduledTaskQueue.java
|
||||
@@ -12,7 +12,8 @@ import org.galemc.gale.executor.thread.pool.BaseThreadActivation;
|
||||
@@ -123,10 +192,10 @@ index bbe92d92f58d484084114da73003c345d00aac7e..2a42fa59fb09196959be66d58b678629
|
||||
* All tasks provided by this queue must be yield-free.
|
||||
*
|
||||
diff --git a/src/main/java/org/galemc/gale/executor/queue/BaseTaskQueueTier.java b/src/main/java/org/galemc/gale/executor/queue/BaseTaskQueueTier.java
|
||||
index c5e05b115eb3f8b2d55c5aa2ffce8d86822becf2..27fee00edfa768679d7f998bc997f2d69c6d7ac5 100644
|
||||
index 85f467b3cedef57b6b51f04eb34316d43e192d87..152d3c9805365ff157e484e644b982febdcb6693 100644
|
||||
--- a/src/main/java/org/galemc/gale/executor/queue/BaseTaskQueueTier.java
|
||||
+++ b/src/main/java/org/galemc/gale/executor/queue/BaseTaskQueueTier.java
|
||||
@@ -49,7 +49,8 @@ public enum BaseTaskQueueTier {
|
||||
@@ -59,7 +59,8 @@ public enum BaseTaskQueueTier {
|
||||
SERVER(new AbstractTaskQueue[]{
|
||||
BaseTaskQueues.deferredToServerThread,
|
||||
BaseTaskQueues.serverThreadTick,
|
||||
@@ -83,7 +83,7 @@ index 84cc9397237fa0c17aa1012dfb5683c90eb6d3b8..f5c15d40094c2ddc6220b0595597d121
|
||||
final ReentrantLock schedulingLock = new ReentrantLock();
|
||||
public final ChunkHolderManager chunkHolderManager;
|
||||
diff --git a/src/main/java/org/galemc/gale/executor/queue/AllLevelsScheduledTaskQueue.java b/src/main/java/org/galemc/gale/executor/queue/AllLevelsScheduledTaskQueue.java
|
||||
index 2a42fa59fb09196959be66d58b67862902a5e17f..5a83bd0edb8f74b6603da9be79913f70cab5b50a 100644
|
||||
index c9ded5f30e5465c8e15719ac785797cf476474f1..2d5a6d60d87b34d03d2d8bbda6d92fddec86924a 100644
|
||||
--- a/src/main/java/org/galemc/gale/executor/queue/AllLevelsScheduledTaskQueue.java
|
||||
+++ b/src/main/java/org/galemc/gale/executor/queue/AllLevelsScheduledTaskQueue.java
|
||||
@@ -13,7 +13,7 @@ import org.jetbrains.annotations.Nullable;
|
||||
@@ -156,10 +156,10 @@ index 0000000000000000000000000000000000000000..117797c4ac2a81218e9f3e977467b62a
|
||||
+
|
||||
+}
|
||||
diff --git a/src/main/java/org/galemc/gale/executor/queue/BaseTaskQueueTier.java b/src/main/java/org/galemc/gale/executor/queue/BaseTaskQueueTier.java
|
||||
index 27fee00edfa768679d7f998bc997f2d69c6d7ac5..9a908566ccdda9ca77e0f9236f674f17f79e9c40 100644
|
||||
index 152d3c9805365ff157e484e644b982febdcb6693..303bdcf41c7836c35bbe0fe00e0d14b5472df072 100644
|
||||
--- a/src/main/java/org/galemc/gale/executor/queue/BaseTaskQueueTier.java
|
||||
+++ b/src/main/java/org/galemc/gale/executor/queue/BaseTaskQueueTier.java
|
||||
@@ -50,7 +50,8 @@ public enum BaseTaskQueueTier {
|
||||
@@ -60,7 +60,8 @@ public enum BaseTaskQueueTier {
|
||||
BaseTaskQueues.deferredToServerThread,
|
||||
BaseTaskQueues.serverThreadTick,
|
||||
BaseTaskQueues.anyTickScheduledServerThread,
|
||||
@@ -507,10 +507,10 @@ index abdec5529763b77126494ae0c2be9b48de900bc1..35233587de14cf52da30324df89d9ec7
|
||||
* This class is a copy of {@link PrioritisedQueueExecutorThread}, with the notable difference
|
||||
* that it does not extend {@link Thread}, but may be instantiated on its own, as an agent representing
|
||||
diff --git a/src/main/java/org/galemc/gale/executor/queue/BaseTaskQueueTier.java b/src/main/java/org/galemc/gale/executor/queue/BaseTaskQueueTier.java
|
||||
index 9a908566ccdda9ca77e0f9236f674f17f79e9c40..dc006d9940ef8114a3a3e4860fbc1da0f7c2ee60 100644
|
||||
index 303bdcf41c7836c35bbe0fe00e0d14b5472df072..e9b9d016309e8fac445b223b4412c479a5aca0e8 100644
|
||||
--- a/src/main/java/org/galemc/gale/executor/queue/BaseTaskQueueTier.java
|
||||
+++ b/src/main/java/org/galemc/gale/executor/queue/BaseTaskQueueTier.java
|
||||
@@ -63,7 +63,6 @@ public enum BaseTaskQueueTier {
|
||||
@@ -73,7 +73,6 @@ public enum BaseTaskQueueTier {
|
||||
/**
|
||||
* A tier for queues that contain general tasks that must be performed at some point in time,
|
||||
* asynchronously with respect to the {@link ServerThread} and the ticking of the server.
|
||||
@@ -518,7 +518,7 @@ index 9a908566ccdda9ca77e0f9236f674f17f79e9c40..dc006d9940ef8114a3a3e4860fbc1da0
|
||||
*/
|
||||
ASYNC(new AbstractTaskQueue[]{
|
||||
// The cleaner queue has high priority because it releases resources back to a pool, thereby saving memory
|
||||
@@ -74,7 +73,9 @@ public enum BaseTaskQueueTier {
|
||||
@@ -84,7 +83,9 @@ public enum BaseTaskQueueTier {
|
||||
* A tier for queues that contain tasks with the same considerations as {@link #ASYNC},
|
||||
* but with a low priority.
|
||||
*/
|
||||
@@ -652,7 +652,7 @@ index 0000000000000000000000000000000000000000..6f78603c87517bb681ae473d0c72b75e
|
||||
+
|
||||
+}
|
||||
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 633c1aeddca31f5fe95cc8c9ad54e6a2cf1b4ac1..81e244e94c7422de589e91db44b8e493e1daf5e7 100644
|
||||
index 1c7275dd53e29594d25c63d3df54311a300a08fb..00a52c8c21ebfa95cfd3e6e9d9d76c5789ea6d83 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 @@
|
||||
@@ -672,7 +672,7 @@ index 633c1aeddca31f5fe95cc8c9ad54e6a2cf1b4ac1..81e244e94c7422de589e91db44b8e493
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
@@ -698,6 +701,20 @@ public abstract class BaseThread extends Thread implements AbstractYieldingThrea
|
||||
@@ -696,6 +699,20 @@ public abstract class BaseThread extends Thread implements AbstractYieldingThrea
|
||||
}
|
||||
}
|
||||
|
||||
@@ -200,10 +200,10 @@ index 7ed820d2483bf6741a355b062f062a04866ba938..575f8ba79cf3547b837abb5957fed0aa
|
||||
for (int i = 0; i < this.tickables.size(); ++i) {
|
||||
((Runnable) this.tickables.get(i)).run();
|
||||
diff --git a/src/main/java/net/minecraft/server/level/ServerChunkCache.java b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
|
||||
index ba352013692b987518dd200d376fb6cf2c90da19..99fe51c3654bf5f978b6576290504c7b7bf2e7a1 100644
|
||||
index 2a0cbe5146eb444a8cb7ab4960904143af4456a7..aa49a3d3827128c7d4c7b424211f80abd7e2ff80 100644
|
||||
--- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java
|
||||
+++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
|
||||
@@ -629,23 +629,99 @@ public class ServerChunkCache extends ChunkSource {
|
||||
@@ -630,23 +630,99 @@ public class ServerChunkCache extends ChunkSource {
|
||||
}
|
||||
// CraftBukkit end
|
||||
|
||||
30
patches/server/0164-Yielding-ChunkTaskScheduler.patch
Normal file
30
patches/server/0164-Yielding-ChunkTaskScheduler.patch
Normal file
@@ -0,0 +1,30 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Martijn Muijsers <martijnmuijsers@live.nl>
|
||||
Date: Sun, 5 Feb 2023 19:04:45 +0100
|
||||
Subject: [PATCH] Yielding ChunkTaskScheduler
|
||||
|
||||
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/chunk/system/scheduling/ChunkTaskScheduler.java b/src/main/java/io/papermc/paper/chunk/system/scheduling/ChunkTaskScheduler.java
|
||||
index 79ef41d2bb30beee2355d1de3dc99c9e00d929d5..0b41ffacba43ba784450ec52b0a6e96ac5bd864b 100644
|
||||
--- a/src/main/java/io/papermc/paper/chunk/system/scheduling/ChunkTaskScheduler.java
|
||||
+++ b/src/main/java/io/papermc/paper/chunk/system/scheduling/ChunkTaskScheduler.java
|
||||
@@ -22,6 +22,8 @@ import net.minecraft.world.level.chunk.ChunkAccess;
|
||||
import net.minecraft.world.level.chunk.ChunkStatus;
|
||||
import net.minecraft.world.level.chunk.LevelChunk;
|
||||
import org.bukkit.Bukkit;
|
||||
+import org.galemc.gale.executor.lock.MultipleWaitingBaseThreadsYieldingLock;
|
||||
+import org.galemc.gale.executor.lock.YieldingLock;
|
||||
import org.galemc.gale.executor.thread.pool.BaseThreadPool;
|
||||
import org.slf4j.Logger;
|
||||
import java.io.File;
|
||||
@@ -116,7 +118,7 @@ public final class ChunkTaskScheduler {
|
||||
|
||||
public final PrioritisedThreadedTaskQueue mainThreadExecutor = new PrioritisedThreadedTaskQueue(true); // Gale - base thread pool - private -> public, count delayed tasks
|
||||
|
||||
- final ReentrantLock schedulingLock = new ReentrantLock();
|
||||
+ final YieldingLock schedulingLock = new MultipleWaitingBaseThreadsYieldingLock(new ReentrantLock()); // Gale - base thread pool - yielding ChunkTaskScheduler
|
||||
public final ChunkHolderManager chunkHolderManager;
|
||||
|
||||
static {
|
||||
Reference in New Issue
Block a user