From 62efc6ab31c50853b2aa5923b418754705c7f8b0 Mon Sep 17 00:00:00 2001 From: Aikar Date: Wed, 10 Oct 2018 22:48:35 -0400 Subject: [PATCH] Improvements to Async Chunks If a chunk load comes in on a chunk load or gen thread, execute it synchronously on that thread instead of enqueueing it. It doesn't make sense to enqueue it as that thread is then going to future.join() it and block until it's ready anyways. This opens risk to a deadlock if every load or gen thread is waiting on a recursive chunk but it will never finish because all of the threads are waiting. --- ...tect-and-repair-corrupt-Region-Files.patch | 9 ++--- ...5-Async-Chunk-Loading-and-Generation.patch | 37 ++++++++++++++++--- 2 files changed, 34 insertions(+), 12 deletions(-) diff --git a/Spigot-Server-Patches/0326-Detect-and-repair-corrupt-Region-Files.patch b/Spigot-Server-Patches/0326-Detect-and-repair-corrupt-Region-Files.patch index 7fddd1a44..a2dd80e82 100644 --- a/Spigot-Server-Patches/0326-Detect-and-repair-corrupt-Region-Files.patch +++ b/Spigot-Server-Patches/0326-Detect-and-repair-corrupt-Region-Files.patch @@ -1,4 +1,4 @@ -From 56d73274d938d16968b54d62069ba376b53d818e Mon Sep 17 00:00:00 2001 +From 8375c097918c91e3a4ff945847903ff33c1f770f Mon Sep 17 00:00:00 2001 From: Aikar Date: Sat, 11 Aug 2018 00:49:20 -0400 Subject: [PATCH] Detect and repair corrupt Region Files @@ -11,7 +11,7 @@ I don't know why mojang only checks for 4096, when anything less than 8192 is a But to be safe, it will attempt to back up the file. diff --git a/src/main/java/net/minecraft/server/RegionFile.java b/src/main/java/net/minecraft/server/RegionFile.java -index 5d2853b9ce..5c0d4f912a 100644 +index 5d2853b9ce..6a2edfd1e1 100644 --- a/src/main/java/net/minecraft/server/RegionFile.java +++ b/src/main/java/net/minecraft/server/RegionFile.java @@ -22,10 +22,10 @@ import javax.annotation.Nullable; @@ -58,14 +58,11 @@ index 5d2853b9ce..5c0d4f912a 100644 } } catch (IOException ioexception) { ioexception.printStackTrace(); -@@ -276,6 +276,58 @@ public class RegionFile { +@@ -276,6 +276,55 @@ public class RegionFile { } + // Paper start -+ public void deleteChunk(int x, int z) { -+ deleteChunk(x + z * 32); -+ } + public synchronized void deleteChunk(int j1) { + backup(); + int k = offsets[j1]; 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 c9239d0b5..a5e527c06 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 f7cdbb488e556812e697024408c1ac7e9c57a18b Mon Sep 17 00:00:00 2001 +From 1248921187e1c418988fe86a9e28b2ae810a092f 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,10 +904,10 @@ 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..2dfa59b204 +index 0000000000..4fc5fad09e --- /dev/null +++ b/src/main/java/net/minecraft/server/PaperAsyncChunkProvider.java -@@ -0,0 +1,568 @@ +@@ -0,0 +1,593 @@ +/* + * This file is licensed under the MIT License (MIT). + * @@ -965,6 +965,8 @@ index 0000000000..2dfa59b204 + 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; @@ -1069,6 +1071,9 @@ index 0000000000..2dfa59b204 + // Listen for when result is ready + final CompletableFuture future = new CompletableFuture<>(); + PendingChunkRequest request = pending.addListener(future, gen); ++ if (isChunkThread.get()) { ++ pending.loadTask.run(); ++ } + + if (isBlockingMain && pending.hasFinished) { + processChunkLoads(); @@ -1248,6 +1253,11 @@ index 0000000000..2dfa59b204 + } + } + ++ private Chunk generateChunkExecutor() { ++ isChunkThread.set(true); ++ isChunkGenThread.set(true); ++ return generateChunk(); ++ } + private Chunk generateChunk() { + synchronized (this) { + if (requests.get() <= 0) { @@ -1405,8 +1415,16 @@ index 0000000000..2dfa59b204 + if (loadTask == null) { + // Take care of a race condition in that a request could be cancelled after the synchronize + // on pendingChunks, but before a listener is added, which would erase these pending tasks. -+ genTask = generationExecutor.createPendingTask(this::generateChunk, taskPriority); -+ loadTask = EXECUTOR.submitTask(this, taskPriority); ++ if (shouldGenSync) { ++ genTask = generationExecutor.createPendingTask(this::generateChunk, taskPriority); ++ } else { ++ genTask = generationExecutor.createPendingTask(this::generateChunkExecutor, taskPriority); ++ } ++ loadTask = EXECUTOR.createPendingTask(this, taskPriority); ++ if (!isChunkThread.get()) { ++ // We will execute it outside of the synchronized context immediately after ++ EXECUTOR.submitTask(loadTask); ++ } + } + return new PendingChunkRequest(this, gen); + } @@ -1414,6 +1432,7 @@ index 0000000000..2dfa59b204 + + @Override + public void run() { ++ isChunkThread.set(true); + try { + if (!loadFinished(loadChunk(x, z))) { + return; @@ -1435,7 +1454,13 @@ index 0000000000..2dfa59b204 + mainThreadQueue.notify(); + } + } else { -+ generationExecutor.submitTask(genTask); ++ if (isChunkGenThread.get()) { ++ // ideally we should never run into 1 chunk generating another chunk... ++ // but if we do, let's apply same solution ++ genTask.run(); ++ } else { ++ generationExecutor.submitTask(genTask); ++ } + } + } +