From 49d29920a5e05c35ab5c14511cba277e020867df Mon Sep 17 00:00:00 2001 From: hayanesuru Date: Fri, 25 Jul 2025 12:36:13 +0900 Subject: [PATCH] Revert "Optimize async pathfinding (#418)" This reverts commit 9f5f764e7d11ae633629dfecac0cc8aad217df80. --- .../org/dreeam/leaf/async/path/AsyncPath.java | 99 ++++--------------- .../leaf/async/path/AsyncPathProcessor.java | 8 +- .../leaf/async/path/NodeEvaluatorCache.java | 8 +- 3 files changed, 24 insertions(+), 91 deletions(-) diff --git a/leaf-server/src/main/java/org/dreeam/leaf/async/path/AsyncPath.java b/leaf-server/src/main/java/org/dreeam/leaf/async/path/AsyncPath.java index fac99840..9afa9c8d 100644 --- a/leaf-server/src/main/java/org/dreeam/leaf/async/path/AsyncPath.java +++ b/leaf-server/src/main/java/org/dreeam/leaf/async/path/AsyncPath.java @@ -18,10 +18,6 @@ import java.util.function.Supplier; */ public class AsyncPath extends Path { - private volatile boolean fastPath = false; - - private volatile boolean processingFinished = false; - /** * marks whether this async path has been processed */ @@ -30,7 +26,7 @@ public class AsyncPath extends Path { /** * runnables waiting for this to be processed */ - private final List postProcessing = new ArrayList<>(2); + private final List postProcessing = new ArrayList<>(0); /** * a list of positions that this path could path towards @@ -82,35 +78,17 @@ public class AsyncPath extends Path { @Override public boolean isProcessed() { - return fastPath || (this.processState == PathProcessState.COMPLETED); + return this.processState == PathProcessState.COMPLETED; } /** * returns the future representing the processing state of this path */ - public void postProcessing(@NotNull Runnable runnable) { + public synchronized void postProcessing(@NotNull Runnable runnable) { if (isProcessed()) { runnable.run(); - return; - } - - boolean finished = this.processingFinished; - if (finished) { - runnable.run(); - return; - } - - boolean shouldRun = false; - synchronized (this) { - if (isProcessed()) { - shouldRun = true; - } else { - this.postProcessing.add(runnable); - } - } - - if (shouldRun) { - runnable.run(); + } else { + this.postProcessing.add(runnable); } } @@ -125,79 +103,40 @@ public class AsyncPath extends Path { return false; } - // For single position (common case), do direct comparison - if (positions.size() == 1 && this.positions.size() == 1) { - return this.positions.iterator().next().equals(positions.iterator().next()); - } - return this.positions.containsAll(positions); } /** * starts processing this path */ - public void process() { - // Single check - if not WAITING, we're either COMPLETED or PROCESSING - if (this.processState != PathProcessState.WAITING) { + public synchronized void process() { + if (this.processState == PathProcessState.COMPLETED || + this.processState == PathProcessState.PROCESSING) { return; } - synchronized (this) { - // Double-check after acquiring lock - if (this.processState != PathProcessState.WAITING) { - return; - } + processState = PathProcessState.PROCESSING; - processState = PathProcessState.PROCESSING; - } + final Path bestPath = this.pathSupplier.get(); - // computation outside synchronized block - final Path bestPath; - try { - bestPath = this.pathSupplier.get(); - } catch (Exception e) { - // Handle pathfinding failures gracefully - synchronized (this) { - processState = PathProcessState.COMPLETED; - this.processingFinished = true; + this.nodes.addAll(bestPath.nodes); // we mutate this list to reuse the logic in Path + this.target = bestPath.getTarget(); + this.distToTarget = bestPath.getDistToTarget(); + this.canReach = bestPath.canReach(); - // Still run callbacks even if pathfinding failed - for (Runnable runnable : this.postProcessing) { - runnable.run(); - } - this.postProcessing.clear(); - } - return; - } + processState = PathProcessState.COMPLETED; - // Final state update - minimal synchronization - List callbacksToRun; - synchronized (this) { - this.nodes.addAll(bestPath.nodes); - this.target = bestPath.getTarget(); - this.distToTarget = bestPath.getDistToTarget(); - this.canReach = bestPath.canReach(); - - processState = PathProcessState.COMPLETED; - this.processingFinished = true; // Mark as finished for postProcessing - - // Copy callbacks to run outside synchronized block - callbacksToRun = new ArrayList<>(this.postProcessing); - this.postProcessing.clear(); - } - - for (Runnable runnable : callbacksToRun) { + for (Runnable runnable : this.postProcessing) { runnable.run(); - } + } // Run tasks after processing } /** * if this path is accessed while it hasn't processed, just process it in-place */ private void checkProcessed() { - // Use single volatile read instead of multiple comparisons - PathProcessState state = this.processState; - if (state != PathProcessState.COMPLETED) { + if (this.processState == PathProcessState.WAITING || + this.processState == PathProcessState.PROCESSING) { // Block if we are on processing this.process(); } } diff --git a/leaf-server/src/main/java/org/dreeam/leaf/async/path/AsyncPathProcessor.java b/leaf-server/src/main/java/org/dreeam/leaf/async/path/AsyncPathProcessor.java index 8502b8d3..a3ab0bac 100644 --- a/leaf-server/src/main/java/org/dreeam/leaf/async/path/AsyncPathProcessor.java +++ b/leaf-server/src/main/java/org/dreeam/leaf/async/path/AsyncPathProcessor.java @@ -31,7 +31,6 @@ public class AsyncPathProcessor { private static final Logger LOGGER = LogManager.getLogger(THREAD_PREFIX); private static long lastWarnMillis = System.currentTimeMillis(); public static ThreadPoolExecutor PATH_PROCESSING_EXECUTOR = null; - private static long lastWarnNanos = System.nanoTime(); public static void init() { if (PATH_PROCESSING_EXECUTOR == null) { @@ -125,10 +124,9 @@ public class AsyncPathProcessor { } } - long currentNanos = System.nanoTime(); - if (currentNanos - lastWarnNanos > 30_000_000_000L) { - LOGGER.warn("Async pathfinding processor is busy! Consider increasing max-threads."); - lastWarnNanos = currentNanos; + if (System.currentTimeMillis() - lastWarnMillis > 30000L) { + LOGGER.warn("Async pathfinding processor is busy! Pathfinding tasks will be treated as policy defined in config. Increasing max-threads in Leaf config may help."); + lastWarnMillis = System.currentTimeMillis(); } }; } diff --git a/leaf-server/src/main/java/org/dreeam/leaf/async/path/NodeEvaluatorCache.java b/leaf-server/src/main/java/org/dreeam/leaf/async/path/NodeEvaluatorCache.java index 92cc3e9b..c98a0e2b 100644 --- a/leaf-server/src/main/java/org/dreeam/leaf/async/path/NodeEvaluatorCache.java +++ b/leaf-server/src/main/java/org/dreeam/leaf/async/path/NodeEvaluatorCache.java @@ -14,12 +14,8 @@ public class NodeEvaluatorCache { private static final Map> threadLocalNodeEvaluators = new ConcurrentHashMap<>(); private static final Map nodeEvaluatorToGenerator = new ConcurrentHashMap<>(); - private static @NotNull Queue getQueueForFeatures(@NotNull NodeEvaluatorFeatures features) { - Queue queue = threadLocalNodeEvaluators.get(features); - if (queue == null) { - queue = threadLocalNodeEvaluators.computeIfAbsent(features, k -> new MultiThreadedQueue<>()); - } - return queue; + private static @NotNull Queue getQueueForFeatures(@NotNull NodeEvaluatorFeatures nodeEvaluatorFeatures) { + return threadLocalNodeEvaluators.computeIfAbsent(nodeEvaluatorFeatures, key -> new MultiThreadedQueue<>()); } public static @NotNull NodeEvaluator takeNodeEvaluator(@NotNull NodeEvaluatorGenerator generator, @NotNull NodeEvaluator localNodeEvaluator) {