Skip to content

模型Model

异步请求取消与安全退休

Async request cancellation and retirement · Cancellation completion race · 异步缓冲生命周期

分别记录逻辑结果、目标终结、取消完成与缓冲借用,证明超时返回之后的所有权守恒、唯一释放和代际安全。

形式陈述 ​

三件完成,两种资源 ​

本页把设备在途所有权接到可提前返回的调用者。固定单线程处理事件、一次提交只有一份目标终结记录的接口;不含多次完成、回调重入和进程崩溃。平台保证目标终结记录意味着该目标不会再访问缓冲,并提供处理完成时所需的数据可见性。普通“收到某个通知”没有这项保证。

请求身份 R=(slot=3,generation=7),缓冲对象另记为 B。目标 I/O 和取消操作 C 各有不同 token,例如 (target,3,7) 与 (cancel,3,7),token是数值身份而非可随意解引用的悬空指针。代号不在可能残留事件期间回绕。R保存四份状态:

  • 逻辑 L:WAITING、RESULT 或 TIMEOUT;后两者为该次调用的终态
  • 目标 T:INFLIGHT 或 TERMINAL(result);仅消费并验证目标终结记录才转换
  • 取消 C:NONE、PENDING 或 DONE(result);本页每个R至多发一次取消
  • 缓冲持有者:一份在途份额 d∈{0,1},以及具名借用集合 H,b=|H|;另记池状态P为HELD或RETURNED,初值HELD

提交成功前,登记表先取得R和B并准备一份d;提交若明确“未接纳”,撤回这份所有权,直接报告提交失败。成功提交后 d=1,T=INFLIGHT。提交结果若含糊不明,不能走“未接纳”释放分支。缓冲写权限按操作方向另受限制:设备写入的读缓冲,在目标终结之前消费者不得读取或改写;持有一份只保证保留地址的租约不代表可以读取数据。

超时后由谁负责 ​

deadline到期且L仍WAITING时,置TIMEOUT,禁止此逻辑调用的新提交;登记表继续持有R,将清理责任交给仍运行的完成分派器,然后提交C并记PENDING。不能先销毁唯一记录,再期待某个迟到回调自行知道该释放什么。C提交明确失败则记DONE(submit-error),继续等目标;清理责任不消失。

目标完成处理先按完整token匹配活记录,验证结果与长度,再令T=TERMINAL、d从1变0,且只做一次。若L仍WAITING且处理时尚未到deadline,记RESULT;若到期,记TIMEOUT;已经TIMEOUT不被迟到成功改成及时成功。业务副作用也不因此回滚。需要消费数据时,在这次不可重入处理内先登记借用,再撤销d并把访问权交给回调,避免两步之间出现假零引用。超时通常丢弃用户结果,但内部清理仍可执行。

取消完成只把C从PENDING改为DONE。成功、未找到、已经不能取消和参数错误都是C的结果,不能不加条件地直接改T。回调拿到借用 token 后必须在 finally 一类必经清理路径中归还;若合法转交后台任务,先登记接收者再撤销发送者,不能留下无人负责的间隙。

在每次事件处理结束的稳定边界,采用以下两个不同判据:

bufferReusable⟺T=TERMINAL∧d=0∧b=0,requestRetirable⟺bufferReusable∧P=RETURNED∧C≠PENDING.

bufferReusable是交还资格,不是每轮都再执行一次free:仅在P=HELD且该谓词成立时执行一次归池,并原子地置P=RETURNED;P=RETURNED之后不再登记新借用或操作B。requestRetirable还要求P=RETURNED。因此最后一份取消回执不会再次交还同一缓冲。

第二式还要求当前处理器已退出,不再保留R指针;本模型在稳定边界执行删除,自然满足这点。B可先归还池,R的轻量记录继续等C;删除R之后才允许复用slot并增加generation。若其他接口允许目标完成后仍有额外事件,必须把那些持有者也加入退休条件,不能套用本单次模型。

