Home > Blockchain >  Unsafely define struct to share mutable references between threads
Unsafely define struct to share mutable references between threads

Time:10-17

I need to unsafely define a Rust struct that I can share between 2 threads and mutate the content of the struct from both threads.

I do not want to use Mutex nor RwLock because I want to implement the thread safety myself. For performance concerns, I do not want to check the mutex when time I want to access the content and I know it is not in the critical section.

If I only use Arc to share the struct between threads, I get cannot borrow data in an Arc as mutable and help: trait DerefMut is required to modify through a dereference, but it is not implemented for std::sync::Arc<Foo>.

The safe way to do this:

struct Foo {
    bar: usize,
}

impl Foo {
    pub fn set_bar(&mut self, a: usize) {
        self.bar = a;
    }
}

fn main() {
    let mut foo = Foo { bar: 32 };
    foo.bar = 33;

    let foo_arc = std::sync::Arc::new(std::sync::Mutex::new(foo));
    let foo_arc_2 = std::sync::Arc::clone(&foo_arc);

    let handle = std::thread::spawn(move || {
        foo_arc_2.lock().unwrap().set_bar(32);
    });
    foo_arc.lock().unwrap().set_bar(31);
    handle.join().unwrap();
}

What I unsafely want to achieve:

struct Foo {
    bar: usize,

    // My own lock
    // lock: std::sync::Mutex<usize>,
}

unsafe impl Sync for Foo {}

impl Foo {
    pub fn set_bar(&mut self, a: usize) {
        self.bar = a;
    }
}

fn main() {
    let mut foo = Foo { bar: 32 };
    foo.bar = 33;

    let foo_arc = std::sync::Arc::new(foo);
    let foo_arc_2 = std::sync::Arc::clone(&foo_arc);

    let handle = std::thread::spawn(move || {
        foo_arc_2.set_bar(32);
    });
    foo_arc.set_bar(31);
    handle.join().unwrap();
}

I might not have to use Arc and use something more low level unknown to me at the moment.

CodePudding user response:

If you want to do this to later use it in production, don't do it! Many people smarter than you and me already done this correctly. Use what they wrote instead. If you want to do this as an exercise, or for learning purposes, then go ahead and do it.

If you want to provide a type with interior mutability then you must use UnsafeCell. This type is at a core of every interior mutability in rust and using it is the only way to get a &mut T from &T. You should read really carefully it's documentation, the documentation of the cell module and The Nomicon (preferably all of it, but at least concurency chapter).

If you prefer watching videos, Jon Gjengset has, among many others, this amazing video on cell types. And this video on atomic memory and implementing (bad) mutex.

  • Related