diff --git a/crates/oxide_kernel/src/kernel/core.rs b/crates/oxide_kernel/src/kernel/core.rs new file mode 100644 index 00000000..56be5450 --- /dev/null +++ b/crates/oxide_kernel/src/kernel/core.rs @@ -0,0 +1,194 @@ +//! 跨所有 VM 实例永久共享的不可变核心:持有 perm/shape/code/prop 四张 forge +//! Arc、VM 边界守卫计数(active_vms)、批边界 sweep 与 perm interner advisory +//! 重建信号。 + +use std::num::NonZeroUsize; +use std::sync::atomic::{AtomicUsize, Ordering}; +use std::sync::Arc; + +use crate::code_forge::CodeForge; +use crate::kernel_info; +use crate::prop_forge::PropForge; +use crate::shape_forge::ShapeForge; +use crate::string_forge::PermInterner; +use oxide_log; + +use super::KernelConfig; + +/// 不可变、跨所有 VM 实例永久共享的状态。 +/// 构造后从不原地重建——forge 表为 append-only;宿主可经 +/// [`KernelCore::should_rebuild_perm`] 阈值在安全边界(无存活 VM)整体 +/// 重建(换 `Arc`,非原地清表)。 +pub struct KernelCore { + pub config: KernelConfig, + pub perm_interner: Arc, + pub shape_forge: Arc, + pub code_forge: Arc, + pub prop_forge: Arc, + /// 边界不变量守卫计数:当前持有本 kernel `Arc` 的 Vm 数。 + /// `Vm` 构造器增、`Drop for Vm` 减(恰好一次),供 sweep / kernel 重建 / + /// kernel drop 处断言"无存活 VM"边界。 + active_vms: AtomicUsize, +} + +impl KernelCore { + /// 按配置创建共享核心:初始化日志系统与四个共享 forge(interner/shape/code/prop)。 + pub fn new(config: KernelConfig) -> Arc { + oxide_log::init(&oxide_log::LogConfig { + output: oxide_log::Output::Stderr, + levels: config.log_levels, + }); + let perm_interner = Arc::new(PermInterner::new()); + let shape_forge = Arc::new(ShapeForge::new()); + let code_forge = Arc::new(CodeForge::new( + NonZeroUsize::new(config.max_cached_modules).expect("max_cached_modules must be greater than zero"), + )); + let prop_forge = Arc::new(PropForge::new()); + let max_cached = config.max_cached_modules; + let min_pool = config.min_pool_size; + let core = Arc::new(Self { + config, + perm_interner, + shape_forge, + code_forge, + prop_forge, + active_vms: AtomicUsize::new(0), + }); + kernel_info!("KernelCore initialized: max_cached_modules={}, min_pool={}", max_cached, min_pool); + core + } + + /// 只读访问永久字符串 intern 表。 + pub fn perm_interner(&self) -> &Arc { + &self.perm_interner + } + + /// 只读访问共享 hidden class(shape)存储。 + pub fn shape_forge(&self) -> &Arc { + &self.shape_forge + } + + /// 只读访问共享 bytecode cache。 + pub fn code_forge(&self) -> &Arc { + &self.code_forge + } + + /// 只读访问共享属性模板缓存。 + pub fn prop_forge(&self) -> &Arc { + &self.prop_forge + } + + /// 只读访问构建时固定的配置。 + pub fn config(&self) -> &KernelConfig { + &self.config + } + + /// 读取 session GC 阈值(字节数)。 + pub fn session_gc_threshold(&self) -> usize { + self.config.session_gc_threshold + } + + /// 设置 session GC 阈值(字节数)。 + pub fn set_session_gc_threshold(&mut self, bytes: usize) { + self.config.session_gc_threshold = bytes; + } + + /// 读取 code cache 的 module 数量上限。 + pub fn max_cached_modules(&self) -> usize { + self.config.max_cached_modules + } + + /// 设置 code cache 的 module 数量上限。 + pub fn set_max_cached_modules(&mut self, cap: usize) { + self.config.max_cached_modules = cap; + } + + /// 批边界清理瞬时 shape/prop 缓存,防止跨测试累积膨胀。 + /// + /// 瞬时 forge(非根 shape/transition/position + prop 模板)的 id 空间只在两类 + /// 边界复位:(1)kernel 整体重建——新核构造即空(结构性必清;宿主义务 = + /// 重建前旧核无存活 VM,否则旧核连同其 forge 永久驻留);(2)批内兜底 sweep + /// ——本函数,数据依赖:仅当 shape 或 prop 表超过 50k 阈值时才执行 + /// [`ShapeForge::clear_transient`] 与 [`PropForge::clear`](阈值是增长闸门, + /// 宿主的检查节奏只是采样点)。字符串 intern 表 append-only、CodeForge LRU + /// 自管理,均不碰。 + /// + /// # 边界与前提 + /// - 调用点须无存活 VM:对象头与 IC 词持 shape id,清空使 id 空间复位,跨复位 + /// 存活的 VM 会因 id 复用碰撞静默错槽;该前提由 debug_assert 守卫(开发期 + /// fail-fast),release 构建无断言,宿主可经 [`Self::active_vms`] 自检; + /// - 引擎不自动重建:id 空间的另一类复位(kernel 整体重建)由宿主在 + /// "无存活 VM" 边界驱动(runner 由循环结构满足:VM 每测试新建即弃, + /// 重建/sweep 点都在 VM 作用域外)。 + pub fn sweep_runner_forges(&self) { + // id 空间复位在有存活 VM 时是静默错槽隐患,守卫先于阈值判断。 + debug_assert!( + self.active_vms.load(Ordering::Relaxed) == 0, + "sweep_runner_forges requires no live VMs (shape id space reset)" + ); + // 键 interner 是 append-only(无逐次清理);仅当瞬时 shape/prop 表超阈值 + // 时清表(批内兜底)。 + if self.shape_forge.len() > 50_000 { + self.shape_forge.clear_transient(); + self.prop_forge.clear(); + } else if self.prop_forge.len() > 50_000 { + self.prop_forge.clear(); + } + } + + /// 宿主是否应整体重建本 kernel,并返回重建后的建议上限(advisory 信号)。 + /// + /// perm interner 唯一键数 `entry_count` **超过**配置的 + /// [`KernelConfig::perm_interner_max_entries`] 阈值时返回 + /// `Some(建议上限)`——建议上限 = 阈值取 2 的幂后加倍,宿主应在整体重建 + /// 后把它写入新 kernel 的 `perm_interner_max_entries`(增长不立即再次 + /// 触顶);阈值未设或键数未超阈值时返回 `None`。纯 advisory 契约:引擎 + /// 不自动重建——重建须由宿主在"无存活 VM"边界驱动:归还全部 `VmGuard` + /// (池排空)→ 旧 `Arc` 归零整体释放(PermInterner/ShapeForge/ + /// CodeForge/PropForge,含全部泄漏键文本与物化串)→ 新建 kernel + 新池/ + /// VM。存活 VM 的代际表注册表(`immutables`)与 P 对象持有旧 kernel 的物化串裸 + /// 指针与 shape id,VM 存活期间重建会使其悬垂。三个预设默认 None = 永不 + /// 触发(有意裁定:CLI eval/run/REPL/bench/test262 宿主均未接重建边界, + /// None 保证零行为漂移)。 + /// + /// # 边界与前提 + /// - 仅在宿主边界(run 间 / 测试间 / 迭代间)调用,勿入 dispatch 热路径: + /// `entry_count` 为 O(1) 读锁,intern 路径零加码; + /// - 仅具备安全重建边界的宿主(test262 runner 批边界、嵌入宿主迭代 + /// 之间)应启用旋钮;REPL 类宿主(单持久 VM,重建 = 丢失顶层状态) + /// 不应启用。 + /// + /// # 副作用 + /// 无:只读查询。 + pub fn should_rebuild_perm(&self) -> Option { + let cap = self.config.perm_interner_max_entries?; + (self.perm_interner.entry_count() > cap).then_some(cap.next_power_of_two().saturating_mul(2)) + } + + /// 边界不变量守卫计数:登记一个 Vm 出生(`Vm` 两条构造路径完整构造后调用)。 + pub fn note_vm_started(&self) { + self.active_vms.fetch_add(1, Ordering::Relaxed); + } + + /// 边界不变量守卫计数:注销一个 Vm 死亡(`Drop for Vm` 恰好调用一次)。 + pub fn note_vm_ended(&self) { + self.active_vms.fetch_sub(1, Ordering::Relaxed); + } + + /// 读取当前持有本 kernel 的存活 Vm 数;release 宿主可在 id 空间复位边界自检。 + pub fn active_vms(&self) -> usize { + self.active_vms.load(Ordering::Relaxed) + } +} + +impl Drop for KernelCore { + fn drop(&mut self) { + // kernel 能 drop 说明持 Arc 的 Vm 已全部归零;计数非零 = 纯计数漂移 + // (note_vm_started/note_vm_ended 配对挂接回归探测)。 + debug_assert_eq!( + self.active_vms.load(Ordering::Relaxed), + 0, + "KernelCore dropped with live VMs (note_vm_ended counter drift)" + ); + } +} diff --git a/crates/oxide_kernel/src/kernel/mod.rs b/crates/oxide_kernel/src/kernel/mod.rs index 7be53cb3..e69d8f27 100644 --- a/crates/oxide_kernel/src/kernel/mod.rs +++ b/crates/oxide_kernel/src/kernel/mod.rs @@ -1,7 +1,5 @@ #![allow(clippy::arc_with_non_send_sync)] -use std::num::NonZeroUsize; -use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::Arc; use oxide_types::mem::P; @@ -9,196 +7,15 @@ use oxide_types::object::JsObject; use oxide_types::value::JsValue; use crate::builtin::BuiltinWorld; -use crate::code_forge::CodeForge; -use crate::kernel_info; -use crate::prop_forge::PropForge; -use crate::shape_forge::{ShapeForge, EMPTY_SHAPE_ID}; -use crate::string_forge::PermInterner; -use oxide_log; +use crate::shape_forge::EMPTY_SHAPE_ID; mod builtin_id; mod config; +mod core; pub use builtin_id::{BuiltinDirtySet, BuiltinId, BuiltinSnapshot, NUM_BUILTINS}; pub use config::KernelConfig; - -/// 不可变、跨所有 VM 实例永久共享的状态。 -/// 构造后从不原地重建——forge 表为 append-only;宿主可经 -/// [`KernelCore::should_rebuild_perm`] 阈值在安全边界(无存活 VM)整体 -/// 重建(换 `Arc`,非原地清表)。 -pub struct KernelCore { - pub config: KernelConfig, - pub perm_interner: Arc, - pub shape_forge: Arc, - pub code_forge: Arc, - pub prop_forge: Arc, - /// 边界不变量守卫计数:当前持有本 kernel `Arc` 的 Vm 数。 - /// `Vm` 构造器增、`Drop for Vm` 减(恰好一次),供 sweep / kernel 重建 / - /// kernel drop 处断言"无存活 VM"边界。 - active_vms: AtomicUsize, -} - -impl KernelCore { - /// 按配置创建共享核心:初始化日志系统与四个共享 forge(interner/shape/code/prop)。 - pub fn new(config: KernelConfig) -> Arc { - oxide_log::init(&oxide_log::LogConfig { - output: oxide_log::Output::Stderr, - levels: config.log_levels, - }); - let perm_interner = Arc::new(PermInterner::new()); - let shape_forge = Arc::new(ShapeForge::new()); - let code_forge = Arc::new(CodeForge::new( - NonZeroUsize::new(config.max_cached_modules).expect("max_cached_modules must be greater than zero"), - )); - let prop_forge = Arc::new(PropForge::new()); - let max_cached = config.max_cached_modules; - let min_pool = config.min_pool_size; - let core = Arc::new(Self { - config, - perm_interner, - shape_forge, - code_forge, - prop_forge, - active_vms: AtomicUsize::new(0), - }); - kernel_info!("KernelCore initialized: max_cached_modules={}, min_pool={}", max_cached, min_pool); - core - } - - /// 只读访问永久字符串 intern 表。 - pub fn perm_interner(&self) -> &Arc { - &self.perm_interner - } - - /// 只读访问共享 hidden class(shape)存储。 - pub fn shape_forge(&self) -> &Arc { - &self.shape_forge - } - - /// 只读访问共享 bytecode cache。 - pub fn code_forge(&self) -> &Arc { - &self.code_forge - } - - /// 只读访问共享属性模板缓存。 - pub fn prop_forge(&self) -> &Arc { - &self.prop_forge - } - - /// 只读访问构建时固定的配置。 - pub fn config(&self) -> &KernelConfig { - &self.config - } - - /// 读取 session GC 阈值(字节数)。 - pub fn session_gc_threshold(&self) -> usize { - self.config.session_gc_threshold - } - - /// 设置 session GC 阈值(字节数)。 - pub fn set_session_gc_threshold(&mut self, bytes: usize) { - self.config.session_gc_threshold = bytes; - } - - /// 读取 code cache 的 module 数量上限。 - pub fn max_cached_modules(&self) -> usize { - self.config.max_cached_modules - } - - /// 设置 code cache 的 module 数量上限。 - pub fn set_max_cached_modules(&mut self, cap: usize) { - self.config.max_cached_modules = cap; - } - - /// 批边界清理瞬时 shape/prop 缓存,防止跨测试累积膨胀。 - /// - /// 瞬时 forge(非根 shape/transition/position + prop 模板)的 id 空间只在两类 - /// 边界复位:(1)kernel 整体重建——新核构造即空(结构性必清;宿主义务 = - /// 重建前旧核无存活 VM,否则旧核连同其 forge 永久驻留);(2)批内兜底 sweep - /// ——本函数,数据依赖:仅当 shape 或 prop 表超过 50k 阈值时才执行 - /// [`ShapeForge::clear_transient`] 与 [`PropForge::clear`](阈值是增长闸门, - /// 宿主的检查节奏只是采样点)。字符串 intern 表 append-only、CodeForge LRU - /// 自管理,均不碰。 - /// - /// # 边界与前提 - /// - 调用点须无存活 VM:对象头与 IC 词持 shape id,清空使 id 空间复位,跨复位 - /// 存活的 VM 会因 id 复用碰撞静默错槽;该前提由 debug_assert 守卫(开发期 - /// fail-fast),release 构建无断言,宿主可经 [`Self::active_vms`] 自检; - /// - 引擎不自动重建:id 空间的另一类复位(kernel 整体重建)由宿主在 - /// "无存活 VM" 边界驱动(runner 由循环结构满足:VM 每测试新建即弃, - /// 重建/sweep 点都在 VM 作用域外)。 - pub fn sweep_runner_forges(&self) { - // id 空间复位在有存活 VM 时是静默错槽隐患,守卫先于阈值判断。 - debug_assert!( - self.active_vms.load(Ordering::Relaxed) == 0, - "sweep_runner_forges requires no live VMs (shape id space reset)" - ); - // 键 interner 是 append-only(无逐次清理);仅当瞬时 shape/prop 表超阈值 - // 时清表(批内兜底)。 - if self.shape_forge.len() > 50_000 { - self.shape_forge.clear_transient(); - self.prop_forge.clear(); - } else if self.prop_forge.len() > 50_000 { - self.prop_forge.clear(); - } - } - - /// 宿主是否应整体重建本 kernel,并返回重建后的建议上限(advisory 信号)。 - /// - /// perm interner 唯一键数 `entry_count` **超过**配置的 - /// [`KernelConfig::perm_interner_max_entries`] 阈值时返回 - /// `Some(建议上限)`——建议上限 = 阈值取 2 的幂后加倍,宿主应在整体重建 - /// 后把它写入新 kernel 的 `perm_interner_max_entries`(增长不立即再次 - /// 触顶);阈值未设或键数未超阈值时返回 `None`。纯 advisory 契约:引擎 - /// 不自动重建——重建须由宿主在"无存活 VM"边界驱动:归还全部 `VmGuard` - /// (池排空)→ 旧 `Arc` 归零整体释放(PermInterner/ShapeForge/ - /// CodeForge/PropForge,含全部泄漏键文本与物化串)→ 新建 kernel + 新池/ - /// VM。存活 VM 的代际表注册表(`immutables`)与 P 对象持有旧 kernel 的物化串裸 - /// 指针与 shape id,VM 存活期间重建会使其悬垂。三个预设默认 None = 永不 - /// 触发(有意裁定:CLI eval/run/REPL/bench/test262 宿主均未接重建边界, - /// None 保证零行为漂移)。 - /// - /// # 边界与前提 - /// - 仅在宿主边界(run 间 / 测试间 / 迭代间)调用,勿入 dispatch 热路径: - /// `entry_count` 为 O(1) 读锁,intern 路径零加码; - /// - 仅具备安全重建边界的宿主(test262 runner 批边界、嵌入宿主迭代 - /// 之间)应启用旋钮;REPL 类宿主(单持久 VM,重建 = 丢失顶层状态) - /// 不应启用。 - /// - /// # 副作用 - /// 无:只读查询。 - pub fn should_rebuild_perm(&self) -> Option { - let cap = self.config.perm_interner_max_entries?; - (self.perm_interner.entry_count() > cap).then_some(cap.next_power_of_two().saturating_mul(2)) - } - - /// 边界不变量守卫计数:登记一个 Vm 出生(`Vm` 两条构造路径完整构造后调用)。 - pub fn note_vm_started(&self) { - self.active_vms.fetch_add(1, Ordering::Relaxed); - } - - /// 边界不变量守卫计数:注销一个 Vm 死亡(`Drop for Vm` 恰好调用一次)。 - pub fn note_vm_ended(&self) { - self.active_vms.fetch_sub(1, Ordering::Relaxed); - } - - /// 读取当前持有本 kernel 的存活 Vm 数;release 宿主可在 id 空间复位边界自检。 - pub fn active_vms(&self) -> usize { - self.active_vms.load(Ordering::Relaxed) - } -} - -impl Drop for KernelCore { - fn drop(&mut self) { - // kernel 能 drop 说明持 Arc 的 Vm 已全部归零;计数非零 = 纯计数漂移 - // (note_vm_started/note_vm_ended 配对挂接回归探测)。 - debug_assert_eq!( - self.active_vms.load(Ordering::Relaxed), - 0, - "KernelCore dropped with live VMs (note_vm_ended counter drift)" - ); - } -} +pub use core::KernelCore; /// 每会话可变状态:内置原型对象与全局对象。 /// 在 full_reset() 时重建,实现 JS 执行之间的完全隔离。