9
0
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:
Martijn Muijsers
2023-02-05 21:58:32 +01:00
parent 4d37dc3a68
commit 2c62764cce
25 changed files with 539 additions and 248 deletions

View File

@@ -25,7 +25,7 @@ index 01780a73ee9cc602951bdf568c17673c4c639f79..4b279948e82a6dfd2f471ba698e361dc
// Gale end - hide irrelevant compilation warnings // 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 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 --- a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java
+++ b/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; @@ -14,6 +14,8 @@ import java.util.Locale;
@@ -45,7 +45,7 @@ index 9c3ccbbd657d1605b8fabb6e01c11ff31f39a17e..1d1db89362b78ac34d46cdd71b9ab274
import org.slf4j.Logger; import org.slf4j.Logger;
// CraftBukkit start // 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 io.papermc.paper.brigadier.PaperBrigadierProviderImpl.INSTANCE.getClass(); // init PaperBrigadierProvider
// Paper end // Paper end
@@ -59,7 +59,6 @@ index 9c3ccbbd657d1605b8fabb6e01c11ff31f39a17e..1d1db89362b78ac34d46cdd71b9ab274
+ LOGGER.warn("SIMD operations are available for your server, but are not configured!"); + 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("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("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"); + 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 + // Gale start - Pufferfish - SIMD support

View File

@@ -41,10 +41,10 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE. SOFTWARE.
diff --git a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java 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 --- a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java
+++ b/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); server.enablePlugins(org.bukkit.plugin.PluginLoadOrder.STARTUP);
// CraftBukkit end // CraftBukkit end

View File

