From 35fcc2e2d094d5f2b7317f4d4eb4218b318d1534 Mon Sep 17 00:00:00 2001 From: Shane Freeder Date: Fri, 12 Oct 2018 15:41:15 +0100 Subject: [PATCH] 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 --- ...5-Async-Chunk-Loading-and-Generation.patch | 40 +++++++++---------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/Spigot-Server-Patches/0375-Async-Chunk-Loading-and-Generation.patch b/Spigot-Server-Patches/0375-Async-Chunk-Loading-and-Generation.patch index a5e527c061..ad31d6701a 100644 --- a/Spigot-Server-Patches/0375-Async-Chunk-Loading-and-Generation.patch +++ b/Spigot-Server-Patches/0375-Async-Chunk-Loading-and-Generation.patch @@ -1,4 +1,4 @@ -From 1248921187e1c418988fe86a9e28b2ae810a092f Mon Sep 17 00:00:00 2001 +From 662563828cc23dfd798fb73fac09c21be18cce82 Mon Sep 17 00:00:00 2001 From: Aikar Date: Sat, 21 Jul 2018 16:55:04 -0400 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 new file mode 100644 -index 0000000000..4fc5fad09e +index 0000000000..5823917a65 --- /dev/null +++ b/src/main/java/net/minecraft/server/PaperAsyncChunkProvider.java @@ -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 SINGLE_GEN_EXECUTOR = new PriorityQueuedExecutor("PaperChunkGenerator", PaperConfig.asyncChunks && PaperConfig.asyncChunkGeneration && !PaperConfig.asyncChunkGenThreadPerWorld ? 1 : 0); ++ private static final ConcurrentLinkedQueue MAIN_THREAD_QUEUE = new ConcurrentLinkedQueue<>(); ++ private static final ThreadLocal IS_CHUNK_THREAD = ThreadLocal.withInitial(() -> false); ++ private static final ThreadLocal IS_CHUNK_GEN_THREAD = ThreadLocal.withInitial(() -> false); + + private final PriorityQueuedExecutor generationExecutor; + //private static final PriorityQueuedExecutor generationExecutor = new PriorityQueuedExecutor("PaperChunkGen", 1); + private final Long2ObjectMap pendingChunks = Long2ObjectMaps.synchronize(new Long2ObjectOpenHashMap<>()); -+ private final ConcurrentLinkedQueue mainThreadQueue = new ConcurrentLinkedQueue<>(); + private final IAsyncTaskHandler asyncHandler; -+ private final ThreadLocal isChunkThread = ThreadLocal.withInitial(() -> false); -+ private final ThreadLocal isChunkGenThread = ThreadLocal.withInitial(() -> false); + + private final WorldServer world; + private final IChunkLoader chunkLoader; @@ -1007,7 +1007,7 @@ index 0000000000..4fc5fad09e + private boolean processChunkLoads() { + Runnable run; + boolean hadLoad = false; -+ while ((run = mainThreadQueue.poll()) != null) { ++ while ((run = MAIN_THREAD_QUEUE.poll()) != null) { + run.run(); + hadLoad = true; + } @@ -1071,7 +1071,7 @@ index 0000000000..4fc5fad09e + // Listen for when result is ready + final CompletableFuture future = new CompletableFuture<>(); + PendingChunkRequest request = pending.addListener(future, gen); -+ if (isChunkThread.get()) { ++ if (IS_CHUNK_THREAD.get()) { + pending.loadTask.run(); + } + @@ -1085,7 +1085,7 @@ index 0000000000..4fc5fad09e + try (co.aikar.timings.Timing timing = world.timings.syncChunkLoadTimer.startTiming()) { + while (!future.isDone()) { + // We aren't done, obtain lock on queue -+ synchronized (mainThreadQueue) { ++ synchronized (MAIN_THREAD_QUEUE) { + // We may of received our request now, check it + if (processChunkLoads()) { + // If we processed SOMETHING, don't wait @@ -1093,7 +1093,7 @@ index 0000000000..4fc5fad09e + } + try { + // We got nothing from the queue, wait until something has been added -+ mainThreadQueue.wait(1); ++ MAIN_THREAD_QUEUE.wait(1); + } catch (InterruptedException ignored) { + } + } @@ -1254,8 +1254,8 @@ index 0000000000..4fc5fad09e + } + + private Chunk generateChunkExecutor() { -+ isChunkThread.set(true); -+ isChunkGenThread.set(true); ++ IS_CHUNK_THREAD.set(true); ++ IS_CHUNK_GEN_THREAD.set(true); + return 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 + // schedulers, as post stage may trigger a new generation and cause errors -+ synchronized (mainThreadQueue) { -+ mainThreadQueue.add(this::postChunk); -+ mainThreadQueue.notify(); ++ synchronized (MAIN_THREAD_QUEUE) { ++ MAIN_THREAD_QUEUE.add(this::postChunk); ++ MAIN_THREAD_QUEUE.notify(); + } + } + @@ -1421,7 +1421,7 @@ index 0000000000..4fc5fad09e + genTask = generationExecutor.createPendingTask(this::generateChunkExecutor, 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 + EXECUTOR.submitTask(loadTask); + } @@ -1432,7 +1432,7 @@ index 0000000000..4fc5fad09e + + @Override + public void run() { -+ isChunkThread.set(true); ++ IS_CHUNK_THREAD.set(true); + try { + if (!loadFinished(loadChunk(x, z))) { + return; @@ -1448,13 +1448,13 @@ index 0000000000..4fc5fad09e + if (shouldGenSync) { + synchronized (this) { + setStatus(PendingStatus.GENERATION_PENDING); -+ mainThreadQueue.add(() -> generateFinished(this.generateChunk())); ++ MAIN_THREAD_QUEUE.add(() -> generateFinished(this.generateChunk())); + } -+ synchronized (mainThreadQueue) { -+ mainThreadQueue.notify(); ++ synchronized (MAIN_THREAD_QUEUE) { ++ MAIN_THREAD_QUEUE.notify(); + } + } else { -+ if (isChunkGenThread.get()) { ++ if (IS_CHUNK_GEN_THREAD.get()) { + // ideally we should never run into 1 chunk generating another chunk... + // but if we do, let's apply same solution + genTask.run();