fix(phase): 相位等待期间主线程无条件代维度 poll chunkSource,解 getChunk 死锁

死锁现场(threaddump 抓到):
- Server thread RUNNABLE 卡在 MainThreadPhaseCoordinator.run:54 的 managedBlock 自旋,
  等所有维度 loop isTicking()==false。
- dimensionalripper_server_overworld TIMED_WAITING on String,栈:
  LevelTickLoop.drainChunkTasks -> PlayerPacketQueue.drain -> 处理 ServerboundMovePlayerPacket
  -> Entity.move 碰撞检测 -> lithium ChunkAwareBlockCollisionSweeper -> Level.getChunk
  -> ServerChunkCache.getChunk:159 -> mainThreadProcessor.managedBlock(future::isDone) park。
  即维度线程在 tick 中请求未加载区块,park 等区块生成 future。

根因:DR 把维度 tick 丢独立线程后,主线程在 managedBlock 等待期间需代维度推进区块。
原版 MinecraftServer.pollTaskInternal 的「代各维度 chunkSource.pollTask」被
if (isSprinting() || haveTime()) 挡住——卡顿累积后 haveTime()==false,主线程停止代 poll
→ 维度 park 的区块 future 永不完成 → 维度永不停 → 与主线程等待互锁,单 tick 从 40s 涨到 200s。

修复:MainThreadPhaseCoordinator.run 的 managedBlock 条件内,无条件遍历所有维度
loop.getLevel().getChunkSource().pollTask(),与原版 pollTaskInternal 语义一致但去掉 haveTime 门槛。
单维度异常不影响相位收口。
This commit is contained in:
Xiao-no-love committed 2026-10-13 19:58:30 +08:00
1 parent 01a966fffc
commit fd38301937
1 file changed
+14 -1
@@ -53,7 +53,20 @@ public final class MainThreadPhaseCoordinator {
try {
server.managedBlock(() -> {
boolean idle = loops(server).stream().noneMatch(LevelTickLoop::isTicking);
if (!idle) ThreadOwnershipDiagnostics.slowPhase(server, reason, began);
if (!idle) {
// 维度线程可能 park 在 getChunk 的 future 上(mainThreadProcessor.managedBlock)等
// 主线程推进区块生成。主线程在此等待相位期间必须代维度 poll 各区块源,
// 否则原版 pollTaskInternal 的 haveTime() 条件会在卡顿累积后停止代 poll
// → 区块 future 永不完成 → 维度永不停 → 死锁。
for (LevelTickLoop loop : loops(server)) {
try {
loop.getLevel().getChunkSource().pollTask();
} catch (Throwable ignored) {
// 诊断用代 poll,单维度异常不阻断相位收口
}
}
ThreadOwnershipDiagnostics.slowPhase(server, reason, began);
}
return idle;
});
synchronized (s) {