From 8822da77b795fda6fc290381f6786fb847baaa69 Mon Sep 17 00:00:00 2001 From: Spottedleaf Date: Fri, 21 Mar 2025 08:36:05 -0700 Subject: [PATCH] Update ScheduledTaskThreadPool 1. Adjust insertion last task process time to be the current time. This makes fresh inserts to be scheduled fairly with current inserts. 2. Always attempt to watch global first task This should try to prevent multiple scheduler threads from waiting on the same task. If the global first task is our task, then we can avoid retrying for the next task. 3. Correctly set runner state to TASKS when parsing intermediate tasks. This will prevent the runner from being interrupted by notifyTasks. --- .../0009-fixup-Region-Threading-Base.patch | 34 +++++++++++-------- 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/folia-server/minecraft-patches/features/0009-fixup-Region-Threading-Base.patch b/folia-server/minecraft-patches/features/0009-fixup-Region-Threading-Base.patch index 0531f4d..f0dd2f6 100644 --- a/folia-server/minecraft-patches/features/0009-fixup-Region-Threading-Base.patch +++ b/folia-server/minecraft-patches/features/0009-fixup-Region-Threading-Base.patch @@ -396,10 +396,10 @@ index c6e487a4c14e6b82533881d01f32349b9ae28728..b8f1f042342d3fed5fa26df0de07e8e2 } diff --git a/io/papermc/paper/threadedregions/ScheduledTaskThreadPool.java b/io/papermc/paper/threadedregions/ScheduledTaskThreadPool.java new file mode 100644 -index 0000000000000000000000000000000000000000..1525be087f2b021e9ba4c1489d2144c1df90761d +index 0000000000000000000000000000000000000000..c7627ababadf72231682baa9c056a4082170410a --- /dev/null +++ b/io/papermc/paper/threadedregions/ScheduledTaskThreadPool.java -@@ -0,0 +1,1158 @@ +@@ -0,0 +1,1164 @@ +package io.papermc.paper.threadedregions; + +import ca.spottedleaf.concurrentutil.util.ConcurrentUtil; @@ -638,7 +638,6 @@ index 0000000000000000000000000000000000000000..1525be087f2b021e9ba4c1489d2144c1 + private void insert(final SchedulableTick tick, final boolean hasTasks) { + final long scheduleTime = tick.getScheduledStart(); + final long timeNow = System.nanoTime(); -+ final long nextTaskProcess = timeNow + (this.taskTimeSliceNS >>> 1); + + for (;;) { + final Map.Entry lastIdle = this.waitingOrIdleRunners.firstEntry(); @@ -651,7 +650,7 @@ index 0000000000000000000000000000000000000000..1525be087f2b021e9ba4c1489d2144c1 + tick, + // offset start by steal threshold so that it will hopefully start at its scheduled time + scheduleTime - this.stealThresholdNS, -+ nextTaskProcess, ++ timeNow, + null + ); + @@ -672,7 +671,7 @@ index 0000000000000000000000000000000000000000..1525be087f2b021e9ba4c1489d2144c1 + continue; + } + -+ final ScheduledTickTask task = tick.task = new ScheduledTickTask(tick, scheduleTime, nextTaskProcess, waitState.runner); ++ final ScheduledTickTask task = tick.task = new ScheduledTickTask(tick, scheduleTime, timeNow, waitState.runner); + + this.unwatchedScheduledTicks.put(task, task); + waitState.runner.scheduledTicks.put(task, task); @@ -1226,12 +1225,13 @@ index 0000000000000000000000000000000000000000..1525be087f2b021e9ba4c1489d2144c1 + return toWaitFor; + } + -+ if (toWaitFor == globalFirst && globalFirst != ourFirst) { -+ if (!toWaitFor.watch()) { ++ if (toWaitFor == globalFirst) { ++ if (toWaitFor.watch()) { ++ this.scheduler.unwatchedScheduledTicks.remove(toWaitFor); ++ this.watch = toWaitFor; ++ } else if (toWaitFor != ourFirst) { + continue; -+ } -+ this.scheduler.unwatchedScheduledTicks.remove(toWaitFor); -+ this.watch = toWaitFor; ++ } // else: failed to watch, but we are waiting for our task + } + + return toWaitFor; @@ -1261,7 +1261,6 @@ index 0000000000000000000000000000000000000000..1525be087f2b021e9ba4c1489d2144c1 + } + break; + } -+ + } + } + } @@ -1286,11 +1285,10 @@ index 0000000000000000000000000000000000000000..1525be087f2b021e9ba4c1489d2144c1 + } + + private void reinsert(final ScheduledTickTask tick, final TickThreadRunner owner) { -+ final ScheduledTickTask newTask = new ScheduledTickTask( ++ final ScheduledTickTask newTask = tick.tick.task = new ScheduledTickTask( + tick.tick, tick.tick.getScheduledStart(), System.nanoTime(), owner + ); + -+ newTask.tick.task = newTask; + this.scheduler.unwatchedScheduledTicks.put(newTask, newTask); + if (owner != null) { + owner.scheduledTicks.put(newTask, newTask); @@ -1302,7 +1300,13 @@ index 0000000000000000000000000000000000000000..1525be087f2b021e9ba4c1489d2144c1 + } + + private void runTasks(final ScheduledTickTask tick, final long deadline) { ++ if (STATE_WAITING != this.compareAndExchangeStateVolatile(STATE_WAITING, STATE_TASKS)) { ++ // interrupted or halted ++ return; ++ } ++ + if (!tick.take()) { ++ this.compareAndExchangeStateVolatile(STATE_TASKS, STATE_WAITING); + return; + } + @@ -1315,12 +1319,14 @@ index 0000000000000000000000000000000000000000..1525be087f2b021e9ba4c1489d2144c1 + } + + final BooleanSupplier canContinue = () -> { -+ return TickThreadRunner.this.getStateVolatile() == STATE_WAITING && (System.nanoTime() - deadline < 0L); ++ return TickThreadRunner.this.getStateVolatile() == STATE_TASKS && (System.nanoTime() - deadline < 0L); + }; + + if (tick.tick.tasks(canContinue)) { + this.reinsert(tick, tick.owner == null ? this : tick.owner); + } ++ ++ this.compareAndExchangeStateVolatile(STATE_TASKS, STATE_WAITING); + } + + private ScheduledTickTask waitForTick() {