docs/current/06-出口与协议/wasm多实例并发模型与U2拍板.md
GitHub ↗
当前有效

wasm 多实例并发模型与 U2 拍板

1834 字·约 5 分钟 阅读 2026-10-10 17:07

裁定日期:2026-09-19 | 状态:已裁定(U2 拍板输入) 上位文档:MoonBit迁移总计划(F-5 capi 砍除)、MoonBit迁移第一阶段计划(已归档)(§2 U2) 关联文档:下游需求处置回执(C1 第二批 capi 窗口)、CAPI评审回复与实现状态(已归档)


1. 裁定结论

宿主侧并发模型定为"每线程一个完全独立的引擎实例":同一进程内将引擎模块实例化 N 次, 每实例拥有独立的线性内存、全局状态与(wasm-gc 终局下的)GC 堆——隔离是 wasm 实例模型的 构造性质,不依赖引擎侧任何线程安全改造。三种宿主形态均为现成能力:

宿主形态 并发载体 实例化方式 铁律的硬度
浏览器(IBrowserHost,终局) N × Web Worker WebAssembly.Module 结构化克隆进各 worker 后各自 instantiate(编译一次、实例化 N 次) 结构性(worker 各持实例)
Node worker_threads 同上(Node 的 structuredClone 支持 WebAssembly.Module 转移) 结构性
.NET 内嵌(Wasmtime) 每 .NET 线程一个独立 Store + Instance Module 编译一次(本身 Send+Sync,跨线程共享),每线程 Store::new + instantiate 类型系统强制(Store 非 Sync,跨线程共享直接编译失败)

铁律:1 实例 = 1 线程 = 1 会话。 同一实例不得跨线程并发调用——实例内部保持单线程 不是缺陷,是判分确定性与 seek 回放可重放的来源。Wasmtime 宿主下该铁律由编译器保证, 浏览器/Node 宿主下由 worker 结构保证。

并发隔离模型图(由 go run ./scripts/gen_svg 生成,对账本节与 §2):

wasm 多实例并发隔离模型

2. 依据

2.1 隔离来自 core wasm 实例模型(不必等 wasm-gc)

每次 instantiate 产生的 Instance 自带独立的 linear memory / globals / table,这是 core 规范性质;wasm-gc 只是把对象数据从线性内存挪到 GC 堆(同样挂在 Instance 上, per-instance)。因此当前出口 2(Rust→wasm32-unknown-unknown,冒烟实证 3.75MB) 就已具备本文全部并发性质——引擎的 1MB 线性内存、符号表、会话状态全在实例内部。 下游并发对接现在就能用 wasm32 产物验证与实施,不是 wasm-gc 才解锁的能力; MoonBit/wasm-gc 终局只是延续同一模型。

2.2 本仓库实证交叉验证

  • capi 形态是构造性反例:单进程单实例全局状态,实证"DLL 并发调用 → 堆损坏 (引擎非线程安全),Vitro 侧调用必须互斥"(原 native/AGENTS.md §D5 实证发现,档案 tag rust-oracle-freeze)。 这不是可修的 bug,是形态本身的构造缺陷;wasm 多实例是其正解——同进程内 N 实例替代"N 进程 DLL 池"或"单实例互斥"。
  • 引擎状态全部实例内(跨实例"同输入同输出"可复现的前提,逐项核对): 1MB 线性内存 ✓;VFS 沙盒文件系统(RuntimeState 内)✓;rand 状态(引擎内 MT19937,pyrandom 逐比特复刻的确定性正依赖它)✓;教学子集无 time 类非确定 host func ✓。
  • 容量参考:serve 侧 RSS 先例(预算 64MB、实测峰值 ~24MB/会话)给出 N 实例 宿主预算量级——N 的上限由内存决定,不是并发正确性决定。

2.3 边界(诚实记录)

  • 宿主 import 面不在构造性隔离之内:实例间隔离覆盖引擎内部状态;输出/输入 通道由宿主提供——若输出挂宿主全局(console/单管道),N 会话输出会交错 (内存安全无虞,会话分流会乱)。宿主侧须按实例绑定输出回调;serve 协议的 session id 概念平移,宿主只做帧路由。
  • 实证缺口:出口 2 冒烟覆盖"C API 全链路 + 安全检测在 wasm 下工作",但 同进程 N 实例并发互不干扰尚无实测记录。按"实测大于脑测",拍板生效前 补一个小锚(见 §4 待办)。

3. U2 拍板(vitro_capi.h 19 声明的迁移期处置)

以本文并发模型为依据,对 第一阶段计划(已归档) §2 U2("vitro_capi.h 19 声明与开工同批拍板")裁定如下:

对象 裁定 说明
第一批 capi 19 声明(W0-3 已补齐 + 三种所有权标注,SharpTutor 现依赖) 冻结现状,维护至 Rust oracle 退役 不再新增能力;缺陷修复与防线维护照常(冻结区纪律);随 Rust 区整体删除而消失(总计划 §10)
第二批 capi(memory/breakpoints 的"语言中立化 + capi 导出",回执 §3 C1 窗口) 裁定不做 并发与嵌入诉求由本文模型承担(.NET 宿主走 Wasmtime 多 Store 多实例);对应能力以 serve 协议为终态载体(C2 的 serve 过渡形态即正式形态,不再语言中立化导出到 capi)
SharpTutor 对接形态 并发走 wasm 多实例,交互走 serve 协议帧 .NET 内嵌 Wasmtime 承担多会话并发;协议帧(NDJSON)语义在 wasm 宿主内由适配层承载,出口 3 的会话语义(id 关联 / 错误帧同构 / session.reset)原样平移

通知义务(按第一阶段计划(已归档) U2 规则"若裁不做须通知下游改期"):第二批 capi 的 裁"不做"须正式通知 SharpTutor——其影响与替代路径:

  • 回执 §3 原降级线("第二批整体后移到 CS2 之后")升级为改道:M3 调试面板 依赖的三段式 kind 以 serve 协议为唯一载体(C2 已落地 serve 过渡形态);
  • CS2 的 region 高亮 / 透视模式内存断点数值形态,改由 wasm 实例 + serve 帧承载;
  • 对端集成成本变化:新增 .NET 侧 Wasmtime 依赖(Wasmtime NuGet),换取并发 能力从"单实例互斥"升为"N 实例构造性隔离"——这是能力增强不是降级。

4. 待办(拍板生效前的加固动作)

  • N 实例并发实证锚——单实例冒烟已固化:scripts/wasm_smoke/wasm_smoke.js(R4 G2,2026-09-12 进 CI);同进程 N 实例互不干扰实证仍开放(移入后续 wasm-gc 批随 S7 gateway 片实测——届时 MoonBit wasm-gc 产物为对拍主体)
  • SharpTutor 通知发出(用户动作,非本仓库任务):引用本文 §3。
  • S1 vitro/engine/source 的列单位契约(双坐标 Pos{byte_off, col_scalar, col_utf16})✅ 已随 S1 收官落地(moonbit/source 双坐标 wbtest 在库)——单实例单线程语义下坐标口径无需考虑并发快照一致性。