Entangled

Entangled

26M Downloads

[Crash] Deadlock when loading world under unknown conditions

campbellcole opened this issue ยท 4 comments

commented

Version Info

  • Minecraft, 1.19.2 (ATM8 modpack)
  • Entangled, 1.3.14

Steps to Reproduce
I am not certain what conditions cause this. The way it happened for me was creating a MineColonies + Refined Storage integration system, loosely adapted from this video. I use the entangled block so that the computer can be away from a building so it doesn't get destroyed. The entangled block is bound to the warehouse hut block.

Crash report (~/logs/latest.log)
crash-2023-08-11_11.57.02-server.txt

I am currently attempting to reproduce this using 1.3.15 because I see that there's a bug fix that could possibly be related, but it doesn't seem like this issue is related to block rendering.

commented

It is worth nothing, this issue only occurs when the dimension containing the block is loaded. I can join the server without issue if I join to another dimension, but as soon as I teleport/load into the overworld, the server stops ticking and eventually gets smacked by the watchdog. I tried increasing the timeout to 4 minutes (which you can see in the crash log) and this did not help. The stacktrace clearly indicates recursion as well.

commented

I have been doing some testing (though keep in mind I am not very experienced with minecraft development) and it seems like the actual issue here is a deadlock. I forked the code and added a depth check for the update function, similar to the one already implemented, and it turns out the code simply hangs on the call to BlockState state = level.getBlockState(this.boundPos);. Will keep experimenting.

commented

The following steps is what I think is happening according to the logs:

  1. The entangled block gets ticked
  2. The entangled block updates the bound block's data with forceLoad=true. Since forceLoad=true, the entangled block calls Level#getBlockState
  3. Level#getBlockState calls ChunkSource#getChunk(x=..., y=..., status=FULL, load=true)
  4. The chunk is not loaded yet and thus a future is spawned and chunk stuff is processed until it gets to loading the chunk
  5. During this, the chunk which contains the entangled block happens to get send to the client. To do this, ClientboundLevelChunkPacketData is created which in turn calls BlockEntityInfo#create on the entangled block
  6. The following mixin from Supplementaries requests a capability from the entangled block: https://github.com/MehVahdJukaar/Supplementaries/blob/1.20/forge/src/main/java/net/mehvahdjukaar/supplementaries/mixins/forge/ChunkHolderMixin.java
  7. The entangled block updates the bound block's data with forceLoad=false. Since forceLoad=false and we know the if statement is entered from the logs, it must be that level.hasChunkAt(this.boundPos) returns true. Now another call to Level#getBlockState gets made and in turn it calls ChunkSource#getChunk(x=..., y=..., status=FULL, load=true)
  8. The call to ChunkSource#getChunk(x=..., y=..., status=FULL, load=true) deadlocks

The problem here is the level.hasChunkAt(this.boundPos) check. ServerChunkCache simply checks for the ticket level in the ChunkHolder for the relevant chunk. It seems that ChunkMap sets the ticket level in the ChunkHolder to the requested status before the chunk actually gets loaded. Thus, the level.hasChunkAt(this.boundPos) check is passed and it leads to the deadlock described above.

To replace this check, we can indeed use ChunkSource#getChunkNow as you proposed in #67. ChunkSource#getChunkNow does not check the ticket level of the ChunkHolder, but instead checks whether the ChunkHolder's future for the FULL status is completed. This should avoid any deadlock.

commented

#66 is now included in Entangled version 1.3.16, so it should be fixed now.
Thank you for reporting the issue and helping fix it! Of course, if the issue does persist, let me know.