From efb4b6ef8b1c7d62ed90f8ced811fc935fbe1ef6 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. --- .../Async-Chunk-Loading-and-Generation.patch | 55 ++++++++++++++++++- 1 file changed, 52 insertions(+), 3 deletions(-) diff --git a/Spigot-Server-Patches/Async-Chunk-Loading-and-Generation.patch b/Spigot-Server-Patches/Async-Chunk-Loading-and-Generation.patch index a008cbb762..e665f92a4e 100644 --- a/Spigot-Server-Patches/Async-Chunk-Loading-and-Generation.patch +++ b/Spigot-Server-Patches/Async-Chunk-Loading-and-Generation.patch @@ -599,7 +599,7 @@ 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 @@ -0,0 +0,0 @@ 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, () -> { @@ -0,0 +0,0 @@ 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 { +@@ -0,0 +0,0 @@ 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