mirror of
https://github.com/Winds-Studio/Leaf.git
synced 2025-12-25 18:09:17 +00:00
Revert "Optimize async pathfinding (#418)"
This reverts commit 9f5f764e7d.
This commit is contained in:
@@ -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<Runnable> postProcessing = new ArrayList<>(2);
|
||||
private final List<Runnable> 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<Runnable> 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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -14,12 +14,8 @@ public class NodeEvaluatorCache {
|
||||
private static final Map<NodeEvaluatorFeatures, MultiThreadedQueue<NodeEvaluator>> threadLocalNodeEvaluators = new ConcurrentHashMap<>();
|
||||
private static final Map<NodeEvaluator, NodeEvaluatorGenerator> nodeEvaluatorToGenerator = new ConcurrentHashMap<>();
|
||||
|
||||
private static @NotNull Queue<NodeEvaluator> getQueueForFeatures(@NotNull NodeEvaluatorFeatures features) {
|
||||
Queue<NodeEvaluator> queue = threadLocalNodeEvaluators.get(features);
|
||||
if (queue == null) {
|
||||
queue = threadLocalNodeEvaluators.computeIfAbsent(features, k -> new MultiThreadedQueue<>());
|
||||
}
|
||||
return queue;
|
||||
private static @NotNull Queue<NodeEvaluator> getQueueForFeatures(@NotNull NodeEvaluatorFeatures nodeEvaluatorFeatures) {
|
||||
return threadLocalNodeEvaluators.computeIfAbsent(nodeEvaluatorFeatures, key -> new MultiThreadedQueue<>());
|
||||
}
|
||||
|
||||
public static @NotNull NodeEvaluator takeNodeEvaluator(@NotNull NodeEvaluatorGenerator generator, @NotNull NodeEvaluator localNodeEvaluator) {
|
||||
|
||||
Reference in New Issue
Block a user