直觉

超时回答的是“调用者还等不等”,退休回答的是“谁还能碰这块内存”。调用者可以先离场,但把资源交给收尾者时必须真的完成交接。一个取消请求的回执,只说明这次取消查找和处理得到什么结果;目标自己的终结证据、尚在使用结果的回调,仍有各自责任。

引用数不是魔法护身符。d明确代表未交还的设备或内核访问责任;H里的每项明确代表一个仍持有地址的消费者。若忘了登记后台任务,数字减到零只说明账本漏项,不能说明访问已经结束。

缓冲回收与请求身份退休的两个闸门
例子与边界

一条到零的收尾轨迹 ​

R已经提交,B大小4096B。deadline到期后,目标先成功,回调借用只作内部校验;C随后报告未找到。每行均为稳定边界,目标处理行包含借用登记与d交还。

事件 L T C d b B / R
提交接纳 WAITING INFLIGHT NONE 1 0 都保留
deadline,发C TIMEOUT INFLIGHT PENDING 1 0 都保留
消费目标成功,登记校验借用 TIMEOUT TERMINAL(ok) PENDING 0 1 都保留
校验结束,释放借用 TIMEOUT TERMINAL(ok) PENDING 0 0 B归池,R保留
消费C的未找到 TIMEOUT TERMINAL(ok) DONE(not-found) 0 0 R退休

逻辑未完成数在第二行变0,物理未退休请求数到最后一行才变0。第四行之后若新请求使用同一B,也不能让迟到C拿着旧指针改B;C只更新R的控制状态,不再访问B。R退休后slot3可生成第8代,任何第7代陈旧通知都先被身份检查拒绝。

每种竞争必须有出口 ​

  1. 目标先于deadline成功:消费目标后L=RESULT;定时器后来发现已终态,不再发C。没有借用则直接退休
  2. 取消成功:C的成功回执与目标的canceled终结分别处理。只有C已到而T未消费时继续保留;消费目标且最后借用结束后才回收
  3. 已经不可取消:C报告already-running,T仍INFLIGHT;等待稍后成功或错误的目标终结。不能靠多次减引用“催促”它完成
  4. 未找到或标识写错:可能目标早已完成但CQE尚未被消费,也可能C查错对象。两个原因都不授权释放正确R;记录取消故障,继续按正确token接收目标。若目标最终不终结,进入下文的保留状态
  5. 回调抛错:在finally归还自己的一份,记录回调失败;目标已成功并不强制业务处理成功。错误处理也不得第二次交还d
  6. fd关闭再复用:按fd与打开对象的区别,R的底层打开对象引用由接口持有,新fd3可指向别的对象。完成匹配用R代际,不重新按当前fd3查找。关闭fd本身不是本页的目标终结证据
  7. 只取消逻辑等待:若唯一等待者退出,并把完成循环也一起停掉,目标CQE即使已在队列中,d仍无人交还。这是悬挂资源的反例;必须让登记表的收尾执行器活到全部目标和取消记录被处理

以上分支都以平台最终投递目标与已接纳取消的终结记录、分派器持续运行且公平地最终处理每份已接纳的目标/取消完成与借用归还事件、借用者最终归还为活性前提。设备永久不响应、完成通道损坏或后台任务永不释放时,安全选择是保留/隔离资源并停止或限制准入,报告尚未退休项;仅凭又过了一个timeout不能free。经平台明文规定的设备复位/注销确认能否构成更强终结证据,需要另建协议,本页不假设它自动成功。

io_uring的精确对照 ​

这里的抽象协议故意不要求取消成功记录先于或后于目标记录。liburing的具体 io_uring_prep_cancel 文档更强:成功取消时,提交返回前目标CQE已被投递;-EALREADY分支仍需等待目标CQE。这里的“已投递”不等于应用已经消费,也不等于借用结束。其-ENOENT还可能来自错误user_data,不能由它反推出自己的目标记录不存在。[1]

