core 设计

设计目标

根包是大多数程序导入的门面。它应当让程序通过一次导入就能构建策略、计划和工作流,运行内核并提交工作流,并提供合理的默认值,而真正的定义留在拥有它们的包中。后端也是在这里选择的,因此它是除 backend/native 之外唯一依赖 C 运行时的包。

数学背景

门面没有引入新概念;它是其函数与底层包函数之间的一组等式。记 d\mathbf{d} 为默认策略 (Native,Synchronous,1,1,PreserveInputOrder)(\mathrm{Native}, \mathrm{Synchronous}, 1, 1, \mathrm{PreserveInputOrder}):

@luna_thread.map(ℓ,t,n)=@plan.map(ℓ,t,n,d,PreserveInputOrder),@luna_thread.submit_workflow(w,Native)=@workflow.submit(w,Native),@luna_thread.execute_map_i32(x,w,c)=@native.execute_map_i32(x,w,c),\begin{aligned} \texttt{@luna\_thread.map}(\ell, t, n) &= \texttt{@plan.map}(\ell, t, n, \mathbf{d}, \mathrm{PreserveInputOrder}), \\ \texttt{@luna\_thread.submit\_workflow}(w, \mathrm{Native}) &= \texttt{@workflow.submit}(w, \mathrm{Native}), \\ \texttt{@luna\_thread.execute\_map\_i32}(x, w, c) &= \texttt{@native.execute\_map\_i32}(x, w, c), \end{aligned}

其他每个函数也是如此。门面特有的事实只有它的默认值。对内核而言默认值为 w=c=1w = c = 1,原生的覆盖条件 w≥⌈n/c⌉w \ge \lceil n / c \rceil 就变成

1≥⌈n1⌉=n,1 \ge \left\lceil \frac{n}{1} \right\rceil = n ,

因此使用默认参数时,内核只接受长度为一的输入。

设计决策

带默认值的自由函数

门面暴露的是 map、workflow、spawn_task 这样的自由函数,而不是导入类型上的方法,因为这些类型属于其他包,无法在这里添加方法。可选参数承载默认值,所以常见的调用很短:@luna_thread.map("double", @luna_thread.i32_type(), 16) 就是一个完整且有效的计划。能力辅助函数把每种能力绑定到唯一有效的访问模式(channel_capability 对应 MoveOnly,mutex_capability 对应 SynchronizeOnly,shared_read_capability 对应 ReadOnly),从而消除了一整类 InvalidCapabilityAccess 问题。

在一处进行后端路由

submit_workflow 按后端分派:Native 交给 @native.submit_workflow,其他交给 @workflow.submit。两者如今产生相同的经过验证的提交,但通过后端包路由正是将来接入执行的位置。直接执行和异步函数无条件调用 backend/native,因为它是唯一能执行的后端。

门面只支持原生目标

由于它导入了 backend/native,而后者的外部函数只存在于原生目标上,门面声明了 supported_targets = "native"。必须在所有目标上构建的代码直接导入 plan、shared 和 workflow;除执行之外,它们提供了一切。

沿用原有接口的内核默认值

内核默认值 w=c=1w = c = 1 是最小的有效策略值,与 make_policy 一致,但如上文推导,它们只在只有一个元素时满足覆盖条件。改变它们会改变现有调用的含义;API 和教程页面会提醒用户把两个参数都传上。

正确性 / 不变量

  • 忠实转发。 对同样的参数和默认值,每个门面函数返回的正是底层函数返回的结果;门面不持有状态。
  • 默认值是有效策略。 default_policy() 属于 v1 策略子集;不带参数调用 make_policy 返回 Ok(default_policy())。
  • 对 C 的单一依赖。 只有 execute_*、submit_workflow_async、poll_workflow、wait_workflow 和 drop_workflow 会触及 C 运行时。

被否决的方案

  • 重新导出整个包。 重新导出 plan、shared 和 workflow 的每个名称会重复它们的 API 页面;门面只保留典型程序需要的入口,而那些包仍然可以直接导入。
  • 在编译时按目标选择后端。 这会让门面能在所有目标上构建,却会随目标悄悄改变行为;v1 改为把原生要求明确写出来。

边界

门面不定义类型,自身不验证任何东西,不执行计划,不管理线程池,也不支持 JavaScript 后端;它只转发给已经或将要做这些事的包。