9
0
mirror of https://github.com/Dreeam-qwq/Gale.git synced 2025-12-27 18:49:11 +00:00

Let threads take over update calls from the server thread

This commit is contained in:
Martijn Muijsers
2023-02-07 17:55:33 +01:00
parent ac4ce86d65
commit 51dd134421

View File

@@ -5675,10 +5675,10 @@ index 0000000000000000000000000000000000000000..77fe10e51b00115da520cfc211bf84ba
+}
diff --git a/src/main/java/org/galemc/gale/executor/thread/pool/BaseThreadActivation.java b/src/main/java/org/galemc/gale/executor/thread/pool/BaseThreadActivation.java
new file mode 100644
index 0000000000000000000000000000000000000000..20818236c21bc6f42642ddd788cbca38d0ca1051
index 0000000000000000000000000000000000000000..ba9a1ffca55354a35bcc7b45227dddecd335da94
--- /dev/null
+++ b/src/main/java/org/galemc/gale/executor/thread/pool/BaseThreadActivation.java
@@ -0,0 +1,594 @@
@@ -0,0 +1,648 @@
+// Gale - base thread pool
+
+package org.galemc.gale.executor.thread.pool;
@@ -5701,6 +5701,7 @@ index 0000000000000000000000000000000000000000..20818236c21bc6f42642ddd788cbca38
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.concurrent.atomic.AtomicReference;
+
+/**
+ * A class providing the static functionality needed to activate more threads in the {@link BaseThreadPool}
@@ -5728,9 +5729,19 @@ index 0000000000000000000000000000000000000000..20818236c21bc6f42642ddd788cbca38
+ private static final AtomicLong nextAllowedFrequentSignalNewTasksTime = new AtomicLong(System.nanoTime() - 1L);
+
+ /**
+ * This value is not null while an update is ongoing.
+ *
+ * @see #update()
+ */
+ static final AtomicBoolean isUpdateOngoing = new AtomicBoolean();
+ static final AtomicReference<Thread> updateOngoingOnThread = new AtomicReference();
+
+ /**
+ * Whether a non-{@link ServerThread} thread is ready to take over the {@link #update} call
+ * that is ongoing on a {@link ServerThread}.
+ *
+ * @see #callForUpdate()
+ */
+ private static final AtomicBoolean isNonServerThreadReadyToTakeOverUpdate = new AtomicBoolean();
+
+ /**
+ * @see #update()
@@ -5854,7 +5865,8 @@ index 0000000000000000000000000000000000000000..20818236c21bc6f42642ddd788cbca38
+ * that it will have to do another one.
+ * <br>
+ * Only one thread can be performing an update at a time.
+ * If a second thread calls this method while an update is ongoing (signified by {@link #isUpdateOngoing}),
+ * If a second thread calls this method while an update is ongoing
+ * (signified by {@link #updateOngoingOnThread} being non-null),
+ * the thread performing an update will perform another update after finishing the current one, due to the
+ * second thread incrementing {@link #newUpdateCallsReceived}.
+ * <br>
@@ -5881,7 +5893,7 @@ index 0000000000000000000000000000000000000000..20818236c21bc6f42642ddd788cbca38
+ * in the meta-handling of {@link #update()}, not in the activation of threads:
+ * <ul>
+ * <li>{@link #newUpdateCallsReceived}</li>
+ * <li>{@link #isUpdateOngoing}</li>
+ * <li>{@link #updateOngoingOnThread}</li>
+ * </ul>
+ * </li>
+ * <li>
@@ -5903,10 +5915,35 @@ index 0000000000000000000000000000000000000000..20818236c21bc6f42642ddd788cbca38
+ * </ul>
+ */
+ public static void callForUpdate() {
+ Thread currentThread = Thread.currentThread();
+ // Make sure the updating thread repeats (must be set before evaluating isUpdateOngoing)
+ newUpdateCallsReceived.incrementAndGet();
+ // Start the update ourselves if not ongoing
+ if (!isUpdateOngoing.get() && !isUpdateOngoing.getAndSet(true)) {
+ // Start the update ourselves if necessary
+ boolean amIServerThread = currentThread instanceof ServerThread;
+ boolean amIDoingUpdate = false;
+ // Start the update if not ongoing
+ if (updateOngoingOnThread.get() == null && updateOngoingOnThread.compareAndSet(null, currentThread)) {
+ amIDoingUpdate = true;
+ } else if (!amIServerThread) {
+ // Take over the update from the server thread if necessary
+ Thread updatePerformingThread = updateOngoingOnThread.get();
+ if (updatePerformingThread instanceof ServerThread) {
+ // Make sure we are the only thread ready to taking over from the server thread
+ if (!isNonServerThreadReadyToTakeOverUpdate.get() && !isNonServerThreadReadyToTakeOverUpdate.getAndSet(true)) {
+ // Busy wait until the server thread has stopped updating
+ while (updateOngoingOnThread.get() instanceof ServerThread) {
+ Thread.onSpinWait();
+ }
+ // Start the update, if another thread did not already quickly claim it in the meantime
+ if (updateOngoingOnThread.compareAndSet(null, currentThread)) {
+ amIDoingUpdate = true;
+ }
+ isNonServerThreadReadyToTakeOverUpdate.set(false);
+ }
+ }
+ }
+
+ if (amIDoingUpdate) {
+ // Perform an update
+ do {
+ try {
@@ -5920,13 +5957,23 @@ index 0000000000000000000000000000000000000000..20818236c21bc6f42642ddd788cbca38
+ update();
+ }
+ } finally {
+ isUpdateOngoing.set(false);
+ // Take actions to let another thread take over the update
+ boolean isBeingTakenOver = amIServerThread && isNonServerThreadReadyToTakeOverUpdate.get();
+ if (isBeingTakenOver) {
+ // Make sure an iteration is performed
+ newUpdateCallsReceived.incrementAndGet();
+ }
+ updateOngoingOnThread.set(null);
+ if (isBeingTakenOver) {
+ // Skip the loop checks
+ break;
+ }
+ }
+ /*
+ If newUpdateCallsReceived is positive here, it was increased between it being set to 0 and
+ isUpdateOngoing being set to false, so we must repeat.
+ updateOngoingOnThread being set to null, so we must repeat.
+ */
+ } while (newUpdateCallsReceived.get() > 0 && !isUpdateOngoing.get() && !isUpdateOngoing.getAndSet(true));
+ } while (newUpdateCallsReceived.get() > 0 && updateOngoingOnThread.get() == null && updateOngoingOnThread.compareAndSet(null, currentThread));
+ }
+ }
+
@@ -5985,6 +6032,7 @@ index 0000000000000000000000000000000000000000..20818236c21bc6f42642ddd788cbca38
+ */
+ static void update() {
+ MinecraftServer.THREAD_DEBUG_LOGGER.ifPresent(it -> it.info("update"));
+ boolean amIServerThread = Thread.currentThread() instanceof ServerThread;
+ boolean madeChangesInLastIteration = false;
+ int numberOfUpdateCallsAtStartOfLastIteration = -1;
+ boolean isFirstIteration = true;
@@ -5997,6 +6045,12 @@ index 0000000000000000000000000000000000000000..20818236c21bc6f42642ddd788cbca38
+ while (true) {
+ MinecraftServer.THREAD_DEBUG_LOGGER.ifPresent(it -> it.info("iteration of update"));
+
+ // Let a non-server thread take over if needed
+ if (amIServerThread && isNonServerThreadReadyToTakeOverUpdate.get()) {
+ // All preparations for the take-over are performed in #callForUpdate
+ break;
+ }
+
+ // Break the loop if needed
+ if (isFirstIteration) {
+ // Always run an iteration if this is the first one