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

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

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.

  1. #[derive(Clone, Copy)]
  2. struct CgPtrSet {
  3. a: *const SparseMatrix,
  4. b: *const DenseVector,
  5. x: *mut DenseVector,
  6. d: *mut DenseVector,
  7. r: *mut DenseVector,
  8. z: *mut DenseVector,
  9. accuracy: *mut f64,
  10. iterations: *mut usize,
  11. }
  12. fn cg_barrier_thread_worker(&self, ptrs: CgPtrSet, start: usize, end: usize) {
  13. unsafe {
  14. // ...
  15. }
  16. }

This works well while done in one thread:

  1. self.cg_barrier_thread_worker(ptrs, start, end);

Now I run it in its own thread:

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

That gives me the compiler output:

  1. error[E0277]: `*const sparse_matrix::SparseMatrix` cannot be shared between threads safely
  2. 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.

  1. #[derive(Clone, Copy)]
  2. struct CgPtrSet {
  3. a: *const SparseMatrix,
  4. b: *const DenseVector,
  5. x: *mut DenseVector,
  6. d: *mut DenseVector,
  7. r: *mut DenseVector,
  8. z: *mut DenseVector,
  9. accuracy: *mut f64,
  10. iterations: *mut usize,
  11. }
  12. fn cg_barrier_thread_worker(&self, ptrs: CgPtrSet, start: usize, end: usize) {
  13. unsafe {
  14. ...
  15. }
  16. }

This works well while done in one thread:

  1. self.cg_barrier_thread_worker(ptrs, start, end);

Now I run it in its own thread:

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

That get me the compiler output:

  1. error[E0277]: `*const sparse_matrix::SparseMatrix` cannot be shared between threads safely
  2. 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

  1. // 让我们制作一个最小的结构示例,其中包含一个指针
  2. struct HoldsRawPtr {
  3. ptr: *const u8,
  4. }
  5. // 通过实现 Send 特性,我们告诉编译器这种类型可以在线程之间传递而不违反安全性。它是否真的安全由我们负责
  6. unsafe impl Send for HoldsRawPtr {}
  7. // Sync 特性告诉编译器`&T`在线程之间共享是安全的
  8. unsafe impl Sync for HoldsRawPtr {}
  9. fn main() {
  10. let ptr = HoldsRawPtr {
  11. ptr: std::ptr::null(),
  12. };
  13. // 现在可以这样做
  14. std::thread::spawn(move || {
  15. // 使用 ptr 做一些操作
  16. let _moved_ptr = ptr;
  17. });
  18. // 这里您可以看到 std::thread::spawn 的签名
  19. pub fn spawn<F, T>(f: F) -> std::thread::JoinHandle<T&gt
  20. where
  21. // 闭包的发送约束要求所有捕获的值都是可发送的
  22. // 因此,HoldsRawPtr 上不需要 Sync 约束,尽管如果您将其包装在 Arc 中,就需要 Sync 约束
  23. F: FnOnce() -> T + Send + 'static,
  24. T: Send + 'static,
  25. {
  26. unreachable!("...")
  27. }
  28. }
英文:
  1. // lets make minimal example of struct holding a pointer
  2. struct HoldsRawPtr {
  3. ptr: *const u8,
  4. }
  5. // By implementing the Send trait, we tell compiler that this type can be transferred between
  6. // threads without violating safety. Whether it actually is safe is our responsibility
  7. unsafe impl Send for HoldsRawPtr {}
  8. // Sync trait tells compiler that `&T` is safe to share between threads
  9. unsafe impl Sync for HoldsRawPtr {}
  10. fn main() {
  11. let ptr = HoldsRawPtr {
  12. ptr: std::ptr::null(),
  13. };
  14. // This is now possible
  15. std::thread::spawn(move || {
  16. // do something with ptr
  17. let _moved_ptr = ptr;
  18. });
  19. // here you can see signature of std::thread::spawn
  20. pub fn spawn<F, T>(f: F) -> std::thread::JoinHandle<T>
  21. where
  22. // send bound on closure requires that all captures are send
  23. // so we don't need Sync bound on HoldsRawPtr, though if you wrapped it in Arc,
  24. // you would need Sync bound
  25. F: FnOnce() -> T + Send + 'static,
  26. T: Send + 'static,
  27. {
  28. unreachable!("...")
  29. }
  30. }

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:

确定