如何在Rust中从不安全的内存创建原子

时间:2019-07-12 14:28:45

标签: rust

我正在学习不安全的Rust,并试图创建一个由指向某些不安全内存(例如,来自C或内存映射文件的缓冲区)支持的原子。

我尝试过:

use std::sync::atomic::{AtomicI64, Ordering};

fn main() -> () {
    let mut v = vec![1i64, 2i64];
    let ptr = &mut v[0] as *mut i64;
    unsafe {
        let a = std::mem::transmute::<*mut i64, AtomicI64>(ptr);
        println!("{}", a.load(Ordering::Relaxed));
    }
}

但是它显示指针的地址(例如2119547391296)而不是1

在外部缓冲区中创建原子的正确方法是什么?

我想要相同的功能,例如C#Interlocked.CompareExchange(ref *(long*)ptr, ...),所以也许还有其他方法可以在Rust中获得无锁同步原语?

更新

似乎我需要std::intrinsics::{*},但在稳定的Rust中不可用。

更新2:

这将编译并打印1 2 2(即通过指针强制转换创建的v[0]通过预期的更新AtomicI64,然后通过AtomicI64取消引用& *ptr)。但这是正确的吗?

use std::sync::atomic::{AtomicI64, Ordering};

fn main() -> () {
    let v = vec![1i64, 2i64];
    let ptr = &v[0] as *const i64 as *const AtomicI64;

    unsafe {
        let a = & *ptr;
        println!("{}", a.load(Ordering::SeqCst));
        a.fetch_add(1i64, Ordering::SeqCst);
        println!("{}", a.load(Ordering::SeqCst));
        println!("{}", v[0]);
    }
}

2 个答案:

答案 0 :(得分:2)

documentation for AtomicI64这样说:

  

此类型与基础整数类型i64具有相同的内存表示形式。

但是,您正在尝试将 pointer 转换为i64AtomicI64

unsafe {
  let a = std::mem::transmute::<*mut i64, AtomicI64>(ptr);
  //               is a pointer ^^^^^^^^
  //                                      ^^^^^^^^^ is not a pointer
}

相反,您需要将*mut i64转换为指针或对AtomicI64的引用。

这可以这样实现(安全和不安全的变体):

// if we have a mut reference, it must have unqiue ownership over the
// referenced data, so we can safely cast that into an immutable reference
// to AtomicI64
fn make_atomic_i64<'a>(src: &'a mut i64) -> &'a AtomicI64 {
  unsafe {
    &*(src as *mut i64 as *const AtomicI64)
  }
}

// if we have a mut pointer, we have no guarantee of ownership or lifetime, and
// therefore it's unsafe to cast into an immutable reference to AtomicI64
unsafe fn make_ptr_atomic_i64<'a>(src: *mut i64) -> &'a AtomicI64 {
  &*(src as *const AtomicI64)
}

Example

use std::sync::atomic::{AtomicI64, Ordering};

fn main() -> () {
    // declare underlying buffer
    let mut v = vec![1i64, 2i64];

    {
        // get atomic safely
        let atomic = make_atomic_i64(&mut v[0]);

        // try to access atomic
        println!("{}", atomic.swap(10, Ordering::Relaxed)); // = 1
    }

    unsafe {
        // get atomic unsafely
        let atomic = make_ptr_atomic_i64(&mut v[0] as *mut i64);

        // try to access atomic
        println!("{}", atomic.swap(100, Ordering::Relaxed)); // = 10
    }

    // print final state of variable
    println!("{}", v[0]); // = 100
}

答案 1 :(得分:0)

AtomicPtr将完成这项工作。您只能从可变指针构造它,因为它必须拥有指向的数据。

如果您有一个共享/可共享的原始指针(一个常量),则在设计上不能是原子的。如果要共享一个指针,则它必须位于AtomicPtr之后的Arc

提醒一下,可变的引用/指针等于唯一,而常量引用/指针等于共享。如果违反此规则,则您的程序是未定义的行为。