From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Martijn Muijsers Date: Sun, 29 Jan 2023 22:37:12 +0100 Subject: [PATCH] Run chunk cache tasks on base thread pool License: AGPL-3.0 (https://www.gnu.org/licenses/agpl-3.0.html) Gale - https://galemc.org diff --git a/src/main/java/net/minecraft/server/level/ServerChunkCache.java b/src/main/java/net/minecraft/server/level/ServerChunkCache.java index 83a57b9bc59063ed8299f98bc33e14b57f2ea0de..5187fd95577afa4ddf4544206ec6bb467bcdce81 100644 --- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java +++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java @@ -11,7 +11,6 @@ import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.Objects; -import java.util.Optional; import java.util.concurrent.CompletableFuture; import java.util.concurrent.Executor; import java.util.function.BooleanSupplier; @@ -22,6 +21,7 @@ import net.minecraft.Util; import net.minecraft.core.BlockPos; import net.minecraft.core.SectionPos; import net.minecraft.network.protocol.Packet; +import net.minecraft.server.MinecraftServer; import net.minecraft.server.level.progress.ChunkProgressListener; import net.minecraft.util.VisibleForDebug; import net.minecraft.util.thread.BlockableEventLoop; @@ -446,7 +446,7 @@ public class ServerChunkCache extends ChunkSource { // Paper end com.destroystokyo.paper.io.SyncLoadFinder.logSyncLoad(this.level, x1, z1); // Paper - sync load info this.level.timings.syncChunkLoad.startTiming(); // Paper - chunkproviderserver_b.managedBlock(completablefuture::isDone); + chunkproviderserver_b.managedYield(completablefuture); // Gale - base thread pool io.papermc.paper.chunk.system.scheduling.ChunkTaskScheduler.popChunkWait(); // Paper - async chunk debug // Paper - rewrite chunk system this.level.timings.syncChunkLoad.stopTiming(); // Paper } // Paper @@ -489,7 +489,7 @@ public class ServerChunkCache extends ChunkSource { ServerChunkCache.MainThreadExecutor chunkproviderserver_b = this.mainThreadProcessor; 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,15 @@ public class ServerChunkCache extends ChunkSource { public final class MainThreadExecutor extends BlockableEventLoop { + // Gale start - base thread pool + /** + * The time interval for the server thread to yield when this executor is performing + * a {@link #managedYield} but failed to perform any other tasks from this executor itself. + */ + private static final long MANAGED_YIELD_TIMEOUT_TIME = 50_000L; + private static @Nullable YieldingLock yieldingLockToNotifyForNewChunkCacheTasks; + // Gale end - base thread pool + MainThreadExecutor(Level world) { super("Chunk source main thread executor for " + world.dimension().location()); } @@ -1002,6 +1011,40 @@ public class ServerChunkCache extends ChunkSource { super.doRunTask(task); } + // Gale start - base thread pool + @Override + public void tell(Runnable runnable) { + super.tell(runnable); + MinecraftServer.nextTimeAssumeWeMayHaveDelayedTasks = true; + BaseTaskQueues.allLevelsScheduledChunkCache.newTaskWasAdded(); + if (yieldingLockToNotifyForNewChunkCacheTasks != null) { + yieldingLockToNotifyForNewChunkCacheTasks.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()) { + long timeoutTime = System.nanoTime() + MANAGED_YIELD_TIMEOUT_TIME; + currentThread.yieldUntilFuture(timeoutTime, () -> this.hasPendingTasks(), future, autoCompletingLock -> yieldingLockToNotifyForNewChunkCacheTasks = autoCompletingLock); + } + } + yieldingLockToNotifyForNewChunkCacheTasks = null; + } finally { + --this.blockingCount; + } + } + } + // Gale end - base thread pool + @Override // CraftBukkit start - process pending Chunk loadCallback() and unloadCallback() after each run task public boolean pollTask() { diff --git a/src/main/java/net/minecraft/util/thread/BlockableEventLoop.java b/src/main/java/net/minecraft/util/thread/BlockableEventLoop.java index 392e7b4a89669f16b32043b65b69e6593d17f10e..6328c0254b585b3bc169dd2b5d0f25bfd67566d2 100644 --- a/src/main/java/net/minecraft/util/thread/BlockableEventLoop.java +++ b/src/main/java/net/minecraft/util/thread/BlockableEventLoop.java @@ -21,7 +21,7 @@ public abstract class BlockableEventLoop implements Profiler private final String name; private static final Logger LOGGER = LogUtils.getLogger(); private final Queue pendingRunnables = Queues.newConcurrentLinkedQueue(); - private int blockingCount; + protected int blockingCount; // Gale - base thread pool protected BlockableEventLoop(String name) { this.name = name; diff --git a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java index 949feba1264bcafb8dc2dcecd0a566fea80a2ba0..9eae3862abb5f1d7755a8e777fd4bf9a6f8e321d 100644 --- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java +++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java @@ -470,7 +470,7 @@ public class CraftWorld extends CraftRegionAccessor implements World { list, true ); - serverChunkCache.mainThreadProcessor.managedBlock(future::isDone); + serverChunkCache.mainThreadProcessor.managedYield(future); // Gale - base thread pool if (chunkStatus == ChunkStatus.NOISE) { future.join().left().ifPresent(chunk -> net.minecraft.world.level.levelgen.Heightmap.primeHeightmaps(chunk, ChunkStatus.POST_FEATURES)); } diff --git a/src/main/java/org/galemc/gale/executor/queue/AllLevelsScheduledChunkCacheTaskQueue.java b/src/main/java/org/galemc/gale/executor/queue/AllLevelsScheduledChunkCacheTaskQueue.java new file mode 100644 index 0000000000000000000000000000000000000000..fe2e06a827555d81a30697f8b08667692a3eeade --- /dev/null +++ b/src/main/java/org/galemc/gale/executor/queue/AllLevelsScheduledChunkCacheTaskQueue.java @@ -0,0 +1,52 @@ +// Gale - base thread pool + +package org.galemc.gale.executor.queue; + +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.level.ServerChunkCache; +import net.minecraft.server.level.ServerLevel; +import org.galemc.gale.executor.TaskSpan; +import org.galemc.gale.executor.annotation.thread.AnyThreadSafe; +import org.galemc.gale.executor.annotation.YieldFree; +import org.galemc.gale.executor.thread.ServerThread; +import org.jetbrains.annotations.Nullable; + +/** + * This class provides access to, but does not store, the tasks scheduled to be executed on the main thread, + * that are scheduled and normally polled by each world's {@link ServerChunkCache#mainThreadProcessor} in their + * respective {@link ServerChunkCache.MainThreadExecutor#managedBlock}. These tasks could normally also be run in the + * server's {@link MinecraftServer#managedBlock} if there were no more global scheduled server thread tasks, and as + * such we provide access to polling these tasks from a {@link ServerThread}. + *
+ * All tasks provided by this queue must be yield-free. + * + * @author Martijn Muijsers under AGPL-3.0 + */ +@AnyThreadSafe +@YieldFree +public final class AllLevelsScheduledChunkCacheTaskQueue extends AllLevelsScheduledTaskQueue { + + AllLevelsScheduledChunkCacheTaskQueue() { + super(TaskSpan.YIELDING, false); + } + + @Override + public String getName() { + return "AllLevelsScheduledChunkCache"; + } + + @Override + protected boolean hasLevelTasks(ServerLevel level) { + return level.getChunkSource().mainThreadProcessor.hasPendingTasks(); + } + + @Override + protected @Nullable Runnable pollLevel(ServerLevel level) { + var executor = level.getChunkSource().mainThreadProcessor; + if (executor.hasPendingTasks()) { + return executor::pollTask; + } + return null; + } + +} 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 657c3663ed54043e7e4e6660d34903ef746fd8e7..c2acd36b3101042f39afe1436836078dcce2100d 100644 --- a/src/main/java/org/galemc/gale/executor/queue/AllLevelsScheduledTaskQueue.java +++ b/src/main/java/org/galemc/gale/executor/queue/AllLevelsScheduledTaskQueue.java @@ -12,7 +12,8 @@ import org.galemc.gale.executor.thread.pool.BaseThreadActivation; import org.jetbrains.annotations.Nullable; /** - * Common implementation for queues with scheduled tasks for all levels. + * Common implementation for queues with scheduled tasks for all levels, + * such as {@link AllLevelsScheduledChunkCacheTaskQueue}. *
* All tasks provided by this queue must be yield-free. * diff --git a/src/main/java/org/galemc/gale/executor/queue/BaseTaskQueueTier.java b/src/main/java/org/galemc/gale/executor/queue/BaseTaskQueueTier.java index f4adcdcad96b2748c60aecb8f5c25370ee6e8f5b..8465ce8de44d823aac4784fbc5183b9fc49b2825 100644 --- a/src/main/java/org/galemc/gale/executor/queue/BaseTaskQueueTier.java +++ b/src/main/java/org/galemc/gale/executor/queue/BaseTaskQueueTier.java @@ -61,7 +61,8 @@ public enum BaseTaskQueueTier { SERVER(new AbstractTaskQueue[]{ BaseTaskQueues.deferredToServerThread, BaseTaskQueues.serverThreadTick, - BaseTaskQueues.anyTickScheduledServerThread + BaseTaskQueues.anyTickScheduledServerThread, + BaseTaskQueues.allLevelsScheduledChunkCache }, MinecraftServer.SERVER_THREAD_PRIORITY), /** * A tier for queues that contain tasks that are part of ticking, diff --git a/src/main/java/org/galemc/gale/executor/queue/BaseTaskQueues.java b/src/main/java/org/galemc/gale/executor/queue/BaseTaskQueues.java index 92721a51268becb05d708db04e9d6daaa66fb8b2..c608cdfc17e02a37e8f1799af2b26f973a32c839 100644 --- a/src/main/java/org/galemc/gale/executor/queue/BaseTaskQueues.java +++ b/src/main/java/org/galemc/gale/executor/queue/BaseTaskQueues.java @@ -90,6 +90,11 @@ public final class BaseTaskQueues { */ public static final SimpleTaskQueue tickAssist = SimpleTaskQueue.allSpans("TickAssist"); + /** + * @see AllLevelsScheduledChunkCacheTaskQueue + */ + public static final AllLevelsScheduledChunkCacheTaskQueue allLevelsScheduledChunkCache = new AllLevelsScheduledChunkCacheTaskQueue(); + /** * This queue stores the tasks posted to {@link MCUtil#cleanerExecutor}. */