From c568eed76970261c30c3ce404094e816519c12c4 Mon Sep 17 00:00:00 2001 From: pjh456 <147148383@qq.com> Date: Wed, 23 Sep 2026 21:17:43 +0000 Subject: [PATCH] =?UTF-8?q?feat:=20Atomics.waitAsync/pause=20=E5=BC=82?= =?UTF-8?q?=E6=AD=A5=E8=87=82=E4=B8=8E=20notify=20=E5=94=A4=E9=86=92?= =?UTF-8?q?=E9=9D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- crates/oxide_builtins/src/atomics.rs | 86 +++++++++- crates/oxide_runtime_api/src/host.rs | 14 ++ crates/oxide_vm/src/atomics.rs | 63 ++++++++ crates/oxide_vm/src/bindings/bind_atomics.rs | 2 + crates/oxide_vm/src/lib.rs | 1 + crates/oxide_vm/src/vm/gc_hooks.rs | 12 ++ crates/oxide_vm/src/vm/mod.rs | 4 + crates/oxide_vm/src/vm/vmhost.rs | 9 ++ crates/oxide_vm/src/vm_support.rs | 4 + .../tests/builtins_atomics_wait_tests.rs | 150 ++++++++++++++++++ 10 files changed, 338 insertions(+), 7 deletions(-) create mode 100644 crates/oxide_vm/src/atomics.rs diff --git a/crates/oxide_builtins/src/atomics.rs b/crates/oxide_builtins/src/atomics.rs index 5728af0a..a3cb342f 100644 --- a/crates/oxide_builtins/src/atomics.rs +++ b/crates/oxide_builtins/src/atomics.rs @@ -4,7 +4,8 @@ //! 阻塞、无 waiter 面);宽度截断与元素写路径(`write_element`)结构同构。 use num_bigint::BigInt; -use oxide_types::object::TypedArrayKind; +use oxide_kernel::shape_forge::EMPTY_SHAPE_ID; +use oxide_types::object::{JsObject, TypedArrayKind}; use oxide_types::value::JsValue; use crate::array_buffer::{buffer_payload_ptr, ArrayBufferPayload}; @@ -243,19 +244,26 @@ pub fn atomics_wait(vm: &mut H, args: &[u8]) -> NativeResult { /// `Atomics.notify(typedArray, index, count)`:值验证后 count 经 /// ToIntegerOrInfinity(毒传播,NaN → +∞、有限负值 → 0);非 SAB 提前返 0 -/// (先于本早返的越界/类型异常照常抛出);单线程无 waiter 面,恒返 0。 +/// (先于本早返的越界/类型异常照常抛出);SAB 臂唤醒 (缓冲, 偏移) 处登记的 +/// waitAsync waiter,返唤醒数(无登记恒 0)。 pub fn atomics_notify(vm: &mut H, args: &[u8]) -> NativeResult { let ta_val = arg_at(vm, args, 1); let index_val = arg_at(vm, args, 2); let count_val = arg_at(vm, args, 3); - let (_payload_ptr, _view, _offset, is_sab) = native_try!(resolve_atomic_wait_access(vm, ta_val, index_val, false)); - // count 归一:单线程下无观察差,只承载毒传播(语料钉:非 SAB 亦先评估 - // count 再返 0)。 - native_try!(ta_to_integer_or_infinity(vm, count_val)); + let (_payload_ptr, view, offset, is_sab) = native_try!(resolve_atomic_wait_access(vm, ta_val, index_val, false)); + // count 实参缺省 → +∞(全唤醒);显式传入才走强转(毒传播语料钉: + // 非 SAB 亦先评估 count 再返 0)。 + let count = if args.len() > 3 { + native_try!(ta_to_integer_or_infinity(vm, count_val)) + } else { + f64::INFINITY + }; if !is_sab { return NativeResult::Ok(JsValue::int(0)); } - NativeResult::Ok(JsValue::int(0)) + // 与登记侧同一读径取缓冲现指针,同 run 无 GC 时恒匹配。 + let buffer_ptr = view.buffer.as_js_object_ptr(); + NativeResult::Ok(JsValue::int(vm.atomics_wake_waiters(buffer_ptr, offset, count) as i32)) } /// `Atomics.load(typedArray, index)`:读指定索引元素。 @@ -357,3 +365,67 @@ pub fn atomics_is_lock_free(vm: &mut H, args: &[u8]) -> NativeResult let n = native_try!(ta_to_integer_or_infinity(vm, size_val)); NativeResult::Ok(JsValue::bool(matches!(n, 1.0 | 2.0 | 4.0 | 8.0))) } + +/// waitAsync 结果对象:普通对象(proto = Object.prototype),own 属性 +/// async/value 双 data 属性(普通创建路径默认形)。 +fn wait_async_result_object(vm: &mut H, async_arm: bool, value: JsValue) -> JsValue { + let object_proto = vm.session().builtin_world().object_proto.as_ptr() as *mut JsObject; + let obj_ptr = vm.alloc_object(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::from_js_object(object_proto))); + // SAFETY: alloc_object 返回存活 arena 指针,借出期间无别名。 + let obj = unsafe { &mut *obj_ptr }; + let async_si = vm.string_key_si("async"); + let value_si = vm.string_key_si("value"); + vm.set_or_create_prop_value(obj, async_si, JsValue::bool(async_arm)); + vm.set_or_create_prop_value(obj, value_si, value); + JsValue::from_js_object(obj_ptr) +} + +/// `Atomics.waitAsync(typedArray, index, value, timeout)`:值强转(毒传播) +/// 后读载荷比较——不等值返 {async:false, value:"not-equal"};等值再归一 timeout +/// (NaN → +∞、<0 → 0),≤0 返 {async:false, value:"timed-out"};等值且 >0 +/// 建 pending Promise 登记 waiter 表(notify 唤醒结算 "ok"),返 +/// {async:true, value:Promise}。timeout 强转在不等值早返之后(spec 步序)。 +pub fn atomics_wait_async(vm: &mut H, args: &[u8]) -> NativeResult { + let ta_val = arg_at(vm, args, 1); + let index_val = arg_at(vm, args, 2); + let value_val = arg_at(vm, args, 3); + let timeout_val = arg_at(vm, args, 4); + let (payload_ptr, view, offset, _) = native_try!(resolve_atomic_wait_access(vm, ta_val, index_val, true)); + let value = native_try!(ta_element_value(vm, view.kind, value_val)); + let old = atomic_read(vm, payload_ptr, view.kind, offset).unwrap_or(JsValue::undefined()); + let equal = if is_bigint_kind(view.kind) { + let old_bi = vm.bigint_value(old).clone(); + let value_bi = vm.bigint_value(value).clone(); + old_bi.cmp(&value_bi) == std::cmp::Ordering::Equal + } else { + oxide_runtime_api::to_number(old) == oxide_runtime_api::to_number(value) + }; + if !equal { + let str = vm.new_string("not-equal"); + return NativeResult::Ok(wait_async_result_object(vm, false, str)); + } + // 超时 ToNumber 归一:毒传播 + NaN → +∞、负值 → 0。 + let timeout = native_try!( + oxide_runtime_api::to_number_full(timeout_val, vm).map_err(|e| crate::iterator::engine_error(vm, &e)) + ); + let normalized = if timeout.is_nan() { + f64::INFINITY + } else if timeout < 0.0 { + 0.0 + } else { + timeout + }; + if normalized <= 0.0 { + let str = vm.new_string("timed-out"); + return NativeResult::Ok(wait_async_result_object(vm, false, str)); + } + // 登记期取视图 buffer 现指针为键:同 run 无 GC 时 notify 侧同读径恒匹配。 + let promise = vm.atomics_new_waiter_promise(); + vm.atomics_register_waiter(view.buffer.as_js_object_ptr(), offset, promise); + NativeResult::Ok(wait_async_result_object(vm, true, promise)) +} + +/// `Atomics.pause(hint)`:零参语义(不校验、不延迟),任意实参形直返 undefined。 +pub fn atomics_pause(_vm: &mut H, _args: &[u8]) -> NativeResult { + NativeResult::Ok(JsValue::undefined()) +} diff --git a/crates/oxide_runtime_api/src/host.rs b/crates/oxide_runtime_api/src/host.rs index 4396f42a..88cdf689 100644 --- a/crates/oxide_runtime_api/src/host.rs +++ b/crates/oxide_runtime_api/src/host.rs @@ -243,4 +243,18 @@ pub trait VmHost { fn symbol_lookup_global(&self, key: &str) -> Option; fn symbol_register_global(&mut self, key: String, idx: u32); fn symbol_key_for_id(&self, idx: u32) -> Option; + + // Atomics waiter 面(waitAsync 登记 / notify 唤醒) + /// 新建 pending Promise,供 `Atomics.waitAsync` 异步臂的 value 槽;结算不 + /// 经能力闭包,由 notify 唤醒直调引擎结算核。 + fn atomics_new_waiter_promise(&mut self) -> JsValue; + /// 登记 waitAsync waiter:键 = (缓冲对象指针, 元素字节偏移),同键 FIFO 追加。 + /// + /// # 注意事项 + /// `buffer` 须为登记时刻视图的 `buffer` 现指针(与 notify 侧同一读径), + /// 同 run 无 GC 时两侧指针恒匹配。 + fn atomics_register_waiter(&mut self, buffer: *mut JsObject, offset: usize, promise: JsValue); + /// 唤醒 (缓冲, 偏移) 处登记的前 `count` 个 waiter(`<= 0` 不唤醒、`+Inf` + /// 全唤醒),逐个以 "ok" 结算(反应入队随 run 收尾 drain);返回唤醒数。 + fn atomics_wake_waiters(&mut self, buffer: *mut JsObject, offset: usize, count: f64) -> usize; } diff --git a/crates/oxide_vm/src/atomics.rs b/crates/oxide_vm/src/atomics.rs new file mode 100644 index 00000000..7fc33819 --- /dev/null +++ b/crates/oxide_vm/src/atomics.rs @@ -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 = waiters.drain(..n).collect(); + for promise in woken { + let _ = self.fulfill_promise(promise, ok); + } + n + } +} diff --git a/crates/oxide_vm/src/bindings/bind_atomics.rs b/crates/oxide_vm/src/bindings/bind_atomics.rs index df9f7fb4..def6fd12 100644 --- a/crates/oxide_vm/src/bindings/bind_atomics.rs +++ b/crates/oxide_vm/src/bindings/bind_atomics.rs @@ -36,6 +36,8 @@ pub fn bind_atomics(core: &Arc, session: &KernelSession, global: &mu ("isLockFree", oxide_builtins::atomics::atomics_is_lock_free::, 1), ("wait", oxide_builtins::atomics::atomics_wait::, 4), ("notify", oxide_builtins::atomics::atomics_notify::, 3), + ("waitAsync", oxide_builtins::atomics::atomics_wait_async::, 4), + ("pause", oxide_builtins::atomics::atomics_pause::, 0), ); let a_val = JsValue::from_js_object(atomics_ptr); diff --git a/crates/oxide_vm/src/lib.rs b/crates/oxide_vm/src/lib.rs index 935378f0..70f2d1a5 100644 --- a/crates/oxide_vm/src/lib.rs +++ b/crates/oxide_vm/src/lib.rs @@ -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; diff --git a/crates/oxide_vm/src/vm/gc_hooks.rs b/crates/oxide_vm/src/vm/gc_hooks.rs index a2acaf21..ee79f6be 100644 --- a/crates/oxide_vm/src/vm/gc_hooks.rs +++ b/crates/oxide_vm/src/vm/gc_hooks.rs @@ -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; diff --git a/crates/oxide_vm/src/vm/mod.rs b/crates/oxide_vm/src/vm/mod.rs index 6d0f930e..dcd1798a 100644 --- a/crates/oxide_vm/src/vm/mod.rs +++ b/crates/oxide_vm/src/vm/mod.rs @@ -189,6 +189,10 @@ pub struct Vm { pub async_generator_function_proto: P, /// 微任务队列(Promise reactions / thenable 委托),`run()` 末尾 FIFO drain。 pub(crate) job_queue: VecDeque, + /// Atomics.waitAsync waiter 表:键 = (缓冲对象指针, 元素字节偏移),值 = + /// 登记的 promise FIFO。键取登记时刻视图的 `buffer` 现指针(同 run 无 GC 时 + /// notify 侧读同一指针恒匹配);run 边界清空,清位后 promise 无强根自然回收。 + pub(crate) atomics_waiters: HashMap<(u64, usize), Vec>, pub math_rng_state: u64, /// 子模块平表的表代际注册表:键 = 表代际(`current_gen` 为当前 run 正在 /// 装载的代际)。函数对象创建时记录自身所属代际(`JsObject::table_gen`), diff --git a/crates/oxide_vm/src/vm/vmhost.rs b/crates/oxide_vm/src/vm/vmhost.rs index 1167e50b..e8fb2913 100644 --- a/crates/oxide_vm/src/vm/vmhost.rs +++ b/crates/oxide_vm/src/vm/vmhost.rs @@ -216,4 +216,13 @@ impl oxide_runtime_api::VmHost for Vm { fn symbol_key_for_id(&self, idx: u32) -> Option { 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) + } } diff --git a/crates/oxide_vm/src/vm_support.rs b/crates/oxide_vm/src/vm_support.rs index a7058c13..917e30b3 100644 --- a/crates/oxide_vm/src/vm_support.rs +++ b/crates/oxide_vm/src/vm_support.rs @@ -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。 diff --git a/crates/oxide_vm/tests/builtins_atomics_wait_tests.rs b/crates/oxide_vm/tests/builtins_atomics_wait_tests.rs index 60743a65..a2c80bfa 100644 --- a/crates/oxide_vm/tests/builtins_atomics_wait_tests.rs +++ b/crates/oxide_vm/tests/builtins_atomics_wait_tests.rs @@ -253,3 +253,153 @@ fn atomics_wait_notify_name_and_length() { && Atomics.notify.name === 'notify' && Atomics.notify.length === 3", ); } + +/// waitAsync:不等值 → 同步结果对象 {async:false, value:"not-equal"}。 +#[test] +fn eval_waitasync_not_equal() { + let mut vm = Vm::new(); + truthy( + &mut vm, + "(function () { \ + var ta = new Int32Array(new SharedArrayBuffer(8)); \ + ta[0] = 7; \ + var r = Atomics.waitAsync(ta, 0, 8, 100); \ + return r.async === false && r.value === 'not-equal' && !(r.value instanceof Promise); })()", + ); +} + +/// waitAsync:等值 + 归一 timeout ≤ 0 → {async:false, value:"timed-out"}; +/// NaN/缺省 timeout → +∞ 走异步臂。 +#[test] +fn eval_waitasync_timeout_zero() { + let mut vm = Vm::new(); + truthy( + &mut vm, + "(function () { \ + var ta = new Int32Array(new SharedArrayBuffer(8)); \ + ta[0] = 7; \ + var r0 = Atomics.waitAsync(ta, 0, 7, 0); \ + var rneg = Atomics.waitAsync(ta, 0, 7, -5); \ + var rnan = Atomics.waitAsync(ta, 0, 7, NaN); \ + var rdef = Atomics.waitAsync(ta, 0, 7); \ + return r0.async === false && r0.value === 'timed-out' \ + && rneg.async === false && rneg.value === 'timed-out' \ + && rnan.async === true && rnan.value instanceof Promise \ + && rdef.async === true && rdef.value instanceof Promise; })()", + ); +} + +/// waitAsync:等值 + timeout > 0 → {async:true, value:}。 +#[test] +fn eval_waitasync_async_arm() { + let mut vm = Vm::new(); + truthy( + &mut vm, + "(function () { \ + var ta = new Int32Array(new SharedArrayBuffer(8)); \ + ta[0] = 7; \ + var r = Atomics.waitAsync(ta, 0, 7, 100); \ + return r.async === true && r.value instanceof Promise; })()", + ); +} + +/// waitAsync "ok" 结算核心钉:同 run 内 notify 唤醒 → drain 执行 then 处理器 +/// 写 global;跨 run 断言(同 Vm 双段)。 +#[test] +fn eval_waitasync_notify_wakes() { + let mut vm = Vm::new(); + eval( + &mut vm, + "var ta = new Int32Array(new SharedArrayBuffer(8)); \ + ta[0] = 0; \ + var r = Atomics.waitAsync(ta, 0, 0, 100); \ + globalThis.__w2 = Atomics.notify(ta, 0); \ + r.value.then(function (v) { globalThis.__w3 = (v === 'ok'); });", + ) + .unwrap(); + truthy(&mut vm, "globalThis.__w2 === 1 && globalThis.__w3 === true"); +} + +/// waitAsync:非 SAB 视图 → TypeError 同步抛(非 Promise reject),且毒 value +/// 不评估(非 SAB 早返先于值强转)。 +#[test] +fn eval_waitasync_non_sab_throws() { + let mut vm = Vm::new(); + truthy( + &mut vm, + &format!( + "(function () {{ \ + var ta = new Int32Array(new ArrayBuffer(8)); \ + var calls = 0; var poison = {{ valueOf: function () {{ calls = 1; }} }}; \ + return {} && calls === 0; }})()", + throws("Atomics.waitAsync(ta, 0, poison, 1)", "TypeError"), + ), + ); +} + +/// waitAsync:run 边界清位——run1 登记不 notify,run2 同 Vm notify 恒 0。 +#[test] +fn eval_waitasync_per_run_clear() { + let mut vm = Vm::new(); + eval( + &mut vm, + "var ta = new Int32Array(new SharedArrayBuffer(8)); \ + ta[0] = 0; \ + Atomics.waitAsync(ta, 0, 0, 100);", + ) + .unwrap(); + truthy(&mut vm, "Atomics.notify(ta, 0) === 0"); +} + +/// pause:任意实参形(含缺省)恒 undefined。 +#[test] +fn eval_pause_returns_undefined() { + let mut vm = Vm::new(); + truthy( + &mut vm, + "Atomics.pause(undefined) === undefined && Atomics.pause(42) === undefined \ + && Atomics.pause(0) === undefined && Atomics.pause(-0) === undefined \ + && Atomics.pause(9007199254740991) === undefined && Atomics.pause() === undefined", + ); +} + +/// pause:零参语义不校验(true/小数/NaN/串 均不抛)。 +#[test] +fn eval_pause_no_validation() { + let mut vm = Vm::new(); + truthy( + &mut vm, + "Atomics.pause(true) === undefined && Atomics.pause(42.42) === undefined \ + && Atomics.pause(NaN) === undefined && Atomics.pause('42') === undefined \ + && Atomics.pause({}) === undefined", + ); +} + +/// pause 方法元数据钉:length 0、name "pause"。 +#[test] +fn eval_pause_meta() { + let mut vm = Vm::new(); + truthy(&mut vm, "Atomics.pause.length === 0 && Atomics.pause.name === 'pause'"); +} + +/// waitAsync:值强转毒传播(BigInt64 视图配数值/非整串;Int32 视图配 BigInt), +/// 防共享核回归。 +#[test] +fn eval_waitasync_value_coercion() { + let mut vm = Vm::new(); + truthy( + &mut vm, + &format!( + "(function () {{ \ + var ta = new BigInt64Array(new SharedArrayBuffer(8)); \ + ta[0] = 5n; \ + var ab = new Int32Array(new SharedArrayBuffer(4)); \ + ab[0] = 3; \ + return {} && {} && {} && {}; }})()", + throws("Atomics.waitAsync(ta, 0, 0.5, 1)", "TypeError"), + throws("Atomics.waitAsync(ta, 0, null, 1)", "TypeError"), + throws("Atomics.waitAsync(ta, 0, 'x', 1)", "SyntaxError"), + throws("Atomics.waitAsync(ab, 0, 1n, 1)", "TypeError"), + ), + ); +}