https://github.com/nupha/zuqe
本次发布 44 个提交,版本号 0.11.0。主要解决四类问题:原生类的注册方式、多运行时的隔离与调度、栈溢出保护的准确性、原生扩展的生命周期管理。另有一批接口改名,升级时需要调整。
一、原生类不再需要手动注册
痛点:用 def_native_object! 声明原生类后,还必须再调用一次 JSFile::register(&mut rt)。忘记调用,类就不存在;一个进程内若有多个运行时,每个都要注册一遍,同一个类在不同运行时得到的 class id 也不相同——前者是隐性错误,后者使宿主无法缓存或传递 id。
升级:类定义在链接期自动收集,引擎初始化时统一分配编号,使用时直接查询:
let class_id = JSFile::class_id(); // 不再需要 rt意义:注册这一步从 API 中消失,不会再因为忘记调用而出错;class id 在进程内固定、跨运行时一致,可以安全缓存和传递;类注册信息全进程只有一份,多线程各建运行时、或一个线程建多个运行时,都不会为同一份类定义重复占内存。
二、多运行时:一个线程内可并存多个运行时
痛点:以往一个线程一个运行时,多运行时靠多线程实现。线程本身开销不小,实例一多,调度与内存都吃紧。另有两件事也无法表达:宿主自己的状态没有位置存放,只能放进 thread_local 或全局变量,一个线程上多个运行时会互相污染,且不在垃圾回收的追踪范围内;运行时之间也没有主次关系,无法表达"宿主管理若干隔离实例"这类结构。
升级:现在同一个线程内可以并存多个运行时,并补齐两件事。
其一,宿主状态交给运行时保存:
let rt = Runtime::create_with(MyState { /* ... */ })?;let s = rt.user_state::();状态类型在创建时确定,内容可随时修改,并参与垃圾回收:状态中若持有 JSValue,实现 GcTrace 后引擎会一并标记。
其二,新增可选特性 child_runtime,允许建立父子关系。子与父共享挂起状态,子运行时的 IO 完成或定时器到期会唤醒父的事件循环;父的一轮驱动连同子一起推进。该特性默认关闭。
意义:轻量级沙箱与轻量级隔离可以直接在一个线程里实现,不必为每个实例再开一个线程——隔离单位由线程变为运行时,创建与销毁更轻,调度与内存开销随之下降。运行时也可以携带自己的状态并留在 GC 视野内,多实例结构可直接用运行时树表达,不必在外部另写调度层。
三、栈溢出保护:守卫线自动定位
痛点:以往由宿主用 set_stack_overflow 指定"递归最多消耗多少字节"。该数值与线程真实的栈大小无关:执行放在栈较大的线程上,守卫线仍按旧值计算,可用深度被低估;调试构建下栈帧更大,同样的递归深度会提前误报。
升级:默认不再需要设置。引擎向操作系统查询当前线程的栈底地址,据此计算守卫线,并在其下保留余量给抛错路径。Windows、Linux、macOS 与各 BSD 直接支持;无法查询的平台提供常量可先行判断,再退回严格模式。要精确控制时接口仍在,只是改名(见升级须知)。
意义:守卫线对应真实线程栈,可用递归深度不再被低估,误报消除;宿主不必假设固定数字,也不必按平台分别调参。
四、模块加载接口简化,并可查询已加载模块
痛点:注册加载器需要三个参数:名字解析器通常传 None 用默认值,第三个是不透明的裸指针。用 Option 包裹必定存在的默认值没有意义;裸指针与多运行时、宿主状态的设计相冲突。
升级:注册加载器只接受一个参数 set_module_loader(loader);自定义名字解析另调 set_module_normalizer(f);宿主状态经运行时获取,不再经裸指针传递;新增查询已加载模块的接口,可只列原生模块或只列脚本模块。
意义:必填项与可选项分开,接口含义清楚;宿主的模块清单以引擎为准,不必维护一份可能失准的副本。
五、zapi 0.3:补齐扩展的生命周期管理
依赖由 0.2 升至 0.3,针对扩展运行中的四个痛点。
1. 运行时级资源没有释放时机。 现在扩展注册模块时可申报一个析构回调,运行时销毁时按引入顺序的逆序调用一次。意义:按运行时区分的资源(如 thread_local)可以正确释放,不会残留到进程结束。
2. 异步任务被丢弃时数据泄漏。 现在任务因运行时销毁而被清除时,宿主也会调用扩展注册的释放回调。意义:所有退出路径都归还数据,不再有只在"未执行完成"时出现的泄漏。
3. 同一扩展加载到多个运行时会串扰。 现在动态库每进程只加载一次并保持映射,类表、回收追踪与析构回调都归属各自的运行时。意义:一份扩展可同时服务多个运行时,各自的回收只处理各自的对象。
4. 版本校验过严。 以往 ABI 与 API 都必须完全相等,扩展与引擎必须同步升级;现在改为接受一个区间。意义:引擎小幅更新后,已有扩展不必立刻重编。
需说明:ABI 仍未定稿,0.2 到 0.3 已有一批接口改名,扩展需按新版本重编。
六、其他改动
- ArrayBuffer.prototype.transfer 增加快速路径:目标长度与源相同时直接转移底层存储,不再复制。意义:大缓冲区转移由 O(n) 降为 O(1)。
- 新增 FieldDef::GETSET_ENUM:声明描述符为 enumerable: true 的访问器属性。意义:定义 WebIDL 风格宿主对象时不必手工拼接标志位。
- 新增 PromiseResultSlot::attach_runtime_wake:把结果槽绑定到当前运行时的挂起状态。意义:其他线程提交结果时自动唤醒事件循环。
- Atomics.waitAsync 的等待计数改为按运行时统计。意义:一个运行时的等待不再让其他运行时的事件循环持续轮询。
- intl402 移出默认特性。意义:默认构建体积减小,需要国际化时显式开启。
热门跟贴