DEBUG.WIKI
丽萨主机;为跨境业务选好 IP;原生 / 住宅 IP · 按套餐选择;查看 IP →
PanstarCloud:部署应用,接入 AI,云服务器与 AI 网关
IPPeak:从目标地区访问网页,住宅代理、SEO 监测、广告验证

Tokio 暂停时钟等到 17ms,却读到 17.9ms

暂停时钟先推进900微秒,再等待整毫秒deadline,自动推进可能多走这900微秒。区分驱动取整与任务完成顺序,保留有意义的时间断言。

暂停 Tokio 时钟后,先推进900微秒,再等待相对起点17毫秒的 deadline,最终 elapsed() 却是17.9毫秒。这个差值可以来自自动推进的取整计算,不必先认定是 Linux 定时器慢了900微秒。

问题的具体形状是:

let start = tokio::time::Instant::now();
tokio::time::advance(std::time::Duration::from_micros(900)).await;
tokio::time::sleep_until(
    start + std::time::Duration::from_millis(17)
).await;

该片段要求已经暂停的时钟,以及相应的 time、test-util 与运行时功能。不要把它直接放进没有暂停时间的生产流程。

小数偏移为何被加了两次

具体故障在1.54开发快照中同时覆盖启用与不启用I/O的情况。正式1.53.1的时间驱动也使用整数毫秒tick计算下一次等待:先把当前已过时间转换为tick,再从目标tick减去它。

当前时间为0.9毫秒时,整数tick仍是0;目标是17,计算出的等待就可能是17毫秒。暂停时钟将这个时长加到当前0.9毫秒上,结果到达17.9,而不是只推进剩余的16.1毫秒。

这与“deadline本身处于两个毫秒之间,需要向上取整”是两种情况。这里的目标已在17毫秒边界上,小数部分来自当前时钟。

按测试真正要验证的内容调整

如果测试只验证毫秒级业务定时,不需要那个900微秒起始偏移,可以让测试时间轴从整毫秒边界推进,保留原deadline与事件顺序的断言。不要在所有测试中统一放宽一个很大的误差,让真正的提前触发也被接受。

确实要测试小数偏移时,应保留这个场景,并明确控制“跳到哪个时间”与“哪个任务已经完成”两件事。可先按目标时刻与当前 Tokio Instant 的精确剩余时长调用 advance(),随后等待目标定时任务实际完成,再检查业务结果。剩余时长应对已过期目标饱和为零,不能让负差值变成巨大等待。

advance() 是一次跳跃。它不会保证这段时间内的所有 sleep 都已经完成,也不保证多个到期任务按原deadline顺序被轮询;返回后立刻断言后台计数,仍可能有调度竞争。因此,多定时器、timeout竞争或冻结恢复测试,不能用一次大幅推进替代原本需要的事件顺序。

如果测试目的就是核对自动推进的精确计算,应将最小场景作为独立回归保留,而不是把预期改为17.9毫秒来宣布修好。已有修补讨论也不能证明正在使用的正式依赖已经包含它。

保持同一套时钟

比较时使用 tokio::time::Instant,不要与真实的 std::time::Instant 混在同一条耗时断言里。还要检查运行时类型、暂停设置与启用的功能,避免一处暂停、一处仍读真实时间。

最后覆盖整毫秒起点、小数偏移起点和已过期目标,并确认业务任务只触发预期次数。真实运行中的timer仍受平台分辨率与任务调度影响;暂停时钟的这个计算问题,不构成生产定时器能精确到微秒的保证。