khora 是一个用于构建健壮并发系统的并发计算核心演算 (Concurrent Calculus)。其战略目标并非解决所有并发问题,而是专注于提供一个核心问题的、可验证的解决方案:并发计算的生命周期管理。它的设计根植于一组最小化的、正交的系统调用 (Syscalls),它们共同构成了 khora 抽象机器的语义基石。
模型的核心原则是,作用域 (Scope) 是管理生命周期、身份和上下文的唯一一等公民。计算由进程 (Processes) 来完成,且每一个 Process 都是一个 Plan 的动态实例。Process 严格存在于一个 Scope 内部,这种设计从根本上强制了结构化并发,确保所有计算都严格地绑定于一个明确定义的生命周期,从而在模型层面杜绝资源泄漏与孤儿任务。
khora 的设计哲学是元循环 (Meta-circular) 的:一个极简的微内核 (Microkernel) 只负责解释 Syscall,而不包含任何预设策略。所有高级并发策略,包括故障监督 (Supervision) 与执行调度 (Scheduling),都由运行在微内核之上的 Plan 自身来定义。这使得 khora 不仅仅是一个并发运行时,更是一个可用于构建定制化、可验证的并发模型的并发计算核心演算。
核心二元论 (The Core Duality):
- 结构与存在 (Structure & Existence):
Scope的树状拓扑结构定义了存在边界与生命周期从属关系。子Scope的生命周期完全从属于其父级,此为系统强制的结构关系。- 自治与交互 (Autonomy & Interaction): 在其生命周期边界内,每个
Scope都是一个自治实体。其内部策略(如调度和终止仲裁)在创建时被指定。Scope之间的交互通过能力 (Capabilities) 进行,它代表一种基于协议 (Protocol) 的、受控的、协商式的关系,而非结构性的支配关系。
khora 的架构被严格划分为三个层次,以确保安全性和灵活性。
- 微内核 (Microkernel): 系统的最内层。一个不包含任何策略的最小化引擎,其唯一职责是解释
Syscall、维护Scope树的拓扑完整性,并强制执行系统的核心公理。 - 策略层 (Policy Layer): 系统的中间层。由库作者编写,通过组合
Syscall来实现可重用的并发原语 (Primitives),如all,race,channel等。 - 应用层 (Application Layer): 系统的最外层。由最终用户编写,通过组合策略层提供的原语来表达具体的业务逻辑。
-
Scope: 一个结构化的实体,是身份、生命周期、上下文的统一载体,同时也是故障隔离的边界。它拥有唯一的
ScopeId,并构成一个严格的树状结构。 -
Plan: 一个纯粹的、惰性的执行计划。从形式上看,一个
Plan是一个递归的数据结构,它精确地描述了一个由一系列与微内核的交互(Syscall)组成,并最终产生一个结果的计算。任何Plan都可以被归结为以下两种基本形式之一:Pure(value): 代表一个已完成的计算,其结果就是value。Impure(syscall, continuation): 代表一个计算步骤。它包含一个要发往微内核的Syscall请求,以及一个续体 (Continuation) 函数。该续体将在接收到Syscall的返回值后,用于生成后续的Plan。
-
Blueprint: 一个用于延迟创建
Plan的、无参数的 thunk() => Plan。它是一个可重用的计算蓝图,将Plan的实例化与其执行上下文解耦。 -
Process: 一个
Plan的动态、瞬态实例。Process是执行的基本单元,持有执行状态并严格存在于一个Scope内部。 -
Processor: 微内核所拥有的、代表系统中唯一的、原子的计算权的实体。一个
Process必须被授予Processor才能执行其计算。 -
Strand: 一个同步调用链的物化表现。它由一个或多个通过
WeaveSyscall 链接在一起的Process构成。Strand作为一个整体被挂起或恢复。其头部的Process是当前唯一有资格持有Processor的Process。 -
EventQueue: 微内核内部的一个高优先级队列,用于存放需要被立即响应的
Process。微内核的执行循环被设计为优先清空EventQueue,以此来保证系统的反应性。 -
ProcessProfile: 一个在
Process创建时由微内核授予的、描述其内在角色与身份的数据结构。该Profile在Process的整个生命周期中保持其角色定义不变,并通过Self()Syscall 供其查询。 -
Scheduler: 一个特殊的
Process,其ProcessProfile将其标记为策略执行者。其职责是执行策略逻辑,并通过Arm等Syscall来影响未来哪些Process将进入微内核的EventQueue。 -
Portal: 一个在
Scope创建时定义的服务接口。它是一个由多个具名Blueprint构成的记录,定义了该Scope可通过Capability响应的远程调用。 -
Capability: 一个不可伪造的令牌,代表与特定
Scope的Portal进行交互的权利。 -
Sink: 一个与每个
Scope内在关联的、概念上无限容量的缓冲区,作为从外部世界到该Scope的单向数据入口。 -
PostFn: 一个暴露给外部世界的函数句柄
(value) => void,用于将数据异步地、非阻塞地投递到某个特定Scope的Sink中。 -
Fault: 一个描述
Process或Scope失败事件的数据结构。它代表一种终结状态。 -
Reaper: 作为
Scope的一项内在策略,在创建时被指定的Plan。它是一个终止仲裁器。当微内核检测到一个Scope的终止进程停滞不前时,会激活该Reaper,由其对Scope的状态进行仲裁并向微内核提供处置建议。 -
Limbo: 一个特殊的、系统级的
Scope,用于收容被Reaper裁定为无法正常终结的Scope。此隔离操作由微内核执行,以确保整个系统的活性。
-
The Microkernel Execution Model: khora 的执行模型是一个双相循环 (Biphasic Loop),由
Processor的原子性转移所驱动,在反应阶段与策略阶段之间交替进行。- 反应阶段:清空
EventQueue(Reactive Phase: Draining theEventQueue): 只要微内核的EventQueue不为空,它就处于此阶段。微内核会从队列头部取出一个Process,授予其Processor并开始执行。 - 执行与权柄转移 (Execution and Control Relinquishment):
Process的执行会持续进行,直到它发出一个[Blocking]Syscall,这会导致它放弃Processor。此时,控制流的走向有两种可能:- 若
Processor被直接转移给另一个Process(通过Weave),微内核将继续服务于新的Process。EventQueue在此期间不会被处理,因为Processor并未交还给微内核。 - 若
Processor被交还给微内核(通过Yield、AwaitProcess等),微内核则会返回反应阶段,继续处理EventQueue中的下一个Process。
- 若
- 策略阶段:调用
Scheduler(Policy Phase: Invoking the Scheduler): 仅当反应阶段完成(即EventQueue被清空)且Processor已被交还给微内核时,微内核才会进入此阶段。在此阶段,它会调用根Scheduler。Scheduler的职责是执行其策略,并通过Arm等Syscall为系统中的Process布置未来的调度触发器,为下一轮的反应阶段做准备。
- 反应阶段:清空
-
The Termination Protocol: 这是一个由微内核保证的、分阶段的协议。
- Initiation & Cascade: 当
Scope进入Terminating状态,终止信号会沿着Scope树向下级联。 - Convergent Cleanup: 在
Terminating状态下,微内核将拒绝任何扩张性的操作,但仍允许收敛性的操作,以支持健壮的清理逻辑。 - Adjudication & Structural Reaping: 若一个
Scope的终止进程未能收敛,微内核将激活其Reaper。若Reaper裁定需要强制回收,微内核将对该Scope及其后代启动结构性收割流程。此流程通过将阻塞的叶子Scope强制迁移至Limbo来打破僵局。 - Reaping Invariant: 在结构性收割期间,任何位于被收割
Scope树内的Process,都被禁止通过WeaveSyscall 将Processor转移给不处于其当前Strand上的Process。任何违反此规则的尝试都会导致该Process所在的Scope被立即隔离至Limbo。
- Initiation & Cascade: 当
-
Governing Axioms:
- Syscall Invocation Protocol: 一个
Plan通过发出Syscall请求与微内核交互。每一次交互都构成一个原子的请求-响应周期。 - The Concurrency/Control Duality:
Invoke等[Non-Blocking]Syscall 通过创建计算来引入并发,而不转移控制。Weave等[Blocking]Syscall 通过扩展一个Strand来建立同步控制,而不创建计算。 - The Unhandled Fault Principle: 一个未被同
Scope内任何Process等待的Fault将被视为溢出,并直接触发其所在Scope的终止协议。 - Policy as Computation: 监督与调度等核心策略,本身也是
Plan,受 khora 核心公理的约束。 - Policy Failure is Scope Failure: 任何策略
Process的失败,都被视为其管辖Scope的一次不可恢复的致命错误。 - The Idle Scope Termination Principle: 一个不包含任何
Process的Scope会被自动、立即地终止。 - Primacy of Termination: 一旦
Scope进入终止流程,完成该流程是微内核的最高优先级任务。
- Syscall Invocation Protocol: 一个
Syscall 是策略层与微内核之间的原子接口。[Blocking] Syscall 会导致当前 Process 放弃 Processor;[Non-Blocking] Syscall 则不会。
与微内核的交互遵循一个严格的错误模型,该模型明确区分了可处理的结果 (Results) 与致命故障 (Faults):
-
Result: 这是 Syscall 的带内 (in-band) 返回值,代表一次操作的可预见结果。它是一个和类型(Sum Type),封装了成功值或一个可由程序逻辑处理的、明确的错误(例如,“目标Process不存在”或“Capability无效”)。收到Result<T>后,发起调用的Process会恢复执行,并负责对该结果进行后续处理。 -
Fault: 这是一个带外 (out-of-band) 的终止事件,而非一个返回值。当一个 Syscall 请求会违反微内核的核心公理,或遭遇不可恢复的内部状态时,就会产生Fault。此时,发起调用的Process会被立即终止,其后续代码永远不会被执行。Fault本身不会被该Process观察到,而是作为系统事件向上传播,由其所在Scope的监督策略来处理。
Spawn(blueprint: Blueprint, options?: ScopeOptions)->[ScopeId, ProcessId, Capability, PostFn][Non-Blocking]: 纵向扩展。在当前Scope下创建一个新的子Scope,并启动一个初始Process。此操作会自动为新Scope配备一个Sink和一个Portal,并返回相应的PostFn和Capability。ScopeOptions:{ portal?: Portal, reaper?: Blueprint, scheduler?: Blueprint }
Fork(blueprint: Blueprint)->ProcessId[Non-Blocking]: 横向扩展。在当前Scope内创建一个并行的Process。Terminate(targetId: ProcessId)->void[Non-Blocking]: 强制终止。命令微内核终结一个目标Process(仅限同Scope)。Invoke(capability: Capability, method: string, args: any[])->Result[Non-Blocking]: 异步消息传递。通过Capability异步调用目标Scope的Portal中指定的方法,并创建一个Process来处理该消息。Materialize()->[ProcessId, (value) => void, (fault) => void][Non-Blocking]: 外部锚定。创建一个新的、可等待的Process,并返回用于从外部解决或拒绝该Process的函数句柄。Bind(key, value)->void[Non-Blocking]: 将一个值绑定到当前Scope的上下文中。Arm(targetId: ProcessId)->void[Non-Blocking]: 布防加急调度。为目标Process附加一个一次性的“加急”状态标记。此标记是潜在的:- 如果目标
Process当前可运行,它会被“立即”放入EventQueue,同时消耗该标记。 - 如果目标
Process当前不可运行,该标记将保持附加状态。当该Process未来因任何原因首次变为可运行时,它将被自动放入EventQueue,同时消耗该标记。
- 如果目标
Disarm(targetId: ProcessId)->void[Non-Blocking]: 解除加急调度。移除一个先前通过Arm为目标Process附加的、但尚未被触发消耗的“加急”状态标记。如果标记已被消耗或不存在,此操作无效。此Syscall无法将一个已经位于EventQueue中的Process移除。
Receive()->Result[Blocking]: 接收消息。从当前Process所在Scope的Sink中接收一个值。如果Sink为空,则阻塞当前Process。Weave(targetId: ProcessId)->Result[Blocking]: 同步控制编织。将一个目标Process编织到当前的Strand之上,使其成为新的头部。Processor被原子性地、同步地转移给目标Process,而当前Process则被阻塞,直到Processor沿着Strand被传回。Halt()->never[Blocking]: 终止作用域。立即发起当前Process所在Scope的终止协议,并交还Processor。AwaitProcess(id: ProcessId)->Result[Blocking]: 阻塞当前Process,将其Processor交还给微内核并挂起当前Strand,直到目标Process完成。AwaitScope(id: ScopeId)->Result[Blocking]: 阻塞当前Process,将其Processor交还给微内核并挂起当前Strand,直到目标Scope终结。若该Scope的终结涉及对其后代的强制修剪,返回的成功Result将被附加上下文信息以作标识。Yield()->void[Blocking]: 协作式让权。将Processor交还给微内核,以待重新调度,并挂起当前Strand。
PollProcess(id: ProcessId)->Status[Non-Blocking]: 非阻塞地查询目标Process的当前状态。PollScope(id: ScopeId)->Status[Non-Blocking]: 非阻塞地查询目标Scope的当前状态。Self()->ProcessProfile[Non-Blocking]: 自省。获取当前Process的ProcessProfile。Resolve(key)->value[Non-Blocking]: 从当前Scope沿祖先链向上查找一个上下文值。
标准库原语是在策略层中,通过组合 Syscall 实现的、可重用的并发策略。它们本身不是微内核的一部分。
spawn(blueprint): 基于SpawnSyscall 的原语。all(blueprints): 实现“全有或全无”的并行模式。race(blueprints): 实现“竞争”并行模式。resource(body): 将外部资源的获取/释放与当前Scope的生命周期绑定。- Supervision Strategies (
oneForOne,oneForAll, etc.): 封装通用故障处理模式。 - Scheduling Strategies (
withScheduler, etc.): 创建带有特定子调度器服务的Scope。
run(blueprint, options?)->Promise: 系统的主入口点。它负责创建根Scope,配置根Scheduler,并启动整个khora执行模型,最终将结果桥接到宿主环境。fromPromise(promiseFn)->Plan: 将一个宿主环境的异步操作,适配为一个可在khora世界中安全管理的Plan。