Paper/patches/server/0398-Fix-Chunk-Post-Processing-deadlock-risk.patch
Nassim Jahnke 1358d1e914
Updated Upstream (CraftBukkit/Spigot) (#7580)
Upstream has released updates that appear to apply and compile correctly.
This update has not been tested by PaperMC and as with ANY update, please do your own testing

Bukkit Changes:
881e06e5 PR-725: Add Item Unlimited Lifetime APIs

CraftBukkit Changes:
74c08312 SPIGOT-6962: Call EntityChangeBlockEvent when when FallingBlockEntity starts to fall
64db5126 SPIGOT-6959: Make /loot command ignore empty items for spawn
2d760831 Increase outdated build delay
9ed7e4fb SPIGOT-6138, SPIGOT-6415: Don't call CreatureSpawnEvent after cross-dimensional travel
fc4ad813 SPIGOT-6895: Trees grown with applyBoneMeal() don't fire the StructureGrowthEvent
59733a2e SPIGOT-6961: Actually return a copy of the ItemMeta

Spigot Changes:
ffceeae3 SPIGOT-6956: Drop unload queue patch as attempt at fixing stop issue
e19ddabd PR-1011: Add Item Unlimited Lifetime APIs
34d40b0e SPIGOT-2942: give command fires PlayerDropItemEvent, cancelling it causes Item Duplication
2022-03-13 08:47:54 +01:00

74 lines
4 KiB
Diff

From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Aikar <aikar@aikar.co>
Date: Sat, 18 Apr 2020 04:36:11 -0400
Subject: [PATCH] Fix Chunk Post Processing deadlock risk
See: https://gist.github.com/aikar/dd22bbd2a3d78a2fd3d92e95e9f28dc6
as part of post processing a chunk, we can call ChunkConverter.
ChunkConverter then kicks off major physics updates, and when blocks
that have connections across chunk boundaries occur, a recursive risk
can occur where A updates a block that triggers a physics request.
That physics request may trigger a chunk request, that then enqueues
a task into the Mailbox ChunkTaskQueueSorter.
If anything requests that same chunk that is in the middle of conversion,
it's mailbox queue is going to be held up, so the subsequent chunk request
will be unable to proceed.
We delay post processing of Chunk.A() 1 "pass" by re stuffing it back into
the executor so that the mailbox ChunkQueue is now considered empty.
This successfully fixed a reoccurring and highly reproducible crash
for heightmaps.
diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java
index 7776c22744cdd31459e9634d1d549bdf2876e04f..43e5e148f1289ff5e42311981c597c66d98447aa 100644
--- a/src/main/java/net/minecraft/server/level/ChunkMap.java
+++ b/src/main/java/net/minecraft/server/level/ChunkMap.java
@@ -178,6 +178,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
};
// CraftBukkit end
+ final CallbackExecutor chunkLoadConversionCallbackExecutor = new CallbackExecutor(); // Paper
// Paper start - distance maps
private final com.destroystokyo.paper.util.misc.PooledLinkedHashSets<ServerPlayer> pooledLinkedPlayerHashSets = new com.destroystokyo.paper.util.misc.PooledLinkedHashSets<>();
@@ -995,16 +996,15 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
});
CompletableFuture<Either<LevelChunk, ChunkHolder.ChunkLoadingFailure>> completablefuture1 = completablefuture.thenApplyAsync((either) -> {
return either.mapLeft((list) -> {
- return (LevelChunk) list.get(list.size() / 2);
- });
- }, (runnable) -> {
- this.mainThreadMailbox.tell(ChunkTaskPriorityQueueSorter.message(holder, runnable));
- }).thenApplyAsync((either) -> {
- return either.ifLeft((chunk) -> {
+ // Paper start - revert 1.18.2 diff
+ final LevelChunk chunk = (LevelChunk) list.get(list.size() / 2);
chunk.postProcessGeneration();
this.level.startTickingChunk(chunk);
+ return chunk;
});
- }, this.mainThreadExecutor);
+ }, (runnable) -> {
+ this.mainThreadMailbox.tell(ChunkTaskPriorityQueueSorter.message(holder, () -> ChunkMap.this.chunkLoadConversionCallbackExecutor.execute(runnable))); // Paper - delay running Chunk post processing until outside of the sorter to prevent a deadlock scenario when post processing causes another chunk request.
+ }); // Paper end - revert 1.18.2 diff
completablefuture1.thenAcceptAsync((either) -> {
either.ifLeft((chunk) -> {
diff --git a/src/main/java/net/minecraft/server/level/ServerChunkCache.java b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
index cf56b2ed508352205ef6ec71519b0626aa698987..1b6fb81079d3ad5d3c33be67a1c05111f9dd5f2d 100644
--- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java
+++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
@@ -1141,6 +1141,7 @@ public class ServerChunkCache extends ChunkSource {
return super.pollTask() || execChunkTask; // Paper
}
} finally {
+ chunkMap.chunkLoadConversionCallbackExecutor.run(); // Paper - Add chunk load conversion callback executor to prevent deadlock due to recursion in the chunk task queue sorter
chunkMap.callbackExecutor.run();
}
// CraftBukkit end