Vee*_*rac 5 casting atomic undefined-behavior rust
我写了一些代码.它有效......但它安全吗?
use std::mem;
use std::ptr;
use std::marker::PhantomData;
struct Atomic<T: Copy>(AtomicUsize, PhantomData<T>);
impl<T: Copy> Atomic<T> {
unsafe fn encode(src: T) -> usize {
assert!(mem::size_of::<T>() <= mem::size_of::<usize>());
let mut dst = 0;
ptr::write(&mut dst as *mut usize as *mut T, src);
dst
}
unsafe fn decode(src: usize) -> T {
assert!(mem::size_of::<T>() <= mem::size_of::<usize>());
ptr::read(&src as *const usize as *const T)
}
fn new(val: T) -> Atomic<T> {
unsafe {
Atomic(AtomicUsize::new(Self::encode(val)), PhantomData)
}
}
fn load(&self, order: Ordering) -> T {
unsafe { Self::decode(self.0.load(order)) }
}
fn store(&self, val: T, order: Ordering) {
unsafe { self.0.store(Self::encode(val), order) }
}
}
impl<T: Copy + Default> Default for Atomic<T> {
fn default() -> Atomic<T> {
Self::new(T::default())
}
}
Run Code Online (Sandbox Code Playgroud)
正如你所看到的,我将一个Copy足够小的任意值写入a中usize,并将其运送到一个Atomic.然后我把它作为一个新值读出来.
本质上,我使用usize大小作为内存块size_of::<usize>().
如果这是安全的,下一步是考虑更高级的操作.
unsafe trait PackedInt {}
unsafe impl PackedInt for u8 {}
unsafe impl PackedInt for i8 {}
unsafe impl PackedInt for u32 {}
unsafe impl PackedInt for i32 {}
unsafe impl PackedInt for u64 {}
unsafe impl PackedInt for i64 {}
impl<T: Copy + PackedInt> Atomic<T> {
fn compare_and_swap(&self, current: T, new: T, order: Ordering) -> T {
unsafe {
Self::decode(self.0.compare_and_swap(
Self::encode(current),
Self::encode(new),
order
))
}
}
fn fetch_add(&self, val: T, order: Ordering) -> T {
unsafe {
Self::decode(self.0.fetch_add(Self::encode(val), order))
}
}
fn fetch_sub(&self, val: T, order: Ordering) -> T {
unsafe {
Self::decode(self.0.fetch_sub(Self::encode(val), order))
}
}
}
Run Code Online (Sandbox Code Playgroud)
这些当然在溢出时并不总是特别明智(因为两个"相等"的值可能比较由于位之外的位而不相等T),但它们看起来仍然很明确......我想.
那么,这是安全的,为什么?
这几乎是安全的……但也不完全安全。您可能只考虑人们使用Atomic整数和浮点数,但引用也是Copy. 用户使用宽松的负载和存储很容易导致崩溃Atomic<&&u32>。
顺便说一句,您的fetch_add和fetch_sub无法在大端系统上正常工作。