取消与目标各有CQE,应用必须分开识别;io_uring持有在途请求的文件引用,普通close不会自动取消这些请求。[2] 本页没有真实提交io_uring,也没有覆盖multishot、CQE跳过选项、链式取消、固定缓冲注销或任意重复投递;这些改变“何时再无访问/事件”的合同,需要重新列持有者。

推论与应用

守恒、唯一释放与身份隔离 ​

对每个成功提交、尚未目标终结的R,d=1且B不在空闲池;提交建立这条性质,超时与C事件不改变它,唯一合法目标转移才撤销d。撤销之前登记所需借用,H非空就继续保留。归还借用只能消费匹配的一次性token,重复归还或重复目标终结被报告为协议错误,不再减数。因此B归池恰一次,且归池前所有声明的旧访问都已结束。

安全匹配先查(kind,slot,generation),再读取记录;不匹配便丢弃陈旧通知并计错,绝不先解引用旧地址。第7代事件不能修改第8代的状态。代际不能解决代号回绕,也不能替代真正的设备终结:只把generation加一、同时复用仍被设备写入的B,照样发生破坏。

这份证明复用旧页的pin与可见性责任,不把应用租约计数当Linux页计数实现。对象在GC中可达也不保证地址不移动;外部缓冲须采用运行时批准的固定/非移动存储或受控拷贝,直到外部访问终结。页pin的短期DIO、长期DMA与MMU notifier合同各不相同。[3] 线程epoch退出同样不能证明设备已停止使用地址。

空间与收尾成本 ​

若准入规则最多保留Q个未退休请求,每个B至多M字节,且缓冲池中的空闲储备另有固定上限,则这些请求保留的缓冲峰值≤Q×M,另加R、C、借用token及完成队列元数据。Q必须包括已超时而未退休项;只限制当前等待者数,会让连续超时留下无限缓冲。元数据要有独立上限,借用者不能无限分裂租约。

目标处理、取消处理和一个租约归还在有界token表中各需一次查找与常数状态更新;哈希实现是相应假设下的期望成本,不能无条件写成最坏O(1)。轮询扫描Q项一次为O(Q)。取消后的滞留空间为尚不可复用B的大小之和;滞留时间没有单靠deadline给出的上界。把逻辑延迟、完成排队等待、物理退休延迟、取消调用数与缓冲高水位分开测量。

迁移题:目标已终结,回调把B交给后台解码任务。若它先注销自己的借用再登记后台,会出现b=0的窗口,池可复用B。正确交接先取得新token再释放旧token;目标完成时d=0但后台token仍在,故B继续保留。待后台finally归还最后token才允许复用。终点任务给出三种合法目标/取消/回调次序、六种预留租约释放次序及故障反例的标准库复算。

参考资料

[1] liburing,io_uring_prep_cancel(3),ERRORS、NOTES,2026-10-08实读,页面上游快照2026-08-04;具体成功投递保证采用本接口文档,而非把概览的异步说明扩张成相反结论。

[2] liburing,io_uring_cancelation(7),Cancelation results、Race conditions、Canceling by file descriptor,2026-10-08实读。本文三状态联合账本、两级退休判据与数值例是自定协议,不是内核实现证明。

[3] Linux v6.2,pin_user_pages() and related calls,CASE 1–3:短期DIO、长期DMA与MMU notifier条件。实际固定缓冲与可移动GC接口仍须按目标运行时和平台核验。

关系图谱5 个相邻概念 · 2 类关系

拖动节点调整位置。

显示关系

显示:依赖

  1. 前置三跳
  2. 前置二跳
  3. 前置一跳
  4. 当前条目
  5. 后续一跳
  6. 后续二跳
  7. 后续三跳
文字版关系按与当前条目的最短距离分组
类型化关系