mirror of
https://github.com/PaperMC/Paper.git
synced 2024-11-04 01:39:54 +01:00
Share the main thread queue for AsyncChunkProvider
fixes an issue in which thread requests are only processed for the current provider which can cause a deadlock should multiple requests exist across providers
This commit is contained in:
parent
69a4a30e47
commit
35fcc2e2d0
@ -1,4 +1,4 @@
|
|||||||
From 1248921187e1c418988fe86a9e28b2ae810a092f Mon Sep 17 00:00:00 2001
|
From 662563828cc23dfd798fb73fac09c21be18cce82 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
|
||||||
@ -904,7 +904,7 @@ index 98d182fdb8..487d98eb1b 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 0000000000..4fc5fad09e
|
index 0000000000..5823917a65
|
||||||
--- /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,593 @@
|
||||||
@ -959,14 +959,14 @@ index 0000000000..4fc5fad09e
|
|||||||
+
|
+
|
||||||
+ private static final PriorityQueuedExecutor EXECUTOR = new PriorityQueuedExecutor("PaperChunkLoader", PaperConfig.asyncChunks ? PaperConfig.asyncChunkLoadThreads : 0);
|
+ private static final PriorityQueuedExecutor EXECUTOR = new PriorityQueuedExecutor("PaperChunkLoader", PaperConfig.asyncChunks ? PaperConfig.asyncChunkLoadThreads : 0);
|
||||||
+ private static final PriorityQueuedExecutor SINGLE_GEN_EXECUTOR = new PriorityQueuedExecutor("PaperChunkGenerator", PaperConfig.asyncChunks && PaperConfig.asyncChunkGeneration && !PaperConfig.asyncChunkGenThreadPerWorld ? 1 : 0);
|
+ private static final PriorityQueuedExecutor SINGLE_GEN_EXECUTOR = new PriorityQueuedExecutor("PaperChunkGenerator", PaperConfig.asyncChunks && PaperConfig.asyncChunkGeneration && !PaperConfig.asyncChunkGenThreadPerWorld ? 1 : 0);
|
||||||
|
+ private static final ConcurrentLinkedQueue<Runnable> MAIN_THREAD_QUEUE = new ConcurrentLinkedQueue<>();
|
||||||
|
+ private static final ThreadLocal<Boolean> IS_CHUNK_THREAD = ThreadLocal.withInitial(() -> false);
|
||||||
|
+ private static final ThreadLocal<Boolean> IS_CHUNK_GEN_THREAD = ThreadLocal.withInitial(() -> false);
|
||||||
+
|
+
|
||||||
+ private final PriorityQueuedExecutor generationExecutor;
|
+ private final PriorityQueuedExecutor generationExecutor;
|
||||||
+ //private static final PriorityQueuedExecutor generationExecutor = new PriorityQueuedExecutor("PaperChunkGen", 1);
|
+ //private static final PriorityQueuedExecutor generationExecutor = new PriorityQueuedExecutor("PaperChunkGen", 1);
|
||||||
+ private final Long2ObjectMap<PendingChunk> pendingChunks = Long2ObjectMaps.synchronize(new Long2ObjectOpenHashMap<>());
|
+ private final Long2ObjectMap<PendingChunk> pendingChunks = Long2ObjectMaps.synchronize(new Long2ObjectOpenHashMap<>());
|
||||||
+ private final ConcurrentLinkedQueue<Runnable> mainThreadQueue = new ConcurrentLinkedQueue<>();
|
|
||||||
+ private final IAsyncTaskHandler asyncHandler;
|
+ private final IAsyncTaskHandler asyncHandler;
|
||||||
+ private final ThreadLocal<Boolean> isChunkThread = ThreadLocal.withInitial(() -> false);
|
|
||||||
+ private final ThreadLocal<Boolean> isChunkGenThread = ThreadLocal.withInitial(() -> false);
|
|
||||||
+
|
+
|
||||||
+ private final WorldServer world;
|
+ private final WorldServer world;
|
||||||
+ private final IChunkLoader chunkLoader;
|
+ private final IChunkLoader chunkLoader;
|
||||||
@ -1007,7 +1007,7 @@ index 0000000000..4fc5fad09e
|
|||||||
+ private boolean processChunkLoads() {
|
+ private boolean processChunkLoads() {
|
||||||
+ Runnable run;
|
+ Runnable run;
|
||||||
+ boolean hadLoad = false;
|
+ boolean hadLoad = false;
|
||||||
+ while ((run = mainThreadQueue.poll()) != null) {
|
+ while ((run = MAIN_THREAD_QUEUE.poll()) != null) {
|
||||||
+ run.run();
|
+ run.run();
|
||||||
+ hadLoad = true;
|
+ hadLoad = true;
|
||||||
+ }
|
+ }
|
||||||
@ -1071,7 +1071,7 @@ index 0000000000..4fc5fad09e
|
|||||||
+ // Listen for when result is ready
|
+ // Listen for when result is ready
|
||||||
+ final CompletableFuture<Chunk> future = new CompletableFuture<>();
|
+ final CompletableFuture<Chunk> future = new CompletableFuture<>();
|
||||||
+ PendingChunkRequest request = pending.addListener(future, gen);
|
+ PendingChunkRequest request = pending.addListener(future, gen);
|
||||||
+ if (isChunkThread.get()) {
|
+ if (IS_CHUNK_THREAD.get()) {
|
||||||
+ pending.loadTask.run();
|
+ pending.loadTask.run();
|
||||||
+ }
|
+ }
|
||||||
+
|
+
|
||||||
@ -1085,7 +1085,7 @@ index 0000000000..4fc5fad09e
|
|||||||
+ try (co.aikar.timings.Timing timing = world.timings.syncChunkLoadTimer.startTiming()) {
|
+ try (co.aikar.timings.Timing timing = world.timings.syncChunkLoadTimer.startTiming()) {
|
||||||
+ while (!future.isDone()) {
|
+ while (!future.isDone()) {
|
||||||
+ // We aren't done, obtain lock on queue
|
+ // We aren't done, obtain lock on queue
|
||||||
+ synchronized (mainThreadQueue) {
|
+ synchronized (MAIN_THREAD_QUEUE) {
|
||||||
+ // We may of received our request now, check it
|
+ // We may of received our request now, check it
|
||||||
+ if (processChunkLoads()) {
|
+ if (processChunkLoads()) {
|
||||||
+ // If we processed SOMETHING, don't wait
|
+ // If we processed SOMETHING, don't wait
|
||||||
@ -1093,7 +1093,7 @@ index 0000000000..4fc5fad09e
|
|||||||
+ }
|
+ }
|
||||||
+ try {
|
+ try {
|
||||||
+ // We got nothing from the queue, wait until something has been added
|
+ // We got nothing from the queue, wait until something has been added
|
||||||
+ mainThreadQueue.wait(1);
|
+ MAIN_THREAD_QUEUE.wait(1);
|
||||||
+ } catch (InterruptedException ignored) {
|
+ } catch (InterruptedException ignored) {
|
||||||
+ }
|
+ }
|
||||||
+ }
|
+ }
|
||||||
@ -1254,8 +1254,8 @@ index 0000000000..4fc5fad09e
|
|||||||
+ }
|
+ }
|
||||||
+
|
+
|
||||||
+ private Chunk generateChunkExecutor() {
|
+ private Chunk generateChunkExecutor() {
|
||||||
+ isChunkThread.set(true);
|
+ IS_CHUNK_THREAD.set(true);
|
||||||
+ isChunkGenThread.set(true);
|
+ IS_CHUNK_GEN_THREAD.set(true);
|
||||||
+ return generateChunk();
|
+ return generateChunk();
|
||||||
+ }
|
+ }
|
||||||
+ private Chunk generateChunk() {
|
+ private Chunk generateChunk() {
|
||||||
@ -1347,9 +1347,9 @@ index 0000000000..4fc5fad09e
|
|||||||
+
|
+
|
||||||
+ // Don't post here, even if on main, it must enter the queue so we can exit any open batch
|
+ // Don't post here, even if on main, it must enter the queue so we can exit any open batch
|
||||||
+ // schedulers, as post stage may trigger a new generation and cause errors
|
+ // schedulers, as post stage may trigger a new generation and cause errors
|
||||||
+ synchronized (mainThreadQueue) {
|
+ synchronized (MAIN_THREAD_QUEUE) {
|
||||||
+ mainThreadQueue.add(this::postChunk);
|
+ MAIN_THREAD_QUEUE.add(this::postChunk);
|
||||||
+ mainThreadQueue.notify();
|
+ MAIN_THREAD_QUEUE.notify();
|
||||||
+ }
|
+ }
|
||||||
+ }
|
+ }
|
||||||
+
|
+
|
||||||
@ -1421,7 +1421,7 @@ index 0000000000..4fc5fad09e
|
|||||||
+ genTask = generationExecutor.createPendingTask(this::generateChunkExecutor, taskPriority);
|
+ genTask = generationExecutor.createPendingTask(this::generateChunkExecutor, taskPriority);
|
||||||
+ }
|
+ }
|
||||||
+ loadTask = EXECUTOR.createPendingTask(this, taskPriority);
|
+ loadTask = EXECUTOR.createPendingTask(this, taskPriority);
|
||||||
+ if (!isChunkThread.get()) {
|
+ if (!IS_CHUNK_THREAD.get()) {
|
||||||
+ // We will execute it outside of the synchronized context immediately after
|
+ // We will execute it outside of the synchronized context immediately after
|
||||||
+ EXECUTOR.submitTask(loadTask);
|
+ EXECUTOR.submitTask(loadTask);
|
||||||
+ }
|
+ }
|
||||||
@ -1432,7 +1432,7 @@ index 0000000000..4fc5fad09e
|
|||||||
+
|
+
|
||||||
+ @Override
|
+ @Override
|
||||||
+ public void run() {
|
+ public void run() {
|
||||||
+ isChunkThread.set(true);
|
+ IS_CHUNK_THREAD.set(true);
|
||||||
+ try {
|
+ try {
|
||||||
+ if (!loadFinished(loadChunk(x, z))) {
|
+ if (!loadFinished(loadChunk(x, z))) {
|
||||||
+ return;
|
+ return;
|
||||||
@ -1448,13 +1448,13 @@ index 0000000000..4fc5fad09e
|
|||||||
+ if (shouldGenSync) {
|
+ if (shouldGenSync) {
|
||||||
+ synchronized (this) {
|
+ synchronized (this) {
|
||||||
+ setStatus(PendingStatus.GENERATION_PENDING);
|
+ setStatus(PendingStatus.GENERATION_PENDING);
|
||||||
+ mainThreadQueue.add(() -> generateFinished(this.generateChunk()));
|
+ MAIN_THREAD_QUEUE.add(() -> generateFinished(this.generateChunk()));
|
||||||
+ }
|
+ }
|
||||||
+ synchronized (mainThreadQueue) {
|
+ synchronized (MAIN_THREAD_QUEUE) {
|
||||||
+ mainThreadQueue.notify();
|
+ MAIN_THREAD_QUEUE.notify();
|
||||||
+ }
|
+ }
|
||||||
+ } else {
|
+ } else {
|
||||||
+ if (isChunkGenThread.get()) {
|
+ if (IS_CHUNK_GEN_THREAD.get()) {
|
||||||
+ // ideally we should never run into 1 chunk generating another chunk...
|
+ // ideally we should never run into 1 chunk generating another chunk...
|
||||||
+ // but if we do, let's apply same solution
|
+ // but if we do, let's apply same solution
|
||||||
+ genTask.run();
|
+ genTask.run();
|
||||||
|
Loading…
Reference in New Issue
Block a user