From c5c1d470fbe17ee5090c4ac8562fd086159acfb8 Mon Sep 17 00:00:00 2001 From: pjh456 <147148383@qq.com> Date: Fri, 9 Oct 2026 02:44:57 +0000 Subject: [PATCH] =?UTF-8?q?feat:=20Atomics=20=E7=9C=9F=E5=8E=9F=E5=AD=90?= =?UTF-8?q?=E6=80=A7=EF=BC=9ASAB=20=E8=87=82=E6=94=B9=E7=BB=8F=E5=85=B1?= =?UTF-8?q?=E4=BA=AB=E7=BC=93=E5=86=B2=20SeqCst=20=E5=8E=9F=E5=AD=90?= =?UTF-8?q?=E6=96=B9=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Cargo.lock | 38 +-- Cargo.toml | 2 +- crates/oxide_builtins/src/atomics.rs | 305 ++++++++++++++++++++++- crates/oxide_kernel/src/shared_buffer.rs | 217 +++++++++++++++- 4 files changed, 530 insertions(+), 32 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 86a05aa1..55fe4627 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1160,7 +1160,7 @@ dependencies = [ [[package]] name = "oxide_builtins" -version = "0.8.81" +version = "0.8.82" dependencies = [ "chrono", "fast-float", @@ -1185,7 +1185,7 @@ dependencies = [ [[package]] name = "oxide_bytecode" -version = "0.8.81" +version = "0.8.82" dependencies = [ "num-bigint", "oxide_log", @@ -1193,7 +1193,7 @@ dependencies = [ [[package]] name = "oxide_cfg" -version = "0.8.81" +version = "0.8.82" dependencies = [ "oxide_bytecode", "oxide_emit", @@ -1204,7 +1204,7 @@ dependencies = [ [[package]] name = "oxide_cli" -version = "0.8.81" +version = "0.8.82" dependencies = [ "ansi_term", "bumpalo", @@ -1226,7 +1226,7 @@ dependencies = [ [[package]] name = "oxide_code_cache" -version = "0.8.81" +version = "0.8.82" dependencies = [ "lru", "oxide_bytecode", @@ -1235,7 +1235,7 @@ dependencies = [ [[package]] name = "oxide_compiler" -version = "0.8.81" +version = "0.8.82" dependencies = [ "oxide_bytecode", "oxide_cfg", @@ -1255,7 +1255,7 @@ dependencies = [ [[package]] name = "oxide_dce" -version = "0.8.81" +version = "0.8.82" dependencies = [ "oxide_bytecode", "oxide_cfg", @@ -1269,7 +1269,7 @@ dependencies = [ [[package]] name = "oxide_emit" -version = "0.8.81" +version = "0.8.82" dependencies = [ "num-bigint", "oxc_allocator", @@ -1284,7 +1284,7 @@ dependencies = [ [[package]] name = "oxide_engine" -version = "0.8.81" +version = "0.8.82" dependencies = [ "oxide_bytecode", "oxide_compiler", @@ -1296,7 +1296,7 @@ dependencies = [ [[package]] name = "oxide_ir" -version = "0.8.81" +version = "0.8.82" dependencies = [ "oxide_bytecode", "oxide_log", @@ -1305,7 +1305,7 @@ dependencies = [ [[package]] name = "oxide_kernel" -version = "0.8.81" +version = "0.8.82" dependencies = [ "bumpalo", "dashmap", @@ -1319,7 +1319,7 @@ dependencies = [ [[package]] name = "oxide_liveness" -version = "0.8.81" +version = "0.8.82" dependencies = [ "oxide_bytecode", "oxide_cfg", @@ -1331,7 +1331,7 @@ dependencies = [ [[package]] name = "oxide_log" -version = "0.8.81" +version = "0.8.82" dependencies = [ "tracing", "tracing-appender", @@ -1340,7 +1340,7 @@ dependencies = [ [[package]] name = "oxide_parser" -version = "0.8.81" +version = "0.8.82" dependencies = [ "oxc_allocator", "oxc_ast", @@ -1355,7 +1355,7 @@ dependencies = [ [[package]] name = "oxide_regalloc" -version = "0.8.81" +version = "0.8.82" dependencies = [ "oxide_bytecode", "oxide_cfg", @@ -1371,7 +1371,7 @@ dependencies = [ [[package]] name = "oxide_runtime_api" -version = "0.8.81" +version = "0.8.82" dependencies = [ "num-bigint", "num-traits", @@ -1384,7 +1384,7 @@ dependencies = [ [[package]] name = "oxide_test262" -version = "0.8.81" +version = "0.8.82" dependencies = [ "oxide_bytecode", "oxide_compiler", @@ -1402,7 +1402,7 @@ dependencies = [ [[package]] name = "oxide_types" -version = "0.8.81" +version = "0.8.82" dependencies = [ "bumpalo", "dashmap", @@ -1415,7 +1415,7 @@ dependencies = [ [[package]] name = "oxide_vm" -version = "0.8.81" +version = "0.8.82" dependencies = [ "bumpalo", "chrono", diff --git a/Cargo.toml b/Cargo.toml index 8842ef90..1a311b87 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,7 +23,7 @@ members = [ ] [workspace.package] -version = "0.8.81" +version = "0.8.82" edition = "2021" license = "MIT" rust-version = "1.80" diff --git a/crates/oxide_builtins/src/atomics.rs b/crates/oxide_builtins/src/atomics.rs index 0aff162a..25dd98ea 100644 --- a/crates/oxide_builtins/src/atomics.rs +++ b/crates/oxide_builtins/src/atomics.rs @@ -5,6 +5,7 @@ use num_bigint::BigInt; use oxide_kernel::shape_forge::EMPTY_SHAPE_ID; +use oxide_kernel::SharedBuffer; use oxide_types::object::{JsObject, TypedArrayKind}; use oxide_types::value::JsValue; @@ -110,15 +111,12 @@ fn resolve_atomic_access<'a, H: VmHost>( return Err(range_error(vm, "invalid indexed access to Atomics")); } // 界判通过,载荷必在场(detached 已越界排除)。 - let store = buffer_store_mut_ptr(obj) - .ok_or_else(|| range_error(vm, "invalid indexed access to Atomics"))?; + let store = buffer_store_mut_ptr(obj).ok_or_else(|| range_error(vm, "invalid indexed access to Atomics"))?; Ok((store, view, offset as usize)) } /// 读指定偏移元素并转为 JS 值(detach 已由 resolve 的越界判收口,载荷恒在场)。 -fn atomic_read( - vm: &mut H, store: &BufferStoreMut, kind: TypedArrayKind, offset: usize, -) -> Option { +fn atomic_read(vm: &mut H, store: &BufferStoreMut, kind: TypedArrayKind, offset: usize) -> Option { // SAFETY: store 经 resolve_atomic_access 校验为存活载荷,只读字节切片。 let bytes: &[u8] = match store { BufferStoreMut::Ab(b) => b, @@ -174,6 +172,149 @@ fn arithmetic_write_value( JsValue::int(r) } +/// SAB 臂探针:共享缓冲(SAB)返回内层共享缓冲借用,AB 臂返 `None`。 +/// 借用止于调用语句内,不跨 JS 调用窗口(与 `resolve_atomic_access` 同边界)。 +fn sab_buffer<'a>(store: &'a BufferStoreMut<'a>) -> Option<&'a SharedBuffer> { + match store { + BufferStoreMut::Sab(buffer) => Some(buffer), + BufferStoreMut::Ab(_) => None, + } +} + +/// 已转换值的位模式:经 `write_element` 写入 8 字节栈上小数组取位模式,完整 +/// 复用元素写路径的宽度截断语义(ToInt8 / ToUint16 / ToInt32 / ToUint32 / +/// BigInt 低 64 位),不重实现截断。 +fn value_bits(vm: &mut H, kind: TypedArrayKind, value: JsValue) -> u64 { + let mut bytes = [0u8; 8]; + write_element(vm, kind, &mut bytes, 0, value); + u64::from_ne_bytes(bytes) +} + +/// 位模式转 JS 值:经 `read_element` 读回位模式(含 BigInt 构造),完整复用 +/// 元素读路径。 +fn bits_to_value(vm: &mut H, kind: TypedArrayKind, bits: u64) -> JsValue { + let bytes = bits.to_ne_bytes(); + read_element(vm, kind, &bytes, 0) +} + +/// SAB 臂 fetch 族运算标签。 +enum SabFetchOp { + Add, + Sub, + And, + Or, + Xor, +} + +/// 按元素宽度分派到共享缓冲对应位宽 fetch 原子方法,返回操作前旧值位模式。 +fn sab_fetch_bits(buf: &SharedBuffer, kind: TypedArrayKind, offset: usize, bits: u64, op: SabFetchOp) -> u64 { + match kind.bytes_per_element() { + 1 => match op { + SabFetchOp::Add => u64::from(buf.fetch_add_u8(offset, bits as u8)), + SabFetchOp::Sub => u64::from(buf.fetch_sub_u8(offset, bits as u8)), + SabFetchOp::And => u64::from(buf.fetch_and_u8(offset, bits as u8)), + SabFetchOp::Or => u64::from(buf.fetch_or_u8(offset, bits as u8)), + SabFetchOp::Xor => u64::from(buf.fetch_xor_u8(offset, bits as u8)), + }, + 2 => match op { + SabFetchOp::Add => u64::from(buf.fetch_add_u16(offset, bits as u16)), + SabFetchOp::Sub => u64::from(buf.fetch_sub_u16(offset, bits as u16)), + SabFetchOp::And => u64::from(buf.fetch_and_u16(offset, bits as u16)), + SabFetchOp::Or => u64::from(buf.fetch_or_u16(offset, bits as u16)), + SabFetchOp::Xor => u64::from(buf.fetch_xor_u16(offset, bits as u16)), + }, + 4 => match op { + SabFetchOp::Add => u64::from(buf.fetch_add_u32(offset, bits as u32)), + SabFetchOp::Sub => u64::from(buf.fetch_sub_u32(offset, bits as u32)), + SabFetchOp::And => u64::from(buf.fetch_and_u32(offset, bits as u32)), + SabFetchOp::Or => u64::from(buf.fetch_or_u32(offset, bits as u32)), + SabFetchOp::Xor => u64::from(buf.fetch_xor_u32(offset, bits as u32)), + }, + _ => match op { + SabFetchOp::Add => buf.fetch_add_u64(offset, bits), + SabFetchOp::Sub => buf.fetch_sub_u64(offset, bits), + SabFetchOp::And => buf.fetch_and_u64(offset, bits), + SabFetchOp::Or => buf.fetch_or_u64(offset, bits), + SabFetchOp::Xor => buf.fetch_xor_u64(offset, bits), + }, + } +} + +/// SAB 臂 load:真原子读。 +fn sab_load(vm: &mut H, buf: &SharedBuffer, kind: TypedArrayKind, offset: usize) -> JsValue { + let bits = match kind.bytes_per_element() { + 1 => u64::from(buf.load_u8(offset)), + 2 => u64::from(buf.load_u16(offset)), + 4 => u64::from(buf.load_u32(offset)), + _ => buf.load_u64(offset), + }; + bits_to_value(vm, kind, bits) +} + +/// SAB 臂 store:位模式原子写,返回写入值(单线程下与现行「写后读回」一致)。 +fn sab_store( + vm: &mut H, buf: &SharedBuffer, kind: TypedArrayKind, offset: usize, value: JsValue, +) -> JsValue { + let bits = value_bits(vm, kind, value); + match kind.bytes_per_element() { + 1 => buf.store_u8(offset, bits as u8), + 2 => buf.store_u16(offset, bits as u16), + 4 => buf.store_u32(offset, bits as u32), + _ => buf.store_u64(offset, bits), + }; + bits_to_value(vm, kind, bits) +} + +/// SAB 臂 exchange:位模式原子交换,返回操作前旧值。 +fn sab_exchange( + vm: &mut H, buf: &SharedBuffer, kind: TypedArrayKind, offset: usize, value: JsValue, +) -> JsValue { + let bits = value_bits(vm, kind, value); + let old_bits = match kind.bytes_per_element() { + 1 => u64::from(buf.swap_u8(offset, bits as u8)), + 2 => u64::from(buf.swap_u16(offset, bits as u16)), + 4 => u64::from(buf.swap_u32(offset, bits as u32)), + _ => buf.swap_u64(offset, bits), + }; + bits_to_value(vm, kind, old_bits) +} + +/// SAB 臂 compareExchange:位模式比较交换,两臂均返回操作前旧值。 +fn sab_compare_exchange( + vm: &mut H, buf: &SharedBuffer, kind: TypedArrayKind, offset: usize, expected: JsValue, replacement: JsValue, +) -> JsValue { + let expected_bits = value_bits(vm, kind, expected); + let replacement_bits = value_bits(vm, kind, replacement); + let old_bits: u64 = match kind.bytes_per_element() { + 1 => { + let r = match buf.compare_exchange_u8(offset, expected_bits as u8, replacement_bits as u8) { + Ok(_) => expected_bits as u8, + Err(actual) => actual, + }; + u64::from(r) + } + 2 => { + let r = match buf.compare_exchange_u16(offset, expected_bits as u16, replacement_bits as u16) { + Ok(_) => expected_bits as u16, + Err(actual) => actual, + }; + u64::from(r) + } + 4 => { + let r = match buf.compare_exchange_u32(offset, expected_bits as u32, replacement_bits as u32) { + Ok(_) => expected_bits as u32, + Err(actual) => actual, + }; + u64::from(r) + } + _ => match buf.compare_exchange_u64(offset, expected_bits, replacement_bits) { + Ok(_) => expected_bits, + Err(actual) => actual, + }, + }; + bits_to_value(vm, kind, old_bits) +} + /// wait/notify 共享验证核:品牌 → {Int32, BigInt64} 宽度守卫 → 越界/detach /// TypeError → 缓冲字节长读取(先于 index 强转,"长度读取"语料钉)→ /// index ToIntegerOrInfinity → 相对字节偏移越界 RangeError。 @@ -228,8 +369,7 @@ fn resolve_atomic_wait_access<'a, H: VmHost>( return Err(range_error(vm, "invalid indexed access to Atomics")); } // 界判通过,载荷必在场(detached 已越界排除)。 - let store = buffer_store_mut_ptr(obj) - .ok_or_else(|| range_error(vm, "invalid indexed access to Atomics"))?; + let store = buffer_store_mut_ptr(obj).ok_or_else(|| range_error(vm, "invalid indexed access to Atomics"))?; Ok((store, view, offset as usize, is_sab)) } @@ -280,33 +420,45 @@ pub fn atomics_notify(vm: &mut H, args: &[u8]) -> NativeResult { NativeResult::Ok(JsValue::int(vm.atomics_wake_waiters(buffer_ptr, offset, count) as i32)) } -/// `Atomics.load(typedArray, index)`:读指定索引元素。 +/// `Atomics.load(typedArray, index)`:读指定索引元素。SAB 臂走真原子读, +/// AB 臂保持读-改-写(零行为)。 pub fn atomics_load(vm: &mut H, args: &[u8]) -> NativeResult { let ta_val = arg_at(vm, args, 1); let index_val = arg_at(vm, args, 2); let (store, view, offset) = native_try!(resolve_atomic_access(vm, ta_val, false, index_val)); + if let Some(buf) = sab_buffer(&store) { + return NativeResult::Ok(sab_load(vm, buf, view.kind, offset)); + } NativeResult::Ok(atomic_read(vm, &store, view.kind, offset).unwrap_or(JsValue::undefined())) } /// `Atomics.store(typedArray, index, value)`:写值后读回返回(宽度归一,-0 → +0)。 +/// SAB 臂走位模式原子写,AB 臂保持读-改-写(零行为)。 pub fn atomics_store(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 (mut store, view, offset) = native_try!(resolve_atomic_access(vm, ta_val, true, index_val)); let value = native_try!(ta_element_value(vm, view.kind, value_val)); + if let Some(buf) = sab_buffer(&store) { + return NativeResult::Ok(sab_store(vm, buf, view.kind, offset, value)); + } atomic_write(vm, &mut store, view.kind, offset, value); NativeResult::Ok(atomic_read(vm, &store, view.kind, offset).unwrap_or(value)) } /// `Atomics.exchange(typedArray, index, value)`:读旧值、写新值、返回旧值。 +/// SAB 臂走位模式原子交换,AB 臂保持读-改-写(零行为)。 pub fn atomics_exchange(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 (mut store, view, offset) = native_try!(resolve_atomic_access(vm, ta_val, true, index_val)); - let old = atomic_read(vm, &store, view.kind, offset).unwrap_or(JsValue::undefined()); let value = native_try!(ta_element_value(vm, view.kind, value_val)); + if let Some(buf) = sab_buffer(&store) { + return NativeResult::Ok(sab_exchange(vm, buf, view.kind, offset, value)); + } + let old = atomic_read(vm, &store, view.kind, offset).unwrap_or(JsValue::undefined()); atomic_write(vm, &mut store, view.kind, offset, value); NativeResult::Ok(old) } @@ -336,12 +488,27 @@ pub fn atomics_xor(vm: &mut H, args: &[u8]) -> NativeResult { atomic_arithmetic(vm, args, "xor") } -/// add/sub/and/or/xor 共享核:读旧值 → 运算值转换 → 读-改-写 → 返回旧值。 +/// add/sub/and/or/xor 共享核:SAB 臂走位模式原子 fetch 族(真原子性,返回 +/// 操作前旧值),AB 臂保持读旧值 → 运算值转换 → 读-改-写 → 返回旧值(零行为)。 fn atomic_arithmetic(vm: &mut H, args: &[u8], op: &str) -> 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 (mut store, view, offset) = native_try!(resolve_atomic_access(vm, ta_val, true, index_val)); + if let Some(buf) = sab_buffer(&store) { + let value = native_try!(ta_element_value(vm, view.kind, value_val)); + let fetch_op = match op { + "add" => SabFetchOp::Add, + "sub" => SabFetchOp::Sub, + "and" => SabFetchOp::And, + "or" => SabFetchOp::Or, + "xor" => SabFetchOp::Xor, + _ => unreachable!("未知原子运算"), + }; + let bits = value_bits(vm, view.kind, value); + let old_bits = sab_fetch_bits(buf, view.kind, offset, bits, fetch_op); + return NativeResult::Ok(bits_to_value(vm, view.kind, old_bits)); + } let old = atomic_read(vm, &store, view.kind, offset).unwrap_or(JsValue::undefined()); let value = native_try!(ta_element_value(vm, view.kind, value_val)); let result = arithmetic_write_value(vm, view.kind, op, old, value); @@ -351,14 +518,20 @@ fn atomic_arithmetic(vm: &mut H, args: &[u8], op: &str) -> NativeResu /// `Atomics.compareExchange(typedArray, index, expected, replacement)`:读旧值; /// 相等(数值宽度 `==`、BigInt 宽度 BigInt 相等)则写 replacement;返回旧值。 +/// SAB 臂走位模式原子比较交换(比较位模式,两臂均返旧值),AB 臂保持读-改-写 +///(零行为)。 pub fn atomics_compare_exchange(vm: &mut H, args: &[u8]) -> NativeResult { let ta_val = arg_at(vm, args, 1); let index_val = arg_at(vm, args, 2); let expected_val = arg_at(vm, args, 3); let replacement_val = arg_at(vm, args, 4); let (mut store, view, offset) = native_try!(resolve_atomic_access(vm, ta_val, true, index_val)); - let old = atomic_read(vm, &store, view.kind, offset).unwrap_or(JsValue::undefined()); let expected = native_try!(ta_element_value(vm, view.kind, expected_val)); + if let Some(buf) = sab_buffer(&store) { + let replacement = native_try!(ta_element_value(vm, view.kind, replacement_val)); + return NativeResult::Ok(sab_compare_exchange(vm, buf, view.kind, offset, expected, replacement)); + } + let old = atomic_read(vm, &store, view.kind, offset).unwrap_or(JsValue::undefined()); let matched = if is_bigint_kind(view.kind) { let old_bi = vm.bigint_value(old).clone(); let expected_bi = vm.bigint_value(expected).clone(); @@ -460,3 +633,113 @@ pub fn atomics_pause(vm: &mut H, args: &[u8]) -> NativeResult { } NativeResult::Ok(JsValue::undefined()) } + +#[cfg(test)] +mod tests { + use super::*; + use oxide_vm::vm::Vm; + + /// JS 求值辅助:单脚本编译执行,返回完成值(与 array_buffer 测试同形)。 + fn eval_atomics(vm: &mut Vm, source: &str) -> Result { + let allocator = oxide_parser::Allocator::default(); + let program = oxide_parser::parse(&allocator, source).map_err(|e| format!("parse: {:?}", e[0].message))?; + let module = oxide_compiler::compiler::Compiler::new() + .compile(&program) + .map_err(|e| format!("compile: {e}"))?; + vm.run(&std::sync::Arc::new(module)) + } + + /// SAB 承载 Uint8Array 上九方法返回值与槽值钉(真原子性面)。 + #[test] + fn sab_nine_methods_u8() { + let mut vm = Vm::new(); + let r = eval_atomics( + &mut vm, + "var sab = new SharedArrayBuffer(8); \ + var ta = new Uint8Array(sab); ta[0] = 5; \ + var r1 = Atomics.load(ta, 0); \ + var r2 = Atomics.store(ta, 0, 10); \ + var r3 = Atomics.exchange(ta, 0, 20); \ + var r4 = Atomics.add(ta, 0, 5); \ + var r5 = Atomics.sub(ta, 0, 5); \ + var r6 = Atomics.and(ta, 0, 0xF0); \ + var r7 = Atomics.or(ta, 0, 1); \ + var r8 = Atomics.xor(ta, 0, 0xFF); \ + [r1, r2, r3, r4, r5, r6, r7, r8, ta[0]].join(',')", + ) + .unwrap(); + assert_eq!(vm.lookup_str(r).unwrap(), "5,10,10,20,25,20,16,17,238"); + } + + /// Uint32 高位边界规范对齐钉:槽值 4294967295 时 add 1 返 4294967295 且 + /// 槽值环绕为 0(SAB 臂原始位路径修复,非回归)。 + #[test] + fn sab_uint32_high_bit_wrap() { + let mut vm = Vm::new(); + let r = eval_atomics( + &mut vm, + "var sab = new SharedArrayBuffer(4); \ + var ta = new Uint32Array(sab); ta[0] = 4294967295; \ + var a = Atomics.add(ta, 0, 1); \ + [a, ta[0]].join(',')", + ) + .unwrap(); + assert_eq!(vm.lookup_str(r).unwrap(), "4294967295,0"); + } + + /// Int32 / BigInt64 / BigUint64 宽度 SAB 臂 add 与槽值钉。 + #[test] + fn sab_int32_and_bigint_widths() { + let mut vm = Vm::new(); + let r = eval_atomics( + &mut vm, + "var r = []; \ + var s1 = new SharedArrayBuffer(8); var i32 = new Int32Array(s1); i32[0] = -100; \ + r.push(Atomics.add(i32, 0, 30)); r.push(i32[0]); \ + var s2 = new SharedArrayBuffer(8); var b64 = new BigInt64Array(s2); b64[0] = 100n; \ + r.push(Atomics.add(b64, 0, 50n)); r.push(b64[0]); \ + var s3 = new SharedArrayBuffer(8); var bu64 = new BigUint64Array(s3); bu64[0] = 10n; \ + r.push(Atomics.add(bu64, 0, 5n)); r.push(bu64[0]); \ + r.join(',')", + ) + .unwrap(); + assert_eq!(vm.lookup_str(r).unwrap(), "-100,-70,100,150,10,15"); + } + + /// compareExchange 匹配臂(写 replacement 返旧值)与不匹配臂(不写返旧值)。 + #[test] + fn sab_compare_exchange_arms() { + let mut vm = Vm::new(); + let r = eval_atomics( + &mut vm, + "var sab = new SharedArrayBuffer(4); var ta = new Uint32Array(sab); ta[0] = 5; \ + var m = Atomics.compareExchange(ta, 0, 5, 9); var slotM = ta[0]; \ + var nm = Atomics.compareExchange(ta, 0, 7, 12); var slotNM = ta[0]; \ + [m, slotM, nm, slotNM].join(',')", + ) + .unwrap(); + assert_eq!(vm.lookup_str(r).unwrap(), "5,9,9,9"); + } + + /// AB 臂零行为钉:同一九方法在 ArrayBuffer 承载 TypedArray 上,结果与 SAB + /// 臂同值(读-改-写路径未动)。 + #[test] + fn ab_arm_zero_behavior() { + let mut vm = Vm::new(); + let r = eval_atomics( + &mut vm, + "var ab = new ArrayBuffer(8); var ta = new Uint8Array(ab); ta[0] = 5; \ + var r1 = Atomics.load(ta, 0); \ + var r2 = Atomics.store(ta, 0, 10); \ + var r3 = Atomics.exchange(ta, 0, 20); \ + var r4 = Atomics.add(ta, 0, 5); \ + var r5 = Atomics.sub(ta, 0, 5); \ + var r6 = Atomics.and(ta, 0, 0xF0); \ + var r7 = Atomics.or(ta, 0, 1); \ + var r8 = Atomics.xor(ta, 0, 0xFF); \ + [r1, r2, r3, r4, r5, r6, r7, r8, ta[0]].join(',')", + ) + .unwrap(); + assert_eq!(vm.lookup_str(r).unwrap(), "5,10,10,20,25,20,16,17,238"); + } +} diff --git a/crates/oxide_kernel/src/shared_buffer.rs b/crates/oxide_kernel/src/shared_buffer.rs index 789747ec..97ff2859 100644 --- a/crates/oxide_kernel/src/shared_buffer.rs +++ b/crates/oxide_kernel/src/shared_buffer.rs @@ -5,7 +5,8 @@ //! - `write_range` 经 `&self` 裸写(字节区归本结构独占,类型不暴露 `&mut [u8]`),多线程并发写安全。 //! - 账目单位是预分配真实上限:构造时计入 `KernelCore::shared_buffer_bytes`,`Drop` 减同值。 -use std::sync::atomic::{AtomicUsize, Ordering}; +use std::mem::size_of; +use std::sync::atomic::{AtomicU16, AtomicU32, AtomicU64, AtomicU8, AtomicUsize, Ordering}; use std::sync::Arc; use crate::KernelCore; @@ -32,6 +33,89 @@ pub struct SharedBuffer { core: Arc, } +// 九操作乘四宽度 SeqCst 原子方法生成宏。 +// +// 对齐前提:`AtomicU{16,32,64}::from_ptr` 要求指针按宽度对齐。字节区经全局 +// 分配器分配(实返 16 字节对齐块),偏移恒为元素字节数倍数(规范保证视图 +// byte_offset 是元素字节数倍数、索引为整数),与既有元素读写路径(`read_element` +// / `write_element` 对 u8 切片做按对齐读)同一前提。 +macro_rules! impl_atomic_width { + ($atomic:ty, $ty:ty, + $load:ident, $store:ident, $swap:ident, $cas:ident, + $fadd:ident, $fsub:ident, $fand:ident, $for:ident, $fxor:ident) => { + /// 读取 `offset` 处的位宽整数值(SeqCst 序)。 + pub fn $load(&self, offset: usize) -> $ty { + debug_assert!(offset + size_of::<$ty>() <= self.len(), "atomic load 越界"); + let ptr = unsafe { self.bytes.as_ptr().add(offset) as *mut $ty }; + // SAFETY: 字节区归本结构独占且类型不暴露 `&mut [u8]`,`from_ptr` + // 不引入别名;对齐前提见宏注记。 + unsafe { <$atomic>::from_ptr(ptr).load(Ordering::SeqCst) } + } + + /// 向 `offset` 处写入位宽整数值(SeqCst 序)。 + pub fn $store(&self, offset: usize, value: $ty) { + debug_assert!(offset + size_of::<$ty>() <= self.len(), "atomic store 越界"); + let ptr = unsafe { self.bytes.as_ptr().add(offset) as *mut $ty }; + unsafe { <$atomic>::from_ptr(ptr).store(value, Ordering::SeqCst) } + } + + /// 交换 `offset` 处值:写入 `value` 并返回操作前旧值(SeqCst 序)。 + pub fn $swap(&self, offset: usize, value: $ty) -> $ty { + debug_assert!(offset + size_of::<$ty>() <= self.len(), "atomic swap 越界"); + let ptr = unsafe { self.bytes.as_ptr().add(offset) as *mut $ty }; + unsafe { <$atomic>::from_ptr(ptr).swap(value, Ordering::SeqCst) } + } + + /// 比较交换:`offset` 处等于 `expected` 时写入 `replacement` 返 + /// `Ok(replacement)`,否则返 `Err(旧值)`(SeqCst 序)。 + pub fn $cas(&self, offset: usize, expected: $ty, replacement: $ty) -> Result<$ty, $ty> { + debug_assert!(offset + size_of::<$ty>() <= self.len(), "atomic compare_exchange 越界"); + let ptr = unsafe { self.bytes.as_ptr().add(offset) as *mut $ty }; + match unsafe { + <$atomic>::from_ptr(ptr).compare_exchange(expected, replacement, Ordering::SeqCst, Ordering::SeqCst) + } { + Ok(_) => Ok(replacement), + Err(actual) => Err(actual), + } + } + + /// 原子加:返回操作前旧值(SeqCst 序,环绕语义)。 + pub fn $fadd(&self, offset: usize, value: $ty) -> $ty { + debug_assert!(offset + size_of::<$ty>() <= self.len(), "atomic fetch_add 越界"); + let ptr = unsafe { self.bytes.as_ptr().add(offset) as *mut $ty }; + unsafe { <$atomic>::from_ptr(ptr).fetch_add(value, Ordering::SeqCst) } + } + + /// 原子减:返回操作前旧值(SeqCst 序,环绕语义)。 + pub fn $fsub(&self, offset: usize, value: $ty) -> $ty { + debug_assert!(offset + size_of::<$ty>() <= self.len(), "atomic fetch_sub 越界"); + let ptr = unsafe { self.bytes.as_ptr().add(offset) as *mut $ty }; + unsafe { <$atomic>::from_ptr(ptr).fetch_sub(value, Ordering::SeqCst) } + } + + /// 原子按位与:返回操作前旧值(SeqCst 序)。 + pub fn $fand(&self, offset: usize, value: $ty) -> $ty { + debug_assert!(offset + size_of::<$ty>() <= self.len(), "atomic fetch_and 越界"); + let ptr = unsafe { self.bytes.as_ptr().add(offset) as *mut $ty }; + unsafe { <$atomic>::from_ptr(ptr).fetch_and(value, Ordering::SeqCst) } + } + + /// 原子按位或:返回操作前旧值(SeqCst 序)。 + pub fn $for(&self, offset: usize, value: $ty) -> $ty { + debug_assert!(offset + size_of::<$ty>() <= self.len(), "atomic fetch_or 越界"); + let ptr = unsafe { self.bytes.as_ptr().add(offset) as *mut $ty }; + unsafe { <$atomic>::from_ptr(ptr).fetch_or(value, Ordering::SeqCst) } + } + + /// 原子按位异或:返回操作前旧值(SeqCst 序)。 + pub fn $fxor(&self, offset: usize, value: $ty) -> $ty { + debug_assert!(offset + size_of::<$ty>() <= self.len(), "atomic fetch_xor 越界"); + let ptr = unsafe { self.bytes.as_ptr().add(offset) as *mut $ty }; + unsafe { <$atomic>::from_ptr(ptr).fetch_xor(value, Ordering::SeqCst) } + } + }; +} + impl SharedBuffer { /// 定长构造:字节序列即活内容,上限即字节序列长度。 /// @@ -135,6 +219,60 @@ impl SharedBuffer { } } } + + // 九操作乘四宽度(u8 / u16 / u32 / u64)SeqCst 原子方法,宏生成。 + impl_atomic_width!( + AtomicU8, + u8, + load_u8, + store_u8, + swap_u8, + compare_exchange_u8, + fetch_add_u8, + fetch_sub_u8, + fetch_and_u8, + fetch_or_u8, + fetch_xor_u8 + ); + impl_atomic_width!( + AtomicU16, + u16, + load_u16, + store_u16, + swap_u16, + compare_exchange_u16, + fetch_add_u16, + fetch_sub_u16, + fetch_and_u16, + fetch_or_u16, + fetch_xor_u16 + ); + impl_atomic_width!( + AtomicU32, + u32, + load_u32, + store_u32, + swap_u32, + compare_exchange_u32, + fetch_add_u32, + fetch_sub_u32, + fetch_and_u32, + fetch_or_u32, + fetch_xor_u32 + ); + impl_atomic_width!( + AtomicU64, + u64, + load_u64, + store_u64, + swap_u64, + compare_exchange_u64, + fetch_add_u64, + fetch_sub_u64, + fetch_and_u64, + fetch_or_u64, + fetch_xor_u64 + ); } impl Drop for SharedBuffer { @@ -244,4 +382,81 @@ mod tests { assert_eq!(len, 8); assert_eq!(buf.as_slice(), &[0, 0, 0, 0, 0xAB, 0xCD, 0, 0]); } + + #[test] + fn fetch_add_atomicity_8_threads() { + let core = new_core(); + let buf = Arc::new(SharedBuffer::new(core, vec![0u8; 4])); + let handles: Vec<_> = (0..8) + .map(|_| { + let buf = Arc::clone(&buf); + std::thread::spawn(move || { + for _ in 0..1000 { + buf.fetch_add_u32(0, 1); + } + }) + }) + .collect(); + for h in handles { + h.join().unwrap(); + } + // 真原子性判别:8 线程各 1000 次自增,终值恰为 8000(读-改-写会丢更新)。 + assert_eq!(buf.load_u32(0), 8000); + } + + #[test] + fn compare_exchange_contention_exactly_one_wins() { + let core = new_core(); + let buf = Arc::new(SharedBuffer::new(core, vec![0u8; 4])); + let handles: Vec<_> = (0..2) + .map(|_| { + let buf = Arc::clone(&buf); + std::thread::spawn(move || match buf.compare_exchange_u32(0, 0, 1) { + Ok(_) => true, + Err(_) => false, + }) + }) + .collect(); + let winners = handles.into_iter().map(|h| h.join().unwrap()).filter(|won| *won).count(); + assert_eq!(winners, 1); + assert_eq!(buf.load_u32(0), 1); + } + + #[test] + fn load_store_cross_thread_visibility() { + let core = new_core(); + let buf = Arc::new(SharedBuffer::new(core, vec![0u8; 8])); + let child = { + let buf = Arc::clone(&buf); + std::thread::spawn(move || { + buf.store_u64(0, 0x1122_3344_5566_7788); + }) + }; + child.join().unwrap(); + assert_eq!(buf.load_u64(0), 0x1122_3344_5566_7788); + } + + #[test] + fn fetch_add_wraps_u8() { + let core = new_core(); + let buf = SharedBuffer::new(core, vec![255u8; 1]); + // 255 + 255 环绕:返回操作前旧值 255,槽值 254。 + let old = buf.fetch_add_u8(0, 255); + assert_eq!(old, 255); + assert_eq!(buf.load_u8(0), 254); + } + + #[test] + fn fetch_add_all_widths() { + let core = new_core(); + let buf = SharedBuffer::new(core, vec![0u8; 16]); + assert_eq!(buf.fetch_add_u8(0, 5), 0); + assert_eq!(buf.fetch_add_u16(2, 7), 0); + assert_eq!(buf.fetch_add_u32(4, 9), 0); + assert_eq!(buf.fetch_add_u64(8, 11), 0); + assert_eq!(buf.load_u8(0), 5); + assert_eq!(buf.load_u16(2), 7); + assert_eq!(buf.load_u32(4), 9); + assert_eq!(buf.load_u64(8), 11); + } }