What is the preferred way to share an immutable reference between threads?

struct SomethingHeavy;

fn foo(something_heavy: &SomethingHeavy) {
    // share `something_heavy` between multiple threads
}

Now I have foo function, which aims to use multiple threads to handle something_heavy, and will not return until those threads are finished (by calling join(), e.g.).

I think the whole logic is completely safe, since something_heavy is an immutable reference, so there would be no race condition; threads are waited for finishing, which would not lead to use-after-free of something_heavy.

Now, what is the most preferred way to implement this logic? I know I can clone SomethingHeavy to bypass those reference-checking rules, but it may have high costs; and since there is no race condition, Mutex or Arc may be unnecessary.

You can use crossbeam scoped thread.

1 Like

Hyeonu is right, use crossbeam scoped thread, or you can use UnsafeCell, but I don't recommend it.

The rayon crate can also do it.

@Dennis-Zhang While it's true you could do it with unsafe, the UnsafeCell type would not be how you do that.

What I mean is wrap the data with UnsafeCell, then pass the pointers.

Yeah, but you don't need UnsafeCell since it's immutable.

1 Like

Technically Cell is wrapped by UnsafeCell too, so it doesn't matter I guess?

You don't need any type of cell.

You only need to use a cell type when you want to mutate something behind a shared reference. Because the OP isn't wanting to do mutation, a cell would be completely redundant.

1 Like

When working on some code that was doing parallel computations, I found the rayon crate more powerful than crossbeam. Correct me if I'm wrong, but I think crossbeam spawns an OS thread for each call of crossbeam::scope, while rayon works with a thread pool and thus decides on its own how many actual OS threads to spawn for a parallel computation. Therefore, you can use rayon to perform parallel computations on a Vec with thousands of elements without actually spawning thousands of OS threads.

1 Like

Yeah, the two work at different levels of abstraction. Rayon is all about doing jobs in parallel, where you don't actually care which thread your code is being run on (so they can use a thread pool), whereas crossbeam gives you control over threads and lets you pass references from the current thread's stack to another thread.

As a side note, there used to be a similar API to crossbeam::scope in the standard library which used an RAII guard to make sure spawned threads can't access variables after the current thread exits but that strategy was found to be unsound. The issue actually turned into a bit of a watershed moment for Rust's safety guarantees.

1 Like