机制图解|资源不是一开始全开:BeginStep 的阶段门控策略

背景

很多人第一次排查 OpenTAP 的资源问题时,会默认“计划一启动就把所有 Instrument 全部 Open”。但真实行为更细:资源何时打开,取决于当前执行阶段(Open / Execute / PrePlanRun / Run / PostPlanRun)以及资源是否已进入打开任务队列。这个差异直接影响启动耗时、失败暴露时机和日志观感。

框架分析

核心在 Engine/ResourceTaskManager.csBeginStep(...)。它不是单一入口做同一件事,而是按 TestPlanExecutionStage 分支:

  • Execute:若存在空资源引用,先触发一次 BeginOpenResources,再启动 StartResourcePromptAsync;提示之后如果仍有未建任务资源,再补一次打开。
  • Open:对整计划静态资源与启用步骤资源做集中打开。
  • PrePlanRun/Run:不会盲目再开,而是检查已有 openTasks 是否全部 RanToCompletion;若未完成,才阻塞等待 WaitUntilAllResourcesOpened
  • PostPlanRun:不新增打开动作。

这说明 OpenTAP 的策略不是“全量预热”,而是“阶段感知 + 必要时等待”。

实现过程

可复现命令(直接定位门控逻辑):

1
2
3
4
5
6
cd /home/ops/clawd/repos/opentap
# 1) 看执行阶段枚举与 BeginStep 分支
grep -n "enum TestPlanExecutionStage\|void BeginStep" Engine/ResourceTaskManager.cs

# 2) 看 PrePlanRun/Run 阶段的等待条件
grep -n "RanToCompletion\|WaitUntilAllResourcesOpened\|StartResourcePromptAsync" Engine/ResourceTaskManager.cs

本地阅读时重点盯三件事:openTasks 的填充时机、resources.Any(...) 的两次判定、以及 Execute 分支里“先处理空引用再弹资源提示”的顺序。把这三点串起来,就能解释为何有些计划在启动时快、但在进入 PrePlanRun 前会短暂停顿。

注意事项

  1. PrePlanRun/Run 阶段的等待是“条件等待”,不是固定阻塞;日志里偶发等待通常意味着前序打开任务仍在跑或曾失败。
  2. 资源提示 (StartResourcePromptAsync) 与打开流程交错存在,插件里不要假设提示完成就代表资源一定已全部可用。
  3. 若自定义资源存在依赖链,建议结合 ResourceOpenAttribute 明确依赖打开语义,避免在阶段切换点出现“看似随机”的等待抖动。

小结

BeginStep 的价值在于把资源管理从“单次动作”改成“阶段门控流程”:该并发时并发、该提示时提示、该等待时再等待。理解这套门控后,排查“为什么不是一开始就全开”或“为何在 PrePlanRun 前卡一下”会快很多。

关键源码路径:

  • Engine/ResourceTaskManager.csTestPlanExecutionStageBeginStepBeginOpenResourcesWaitUntilAllResourcesOpened
  • Engine/TestPlan.cs(测试计划执行阶段与资源管理协作入口)
  • Engine/ILockable.cs(资源打开前后锁管理扩展点)