绕过Rust中的线程安全性,使用可变指针

huangapple go评论54阅读模式
英文:

Circumventing thread safety in rust with mutable pointers

问题

I have a performance critical section of my rust code which I currently try to get faster with unsafe code. In this approach I grantee thread safety manually with barriers and separated index ranges for all threads in the vectors, so I do not want to compiler to check thread safety.

#[derive(Clone, Copy)]
struct CgPtrSet {
    a: *const SparseMatrix,
    b: *const DenseVector,
    x: *mut DenseVector,
    d: *mut DenseVector,
    r: *mut DenseVector,
    z: *mut DenseVector,
    accuracy: *mut f64,
    iterations: *mut usize,
}

fn cg_barrier_thread_worker(&self, ptrs: CgPtrSet, start: usize, end: usize) {
    unsafe {
        // ...
    }
}

This works well while done in one thread:

self.cg_barrier_thread_worker(ptrs, start, end);

Now I run it in its own thread:

let thread_handle = thread::spawn(|| {
    self.cg_barrier_thread_worker(ptrs, start, end);
});
thread_handle.join().unwrap();

That gives me the compiler output:

error[E0277]: `*const sparse_matrix::SparseMatrix` cannot be shared between threads safely
error[E0277]: `*const dense_vector::DenseVector` cannot be shared between threads safely

...and so on for all members of CgPrtSet

I have tried to solve the issue using UnsafeCell with no success. How can I "convince" the compiler that I know my use of the object is thread-safe?

英文:

I have a performance critical section of my rust code which I currently try to get faster with unsafe code. In this approach I grantee thread safety manually with barriers and separated index ranges for all threads in the vectors, so I do not want to compiler to check thread safety.

#[derive(Clone, Copy)]
struct CgPtrSet {
    a: *const SparseMatrix,
    b: *const DenseVector,
    x: *mut DenseVector,
    d: *mut DenseVector,
    r: *mut DenseVector,
    z: *mut DenseVector,
    accuracy: *mut f64,
    iterations: *mut usize,
}

fn cg_barrier_thread_worker(&self, ptrs: CgPtrSet, start: usize, end: usize) {
    unsafe {
        ...
        }
}

This works well while done in one thread:

self.cg_barrier_thread_worker(ptrs, start, end);

Now I run it in its own thread:

let thread_handle = thread::spawn(|| {
    self.cg_barrier_thread_worker(ptrs, start, end);
});
thread_handle.join().unwrap();

That get me the compiler output:

error[E0277]: `*const sparse_matrix::SparseMatrix` cannot be shared between threads safely
error[E0277]: `*const dense_vector::DenseVector` cannot be shared between threads safely

...and so on for all members of CgPrtSet

I have tried to solve the issue using UnsafeCell with no success. How can I “convince” the compiler that I know my use of the object is thread save?

答案1

得分: 0

// 让我们制作一个最小的结构示例,其中包含一个指针
struct HoldsRawPtr {
    ptr: *const u8,
}

// 通过实现 Send 特性,我们告诉编译器这种类型可以在线程之间传递而不违反安全性。它是否真的安全由我们负责
unsafe impl Send for HoldsRawPtr {}
// Sync 特性告诉编译器`&T`在线程之间共享是安全的
unsafe impl Sync for HoldsRawPtr {}

fn main() {
    let ptr = HoldsRawPtr {
        ptr: std::ptr::null(),
    };

    // 现在可以这样做
    std::thread::spawn(move || {
        // 使用 ptr 做一些操作
        let _moved_ptr = ptr;
    });

    // 这里您可以看到 std::thread::spawn 的签名
    pub fn spawn<F, T>(f: F) -> std::thread::JoinHandle<T&gt
    where
        // 闭包的发送约束要求所有捕获的值都是可发送的
        // 因此,HoldsRawPtr 上不需要 Sync 约束,尽管如果您将其包装在 Arc 中,就需要 Sync 约束
        F: FnOnce() -> T + Send + 'static,
        T: Send + 'static,
    {
        unreachable!("...")
    }
}
英文:
// lets make minimal example of struct holding a pointer
struct HoldsRawPtr {
    ptr: *const u8,
}

// By implementing the Send trait, we tell compiler that this type can be transferred between
// threads without violating safety. Whether it actually is safe is our responsibility
unsafe impl Send for HoldsRawPtr {}
// Sync trait tells compiler that `&T` is safe to share between threads
unsafe impl Sync for HoldsRawPtr {}

fn main() {
    let ptr = HoldsRawPtr {
        ptr: std::ptr::null(),
    };

    // This is now possible
    std::thread::spawn(move || {
        // do something with ptr
        let _moved_ptr = ptr;
    });

    // here you can see signature of std::thread::spawn
    pub fn spawn<F, T>(f: F) -> std::thread::JoinHandle<T>
    where
        // send bound on closure requires that all captures are send
        // so we don't need Sync bound on HoldsRawPtr, though if you wrapped it in Arc,
        // you would need Sync bound
        F: FnOnce() -> T + Send + 'static,
        T: Send + 'static,
    {
        unreachable!("...")
    }
}

huangapple
  • 本文由 发表于 2023年4月6日 22:28:32
  • 转载请务必保留本文链接:https://go.coder-hub.com/75950700.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定