feat: Atomics.waitAsync/pause 异步臂与 notify 唤醒面

This commit is contained in:
2026-09-23 21:17:43 +00:00
parent 212588302c
commit c568eed769
10 changed files with 338 additions and 7 deletions
+63
View File
@@ -0,0 +1,63 @@
//! Atomics.waitAsync waiter 核:per-(缓冲对象, 偏移) 的 promise FIFO 表、登记与
//! notify 唤醒(直调 `fulfill_promise` 结算 "ok",反应入队随 run 收尾 FIFO drain)。
//!
//! 键 = 登记时刻视图的 `buffer` 指针:同 run 无 GC 时 notify 侧读同一指针恒匹配;
//! run 内 GC 晋升后指针漂移则唤醒退化 0(单线程语义,无界外观察钉)。
use oxide_types::object::JsObject;
use oxide_types::value::JsValue;
use crate::vm::Vm;
impl Vm {
/// 新建 pending Promise(waitAsync 异步臂的 value 槽)。
///
/// resolve/reject 闭包弃用:结算路径是 notify 唤醒直调 [`Self::fulfill_promise`],
/// 不经闭包。
pub(crate) fn atomics_new_waiter_promise(&mut self) -> JsValue {
let (promise, _resolve, _reject) = self.new_promise_capability();
promise
}
/// 登记 waitAsync waiter:键 = (缓冲对象指针, 元素字节偏移),同键 FIFO 追加。
///
/// # 注意事项
/// 调用方须传登记时刻视图的 `buffer` 现指针(与 notify 侧同一读径);表在
/// run 边界清空(`clear_execution_state`),清位后 promise 无强根自然回收。
pub(crate) fn atomics_register_waiter(&mut self, buffer: *mut JsObject, offset: usize, promise: JsValue) {
self.atomics_waiters.entry((buffer as u64, offset)).or_default().push(promise);
}
/// 唤醒 (缓冲, 偏移) 处登记的前 `count` 个 waiter(`<= 0` 不唤醒、`+Inf` 全唤醒),
/// 逐个直调 `fulfill_promise` 结算 "ok"(反应入队随 run 收尾 drain 执行)。
///
/// # 返回
/// 实际唤醒数(队列不存在或不足时为 0 / 队列长)。
pub(crate) fn atomics_wake_waiters(&mut self, buffer: *mut JsObject, offset: usize, count: f64) -> usize {
// 唤醒数先独立判定(借用即时结束),结算串创建不跨表借用。
let n = {
let Some(waiters) = self.atomics_waiters.get_mut(&(buffer as u64, offset)) else {
return 0;
};
if count <= 0.0 {
0
} else if count.is_infinite() {
waiters.len()
} else {
(count as usize).min(waiters.len())
}
};
if n == 0 {
return 0;
}
let ok = self.new_string("ok");
let Some(waiters) = self.atomics_waiters.get_mut(&(buffer as u64, offset)) else {
return 0;
};
let woken: Vec<JsValue> = waiters.drain(..n).collect();
for promise in woken {
let _ = self.fulfill_promise(promise, ok);
}
n
}
}
@@ -36,6 +36,8 @@ pub fn bind_atomics(core: &Arc<KernelCore>, session: &KernelSession, global: &mu
("isLockFree", oxide_builtins::atomics::atomics_is_lock_free::<crate::vm::Vm>, 1),
("wait", oxide_builtins::atomics::atomics_wait::<crate::vm::Vm>, 4),
("notify", oxide_builtins::atomics::atomics_notify::<crate::vm::Vm>, 3),
("waitAsync", oxide_builtins::atomics::atomics_wait_async::<crate::vm::Vm>, 4),
("pause", oxide_builtins::atomics::atomics_pause::<crate::vm::Vm>, 0),
);
let a_val = JsValue::from_js_object(atomics_ptr);
+1
View File
@@ -11,6 +11,7 @@ mod async_disposable;
mod async_from_sync;
mod async_func;
mod async_generator;
mod atomics;
/// 内置对象绑定模块:向 session 的 global 对象安装 Object/Array/... 及各原型。
pub mod bindings;
mod dispatch;
+12
View File
@@ -75,6 +75,12 @@ impl Vm {
for job in &self.job_queue {
crate::promise::for_each_job_value(job, &mut f);
}
// Atomics waiter 表登记的 promise 是 GC 根(run 内跨调用存活)。
for promises in self.atomics_waiters.values() {
for p in promises {
f(*p);
}
}
for iter in &self.iters.for_in_iters {
if iter.is_null() {
continue;
@@ -150,6 +156,12 @@ impl Vm {
for job in &mut self.job_queue {
crate::promise::rewrite_job_values(job, &mut rewrite);
}
// Atomics waiter 表中的 promise 随 sweep 重写(与 mark 段一一对应)。
for promises in self.atomics_waiters.values_mut() {
for p in promises {
*p = rewrite(*p);
}
}
for iter in &mut self.iters.for_in_iters {
if iter.is_null() {
continue;
+4
View File
@@ -189,6 +189,10 @@ pub struct Vm {
pub async_generator_function_proto: P<JsObject>,
/// 微任务队列(Promise reactions / thenable 委托),`run()` 末尾 FIFO drain。
pub(crate) job_queue: VecDeque<crate::promise::Microtask>,
/// Atomics.waitAsync waiter 表:键 = (缓冲对象指针, 元素字节偏移),值 =
/// 登记的 promise FIFO。键取登记时刻视图的 `buffer` 现指针(同 run 无 GC 时
/// notify 侧读同一指针恒匹配);run 边界清空,清位后 promise 无强根自然回收。
pub(crate) atomics_waiters: HashMap<(u64, usize), Vec<JsValue>>,
pub math_rng_state: u64,
/// 子模块平表的表代际注册表:键 = 表代际(`current_gen` 为当前 run 正在
/// 装载的代际)。函数对象创建时记录自身所属代际(`JsObject::table_gen`),
+9
View File
@@ -216,4 +216,13 @@ impl oxide_runtime_api::VmHost for Vm {
fn symbol_key_for_id(&self, idx: u32) -> Option<String> {
self.symbols.key_for_id(idx)
}
fn atomics_new_waiter_promise(&mut self) -> JsValue {
self.atomics_new_waiter_promise()
}
fn atomics_register_waiter(&mut self, buffer: *mut JsObject, offset: usize, promise: JsValue) {
self.atomics_register_waiter(buffer, offset, promise)
}
fn atomics_wake_waiters(&mut self, buffer: *mut JsObject, offset: usize, count: f64) -> usize {
self.atomics_wake_waiters(buffer, offset, count)
}
}
+4
View File
@@ -67,6 +67,7 @@ impl Vm {
async_generator_proto: P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())),
async_generator_function_proto: P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())),
job_queue: VecDeque::new(),
atomics_waiters: std::collections::HashMap::new(),
math_rng_state: 0,
// gen 0 预登记空表占位:函数对象恒在 run 内创建(彼时 current_gen
// ≥ 1),gen 0 表只是首 run 前路径的占位,首 run 边界即被回收。
@@ -199,6 +200,7 @@ impl Vm {
async_generator_proto: P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())),
async_generator_function_proto: P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())),
job_queue: VecDeque::new(),
atomics_waiters: std::collections::HashMap::new(),
math_rng_state: 0,
// gen 0 预登记空表占位:函数对象恒在 run 内创建(彼时 current_gen
// ≥ 1),gen 0 表只是首 run 前路径的占位,首 run 边界即被回收。
@@ -544,6 +546,8 @@ impl Vm {
self.inline_reg_pool = None;
// 微任务队列是执行期状态:跨 run 不保留。
self.job_queue.clear();
// Atomics waiter 表同属执行期状态:未结算 promise 清位后无强根。
self.atomics_waiters.clear();
}
/// 轻量重置:清空执行状态并回收 epoch 内存,但保留 session 字符串与 builtin。