mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-23 18:21:13 +08:00
feat: Atomics 真原子性:SAB 臂改经共享缓冲 SeqCst 原子方法
This commit is contained in:
Generated
+19
-19
@@ -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",
|
||||
|
||||
+1
-1
@@ -23,7 +23,7 @@ members = [
|
||||
]
|
||||
|
||||
[workspace.package]
|
||||
version = "0.8.81"
|
||||
version = "0.8.82"
|
||||
edition = "2021"
|
||||
license = "MIT"
|
||||
rust-version = "1.80"
|
||||
|
||||
@@ -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<H: VmHost>(
|
||||
vm: &mut H, store: &BufferStoreMut, kind: TypedArrayKind, offset: usize,
|
||||
) -> Option<JsValue> {
|
||||
fn atomic_read<H: VmHost>(vm: &mut H, store: &BufferStoreMut, kind: TypedArrayKind, offset: usize) -> Option<JsValue> {
|
||||
// SAFETY: store 经 resolve_atomic_access 校验为存活载荷,只读字节切片。
|
||||
let bytes: &[u8] = match store {
|
||||
BufferStoreMut::Ab(b) => b,
|
||||
@@ -174,6 +172,149 @@ fn arithmetic_write_value<H: VmHost>(
|
||||
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<H: VmHost>(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<H: VmHost>(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<H: VmHost>(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<H: VmHost>(
|
||||
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<H: VmHost>(
|
||||
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<H: VmHost>(
|
||||
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<H: VmHost>(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<H: VmHost>(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<H: VmHost>(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<H: VmHost>(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<H: VmHost>(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<H: VmHost>(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<H: VmHost>(vm: &mut H, args: &[u8], op: &str) -> NativeResu
|
||||
|
||||
/// `Atomics.compareExchange(typedArray, index, expected, replacement)`:读旧值;
|
||||
/// 相等(数值宽度 `==`、BigInt 宽度 BigInt 相等)则写 replacement;返回旧值。
|
||||
/// SAB 臂走位模式原子比较交换(比较位模式,两臂均返旧值),AB 臂保持读-改-写
|
||||
///(零行为)。
|
||||
pub fn atomics_compare_exchange<H: VmHost>(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<H: VmHost>(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<JsValue, String> {
|
||||
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");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<KernelCore>,
|
||||
}
|
||||
|
||||
// 九操作乘四宽度 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);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user