mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-23 18:21:13 +08:00
feat: 池状态接口加高水位跟踪与 Status 峰值字段
This commit is contained in:
Generated
+18
-18
@@ -1095,7 +1095,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "oxide_builtins"
|
name = "oxide_builtins"
|
||||||
version = "0.8.28"
|
version = "0.8.30"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"chrono",
|
"chrono",
|
||||||
"fast-float",
|
"fast-float",
|
||||||
@@ -1120,7 +1120,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "oxide_bytecode"
|
name = "oxide_bytecode"
|
||||||
version = "0.8.28"
|
version = "0.8.30"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"num-bigint",
|
"num-bigint",
|
||||||
"oxide_log",
|
"oxide_log",
|
||||||
@@ -1128,7 +1128,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "oxide_cfg"
|
name = "oxide_cfg"
|
||||||
version = "0.8.28"
|
version = "0.8.30"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"oxide_bytecode",
|
"oxide_bytecode",
|
||||||
"oxide_emit",
|
"oxide_emit",
|
||||||
@@ -1139,7 +1139,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "oxide_cli"
|
name = "oxide_cli"
|
||||||
version = "0.8.28"
|
version = "0.8.30"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ansi_term",
|
"ansi_term",
|
||||||
"bumpalo",
|
"bumpalo",
|
||||||
@@ -1159,7 +1159,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "oxide_code_cache"
|
name = "oxide_code_cache"
|
||||||
version = "0.8.28"
|
version = "0.8.30"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"lru",
|
"lru",
|
||||||
"oxide_bytecode",
|
"oxide_bytecode",
|
||||||
@@ -1168,7 +1168,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "oxide_compiler"
|
name = "oxide_compiler"
|
||||||
version = "0.8.28"
|
version = "0.8.30"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"oxide_bytecode",
|
"oxide_bytecode",
|
||||||
"oxide_cfg",
|
"oxide_cfg",
|
||||||
@@ -1187,7 +1187,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "oxide_dce"
|
name = "oxide_dce"
|
||||||
version = "0.8.28"
|
version = "0.8.30"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"oxide_bytecode",
|
"oxide_bytecode",
|
||||||
"oxide_cfg",
|
"oxide_cfg",
|
||||||
@@ -1201,7 +1201,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "oxide_emit"
|
name = "oxide_emit"
|
||||||
version = "0.8.28"
|
version = "0.8.30"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"num-bigint",
|
"num-bigint",
|
||||||
"oxc_allocator",
|
"oxc_allocator",
|
||||||
@@ -1216,7 +1216,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "oxide_ir"
|
name = "oxide_ir"
|
||||||
version = "0.8.28"
|
version = "0.8.30"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"oxide_bytecode",
|
"oxide_bytecode",
|
||||||
"oxide_log",
|
"oxide_log",
|
||||||
@@ -1225,7 +1225,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "oxide_kernel"
|
name = "oxide_kernel"
|
||||||
version = "0.8.28"
|
version = "0.8.30"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bumpalo",
|
"bumpalo",
|
||||||
"dashmap",
|
"dashmap",
|
||||||
@@ -1240,7 +1240,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "oxide_liveness"
|
name = "oxide_liveness"
|
||||||
version = "0.8.28"
|
version = "0.8.30"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"oxide_bytecode",
|
"oxide_bytecode",
|
||||||
"oxide_cfg",
|
"oxide_cfg",
|
||||||
@@ -1252,7 +1252,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "oxide_log"
|
name = "oxide_log"
|
||||||
version = "0.8.28"
|
version = "0.8.30"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"tracing",
|
"tracing",
|
||||||
"tracing-appender",
|
"tracing-appender",
|
||||||
@@ -1261,7 +1261,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "oxide_parser"
|
name = "oxide_parser"
|
||||||
version = "0.8.28"
|
version = "0.8.30"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"oxc_allocator",
|
"oxc_allocator",
|
||||||
"oxc_ast",
|
"oxc_ast",
|
||||||
@@ -1276,7 +1276,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "oxide_regalloc"
|
name = "oxide_regalloc"
|
||||||
version = "0.8.28"
|
version = "0.8.30"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"oxide_bytecode",
|
"oxide_bytecode",
|
||||||
"oxide_cfg",
|
"oxide_cfg",
|
||||||
@@ -1292,7 +1292,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "oxide_runtime_api"
|
name = "oxide_runtime_api"
|
||||||
version = "0.8.28"
|
version = "0.8.30"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"num-bigint",
|
"num-bigint",
|
||||||
"num-traits",
|
"num-traits",
|
||||||
@@ -1304,7 +1304,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "oxide_test262"
|
name = "oxide_test262"
|
||||||
version = "0.8.28"
|
version = "0.8.30"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"oxide_bytecode",
|
"oxide_bytecode",
|
||||||
"oxide_compiler",
|
"oxide_compiler",
|
||||||
@@ -1321,7 +1321,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "oxide_types"
|
name = "oxide_types"
|
||||||
version = "0.8.28"
|
version = "0.8.30"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bumpalo",
|
"bumpalo",
|
||||||
"hashbrown 0.14.5",
|
"hashbrown 0.14.5",
|
||||||
@@ -1333,7 +1333,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "oxide_vm"
|
name = "oxide_vm"
|
||||||
version = "0.8.28"
|
version = "0.8.30"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bumpalo",
|
"bumpalo",
|
||||||
"chrono",
|
"chrono",
|
||||||
|
|||||||
+1
-1
@@ -22,7 +22,7 @@ members = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[workspace.package]
|
[workspace.package]
|
||||||
version = "0.8.29"
|
version = "0.8.30"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
rust-version = "1.80"
|
rust-version = "1.80"
|
||||||
|
|||||||
@@ -90,10 +90,11 @@ pub enum ServerResponse {
|
|||||||
},
|
},
|
||||||
/// 版本响应。
|
/// 版本响应。
|
||||||
Version { version: String },
|
Version { version: String },
|
||||||
/// 状态响应:池可用/总数与运行时长。
|
/// 状态响应:池可用/总数、峰值借出数与运行时长。
|
||||||
Status {
|
Status {
|
||||||
pool_available: usize,
|
pool_available: usize,
|
||||||
pool_total: usize,
|
pool_total: usize,
|
||||||
|
pool_peak: usize,
|
||||||
uptime_ms: u64,
|
uptime_ms: u64,
|
||||||
},
|
},
|
||||||
/// 健康检查响应。
|
/// 健康检查响应。
|
||||||
@@ -334,6 +335,7 @@ mod tests {
|
|||||||
ServerResponse::Status {
|
ServerResponse::Status {
|
||||||
pool_available: 2,
|
pool_available: 2,
|
||||||
pool_total: 4,
|
pool_total: 4,
|
||||||
|
pool_peak: 3,
|
||||||
uptime_ms: 1500,
|
uptime_ms: 1500,
|
||||||
},
|
},
|
||||||
ServerResponse::Health { healthy: true },
|
ServerResponse::Health { healthy: true },
|
||||||
|
|||||||
@@ -346,6 +346,7 @@ fn dispatch_control(request: &ServerRequest, ctx: &ServerContext) -> ServerRespo
|
|||||||
ServerRequest::Status => ServerResponse::Status {
|
ServerRequest::Status => ServerResponse::Status {
|
||||||
pool_available: ctx.pool_counters.available(),
|
pool_available: ctx.pool_counters.available(),
|
||||||
pool_total: ctx.pool_counters.total(),
|
pool_total: ctx.pool_counters.total(),
|
||||||
|
pool_peak: ctx.pool_counters.peak(),
|
||||||
uptime_ms: ctx.started_at.elapsed().as_millis() as u64,
|
uptime_ms: ctx.started_at.elapsed().as_millis() as u64,
|
||||||
},
|
},
|
||||||
ServerRequest::Health => ServerResponse::Health { healthy: true },
|
ServerRequest::Health => ServerResponse::Health { healthy: true },
|
||||||
@@ -467,10 +468,12 @@ mod tests {
|
|||||||
ServerResponse::Status {
|
ServerResponse::Status {
|
||||||
pool_available,
|
pool_available,
|
||||||
pool_total,
|
pool_total,
|
||||||
|
pool_peak,
|
||||||
uptime_ms,
|
uptime_ms,
|
||||||
} => {
|
} => {
|
||||||
assert!(pool_available <= pool_total, "空闲数应不超过总数:{pool_available}/{pool_total}");
|
assert!(pool_available <= pool_total, "空闲数应不超过总数:{pool_available}/{pool_total}");
|
||||||
assert!(pool_total >= 1, "总数应不小于 1(标准配置预热 1 个):{pool_total}");
|
assert!(pool_total >= 1, "总数应不小于 1(标准配置预热 1 个):{pool_total}");
|
||||||
|
assert!(pool_peak <= pool_total, "峰值应不超过总数:{pool_peak}/{pool_total}");
|
||||||
assert!(uptime_ms < 10_000, "刚启动的 server 运行时长应小于 10 秒:{uptime_ms}");
|
assert!(uptime_ms < 10_000, "刚启动的 server 运行时长应小于 10 秒:{uptime_ms}");
|
||||||
}
|
}
|
||||||
other => panic!("应得 Status 帧,实得 {other:?}"),
|
other => panic!("应得 Status 帧,实得 {other:?}"),
|
||||||
|
|||||||
@@ -13,13 +13,17 @@ struct VmPoolInner {
|
|||||||
total_count: usize,
|
total_count: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 池状态计数器:空闲数与已创建总数的跨线程可读快照。
|
/// 池状态计数器:空闲数、在借数、峰值借出数与已创建总数的跨线程可读快照。
|
||||||
///
|
///
|
||||||
/// 池本体不是 Send/Sync(Vm 不跨线程),状态接口经独立原子暴露,
|
/// 池本体不是 Send/Sync(Vm 不跨线程),状态接口经独立原子暴露,
|
||||||
/// 与队列变更同步更新;空闲数恒不大于总数(总数只增不减)。
|
/// 与队列变更同步更新;空闲数恒不大于总数(总数只增不减)。
|
||||||
|
/// 安静态(无并发借出归还)下在借数恒等于总数减空闲数;峰值是每次
|
||||||
|
/// 借出后精确在借数的最大值,单调不降。
|
||||||
pub struct PoolCounters {
|
pub struct PoolCounters {
|
||||||
available: AtomicUsize,
|
available: AtomicUsize,
|
||||||
total: AtomicUsize,
|
total: AtomicUsize,
|
||||||
|
in_use: AtomicUsize,
|
||||||
|
peak: AtomicUsize,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PoolCounters {
|
impl PoolCounters {
|
||||||
@@ -30,6 +34,8 @@ impl PoolCounters {
|
|||||||
Arc::new(PoolCounters {
|
Arc::new(PoolCounters {
|
||||||
available: AtomicUsize::new(0),
|
available: AtomicUsize::new(0),
|
||||||
total: AtomicUsize::new(0),
|
total: AtomicUsize::new(0),
|
||||||
|
in_use: AtomicUsize::new(0),
|
||||||
|
peak: AtomicUsize::new(0),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -42,6 +48,25 @@ impl PoolCounters {
|
|||||||
pub fn total(&self) -> usize {
|
pub fn total(&self) -> usize {
|
||||||
self.total.load(Ordering::Relaxed)
|
self.total.load(Ordering::Relaxed)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 读取历史峰值借出数(高水位,单调不降)。
|
||||||
|
pub fn peak(&self) -> usize {
|
||||||
|
self.peak.load(Ordering::Relaxed)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 记录一次借出:在借数加一,并抬高历史峰值。
|
||||||
|
///
|
||||||
|
/// 峰值取借出后精确在借数的最大值,单调不降;归还路径不触碰峰值。
|
||||||
|
/// 诊断计数器,`Relaxed` 序,不参与同步语义。
|
||||||
|
fn record_borrow(&self) {
|
||||||
|
let in_use = self.in_use.fetch_add(1, Ordering::Relaxed) + 1;
|
||||||
|
self.peak.fetch_max(in_use, Ordering::Relaxed);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 记录一次归还:在借数减一。
|
||||||
|
fn record_return(&self) {
|
||||||
|
self.in_use.fetch_sub(1, Ordering::Relaxed);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 共享 `Vm` 实例池:按需创建并复用 VM,避免每次执行重建 kernel 共享状态。
|
/// 共享 `Vm` 实例池:按需创建并复用 VM,避免每次执行重建 kernel 共享状态。
|
||||||
@@ -132,6 +157,7 @@ impl VmPool {
|
|||||||
if let Some(vm) = inner.available.pop() {
|
if let Some(vm) = inner.available.pop() {
|
||||||
vm_trace!("pool: reused vm, {} available", inner.available.len());
|
vm_trace!("pool: reused vm, {} available", inner.available.len());
|
||||||
self.counters.available.fetch_sub(1, Ordering::Relaxed);
|
self.counters.available.fetch_sub(1, Ordering::Relaxed);
|
||||||
|
self.counters.record_borrow();
|
||||||
return VmGuard {
|
return VmGuard {
|
||||||
vm: Some(vm),
|
vm: Some(vm),
|
||||||
pool: Arc::clone(self),
|
pool: Arc::clone(self),
|
||||||
@@ -149,6 +175,7 @@ impl VmPool {
|
|||||||
vm_debug!("pool: growing to {} vms", inner.total_count);
|
vm_debug!("pool: growing to {} vms", inner.total_count);
|
||||||
drop(inner);
|
drop(inner);
|
||||||
self.counters.total.fetch_add(1, Ordering::Relaxed);
|
self.counters.total.fetch_add(1, Ordering::Relaxed);
|
||||||
|
self.counters.record_borrow();
|
||||||
let vm = Self::new_vm(&self.kernel_core);
|
let vm = Self::new_vm(&self.kernel_core);
|
||||||
return VmGuard {
|
return VmGuard {
|
||||||
vm: Some(vm),
|
vm: Some(vm),
|
||||||
@@ -164,6 +191,7 @@ impl VmPool {
|
|||||||
inner.total_count += 1;
|
inner.total_count += 1;
|
||||||
drop(inner);
|
drop(inner);
|
||||||
self.counters.total.fetch_add(1, Ordering::Relaxed);
|
self.counters.total.fetch_add(1, Ordering::Relaxed);
|
||||||
|
self.counters.record_borrow();
|
||||||
let vm = Self::new_vm(&self.kernel_core);
|
let vm = Self::new_vm(&self.kernel_core);
|
||||||
return VmGuard {
|
return VmGuard {
|
||||||
vm: Some(vm),
|
vm: Some(vm),
|
||||||
@@ -189,6 +217,13 @@ impl VmPool {
|
|||||||
self.counters.total()
|
self.counters.total()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 读取历史峰值借出数(高水位)。
|
||||||
|
///
|
||||||
|
/// 读原子快照,不锁池内互斥锁;峰值单调不降。
|
||||||
|
pub fn peak_count(&self) -> usize {
|
||||||
|
self.counters.peak()
|
||||||
|
}
|
||||||
|
|
||||||
/// 跨线程可读的池状态句柄(原子快照,无锁)。
|
/// 跨线程可读的池状态句柄(原子快照,无锁)。
|
||||||
pub fn counters(&self) -> Arc<PoolCounters> {
|
pub fn counters(&self) -> Arc<PoolCounters> {
|
||||||
Arc::clone(&self.counters)
|
Arc::clone(&self.counters)
|
||||||
@@ -234,6 +269,7 @@ impl Drop for VmGuard {
|
|||||||
}
|
}
|
||||||
|
|
||||||
self.pool.counters.available.fetch_add(1, Ordering::Relaxed);
|
self.pool.counters.available.fetch_add(1, Ordering::Relaxed);
|
||||||
|
self.pool.counters.record_return();
|
||||||
self.pool.condvar.notify_one();
|
self.pool.condvar.notify_one();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -329,4 +365,67 @@ mod tests {
|
|||||||
assert_eq!(pool_a.available_count(), counters.available());
|
assert_eq!(pool_a.available_count(), counters.available());
|
||||||
assert_eq!(pool_b.available_count(), counters.available());
|
assert_eq!(pool_b.available_count(), counters.available());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_peak_count_single_thread_exact() {
|
||||||
|
let kernel = test_kernel();
|
||||||
|
let pool = VmPool::new(kernel, 2, None);
|
||||||
|
assert_eq!(pool.peak_count(), 0, "从未借出的池峰值应为零");
|
||||||
|
|
||||||
|
let g1 = pool.spawn();
|
||||||
|
let g2 = pool.spawn();
|
||||||
|
assert_eq!(pool.peak_count(), 2, "连续借出两个后峰值应为 2");
|
||||||
|
drop(g1);
|
||||||
|
drop(g2);
|
||||||
|
|
||||||
|
let g3 = pool.spawn();
|
||||||
|
assert_eq!(pool.peak_count(), 2, "归还后再借一个峰值不回落");
|
||||||
|
drop(g3);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_peak_count_shared_aggregate() {
|
||||||
|
let kernel = test_kernel();
|
||||||
|
let counters = PoolCounters::shared();
|
||||||
|
let pool_a = VmPool::with_counters(Arc::clone(&kernel), 1, Some(4), Arc::clone(&counters));
|
||||||
|
let pool_b = VmPool::with_counters(Arc::clone(&kernel), 1, Some(4), Arc::clone(&counters));
|
||||||
|
|
||||||
|
let g1 = pool_a.spawn();
|
||||||
|
let g2 = pool_b.spawn();
|
||||||
|
assert_eq!(counters.peak(), 2, "两池各借出一个后聚合峰值应为 2");
|
||||||
|
drop(g1);
|
||||||
|
drop(g2);
|
||||||
|
|
||||||
|
let g3 = pool_a.spawn();
|
||||||
|
assert_eq!(counters.peak(), 2, "归还后再借一个聚合峰值不回落");
|
||||||
|
drop(g3);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_peak_count_concurrent_bounded() {
|
||||||
|
let kernel = test_kernel();
|
||||||
|
let counters = PoolCounters::shared();
|
||||||
|
|
||||||
|
let handles: Vec<_> = (0..4)
|
||||||
|
.map(|_| {
|
||||||
|
let kernel = Arc::clone(&kernel);
|
||||||
|
let counters = Arc::clone(&counters);
|
||||||
|
std::thread::spawn(move || {
|
||||||
|
// 池本体不跨线程(Vm 不 Send),每线程自建池、共享同一聚合计数器。
|
||||||
|
let pool = VmPool::with_counters(kernel, 0, Some(8), counters);
|
||||||
|
for _ in 0..50 {
|
||||||
|
let guard = pool.spawn();
|
||||||
|
drop(guard);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
for handle in handles {
|
||||||
|
handle.join().unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 并发下精确峰值是竞争结果,只断言界:4 线程各至多持 1 个在借。
|
||||||
|
let peak = counters.peak();
|
||||||
|
assert!((1..=4).contains(&peak), "并发峰值应落在 1 到 4 之间:{peak}");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user