定时器
tokio::time::sleep 返回的 Sleep 本身不会计时。计时的是运行时里的时间驱动:一个分层时间轮,夹在驱动栈里 IO 驱动的上面。worker 没活干去 park 时,时间驱动先算出最近一个定时器还有多久到期,用它当 park 的超时;醒来后把到期的定时器取出来,挨个调 waker。源码分两层:time/ 是用户层的 Sleep、Timeout、Interval,runtime/time/ 是时间轮和驱动。
这一部分分三页:
- 分层时间轮:6 层、每层 64 个槽,覆盖约 2.18 年;插入时用
elapsed ^ when一步算出层号;时间推进时高层的定时器逐层下放;驱动怎么从下一个到期时间算出 park 超时。 - Sleep 的一生:创建时不碰驱动,第一次 poll 才登记;一个
u64状态字段同时存到期时间和「已触发」标记;drop 时取消;timeout怎么绕开预算;interval错过 tick 后的三种补法。 - (unstable)time_alt:每个 worker 一个本地时间轮,登记、取消、触发都不用抢全局锁;代价是定时器只能由登记它的那个 worker 来触发。
flowchart TB new[sleep:只记下 deadline] --> poll1[第一次 poll] poll1 --> reg[加锁,插入时间轮] reg --> pend[返回 Pending] pend --> park[worker park:超时取自时间轮] park --> proc[醒来,process 到期槽位] proc --> fire[fire:状态改成已触发,取出 waker] fire --> wake[wake,任务重新进队列] wake --> poll2[再次 poll:Ready]
驱动栈怎么一层层套起来、park 和 park_timeout 的分工,见驱动栈与 park。
💬 关联讨论
发起讨论加载中...