Share reference across threads

I am unable to share a reference between threads.

trait Facade { /*some functions*/ }

struct Client<'a> {
    facade: &'a mut Facade,
    join_grd: thread::JoinGuard<'a()>,
}

impl<'a> Client<'a> {
    pub fn new(my_facade: &'a mut Facade) -> Client<'a> {
        Client {
            facade: my_facade,
	    join_grd: thread::scoped(|| Client::start(my_facade)),
       	}
    }

    fn start(my_facade: &'a mut Facade) { unimplemented!() }
}

Given my newbie status in Rust, I'm getting confused with concepts and errors. How do I achieve the above ?

1 Like

A &mut reference isn't shareable by definition.

3 Likes

So how should i go about it?

There's two problems: 1) the lifetimes are unmanaged, if the thread containing the reference is being run asynchronously with the main thread; the reference may or may not outlive the original, and Rust doesn't allow that. Wrap your data into an Arc container that manages the lifetime with a reference count. 2) Since the data is mutable, accessing it from two threads leads to data races. You have to wrap it into a Mutex too, that prevents ("MUTually EXcludes") the threads accessing it simultaneously. Check this out: Mutex in std::sync - Rust

The main problem here is that you can't safely have both threads have mutable access to the data, because one could modify it while the other is using it.

Either store Facade as &'a Facade to get rid of mutability, or Mutex<&'a Facade> to allow controlled mutability.

@GolDDranks I don't think Arc<> is necessarily required here, as thread::scoped does allow for just passing around references. This is because when the JoinGuard is dropped, the current thread waits for the new thread to stop before continuing, so anything borrowed is valid for the thread's entire life as long as the JoinGuard lives as long as the data.

I got it to work with Arc<Mutex<F>> where F: Facade .. Arc is necessary i think otherwise how would you clone() the mutex to send it to the other thread ?

If you just use scoped, you don't need to clone it, you can have the same reference in both threads.

Here's an example of threading with Mutex without Arc:

Note that this won't work at all if you don't store the JoinHandles somewhere, that's part of the key to getting this to work correctly.

use std::thread;
use std::sync;

fn main() {
    let value = sync::Mutex::new(0u32);

    // we *do* need to store the JoinHandles somewhere for this to work correctly.
    // Storing the handles in this vec ensures that the Mutex will outlive all of the threads.
    let mut thread_storage = Vec::new();
    for _ in 0..30 {
        let handle = thread::scoped(|| {
            // some expensive operation here, which modifies value.
            *value.lock().unwrap() += 1;
        });
        thread_storage.push(handle);
    }
 
    // explicitly wait for all threads to finish before printing result.
    for thread in thread_storage {
        thread.join();
    }
    
    println!("Result: {}", *value.lock().unwrap());
}
1 Like