4.5.4 NOP 哨兵指令与 INTERRUPT 强制终止指令
NOP 和 INTERRUPT 是 AmritaSense 指令集中两个特殊的“原子”指令。它们不是 SelfCompileInstruction,没有编译期展开的空间结构,而是直接作为单个节点存在于工作流中,其功能完全体现在运行时的行为上。
NOP 哨兵指令
NOP(No Operation)是一个空操作哨兵节点。它存在的意义不是“做什么”,而是“站在哪里”,为跳转指令提供一个合法的目标地址。
实现
@_node_fun(wrap_to_async=False, address_able=True)
def _no_operation() -> None:
pass
NOP: _Node[None] = _no_operation关键属性
address_able=True:这是它最重要的特性。一个可寻址的节点才能被ALIAS标记,进而成为GOTO或CALL的目标。NOP将这一点作为其核心职责wrap_to_async=False:纯同步函数,执行时仅是一次return None,几乎零开销
典型用途
作为跳转的汇合点:在条件分支中,无论走哪个分支,最终都跳到同一个 NOP 继续执行:
# IF(cond, GOTO("then")) >> ... >> ALIAS(NOP, "end_if")NOP 为不同控制流路径提供了一个统一的汇合地址。
作为子程序的返回点:ARCHIVED_NODES 中的子程序以 NOP 结尾,子程序执行完毕后,解释器步进到 NOP,随即 call_sub 的 finally 块弹栈恢复调用者。
作为 ELSE 空分支:IF(cond, do).ELSE(NOP) —— 语义上表示“条件不成立时什么都不做”,NOP 让这个意图显式化。
INTERRUPT 强制终止指令
INTERRUPT 是工作流的紧急终止按钮。执行到它时,工作流立即、无条件、干净地退出。
实现
@_node_fun(wrap_to_async=False, address_able=False)
def _interrput_operation() -> NoReturn:
raise InterruptNotice("Interrupt Node")
INTERRUPT: _Node[NoReturn] = _interrput_operation执行机制
- 抛出
InterruptNotice:这个异常是BaseException的子类,不是普通的Exception - 全局捕获:解释器主循环在最外层专门捕获
InterruptNotice,一旦捕获,执行清理流程:- 清空
_ret_addr_stack(调用栈) - 重置
_pointer(指针向量) - 重置
_jump_marked标记
- 清空
- 干净退出:工作流以可控、可预测的方式终止,不留残留状态
关键属性
address_able=False:它是终结符,不应该成为跳转目标。跳转到一个立即终止的节点毫无意义NoReturn返回类型:类型系统明确告诉开发者,执行INTERRUPT之后的代码永不可达
异常穿透规则
InterruptNotice 是 BaseException 子类。在 Python 的异常体系中,except Exception 不会捕获 BaseException 的子类。因此,工作流中的 TRY/CATCH 块默认无法捕获 InterruptNotice,它天然具有穿透性。
唯一的例外:如果在 WorkflowInterpreter 初始化时显式将 InterruptNotice 加入 exception_ignored 元组,它将变为可捕获的普通异常。但通常情况下不需要这样做——INTERRUPT 的设计意图就是“不可拦截”的紧急终止。
使用场景
- 外部信号响应:工作流执行过程中,外部系统发出终止信号,下一个节点边界检查到信号后,通过跳转或直接放置
INTERRUPT来终止工作流 - 紧急安全停止:在检测到不可恢复的错误或危险状态时,编排中主动插入的
INTERRUPT节点触发立即退出 - 超时处理:
@Node()节点在开始执行前检查超时条件,若超时则返回INTERRUPT,强制终止后续流程
INTERRUPT_KEEP_CTX 上下文保留终止指令(v0.4.x+)
INTERRUPT_KEEP_CTX 是 INTERRUPT 的变体终止节点,抛出 InterruptKeepContext 而非 InterruptNotice。与 INTERRUPT 不同,解释器捕获此异常后不调用 reset()——指针、调用栈、依赖注入参数和所有执行状态均被保留,供后续恢复使用。
实现
@_node_fun(wrap_to_async=False, address_able=True)
def _interrupt_keep_ctx() -> NoReturn:
raise InterruptKeepContext("Interrupt Node with context retention")
INTERRUPT_KEEP_CTX: _Node[NoReturn] = _interrupt_keep_ctx关键属性
address_able=True:与INTERRUPT不同,此节点可以被别名化并放入ARCHIVED_NODES作为目标。NoReturn:与INTERRUPT相同——节点永远不会正常返回。- 抛出
InterruptKeepContext(InterruptNotice的子类)。
执行机制
- 抛出
InterruptKeepContext:解释器主循环捕获的BaseException子类。 - 解释器响应:主循环检查
isinstance(e, InterruptKeepContext)。若为 true,跳过reset()调用——指针、栈和依赖注入参数保持完整。 - 状态保留:
_panic_exc被设置,_waiter_fut接收异常。 - 恢复:在同一解释器上再次调用
run()或run_step_by()即可从保留状态恢复执行。
与 INTERRUPT 的对比
| 方面 | INTERRUPT | INTERRUPT_KEEP_CTX |
|---|---|---|
| 异常类型 | InterruptNotice | InterruptKeepContext |
| 捕获后 | reset() — 清空所有状态 | 跳过 reset() — 状态保留 |
| 可寻址 | 否 | 是 |
| 可恢复 | 否(需全新启动) | 是(调用 run() 恢复) |
| 适用场景 | 紧急终止 | 暂停-检查-恢复、调试断点 |
使用场景
- 暂停-检查-恢复:保留上下文终止,通过
get_exception()检查状态,然后run()恢复。 - 调试断点:将
INTERRUPT_KEEP_CTX放入ARCHIVED_NODES,通过call_sub(interrupt=True)外部调用以中断并检查。 - 检查点-重启:保存进行中的工作状态,稍后从精确位置重新开始。
对比总结
| NOP | INTERRUPT | INTERRUPT_KEEP_CTX | |
|---|---|---|---|
| 职责 | 占位、汇合、返回点 | 紧急终止 | 暂停并保留上下文 |
| 可寻址 | 是 | 否 | 是 |
| 返回类型 | None | NoReturn | NoReturn |
| 对控制流的影响 | 无,执行后自动推进 | 彻底终止,清空状态 | 终止但保留状态 |
| 典型位置 | ALIAS 目标、分支尾部、子程序末尾 | 错误处理路径末端、超时检查之后 | 调试断点、ARCHIVED_NODES |
NOP 是编织复杂控制流的静默节点,INTERRUPT 是守护工作流安全边界的最后防线。两者一静一动,构成了 AmritaSense 控制流体系的基石。
