mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-23 18:21:13 +08:00
fix: clear_transient 补清 overflow 态(id 空间完整复位)+ kernel 增加 active_vms 计数,sweep/重建/kernel-drop 三处 debug_assert 守卫'清空时点无存活 VM'
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
#![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;
|
||||
@@ -136,6 +137,10 @@ pub struct KernelCore {
|
||||
pub shape_forge: Arc<ShapeForge>,
|
||||
pub code_forge: Arc<CodeForge>,
|
||||
pub prop_forge: Arc<PropForge>,
|
||||
/// 边界不变量守卫计数:当前持有本 kernel `Arc` 的 Vm 数。
|
||||
/// `Vm` 构造器增、`Drop for Vm` 减(恰好一次),供 sweep / kernel 重建 /
|
||||
/// kernel drop 处断言"无存活 VM"边界。
|
||||
active_vms: AtomicUsize,
|
||||
}
|
||||
|
||||
impl KernelCore {
|
||||
@@ -159,6 +164,7 @@ impl KernelCore {
|
||||
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
|
||||
@@ -221,12 +227,19 @@ impl KernelCore {
|
||||
///
|
||||
/// # 边界与前提
|
||||
/// - 调用点须无存活 VM:对象头与 IC 词持 shape id,清空使 id 空间复位,跨复位
|
||||
/// 存活的 VM 会因 id 复用碰撞静默错槽;引擎不自动重建也不自动守卫,该前提
|
||||
/// 由宿主契约承担(runner 由循环结构满足:VM 每测试新建即弃,sweep 点在
|
||||
/// VM 作用域外)。
|
||||
/// 存活的 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 表超阈值
|
||||
// 时清表(批内兜底),调用点无存活 VM 的前提见文档。
|
||||
// 时清表(批内兜底)。
|
||||
if self.shape_forge.len() > 50_000 {
|
||||
self.shape_forge.clear_transient();
|
||||
self.prop_forge.clear();
|
||||
@@ -263,6 +276,33 @@ impl KernelCore {
|
||||
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)"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// 每会话可变状态:内置原型对象与全局对象。
|
||||
|
||||
@@ -159,16 +159,21 @@ impl ShapeForge {
|
||||
self.len() == 0
|
||||
}
|
||||
|
||||
/// 清空所有非根 shape、transition 与 position 缓存,id 计数器复位。
|
||||
/// 清空所有非根 shape、transition、position 缓存与 overflow 态,id 计数器复位。
|
||||
///
|
||||
/// 用于 session 重建:丢弃一次性对象产生的形状,只保留跨 session 共享的根节点。
|
||||
/// overflow 态一并复位(不清则旧 overflow 条目把 key 映射回已被截断的 id,
|
||||
/// 造成静默错深度与去重失效),id 空间完整复位。
|
||||
pub fn clear_transient(&self) {
|
||||
let mut shapes = self.shapes.write().unwrap();
|
||||
if shapes.len() > 1 {
|
||||
shapes.truncate(1);
|
||||
}
|
||||
drop(shapes);
|
||||
self.transitions.clear();
|
||||
self.positions.clear();
|
||||
self.overflow_active.store(false, Ordering::Relaxed);
|
||||
self.overflow_map.write().unwrap().clear();
|
||||
self.next_id.store(2, Ordering::Relaxed);
|
||||
}
|
||||
|
||||
@@ -320,6 +325,23 @@ mod tests {
|
||||
assert_eq!(forge.shapes.read().unwrap().len(), 1);
|
||||
}
|
||||
|
||||
/// overflow 态复位钉:曾触发 overflow 后清空,overflow 标志与映射须一并清空,
|
||||
/// 后续 make_shape 从新空间取小 id(而非旧 overflow 映射 id)。
|
||||
#[test]
|
||||
fn clear_transient_resets_overflow_state() {
|
||||
let forge = ShapeForge::new();
|
||||
let key = ShapeForge::pack_key(EMPTY_SHAPE_ID, 1_000_500);
|
||||
forge.overflow_active.store(true, Ordering::Relaxed);
|
||||
forge.overflow_map.write().unwrap().insert(key, 0xDEAD);
|
||||
|
||||
forge.clear_transient();
|
||||
|
||||
assert!(!forge.overflow_active.load(Ordering::Relaxed));
|
||||
assert!(forge.overflow_map.read().unwrap().is_empty());
|
||||
let id = forge.make_shape(EMPTY_SHAPE_ID, 1_000_500);
|
||||
assert_eq!(id, 2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn two_branches_share_ancestor() {
|
||||
let forge = ShapeForge::new();
|
||||
|
||||
@@ -1802,6 +1802,8 @@ fn run_tests() -> bool {
|
||||
if tests_since_kernel_reset >= kernel_batch {
|
||||
// 重建即全新 forge(结构性必清):旧核连同其表整体丢弃,
|
||||
// 先 sweep 是对 doomed 核白做一次 O(50k) 清理。
|
||||
// 重建边界契约:旧核上无存活 VM(VM 每测试局部,此处作用域外)。
|
||||
debug_assert!(kernel.active_vms() == 0, "kernel rebuild requires no live VMs");
|
||||
kernel = build_runner_kernel();
|
||||
tests_since_kernel_reset = 0;
|
||||
} else if done % 500 == 0 {
|
||||
|
||||
@@ -571,6 +571,8 @@ pub struct Vm {
|
||||
|
||||
impl Drop for Vm {
|
||||
fn drop(&mut self) {
|
||||
// 边界守卫计数:与构造器登记恰好配对(Rust 所有权保证恰好一次)。
|
||||
self.kernel_core.note_vm_ended();
|
||||
// 直接 drop(test262 每测试新建即弃)不经 reset/full_reset 路径:
|
||||
// 统一收尾释放全部 session 堆数据与内建原型属性区,防逐测试累积泄漏。
|
||||
self.teardown_intrinsic_protos();
|
||||
|
||||
@@ -145,6 +145,8 @@ impl Vm {
|
||||
vm.init_async_generator_intrinsics();
|
||||
// Promise 全局绑定发生在快照采集之后,重录快照避免首次 full_reset 误判脏。
|
||||
vm.session.record_snapshot();
|
||||
// 边界守卫计数:VM 完整构造后登记,与 `Drop for Vm` 的注销恰好配对。
|
||||
vm.kernel_core.note_vm_started();
|
||||
vm_info!("Vm created");
|
||||
vm
|
||||
}
|
||||
@@ -260,6 +262,8 @@ impl Vm {
|
||||
vm.init_async_generator_intrinsics();
|
||||
// Promise 全局绑定发生在快照采集之后,重录快照避免首次 full_reset 误判脏。
|
||||
vm.session.record_snapshot();
|
||||
// 边界守卫计数:VM 完整构造后登记,与 `Drop for Vm` 的注销恰好配对。
|
||||
vm.kernel_core.note_vm_started();
|
||||
vm_info!("Vm created (pool)");
|
||||
vm
|
||||
}
|
||||
@@ -820,6 +824,38 @@ mod tests {
|
||||
vm.run(&module).expect("vm run failed")
|
||||
}
|
||||
|
||||
/// 计数往返:两条构造路径(独立核/共享核)的登记与 Drop 注销恰好配对,
|
||||
/// 全部 drop 后计数归零,kernel 可干净 drop(Drop 断言无残留)。
|
||||
#[test]
|
||||
fn active_vms_count_roundtrip() {
|
||||
let vm = Vm::new();
|
||||
assert_eq!(vm.kernel_core.active_vms(), 1);
|
||||
drop(vm);
|
||||
|
||||
let core = KernelCore::new(KernelConfig::minimal());
|
||||
assert_eq!(core.active_vms(), 0);
|
||||
let v1 = Vm::with_kernel_core(Arc::clone(&core));
|
||||
let v2 = Vm::with_kernel_core(Arc::clone(&core));
|
||||
let v3 = Vm::with_kernel_core(Arc::clone(&core));
|
||||
assert_eq!(core.active_vms(), 3);
|
||||
drop(v3);
|
||||
assert_eq!(core.active_vms(), 2);
|
||||
drop(v1);
|
||||
drop(v2);
|
||||
assert_eq!(core.active_vms(), 0);
|
||||
drop(core);
|
||||
}
|
||||
|
||||
/// 守卫判别:持活 VM 调 sweep 触发 debug_assert。VM 声明在 kernel 之后,
|
||||
/// panic unwind 时先注销计数再 drop kernel,drop 断言不受干扰。
|
||||
#[test]
|
||||
#[cfg_attr(debug_assertions, should_panic(expected = "no live VMs"))]
|
||||
fn sweep_runner_forges_rejects_live_vm() {
|
||||
let core = KernelCore::new(KernelConfig::minimal());
|
||||
let _vm = Vm::with_kernel_core(Arc::clone(&core));
|
||||
core.sweep_runner_forges();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn full_reset_with_session_objects_forces_global_rebuild() {
|
||||
let mut vm = Vm::new();
|
||||
|
||||
Reference in New Issue
Block a user