插件关闭与卸载
全库目录221
本文目录7
设插件M包含解码器A与缓存B,A→B表示A的后台工作和析构仍会访问B。界面线程T执行A的线程受限清理,W是A的工作者,Q是延迟回调。宿主协调者H推进模块阶段;单对象协调者S可以把A的唯一关闭记录C_A交给H,所有权代次由3增至4。对象表、令牌及交接协议见 插件接口与对象借用。
模块关闭与停止请求
关闭整个插件时,宿主常驻协调者保存Closing、Draining、Destroying和Unloaded阶段。进入Closing在模块门内关闭普通入口,再撤销外部业务事件来源。创建及依赖修改在模块门内核对入口状态,同时增加对象表修改在途数和调用计数,提交或失败后共同归还。已接纳的这些修改由该计数保护,协调者先等待它们提交或失败,再冻结新增与依赖修改;已有单对象关闭按 插件接口与对象借用交接。清单内对象随销毁回执删除,冻结不阻止既有关闭步骤结清。
CreateAsset尚未返回时,关闭方等待它完成无分配登记,因此不会漏掉新对象。创建入口必须有界返回,长期工作登记后交给后台任务;无法退出的构造使关闭停在Closing并保留模块。这里尚未等待全部后台活动,协调者先推进清单内各对象的停止步骤,工作者才有机会退出。已有RequestStop回执的对象沿原记录等待排空,Destroy已Invoking的对象只等待该调用,不能再向它并发发送RequestStop。其他对象的停止不必等待这次在途Destroy。
H沿A的原停止记录等待W,同时登记B的RequestStop。停止请求幂等地停止业务来源、唤醒工作者并撤销预约,B停止业务后仍保留A需要的缓存。Closing时跳板丢弃尚未开始的普通回调并归还令牌,已进入插件的调用执行完再归还;W结束既有停止工作所需的清理任务按清理派发协议登记。注销来源、取消队列项和排空执行分别完成。
工作排空与清理准备
停止准备先登记并推进 线程受限资源的关闭中的订阅撤销,H进入Draining后等待所有可能访问A待释放资源的使用者退出,再授权窗口和接口收尾。这个集合包括关闭入口前接纳的普通调用、W及其停止子任务、Q和其他已排队或正在执行的业务回调,以及已经领取资源访问权限的清理调用。每次访问在进入前登记相应令牌,退出或取消确认后归还;A进入Stopping关闭新增普通访问。仅W退出,不能证明窗口或COM接口已经无人使用。
例如关闭入口前,调用U已取得A的使用令牌,正在读取COM接口。W和Q先退出,U还持一份,H只能等待,不能先让T执行Release。U返回并归还后,H在同一宿主门内核对RequestStop与订阅撤销回执、资源访问计数为零和当前所有权代次4,关闭这一集合的继续领取,再把显式收尾步骤置Queued。这次核对与授权共同发生,等待期间不持对象锁。停止子任务若还需访问资源,就仍在等待集合中;它必须先结清,不能在收尾授权后追加。
已持令牌的工作者可产生完成既有停止工作所需的子任务,先增子计数再释放父计数;普通工作来源始终关闭。等待释放计数锁和工作者退出所需的业务锁。插件回调要求卸载时只向宿主投递请求并返回,H在外层收尾,回调不能同步等待自己退出。
可能失败的IO、业务结算、资源申请和异步释放在此阶段完成,逐项保存结果。进入Destroying前校验清理权限、参数及所需记录空间,析构只消费准备好的不抛异常同步清理。提供者B停止业务后仍保留缓存、分配器和句柄表,直到A清理结束;停工作者与释放依赖数据是不同边界。
关闭期间的依赖清理调用
H完成准备后进入Destroying,授权A在T执行Destroy。A最后仍需归还B的缓存页,所以H签发限定清理令牌,绑定C_A、A的对象代次、B及允许的操作。宿主核对既有A→B、双方对象与模块保留以及当前关闭阶段,允许批准的同步读取、归还和结算。普通令牌不能转换为这份权限,清理也不创建新业务对象、依赖或工作者。
入口在宿主控制锁内登记双方清理在途计数及模块保留,然后释放锁调用。跨模块按稳定模块ID顺序取得控制锁;执行插件和等待时不持这些锁。提供者按对象同步协议处理并发归还与统计更新,计数保护寿命,对象锁保护数据。Draining中的异步停止子任务另持清理活动令牌,Destroying只允许当前授权销毁步骤在其指定线程内同步清理。
T的跳板把该步骤从Queued改为Invoking,再调用A的Destroy。A用清理令牌归还缓存页,B的归还函数返回后,A完成析构;宿主保存成功回执并共同删除A和A→B。T的跳板退出前仍持模块保护,H等待这份保护归还。B此时还须满足使用者及清理在途为零,才可释放缓存和对象。伪造调用者、缺少原依赖或请求启动新缓存任务均拒绝并保留诊断;实际析构使用的权限已在准备阶段核验。
普通单对象关闭沿相同入口执行,B可仍为Active;插件内部直接借用指针也遵守同一资源保留条件。关闭阶段的推进与在途归零在共同入口比较,不能在锁外观察零后再放入新的清理调用。
依赖销毁与卸载续行
H确认A成功回执和跳板退出后,才授权B的Destroy。本例B的清理允许在H的执行线程运行;它同步释放剩余缓存,返回成功后从对象表移除。销毁按无存活依赖者的对象推进,每个函数在指定线程同步返回并捕获内部异常,期间禁止插件新增异步任务。需要双向协作的业务关系在RequestStop及Draining阶段结清并解除,留下无环析构依赖;互相引用对象也可由组合所有者统一关闭。模块依赖同样保证调用方清理后提供方才结束,宿主调度器一直保持可用。
关闭记录保存阶段、协调者与所有权代次、步骤状态、指定线程、销毁回执和未退出任务身份。超时保留当前阶段与全部尚存对象、函数表及依赖模块,续行重新检查计数和步骤账本;已确认返回的RequestStop可按幂等契约重发,在途步骤继续等待。Destroy成功立即移除对象并保存回执,续行不会再次析构;Destroy失败要求对象保持完整,可诊断并再次关闭,否则隔离故障并阻止普通调用。
最后一次Destroy返回且其跳板归还保护后,H确认M的对象表、清理在途及关闭派发队列均为空,撤销M这一代的清理通道,再调用平台卸载入口,写Unloaded并释放模块引用。T若服务其他模块则继续运行;只有T的全部所属资源与关闭消息结清后,才能结束对应COM初始化并退出线程。M已卸载与T可退出分别判断,宿主调度器保持可用到最后一份关闭回执消费完成。
进程内续行从C_A、B的关闭记录、步骤回执和当前计数继续:A已经销毁就直接等待其旧跳板或推进B,T上的步骤仍Invoking就等待原调用,Queued项按当前授权派发。进程结束后裸地址与这些运行中记录失效,新进程从资产状态正常加载并重建对象。
实现与证据
本文说明机制与宿主协议,沿A、B、T手工推演。关闭步骤、协调者代次、结果收件表及清理授权均为示例宿主协议。尚未编译动态库、加载宿主或执行实际COM异步撤销;编译、运行与性能结果分别验收。
相关主题
参考资料
- C++ Core Guidelines:I.26、C.35与R资源管理。
- Microsoft Dynamic-Link Libraries:模块加载与导出边界。
Closing、Draining和Destroying是本文定义的宿主状态。线程和Apartment规则见线程受限资源的关闭。