@@ -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 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 new file mode 100644
index 0000000000000000000000000000000000000000..d07f68ff73a368c8f0da56152021a95474a601ca index 0000000000000000000000000000000000000000..50541414e1d91ff06d108d9b3fe64dcb4ad09668
--- /dev/null --- /dev/null
+++ b/src/main/java/org/galemc/gale/executor/annotation/Access.java +++ b/src/main/java/org/galemc/gale/executor/annotation/Access.java
@@ -0,0 +1,39 @@ @@ -0,0 +1,39 @@
@@ -41,22 +41,22 @@ index 0000000000000000000000000000000000000000..d07f68ff73a368c8f0da56152021a954
+ WRITE, + WRITE,
+ /** + /**
+ * Both {@link #READ} and {@link #WRITE}: if the annotation is applied to a field, it holds for both access to + * 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> + * <br>
+ * This may or may not extend to conceptual access and/or modifications. + * This may or may not extend to conceptual access and/or modifications.
+ * + *
+ * @see #READ + * @see #READ
+ * @see #WRITE + * @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 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 new file mode 100644
index 0000000000000000000000000000000000000000..84a0bac98a382550c826e6adbecec1fe7be974a1 index 0000000000000000000000000000000000000000..6f1d1960953daf7f6f61643f5165e9a0760a647e
--- /dev/null --- /dev/null
+++ b/src/main/java/org/galemc/gale/executor/annotation/Guarded.java +++ b/src/main/java/org/galemc/gale/executor/annotation/Guarded.java
@@ -0,0 +1,47 @@ @@ -0,0 +1,55 @@
+// Gale - thread-safety annotations +// Gale - thread-safety annotations
+ +
+package org.galemc.gale.executor.annotation; +package org.galemc.gale.executor.annotation;
@@ -83,7 +83,7 @@ index 0000000000000000000000000000000000000000..84a0bac98a382550c826e6adbecec1fe
+ * @author Martijn Muijsers under AGPL-3.0 + * @author Martijn Muijsers under AGPL-3.0
+ */ + */
+@Documented +@Documented
+@Repeatable +@Repeatable(Guarded.Container.class)
+@Target({ElementType.FIELD, ElementType.METHOD, ElementType.TYPE}) +@Target({ElementType.FIELD, ElementType.METHOD, ElementType.TYPE})
+public @interface Guarded { +public @interface Guarded {
+ +
@@ -103,19 +103,25 @@ index 0000000000000000000000000000000000000000..84a0bac98a382550c826e6adbecec1fe
+ */ + */
+ String except() default ""; + 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 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 new file mode 100644
index 0000000000000000000000000000000000000000..71f26852c96dea34ea07efe07f834f8262509957 index 0000000000000000000000000000000000000000..a4dc0ebe48fdd352387f06be42ff46fc11ee5822
--- /dev/null --- /dev/null
+++ b/src/main/java/org/galemc/gale/executor/annotation/PotentiallyBlocking.java +++ b/src/main/java/org/galemc/gale/executor/annotation/PotentiallyBlocking.java
@@ -0,0 +1,39 @@ @@ -0,0 +1,34 @@
+// Gale - thread-safety annotations +// Gale - thread-safety annotations
+ +
+package org.galemc.gale.executor.annotation; +package org.galemc.gale.executor.annotation;
+ +
+import org.galemc.gale.executor.thread.BaseThread;
+
+import java.lang.annotation.Documented; +import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType; +import java.lang.annotation.ElementType;
+import java.lang.annotation.Target; +import java.lang.annotation.Target;
@@ -129,9 +135,6 @@ index 0000000000000000000000000000000000000000..71f26852c96dea34ea07efe07f834f82
+ * <br> + * <br>
+ * In a method annotated with {@link PotentiallyBlocking}, fields and methods annotated with + * In a method annotated with {@link PotentiallyBlocking}, fields and methods annotated with
+ * {@link PotentiallyBlocking}, {@link PotentiallyYielding} or {@link YieldFree} may all be used. + * {@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 + * @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 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 new file mode 100644
index 0000000000000000000000000000000000000000..e87ee2612348fc559b21256cc7cadfc684f01f8e index 0000000000000000000000000000000000000000..44b70d68ba6823ab72ea9af4b7774051785c0a2b
--- /dev/null --- /dev/null
+++ b/src/main/java/org/galemc/gale/executor/annotation/PotentiallyYielding.java +++ b/src/main/java/org/galemc/gale/executor/annotation/PotentiallyYielding.java
@@ -0,0 +1,35 @@ @@ -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 + * 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> + * <br>
+ * When applied to a class, this annotation indicates it holds for all methods, both instance and static, + * 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 + * belonging to the class, or any superclass thereof, or any inner or statically nested class of the class or

View 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();
+
+}

View File

@@ -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 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 new file mode 100644
index 0000000000000000000000000000000000000000..f7bedd5cbe9b48ac94f8cc228a17c8a54db7d7e9 index 0000000000000000000000000000000000000000..649d2cfc1d73699302b4e5e64e9110e7681ae09c
--- /dev/null --- /dev/null
+++ b/src/main/java/org/galemc/gale/concurrent/Mutex.java +++ b/src/main/java/org/galemc/gale/concurrent/Mutex.java
@@ -0,0 +1,123 @@ @@ -0,0 +1,127 @@
+// Gale - mutex utility +// Gale - mutex utility
+ +
+package org.galemc.gale.concurrent; +package org.galemc.gale.concurrent;
+ +
+import org.galemc.gale.executor.annotation.thread.AnyThreadSafe; +import org.galemc.gale.executor.annotation.thread.AnyThreadSafe;
+import org.galemc.gale.executor.lock.CheckableLock;
+import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.NotNull;
+ +
+import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeUnit;
@@ -34,7 +33,7 @@ index 0000000000000000000000000000000000000000..f7bedd5cbe9b48ac94f8cc228a17c8a5
+ * <br> + * <br>
+ * This interface extends {@link AutoCloseable}, where {@link #close()} calls {@link #release()}. + * This interface extends {@link AutoCloseable}, where {@link #close()} calls {@link #release()}.
+ * + *
+ * @author Martijn Muijsers + * @author Martijn Muijsers under AGPL-3.0
+ */ + */
+@AnyThreadSafe +@AnyThreadSafe
+public interface Mutex extends CheckableLock, AutoCloseable { +public interface Mutex extends CheckableLock, AutoCloseable {
@@ -134,10 +133,15 @@ index 0000000000000000000000000000000000000000..f7bedd5cbe9b48ac94f8cc228a17c8a5
+ return create(); + 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 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 new file mode 100644
index 0000000000000000000000000000000000000000..c310264afea6c81ec575bdf6aa5495ccb34d7ae4 index 0000000000000000000000000000000000000000..d31293a2a2151bc9fbdc6eb2175045b429fb4461
--- /dev/null --- /dev/null
+++ b/src/main/java/org/galemc/gale/concurrent/SemaphoreMutex.java +++ b/src/main/java/org/galemc/gale/concurrent/SemaphoreMutex.java
@@ -0,0 +1,39 @@ @@ -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 + * and throws {@link UnsupportedOperationException} for all {@link Lock} methods that do not have a default
+ * implementation in {@link Mutex}. + * implementation in {@link Mutex}.
+ * + *
+ * @author Martijn Muijsers + * @author Martijn Muijsers under AGPL-3.0
+ */ + */
+@AnyThreadSafe +@AnyThreadSafe
+@YieldFree +@YieldFree

View File

@@ -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 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 new file mode 100644
index 0000000000000000000000000000000000000000..1dd9d78f70fe586a9868e046e01ab512af036eeb index 0000000000000000000000000000000000000000..8c14d90d1a907adf994070cbe5d62f1fbfd8d9c8
--- /dev/null --- /dev/null
+++ b/src/main/java/org/galemc/gale/concurrent/ThreadAwareNonReentrantLock.java +++ b/src/main/java/org/galemc/gale/concurrent/ThreadAwareNonReentrantLock.java
@@ -0,0 +1,109 @@ @@ -0,0 +1,113 @@
+// Gale - thread-aware lock utility +// Gale - thread-aware lock utility
+ +
+package org.galemc.gale.concurrent; +package org.galemc.gale.concurrent;
+ +
+import net.minecraft.server.MinecraftServer; +import net.minecraft.server.MinecraftServer;
+import org.galemc.gale.executor.lock.CheckableLock;
+import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable; +import org.jetbrains.annotations.Nullable;
+ +
@@ -120,4 +119,9 @@ index 0000000000000000000000000000000000000000..1dd9d78f70fe586a9868e046e01ab512
+ return this.innerLock.isLocked(); + return this.innerLock.isLocked();
+ } + }
+ +
+ @Override
+ public boolean isHeldByCurrentThread() {
+ return this.innerLock.isHeldByCurrentThread();
+ }
+
+} +}

View File

@@ -1352,7 +1352,7 @@ index f0a7a8df3caa2ea765bb0a87cfede71d0995d276..16f3475b059d2b6b85d2b342e84ab32d
// CraftBukkit start // CraftBukkit start
TimeSkipEvent event = new TimeSkipEvent(worldserver.getWorld(), TimeSkipEvent.SkipReason.COMMAND, time); 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 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 --- a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java
+++ b/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; @@ -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); super(options, worldLoader, thread, convertable_conversionsession, resourcepackrepository, worldstem, Proxy.NO_PROXY, datafixer, services, worldloadlistenerfactory);
// CraftBukkit end // CraftBukkit end
this.settings = dedicatedserversettings; 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 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 index 37e0b6212fec71ec9662e6be3b1e8bea487eb4a6..e7747b19685fd943d7fbefbfef656f8bb7c359f1 100644
--- a/src/main/java/net/minecraft/server/level/ServerLevel.java --- a/src/main/java/net/minecraft/server/level/ServerLevel.java
@@ -1850,32 +1864,6 @@ index 78f53ee557276de85f0431ebcb146445b1f4fb92..6176867eea06c53882dcaacfbde0334b
}); });
return ret; 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 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 index 9571aae593999d11b3908856b0295a7d6b588007..ed2841d3a6c6d90ad02266f38c0821bca4f549f1 100644
--- a/src/main/java/org/galemc/gale/configuration/GaleConfigurations.java --- a/src/main/java/org/galemc/gale/configuration/GaleConfigurations.java
@@ -2420,79 +2408,34 @@ index 0000000000000000000000000000000000000000..1d5bb1ba545200f954c886a2afb9d8ee
+ public static final int length = VALUES.length; + 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 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 --- a/src/main/java/org/galemc/gale/executor/annotation/PotentiallyBlocking.java
+++ b/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; package org.galemc.gale.executor.annotation;
-import org.galemc.gale.executor.thread.BaseThread;
+import org.galemc.gale.executor.thread.AbstractYieldingThread; +import org.galemc.gale.executor.thread.AbstractYieldingThread;
+
import java.lang.annotation.Documented; import java.lang.annotation.Documented;
import java.lang.annotation.ElementType; import java.lang.annotation.ElementType;
@@ -19,7 +19,7 @@ import java.lang.annotation.Target; import java.lang.annotation.Target;
* {@link PotentiallyBlocking}, {@link PotentiallyYielding} or {@link YieldFree} may all be used. @@ -15,6 +17,9 @@ import java.lang.annotation.Target;
* <br> * <br>
* Methods that are potentially blocking, including those annotated with {@link PotentiallyBlocking}, must never * In a method annotated with {@link PotentiallyBlocking}, fields and methods annotated with
- * be called on a {@link BaseThread}. * {@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}. + * be called on an {@link AbstractYieldingThread}.
* *
* @author Martijn Muijsers under AGPL-3.0 * @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 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 --- a/src/main/java/org/galemc/gale/executor/annotation/PotentiallyYielding.java
+++ b/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; package org.galemc.gale.executor.annotation;
@@ -2502,6 +2445,14 @@ index e87ee2612348fc559b21256cc7cadfc684f01f8e..7ff4e4ab43d316e319efb33b2dd365d6
import java.lang.annotation.Documented; import java.lang.annotation.Documented;
import java.lang.annotation.ElementType; import java.lang.annotation.ElementType;
import java.lang.annotation.Target; 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; @@ -16,6 +19,9 @@ import java.lang.annotation.Target;
* <br> * <br>
* In a method annotated with {@link PotentiallyYielding}, the only methods that can be called are those * 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; + 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 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 new file mode 100644
index 0000000000000000000000000000000000000000..a248a9ea644a8bb4175da2e1903483ab6866bc48 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 new file mode 100644
index 0000000000000000000000000000000000000000..3ec790a7b19790731f70f59cc2bdb1919f26dd2d index 0000000000000000000000000000000000000000..686e16da8372085196d8f92adb881f82dd5c2947
--- /dev/null --- /dev/null
+++ b/src/main/java/org/galemc/gale/executor/lock/YieldingLock.java +++ b/src/main/java/org/galemc/gale/executor/lock/SingleWaitingBaseThreadYieldingLock.java
@@ -0,0 +1,163 @@ @@ -0,0 +1,39 @@
+// Gale - base thread pool +// Gale - base thread pool
+ +
+package org.galemc.gale.executor.lock; +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.thread.AnyThreadSafe;
+import org.galemc.gale.executor.annotation.PotentiallyYielding; +import org.galemc.gale.executor.annotation.PotentiallyYielding;
+import org.galemc.gale.executor.annotation.YieldFree; +import org.galemc.gale.executor.annotation.YieldFree;
@@ -2808,6 +2779,13 @@ index 0000000000000000000000000000000000000000..3ec790a7b19790731f70f59cc2bdb191
+ */ + */
+ private final @Nullable ReentrantLock innerReentrantLock; + 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) { + public YieldingLock(Lock innerLock) {
+ this.innerLock = innerLock; + this.innerLock = innerLock;
+ if (innerLock instanceof CheckableLock checkableLock) { + if (innerLock instanceof CheckableLock checkableLock) {
@@ -2829,7 +2807,17 @@ index 0000000000000000000000000000000000000000..3ec790a7b19790731f70f59cc2bdb191
+ @YieldFree + @YieldFree
+ @Override + @Override
+ public boolean tryLock() { + 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,10 +2843,12 @@ index 0000000000000000000000000000000000000000..3ec790a7b19790731f70f59cc2bdb191
+ yieldingThread.yieldUntil(null, this); + yieldingThread.yieldUntil(null, this);
+ } + }
+ // Increment the YieldingLock count of the current thread + // Increment the YieldingLock count of the current thread
+ if (this.canBeHeld) {
+ if (yieldingThread != null) { + if (yieldingThread != null) {
+ yieldingThread.incrementHeldYieldingLockCount(); + yieldingThread.incrementHeldYieldingLockCount();
+ } + }
+ } + }
+ }
+ +
+ /** + /**
+ * Releases the lock (must be called after having completed the computation block that required the lock). + * Releases the lock (must be called after having completed the computation block that required the lock).
@@ -2867,10 +2857,12 @@ index 0000000000000000000000000000000000000000..3ec790a7b19790731f70f59cc2bdb191
+ public void unlock() { + public void unlock() {
+ this.innerLock.unlock(); + this.innerLock.unlock();
+ // Decrement the YieldingLock count of the current thread + // Decrement the YieldingLock count of the current thread
+ if (this.canBeHeld) {
+ @Nullable AbstractYieldingThread yieldingThread = AbstractYieldingThread.currentYieldingThread(); + @Nullable AbstractYieldingThread yieldingThread = AbstractYieldingThread.currentYieldingThread();
+ if (yieldingThread != null) { + if (yieldingThread != null) {
+ yieldingThread.decrementHeldYieldingLockCount(); + yieldingThread.decrementHeldYieldingLockCount();
+ } + }
+ }
+ // Potentially signal a thread that this lock has become available. + // 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, + // 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 + // it may already be too late for the polled thread to acquire this lock
@@ -2906,6 +2898,11 @@ index 0000000000000000000000000000000000000000..3ec790a7b19790731f70f59cc2bdb191
+ return this.innerCheckableLock != null ? this.innerCheckableLock.isLocked() : this.innerReentrantLock.isLocked(); + 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. + * Increments the number of threads waiting for this lock to be released.
+ */ + */
@@ -2924,6 +2921,18 @@ index 0000000000000000000000000000000000000000..3ec790a7b19790731f70f59cc2bdb191
+ @YieldFree + @YieldFree
+ protected abstract boolean hasWaitingThreads(); + 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 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 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 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 new file mode 100644
index 0000000000000000000000000000000000000000..bbe92d92f58d484084114da73003c345d00aac7e index 0000000000000000000000000000000000000000..2eb121798b2feb2a5ce5ebb60316660dbff87de3
--- /dev/null --- /dev/null
+++ b/src/main/java/org/galemc/gale/executor/queue/AllLevelsScheduledTaskQueue.java +++ b/src/main/java/org/galemc/gale/executor/queue/AllLevelsScheduledTaskQueue.java
@@ -0,0 +1,127 @@ @@ -0,0 +1,130 @@
+// Gale - base thread pool +// Gale - base thread pool
+ +
+package org.galemc.gale.executor.queue; +package org.galemc.gale.executor.queue;
@@ -3086,6 +3095,9 @@ index 0000000000000000000000000000000000000000..bbe92d92f58d484084114da73003c345
+ +
+ @Override + @Override
+ public boolean hasTasks() { + public boolean hasTasks() {
+ if (MinecraftServer.SERVER == null) {
+ return false;
+ }
+ for (ServerLevel level : MinecraftServer.SERVER.getAllLevels()) { + for (ServerLevel level : MinecraftServer.SERVER.getAllLevels()) {
+ if (this.hasLevelTasks(level)) { + if (this.hasLevelTasks(level)) {
+ return true; + 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 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 new file mode 100644
index 0000000000000000000000000000000000000000..cc5373e78657d04a43cb844c4fcd5f0f9cacf187 index 0000000000000000000000000000000000000000..e4a0b3085cb22f25246010c43919129283a3b872
--- /dev/null --- /dev/null
+++ b/src/main/java/org/galemc/gale/executor/queue/BaseTaskQueueTier.java +++ b/src/main/java/org/galemc/gale/executor/queue/BaseTaskQueueTier.java
@@ -0,0 +1,115 @@ @@ -0,0 +1,125 @@
+// Gale - base thread pool +// Gale - base thread pool
+ +
+package org.galemc.gale.executor.queue; +package org.galemc.gale.executor.queue;
@@ -3204,6 +3216,7 @@ index 0000000000000000000000000000000000000000..cc5373e78657d04a43cb844c4fcd5f0f
+import net.minecraft.server.MinecraftServer; +import net.minecraft.server.MinecraftServer;
+import net.minecraft.server.level.ServerLevel; +import net.minecraft.server.level.ServerLevel;
+import net.minecraft.world.entity.Entity; +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.AssistThread;
+import org.galemc.gale.executor.thread.ServerThread; +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 + * 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 + * 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. + * 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[]{ + SERVER(new AbstractTaskQueue[]{
+ BaseTaskQueues.deferredToServerThread, + 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 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 new file mode 100644
index 0000000000000000000000000000000000000000..d1c305735488ee02e3d86c777ada068da955495b index 0000000000000000000000000000000000000000..24a30760982244fb0d3fa1933751a8286c27014c
--- /dev/null --- /dev/null
+++ b/src/main/java/org/galemc/gale/executor/thread/AbstractYieldingThread.java +++ b/src/main/java/org/galemc/gale/executor/thread/AbstractYieldingThread.java
@@ -0,0 +1,68 @@ @@ -0,0 +1,147 @@
+// Gale - base thread pool +// Gale - base thread pool
+ +
+package org.galemc.gale.executor.thread; +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.thread.AnyThreadSafe;
+import org.galemc.gale.executor.annotation.YieldFree; +import org.galemc.gale.executor.annotation.YieldFree;
+import org.galemc.gale.executor.annotation.thread.ThisThreadOnly; +import org.galemc.gale.executor.annotation.thread.ThisThreadOnly;
+import org.galemc.gale.executor.lock.SingleWaitingBaseThreadYieldingLock;
+import org.galemc.gale.executor.lock.YieldingLock; +import org.galemc.gale.executor.lock.YieldingLock;
+import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable; +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.BooleanSupplier;
+import java.util.function.Consumer;
+ +
+/** +/**
+ * An interface for threads that can yield to other tasks, for example upon encountering a {@link YieldingLock}, + * 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(); + void decrementHeldYieldingLockCount();
+ +
+ @ThisThreadOnly + @ThisThreadOnly
+ @PotentiallyYielding("this method is meant to yield")
+ void yieldUntil(@Nullable BooleanSupplier stopCondition, @Nullable YieldingLock yieldingLock); + 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 + @ThisThreadOnly
+ void runTasksUntil(@Nullable BooleanSupplier stopCondition, @Nullable YieldingLock yieldingLock); + 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 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 new file mode 100644
index 0000000000000000000000000000000000000000..633c1aeddca31f5fe95cc8c9ad54e6a2cf1b4ac1 index 0000000000000000000000000000000000000000..1c7275dd53e29594d25c63d3df54311a300a08fb
--- /dev/null --- /dev/null
+++ b/src/main/java/org/galemc/gale/executor/thread/BaseThread.java +++ b/src/main/java/org/galemc/gale/executor/thread/BaseThread.java
@@ -0,0 +1,721 @@ @@ -0,0 +1,719 @@
+// Gale - base thread pool +// Gale - base thread pool
+ +
+package org.galemc.gale.executor.thread; +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 + * This method is based on {@link #signal}.
+ * while choosing a thread to activate. + * {@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 + * @see #signal
+ */ + */
@@ -4533,10 +4635,6 @@ index 0000000000000000000000000000000000000000..633c1aeddca31f5fe95cc8c9ad54e6a2
+ if (!this.mayBeStillWaitingButHasBeenSignalled) { + if (!this.mayBeStillWaitingButHasBeenSignalled) {
+ return true; + return true;
+ } + }
+ } else if (this.isPollingTaskOrCheckingStopCondition) {
+ if (!this.skipNextWait) {
+ return true;
+ }
+ } + }
+ return false; + return false;
+ } + }
@@ -4550,8 +4648,6 @@ index 0000000000000000000000000000000000000000..633c1aeddca31f5fe95cc8c9ad54e6a2
+ * <br> + * <br>
+ * Exactly one of {@code stopCondition} and {@code yieldingLock} must be non-null. + * 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) { + public final void yieldUntil(@Nullable BooleanSupplier stopCondition, @Nullable YieldingLock yieldingLock) {
+ int oldYieldDepth = this.yieldDepth; + int oldYieldDepth = this.yieldDepth;
+ int newYieldDepth = oldYieldDepth + 1; + 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', + * 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 + * 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. + * {@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 + * @see #yieldUntil
+ */ + */
@@ -4699,6 +4797,9 @@ index 0000000000000000000000000000000000000000..633c1aeddca31f5fe95cc8c9ad54e6a2
+ for (var queue : tier.taskQueues) { + for (var queue : tier.taskQueues) {
+ Runnable task = tinyOnly ? queue.pollTiny(this) : queue.poll(this); + Runnable task = tinyOnly ? queue.pollTiny(this) : queue.poll(this);
+ if (task != null) { + if (task != null) {
+ this.lastPolledTaskTier = tier;
+ return task;
+ }
+ /* + /*
+ Check if the tier has run out of tasks for a span, + Check if the tier has run out of tasks for a span,
+ in order to update BaseThreadActivation#thereMayBeTasks. + in order to update BaseThreadActivation#thereMayBeTasks.
@@ -4728,9 +4829,6 @@ index 0000000000000000000000000000000000000000..633c1aeddca31f5fe95cc8c9ad54e6a2
+ } + }
+ } + }
+ } + }
+ this.lastPolledTaskTier = tier;
+ return task;
+ }
+ } + }
+ return null; + return null;
+ } + }
@@ -4972,8 +5070,9 @@ index 0000000000000000000000000000000000000000..633c1aeddca31f5fe95cc8c9ad54e6a2
+ @AnyThreadSafe + @AnyThreadSafe
+ @YieldFree + @YieldFree
+ public final boolean signal(@Nullable SignalReason reason) { + 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 { + try {
+ if (this.isWaiting) { + if (this.isWaiting) {
+ if (this.isNotActuallyWaitingYet) { + 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 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 new file mode 100644
index 0000000000000000000000000000000000000000..ac1f646ed846ed6067f77e7526e2bd0a43bf6677 index 0000000000000000000000000000000000000000..a22b63a15fcc737494454c0d91c35eef5bb21d9e
--- /dev/null --- /dev/null
+++ b/src/main/java/org/galemc/gale/executor/thread/pool/BaseThreadActivation.java +++ b/src/main/java/org/galemc/gale/executor/thread/pool/BaseThreadActivation.java
@@ -0,0 +1,518 @@ @@ -0,0 +1,552 @@
+// Gale - base thread pool +// Gale - base thread pool
+ +
+package org.galemc.gale.executor.thread.pool; +package org.galemc.gale.executor.thread.pool;
+ +
+import net.minecraft.server.MinecraftServer; +import net.minecraft.server.MinecraftServer;
+import org.galemc.gale.executor.TaskSpan; +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.lock.YieldingLock;
+import org.galemc.gale.executor.queue.BaseTaskQueueTier; +import org.galemc.gale.executor.queue.BaseTaskQueueTier;
+import org.galemc.gale.executor.thread.BaseThread; +import org.galemc.gale.executor.thread.BaseThread;
@@ -5527,6 +5628,8 @@ index 0000000000000000000000000000000000000000..ac1f646ed846ed6067f77e7526e2bd0a
+ * + *
+ * @author Martijn Muijsers under AGPL-3.0 + * @author Martijn Muijsers under AGPL-3.0
+ */ + */
+@AnyThreadSafe
+@YieldFree
+public final class BaseThreadActivation { +public final class BaseThreadActivation {
+ +
+ private BaseThreadActivation() {} + private BaseThreadActivation() {}
@@ -5563,10 +5666,10 @@ index 0000000000000000000000000000000000000000..ac1f646ed846ed6067f77e7526e2bd0a
+ * A re-usable array for use inside {@link #update()}. + * A re-usable array for use inside {@link #update()}.
+ */ + */
+ @SuppressWarnings("unchecked") + @SuppressWarnings("unchecked")
+ private static final List<BaseThread>[] threadsWaitingForLockForTier = new List[BaseTaskQueueTier.length]; + private static final List<BaseThread>[] threadsWaitingForUnlockedLockForTier = new List[BaseTaskQueueTier.length];
+ static { + static {
+ for (int tierI = 0; tierI < BaseTaskQueueTier.length; tierI++) { + 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()}: + * The following values that are never changed outside of {@link #update()}:
+ * <ul> + * <ul>
+ * <li>{@link #numberOfThreadsActiveForTier}</li> + * <li>{@link #numberOfThreadsActiveForTier}</li>
+ * <li>{@link #threadsWaitingForLockForTier}</li> + * <li>{@link #threadsWaitingForUnlockedLockForTier}</li>
+ * <li>{@link #numberOfThreadsActiveForLowerThanTier}</li> + * <li>{@link #numberOfThreadsActiveForLowerThanTier}</li>
+ * <li>{@link #numberOfThreadsActiveForHigherThanTier}</li> + * <li>{@link #numberOfThreadsActiveForHigherThanTier}</li>
+ * <li>{@link #numberOfThreadsIntendedToBeActiveForTier}</li> + * <li>{@link #numberOfThreadsIntendedToBeActiveForTier}</li>
@@ -5805,32 +5908,48 @@ index 0000000000000000000000000000000000000000..ac1f646ed846ed6067f77e7526e2bd0a
+ +
+ /* + /*
+ Compute for each tier, for how many threads + 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 + 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) + 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). + but that are not executing a task (i.e. do not have any tasks on their stack).
+ */ + */
+ Arrays.fill(numberOfThreadsActiveForTier, 0); + Arrays.fill(numberOfThreadsActiveForTier, 0);
+ for (int tierI = 0; tierI < BaseTaskQueueTier.length; tierI++) { + for (int tierI = 0; tierI < BaseTaskQueueTier.length; tierI++) {
+ threadsWaitingForLockForTier[tierI].clear(); + threadsWaitingForUnlockedLockForTier[tierI].clear();
+ } + }
+ int activeAssistThreadsWithoutTask = 0; + int activeAssistThreadsWithoutTask = 0;
+ for (BaseThread thread : threads) { + for (BaseThread thread : threads) {
+ if (thread != null) { + if (thread != null) {
+ BaseTaskQueueTier tier = thread.highestTierOfTaskOnStack; + BaseTaskQueueTier tier = thread.highestTierOfTaskOnStack;
+ if (tier == null) { + // This value will be unused if tier is null, so we can safely use some dummy value like 1
+ // The thread is doing nothing + int tierOrdinal = thread.baseThreadIndex > 0 ? (tier == null ? -1 : tier.ordinal) : BaseTaskQueueTier.SERVER.ordinal;
+ if (thread.baseThreadIndex > 0 && !thread.isWaitingAndNeedsSignal()) { + if (thread.isWaitingAndNeedsSignal()) {
+ // If it is doing nothing but not waiting, it is available to do anything + var lockWaitingFor = thread.lockWaitingFor;
+ activeAssistThreadsWithoutTask++; + if (lockWaitingFor != null && !lockWaitingFor.isLocked()) {
+ threadsWaitingForUnlockedLockForTier[tierOrdinal].add(thread);
+ } + }
+ } else { + } else {
+ numberOfThreadsActiveForTier[tier.ordinal]++; + if (tier == null && thread.baseThreadIndex > 0) {
+ if (thread.lockWaitingFor != null) { + /*
+ threadsWaitingForLockForTier[tier.ordinal].add(thread); + ^ 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. + 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. + This can then later be compared to the actual number of active threads for that tier.
+ */ + */
+ for (int tierI = 0; tierI < BaseTaskQueueTier.length; tierI++) { + for (int tierI = 1; tierI < BaseTaskQueueTier.length; tierI++) {
+ numberOfThreadsIntendedToBeActiveForTier[tierI] = BaseThreadPool.targetParallelism - (tierI == 0 ? 0 : activeAssistThreadsWithoutTask) - numberOfThreadsActiveForHigherThanTier[tierI] - Math.min(numberOfThreadsActiveForLowerThanTier[tierI], BaseThreadPool.maxUndisturbedLowerTierThreadCount); + 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; + final int finalActiveAssistThreadsWithoutTask = activeAssistThreadsWithoutTask;
@@ -5900,7 +6024,7 @@ index 0000000000000000000000000000000000000000..ac1f646ed846ed6067f77e7526e2bd0a
+ break; + break;
+ } + }
+ } + }
+ if (thereAreTasks || !threadsWaitingForLockForTier[tierI].isEmpty()) { + if (thereAreTasks || !threadsWaitingForUnlockedLockForTier[tierI].isEmpty()) {
+ +
+ /* + /*
+ * We attempt to wake up a thread that is sleeping, + * We attempt to wake up a thread that is sleeping,
@@ -5939,7 +6063,16 @@ index 0000000000000000000000000000000000000000..ac1f646ed846ed6067f77e7526e2bd0a
+ for (int threadI = tryThreadsStart; threadI < tryThreadsEnd; threadI++) { + for (int threadI = tryThreadsStart; threadI < tryThreadsEnd; threadI++) {
+ BaseThread thread = threads[threadI]; + BaseThread thread = threads[threadI];
+ if (thread != null) { + 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 + 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. + 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 highestTierOfTaskOnStack = thread.highestTierOfTaskOnStack;
+ var highestTierOfTaskOnStackOrdinalOrLength = highestTierOfTaskOnStack == null ? BaseTaskQueueTier.length : highestTierOfTaskOnStack.ordinal; + var highestTierOfTaskOnStackOrdinalOrLength = highestTierOfTaskOnStack == null ? BaseTaskQueueTier.length : highestTierOfTaskOnStack.ordinal;
+ @Nullable YieldingLock lockWaitingFor = thread.lockWaitingFor;
+ boolean isThreadWaitingForAvailableYieldingLock = lockWaitingFor != null && !lockWaitingFor.isLocked(); + boolean isThreadWaitingForAvailableYieldingLock = lockWaitingFor != null && !lockWaitingFor.isLocked();
+ if (isThreadWaitingForAvailableYieldingLock || highestTierOfTaskOnStack == null || highestTierOfTaskOnStack.ordinal >= tierI) { + if (isThreadWaitingForAvailableYieldingLock || highestTierOfTaskOnStack == null || highestTierOfTaskOnStack.ordinal >= tierI) {
+ boolean isBestChoice = false; + boolean isBestChoice = false;
@@ -5989,7 +6121,8 @@ index 0000000000000000000000000000000000000000..ac1f646ed846ed6067f77e7526e2bd0a
+ } + }
+ // Check if the thread still seems valid and attempt to activate it + // Check if the thread still seems valid and attempt to activate it
+ BaseThread thread = threads[threadIToUpdate]; + 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 + // Wake up the thread
+ if (thread.signal(thereAreTasks ? SignalReason.TASK : SignalReason.YIELDING_LOCK)) { + if (thread.signal(thereAreTasks ? SignalReason.TASK : SignalReason.YIELDING_LOCK)) {
+ // Do another update + // Do another update

View File

@@ -32,10 +32,10 @@ index 4b279948e82a6dfd2f471ba698e361dcfe4499de..438e7d8ce4e1df13a6a5cc45075996be
doFirst { doFirst {
diff --git a/src/main/java/org/bukkit/craftbukkit/Main.java b/src/main/java/org/bukkit/craftbukkit/Main.java 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 --- a/src/main/java/org/bukkit/craftbukkit/Main.java
+++ b/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.io.IOException;
import java.text.DateFormat; import java.text.DateFormat;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
@@ -55,11 +55,12 @@ index fab5d1c1531fa89113bef6d17df8437b0aec4582..7ec31bf30db664ade4ee4eaf22dccb41
import net.minecraft.world.level.lighting.LayerLightEventListener; import net.minecraft.world.level.lighting.LayerLightEventListener;
import net.minecrell.terminalconsole.TerminalConsoleAppender; // Paper import net.minecrell.terminalconsole.TerminalConsoleAppender; // Paper
+import org.galemc.gale.executor.thread.BaseThread; +import org.galemc.gale.executor.thread.BaseThread;
+import org.spigotmc.WatchdogThread;
+import reactor.blockhound.BlockHound; +import reactor.blockhound.BlockHound;
public class Main { public class Main {
public static boolean useJline = true; 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 // Gale end - include time in startup logs
public static void main(String[] args) { public static void main(String[] args) {
@@ -67,11 +68,15 @@ index fab5d1c1531fa89113bef6d17df8437b0aec4582..7ec31bf30db664ade4ee4eaf22dccb41
+ if (Boolean.getBoolean("gale.detect.thread.blocks")) { + if (Boolean.getBoolean("gale.detect.thread.blocks")) {
+ printlnStartupInfoToSystemOut("Initializing blocking base thread detection..."); + printlnStartupInfoToSystemOut("Initializing blocking base thread detection...");
+ try { + try {
+ var builder = BlockHound.builder() + var builder = BlockHound.builder();
+ // Mark base threads as intended to be non-blocking + // 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)) + builder.nonBlockingThreadPredicate(currentPredicate -> currentPredicate.or(thread -> thread instanceof BaseThread && !(thread instanceof WatchdogThread)));
+ // Set the callback when a base thread blocks + // Set the callback when a base thread blocks
+ builder.blockingMethodCallback(blockingMethod -> { + 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:"; + String message = "A base thread (" + Thread.currentThread() + ") started blocking:";
+ if (MinecraftServer.LOGGER != null) { + if (MinecraftServer.LOGGER != null) {
+ MinecraftServer.LOGGER.error(message); + MinecraftServer.LOGGER.error(message);
@@ -88,7 +93,9 @@ index fab5d1c1531fa89113bef6d17df8437b0aec4582..7ec31bf30db664ade4ee4eaf22dccb41
+ threadBlockingMethods.remove("onSpinWait"); + threadBlockingMethods.remove("onSpinWait");
+ threadBlockingMethods.remove("yield"); + threadBlockingMethods.remove("yield");
+ // Allow base threads to block in the intended way + // 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 + // Install BlockHound
+ builder.install(); + builder.install();
+ printlnStartupInfoToSystemOut("Blocking base thread detection is enabled."); + printlnStartupInfoToSystemOut("Blocking base thread detection is enabled.");

View File

@@ -54,10 +54,10 @@ index ac12cabaf15bc3520ff74d09faa48a135c63f23c..0019e5eefc4b638526a75dd3706a5403
LOGGER.info("Closing Server"); LOGGER.info("Closing Server");
try { 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 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 --- a/src/main/java/org/galemc/gale/executor/queue/BaseTaskQueueTier.java
+++ b/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. * asynchronously with respect to the {@link ServerThread} and the ticking of the server.
* Execution of * Execution of
*/ */

View File

@@ -77,10 +77,10 @@ index 80f9e70d5c4330e079feccc9a4b1b5957c79ef45..e4955e8d04735b74007aae0bf3230281
public static final long INVALID_CHUNK_KEY = getCoordinateKey(Integer.MAX_VALUE, Integer.MAX_VALUE); 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 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 --- a/src/main/java/org/galemc/gale/executor/queue/BaseTaskQueueTier.java
+++ b/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 * Execution of
*/ */
ASYNC(new AbstractTaskQueue[]{ ASYNC(new AbstractTaskQueue[]{

View File

@@ -7,7 +7,7 @@ License: AGPL-3.0 (https://www.gnu.org/licenses/agpl-3.0.html)
Gale - https://galemc.org 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 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 --- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java
+++ b/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; @@ -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.server.level.progress.ChunkProgressListener;
import net.minecraft.util.VisibleForDebug; import net.minecraft.util.VisibleForDebug;
import net.minecraft.util.thread.BlockableEventLoop; import net.minecraft.util.thread.BlockableEventLoop;
@@ -48,6 +48,8 @@ import net.minecraft.world.level.storage.DimensionDataStorage; @@ -446,7 +446,7 @@ public class ServerChunkCache extends ChunkSource {
import net.minecraft.world.level.storage.LevelData; // Paper end
import net.minecraft.world.level.storage.LevelStorageSource; com.destroystokyo.paper.io.SyncLoadFinder.logSyncLoad(this.level, x1, z1); // Paper - sync load info
import it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet; // Paper this.level.timings.syncChunkLoad.startTiming(); // Paper
+import org.galemc.gale.executor.queue.BaseTaskQueues; - chunkproviderserver_b.managedBlock(completablefuture::isDone);
+import org.galemc.gale.executor.thread.pool.BaseThreadActivation; + 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); super.doRunTask(task);
} }
@@ -45,11 +63,62 @@ index df4db98618c6c9261b4ec8e2987c4ed26af4bd4b..ba352013692b987518dd200d376fb6cf
+ super.tell(runnable); + super.tell(runnable);
+ MinecraftServer.nextTimeAssumeWeMayHaveDelayedTasks = true; + MinecraftServer.nextTimeAssumeWeMayHaveDelayedTasks = true;
+ BaseTaskQueues.allLevelsScheduledChunkCache.newTaskWasAdded(); + 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 @Override
// CraftBukkit start - process pending Chunk loadCallback() and unloadCallback() after each run task // CraftBukkit start - process pending Chunk loadCallback() and unloadCallback() after each run task
public boolean pollTask() { 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 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 new file mode 100644
index 0000000000000000000000000000000000000000..60a3b6e935fcea6cf27c31e2b967bf3758283274 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 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 --- a/src/main/java/org/galemc/gale/executor/queue/AllLevelsScheduledTaskQueue.java
+++ b/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; @@ -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. * 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 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 --- a/src/main/java/org/galemc/gale/executor/queue/BaseTaskQueueTier.java
+++ b/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[]{ SERVER(new AbstractTaskQueue[]{
BaseTaskQueues.deferredToServerThread, BaseTaskQueues.deferredToServerThread,
BaseTaskQueues.serverThreadTick, BaseTaskQueues.serverThreadTick,

View File

@@ -83,7 +83,7 @@ index 84cc9397237fa0c17aa1012dfb5683c90eb6d3b8..f5c15d40094c2ddc6220b0595597d121
final ReentrantLock schedulingLock = new ReentrantLock(); final ReentrantLock schedulingLock = new ReentrantLock();
public final ChunkHolderManager chunkHolderManager; 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 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 --- a/src/main/java/org/galemc/gale/executor/queue/AllLevelsScheduledTaskQueue.java
+++ b/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; @@ -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 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 --- a/src/main/java/org/galemc/gale/executor/queue/BaseTaskQueueTier.java
+++ b/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.deferredToServerThread,
BaseTaskQueues.serverThreadTick, BaseTaskQueues.serverThreadTick,
BaseTaskQueues.anyTickScheduledServerThread, BaseTaskQueues.anyTickScheduledServerThread,

View File

@@ -507,10 +507,10 @@ index abdec5529763b77126494ae0c2be9b48de900bc1..35233587de14cf52da30324df89d9ec7
* This class is a copy of {@link PrioritisedQueueExecutorThread}, with the notable difference * 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 * 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 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 --- a/src/main/java/org/galemc/gale/executor/queue/BaseTaskQueueTier.java
+++ b/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, * 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. * asynchronously with respect to the {@link ServerThread} and the ticking of the server.
@@ -518,7 +518,7 @@ index 9a908566ccdda9ca77e0f9236f674f17f79e9c40..dc006d9940ef8114a3a3e4860fbc1da0
*/ */
ASYNC(new AbstractTaskQueue[]{ ASYNC(new AbstractTaskQueue[]{
// The cleaner queue has high priority because it releases resources back to a pool, thereby saving memory // 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}, * A tier for queues that contain tasks with the same considerations as {@link #ASYNC},
* but with a low priority. * 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 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 --- a/src/main/java/org/galemc/gale/executor/thread/BaseThread.java
+++ b/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 @@ @@ -2,6 +2,8 @@
@@ -672,7 +672,7 @@ index 633c1aeddca31f5fe95cc8c9ad54e6a2cf1b4ac1..81e244e94c7422de589e91db44b8e493
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import java.util.concurrent.TimeUnit; 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
} }
} }

View File

@@ -200,10 +200,10 @@ index 7ed820d2483bf6741a355b062f062a04866ba938..575f8ba79cf3547b837abb5957fed0aa
for (int i = 0; i < this.tickables.size(); ++i) { for (int i = 0; i < this.tickables.size(); ++i) {
((Runnable) this.tickables.get(i)).run(); ((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 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 --- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java
+++ b/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 // CraftBukkit end

View 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 {