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:
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) {
|
||||
|
||||
Reference in new issue
Block a user