Process chunks blocking main urgently (#1627)

A chunk load on the main thread will be added to the high priority queue, however, due to existing work on this queue, there was no guarantee that the load would occur within a reasonable amount of time, potentially causing a server to crash while waiting for a chunk in the queue

In order to counteract this, a new urgent priority has been added, allowing us to prioritize these tasks over standard chunk gen/loading

(#1625 #1615 #1575 #1558 (and probably more))
This commit is contained in:
Shane Freeder 2018-10-28 14:40:30 +00:00 committed by Daniel Ennis
parent c4865a6445
commit e6473fc520

View File

@ -1,4 +1,4 @@
From 54cdcb2e434baa254f727db260c6bb57fed41bcd Mon Sep 17 00:00:00 2001 From e2720237c9d98f6fe17d5578784cbea7e826a1da Mon Sep 17 00:00:00 2001
From: Aikar <aikar@aikar.co> From: Aikar <aikar@aikar.co>
Date: Sat, 21 Jul 2018 16:55:04 -0400 Date: Sat, 21 Jul 2018 16:55:04 -0400
Subject: [PATCH] Async Chunk Loading and Generation Subject: [PATCH] Async Chunk Loading and Generation
@ -106,10 +106,10 @@ index b703e0848..77d35ac99 100644
} }
diff --git a/src/main/java/com/destroystokyo/paper/util/PriorityQueuedExecutor.java b/src/main/java/com/destroystokyo/paper/util/PriorityQueuedExecutor.java diff --git a/src/main/java/com/destroystokyo/paper/util/PriorityQueuedExecutor.java b/src/main/java/com/destroystokyo/paper/util/PriorityQueuedExecutor.java
new file mode 100644 new file mode 100644
index 000000000..5c77b6e8e index 000000000..e589aa356
--- /dev/null --- /dev/null
+++ b/src/main/java/com/destroystokyo/paper/util/PriorityQueuedExecutor.java +++ b/src/main/java/com/destroystokyo/paper/util/PriorityQueuedExecutor.java
@@ -0,0 +1,281 @@ @@ -0,0 +1,298 @@
+package com.destroystokyo.paper.util; +package com.destroystokyo.paper.util;
+ +
+import com.google.common.util.concurrent.ThreadFactoryBuilder; +import com.google.common.util.concurrent.ThreadFactoryBuilder;
@ -136,6 +136,7 @@ index 000000000..5c77b6e8e
+ */ + */
+@SuppressWarnings({"WeakerAccess", "UnusedReturnValue", "unused"}) +@SuppressWarnings({"WeakerAccess", "UnusedReturnValue", "unused"})
+public class PriorityQueuedExecutor extends AbstractExecutorService { +public class PriorityQueuedExecutor extends AbstractExecutorService {
+ private final ConcurrentLinkedQueue<Runnable> urgent = new ConcurrentLinkedQueue<>();
+ private final ConcurrentLinkedQueue<Runnable> high = new ConcurrentLinkedQueue<>(); + private final ConcurrentLinkedQueue<Runnable> high = new ConcurrentLinkedQueue<>();
+ private final ConcurrentLinkedQueue<Runnable> normal = new ConcurrentLinkedQueue<>(); + private final ConcurrentLinkedQueue<Runnable> normal = new ConcurrentLinkedQueue<>();
+ private final RejectionHandler handler; + private final RejectionHandler handler;
@ -264,7 +265,11 @@ index 000000000..5c77b6e8e
+ } + }
+ +
+ private Runnable getTask() { + private Runnable getTask() {
+ Runnable run = high.poll(); + Runnable run = urgent.poll();
+ if (run != null) {
+ return run;
+ }
+ run = high.poll();
+ if (run != null) { + if (run != null) {
+ return run; + return run;
+ } + }
@ -300,7 +305,7 @@ index 000000000..5c77b6e8e
+ } + }
+ +
+ public enum Priority { + public enum Priority {
+ NORMAL, HIGH + NORMAL, HIGH, URGENT
+ } + }
+ +
+ public class PendingTask <T> implements Runnable { + public class PendingTask <T> implements Runnable {
@ -339,8 +344,18 @@ index 000000000..5c77b6e8e
+ } + }
+ +
+ public void bumpPriority() { + public void bumpPriority() {
+ if (!priority.compareAndSet(Priority.NORMAL.ordinal(), Priority.HIGH.ordinal())) { + bumpPriority(Priority.HIGH);
+ return; + }
+
+ public void bumpPriority(Priority newPriority) {
+ for (;;) {
+ int current = this.priority.get();
+ if (current >= newPriority.ordinal()) {
+ return;
+ }
+ if (priority.compareAndSet(current, newPriority.ordinal())) {
+ break;
+ }
+ } + }
+ +
+ if (this.executor == null) { + if (this.executor == null) {
@ -362,7 +377,9 @@ index 000000000..5c77b6e8e
+ return; + return;
+ } + }
+ if (this.submitted.compareAndSet(submitted, priority)) { + if (this.submitted.compareAndSet(submitted, priority)) {
+ if (priority == Priority.HIGH.ordinal()) { + if (priority == Priority.URGENT.ordinal()) {
+ urgent.add(this);
+ } else if (priority == Priority.HIGH.ordinal()) {
+ high.add(this); + high.add(this);
+ } else { + } else {
+ normal.add(this); + normal.add(this);
@ -1014,10 +1031,10 @@ index 763130b03..67722440f 100644
diff --git a/src/main/java/net/minecraft/server/PaperAsyncChunkProvider.java b/src/main/java/net/minecraft/server/PaperAsyncChunkProvider.java diff --git a/src/main/java/net/minecraft/server/PaperAsyncChunkProvider.java b/src/main/java/net/minecraft/server/PaperAsyncChunkProvider.java
new file mode 100644 new file mode 100644
index 000000000..5823917a6 index 000000000..c334462f2
--- /dev/null --- /dev/null
+++ b/src/main/java/net/minecraft/server/PaperAsyncChunkProvider.java +++ b/src/main/java/net/minecraft/server/PaperAsyncChunkProvider.java
@@ -0,0 +1,593 @@ @@ -0,0 +1,619 @@
+/* +/*
+ * This file is licensed under the MIT License (MIT). + * This file is licensed under the MIT License (MIT).
+ * + *
@ -1114,6 +1131,18 @@ index 000000000..5823917a6
+ } + }
+ } + }
+ +
+ private static Priority calculatePriority(boolean isBlockingMain, boolean priority) {
+ if (isBlockingMain) {
+ return Priority.URGENT;
+ }
+
+ if (priority) {
+ return Priority.HIGH;
+ }
+
+ return Priority.NORMAL;
+ }
+
+ private boolean processChunkLoads() { + private boolean processChunkLoads() {
+ Runnable run; + Runnable run;
+ boolean hadLoad = false; + boolean hadLoad = false;
@ -1128,7 +1157,7 @@ index 000000000..5823917a6
+ public void bumpPriority(ChunkCoordIntPair coords) { + public void bumpPriority(ChunkCoordIntPair coords) {
+ PendingChunk pending = pendingChunks.get(coords.asLong()); + PendingChunk pending = pendingChunks.get(coords.asLong());
+ if (pending != null) { + if (pending != null) {
+ pending.bumpPriority(); + pending.bumpPriority(Priority.HIGH);
+ } + }
+ } + }
+ +
@ -1165,16 +1194,18 @@ index 000000000..5823917a6
+ synchronized (pendingChunks) { + synchronized (pendingChunks) {
+ PendingChunk pendingChunk = pendingChunks.get(key); + PendingChunk pendingChunk = pendingChunks.get(key);
+ if (pendingChunk == null) { + if (pendingChunk == null) {
+ pending = new PendingChunk(x, z, key, gen, priority || isBlockingMain); + pending = new PendingChunk(x, z, key, gen, calculatePriority(isBlockingMain, priority));
+ pendingChunks.put(key, pending); + pendingChunks.put(key, pending);
+ } else if (pendingChunk.hasFinished && gen && !pendingChunk.canGenerate && pendingChunk.chunk == null) { + } else if (pendingChunk.hasFinished && gen && !pendingChunk.canGenerate && pendingChunk.chunk == null) {
+ // need to overwrite the old + // need to overwrite the old
+ pending = new PendingChunk(x, z, key, true, priority || isBlockingMain); + pending = new PendingChunk(x, z, key, true, calculatePriority(isBlockingMain, priority));
+ pendingChunks.put(key, pending); + pendingChunks.put(key, pending);
+ } else { + } else {
+ pending = pendingChunk; + pending = pendingChunk;
+ if (priority || isBlockingMain) { +
+ pending.bumpPriority(); + Priority newPriority = calculatePriority(isBlockingMain, priority);
+ if (pending.taskPriority != newPriority) {
+ pending.bumpPriority(newPriority);
+ } + }
+ } + }
+ } + }
@ -1347,6 +1378,14 @@ index 000000000..5823917a6
+ taskPriority = priority ? Priority.HIGH : Priority.NORMAL; + taskPriority = priority ? Priority.HIGH : Priority.NORMAL;
+ } + }
+ +
+ PendingChunk(int x, int z, long key, boolean canGenerate, Priority taskPriority) {
+ this.x = x;
+ this.z = z;
+ this.key = key;
+ this.canGenerate = canGenerate;
+ this.taskPriority = taskPriority;
+ }
+
+ private synchronized void setStatus(PendingStatus status) { + private synchronized void setStatus(PendingStatus status) {
+ this.status = status; + this.status = status;
+ } + }
@ -1575,14 +1614,18 @@ index 000000000..5823917a6
+ } + }
+ +
+ void bumpPriority() { + void bumpPriority() {
+ this.taskPriority = Priority.HIGH; + bumpPriority(Priority.HIGH);
+ }
+
+ void bumpPriority(Priority newPriority) {
+ this.taskPriority = newPriority;
+ PriorityQueuedExecutor.PendingTask<Void> loadTask = this.loadTask; + PriorityQueuedExecutor.PendingTask<Void> loadTask = this.loadTask;
+ PriorityQueuedExecutor.PendingTask<Chunk> genTask = this.genTask; + PriorityQueuedExecutor.PendingTask<Chunk> genTask = this.genTask;
+ if (loadTask != null) { + if (loadTask != null) {
+ loadTask.bumpPriority(); + loadTask.bumpPriority(newPriority);
+ } + }
+ if (genTask != null) { + if (genTask != null) {
+ genTask.bumpPriority(); + genTask.bumpPriority(newPriority);
+ } + }
+ } + }
+ +