DEBUG.WIKI
PanstarCloud:部署应用,接入 AI,云服务器与 AI 网关
京东云;让 AI 进入真实业务;零售 · 物流 · 客服 · 硬件;查看方案 →
搬瓦工;先看线路,再选 VPS;CN2 GIA · 对应线路套餐;查看线路 →

Tokio 任务转储报 RefCell already borrowed,检查单线程 runtime

Tokio 1.53.1 的 current-thread taskdump 会跨任务轮询持有调度核心借用。转储期间合法唤醒可能再次借用并 panic;先停用受影响诊断入口,并检查被诊断任务是否也已失败。

Tokio 服务平时运行正常,一触发任务转储,堆栈却经过 Handle::dump 和 current_thread 调度器,并报:

RefCell already borrowed

如果使用 Tokio 1.53.1 的 current-thread runtime、启用了不稳定的 taskdump 能力,需要检查转储时重新轮询任务与任务唤醒之间的冲突。这条路径不是业务代码随意借用了某个 RefCell,也不能仅凭 panic 文字就定位到应用自己的容器。

诊断操作会再次轮询任务

1.53.1 的 current-thread 转储实现先对调度核心取得可变借用,再调用 trace_current_thread。追踪过程中会重新轮询任务;任务在 poll 中唤醒自己或其他任务,本来是合法行为。

问题是同一调度器的 schedule 路径也会尝试可变借用那个核心。前一个借用此时仍没有释放,便可能产生运行时借用检查 panic。这里需要同时满足转储、current-thread 调度及转储期间唤醒等条件,不能推成“Tokio 1.53.1 所有异步任务都会崩溃”。

这项 taskdump 在该正式版本中还受 tokio_unstable、taskdump 功能及目标平台约束,不是普通日志打印或常规栈回溯。核对最终 Cargo 功能和构建 flags,再沿触发入口检查是否真的调用了 Handle::dump()。

先停用受影响的转储入口

暂时不能采用已确认修正实现时,可以在 current-thread 服务上停用这项任务转储,让诊断信号或管理入口不再进入受影响路径。保留普通日志、指标和已有的其他诊断方式;不应因此把全部观测关闭。

把超时包在 dump().await 外面不能消除同步发生的双重借用。仅捕获发起转储的任务 panic 也不充分:唤醒其他任务时,panic 可能在被轮询任务的范围内被捕获,转储调用看似成功,另一个任务却已经以 JoinError 结束。

因此,检查的不只是“转储有没有返回”,还包括被诊断任务的 JoinHandle、锁状态以及业务是否继续推进。不要统一忽略 JoinError,也不要捕获后继续使用已经状态不明的资源。

不为诊断随意更换 runtime

多线程调度器的相关代码采用不同的核心取出方式,不能直接套用 current-thread 的借用链。但把服务改成多线程会影响线程局部状态、非 Send 任务与调度行为,不适合作为未经评估的临时开关。

后续采用依赖修正时,应核对正式工件中转储是否在轮询任务前释放核心借用,并保留原有队列排空与唤醒约束。问题存在补丁或较新版本号,不足以证明所选正式版本已经处理这条路径;本文不提供尚未核证的修复版号。

在隔离环境按真实 runtime、目标平台和构建功能复查,覆盖任务自唤醒、唤醒另一条空闲任务,以及诊断完成后业务继续运行。若当前故障只在开启 taskdump 后发生,记录这个边界,别把降低并发后暂时不复现当作任务逻辑已经修好。