From 1f5866fcf87d0a2b2b69ad4f6c69164b4442da97 Mon Sep 17 00:00:00 2001 From: Aikar Date: Mon, 8 Oct 2018 00:45:04 -0400 Subject: [PATCH] Improve synchronization on chunk gen to not block main - Fixes #1550 Chunk Generation was occuring while inside of the progressCache lock this caused the progressCache to stay blocked for a long period of time which then blocked main when main needed to clean the expiring map. We now maintain a separate map for pending scheduler entries, that we can join on if a 2nd request comes in while one is starting. This strategy keeps the lock only for a fraction of time but maintains thread safety. So now the chunk is generated without holding a lock and wont block the main thread on the expiring map as we will insert it once ready. --- ...5-Async-Chunk-Loading-and-Generation.patch | 61 +++++++++++++++++-- 1 file changed, 55 insertions(+), 6 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 cbce284b6b..b11408238b 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 a10d0f7e8c1c88b4cd66e8e6bddca04e69369ec1 Mon Sep 17 00:00:00 2001 +From be56cc5d2ee5ab579c019b798637538ff0544d52 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 @@ -599,10 +599,10 @@ index bdfc7d81ff..a5c4564d60 100644 public IBlockData getType(int i, int j, int k) { return this.blockIds.a(i, j, k); diff --git a/src/main/java/net/minecraft/server/ChunkTaskScheduler.java b/src/main/java/net/minecraft/server/ChunkTaskScheduler.java -index 34019bd1b3..d7b9ca3ee7 100644 +index 34019bd1b3..fc9091c801 100644 --- a/src/main/java/net/minecraft/server/ChunkTaskScheduler.java +++ b/src/main/java/net/minecraft/server/ChunkTaskScheduler.java -@@ -20,7 +20,7 @@ public class ChunkTaskScheduler extends Scheduler d; private final IChunkLoader e; private final IAsyncTaskHandler f; @@ -611,15 +611,64 @@ index 34019bd1b3..d7b9ca3ee7 100644 protected boolean a(Scheduler.a scheduler_a) { ProtoChunk protochunk = (ProtoChunk) scheduler_a.a(); -@@ -50,7 +50,7 @@ public class ChunkTaskScheduler extends Scheduler> pendingSchedulers = new it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap<>(); // Paper + + public ChunkTaskScheduler(int i, World world, ChunkGenerator chunkgenerator, IChunkLoader ichunkloader, IAsyncTaskHandler iasynctaskhandler) { + super("WorldGen", i, ChunkStatus.FINALIZED, () -> { +@@ -50,8 +51,28 @@ public class ChunkTaskScheduler extends Scheduler { +- return flag ? (Scheduler.a) this.progressCache.computeIfAbsent(chunkcoordintpair.a(), (i) -> { ++ // Paper start - refactor a lot of this - avoid generating a chunk while holding lock on expiring map ++ java.util.concurrent.CompletableFuture pending = null; ++ boolean created = false; ++ long key = chunkcoordintpair.a(); ++ synchronized (pendingSchedulers) { ++ Scheduler.a existing = this.progressCache.get(key); ++ if (existing != null) { ++ return existing; ++ } ++ pending = this.pendingSchedulers.get(key); ++ if (pending == null) { ++ if (!flag) { ++ return null; ++ } ++ created = true; ++ pending = new java.util.concurrent.CompletableFuture<>(); ++ pendingSchedulers.put(key, pending); ++ } ++ } ++ if (created) { ++ java.util.function.Function get = (i) -> { ++ // Paper end ProtoChunk protochunk; + try { +@@ -70,8 +91,18 @@ public class ChunkTaskScheduler extends Scheduler map) { diff --git a/src/main/java/net/minecraft/server/DataPaletteBlock.java b/src/main/java/net/minecraft/server/DataPaletteBlock.java index 71a3636be6..ff0fe25417 100644 --- a/src/main/java/net/minecraft/server/DataPaletteBlock.java