rustparallel-processingunsafeunsafemutablepointer

How to make multiple mutable references to the same element in an array using unsafe code?


I am trying to make a prime sieve in Rust.

I need several mutable references to the same element of the array, not mutable references to different parts of the array.

For the way this works, I know data races are not a relevant problem, so multiple mutable references are acceptable, but the Rust compiler does not accept my unsafe code.

I am using crossbeam 0.8.0.

fn extend_helper(
    primes: &Vec<usize>,
    true_block: &mut Vec<bool>,
    segment_min: usize,
    segment_len: usize,
) {
    crossbeam::scope(|scope| {
        for prime in primes {
            let prime = prime.clone();
            let segment_min = &segment_min;
            let segment_len = &segment_len;
            let shared = unsafe { &mut *true_block };

            scope.spawn(move |_| {
                let tmp = smallest_multiple_of_n_geq_m(prime, *segment_min) - *segment_min;
                for j in (tmp..*segment_len).step_by(prime) {
                    shared[j] = false;
                }
            });
        }
    })
    .unwrap();
}

fn smallest_multiple_of_n_geq_m(n: usize, m: usize) -> usize {
    m + ((n - (m % n)) % n)
}
error[E0499]: cannot borrow `*true_block` as mutable more than once at a time
  --> src/lib.rs:12:35
   |
7  |       crossbeam::scope(|scope| {
   |                         ----- has type `&Scope<'1>`
...
12 |               let shared = unsafe { &mut *true_block };
   |                                     ^^^^^^^^^^^^^^^^ `*true_block` was mutably borrowed here in the previous iteration of the loop
13 | 
14 | /             scope.spawn(move |_| {
15 | |                 let tmp = smallest_multiple_of_n_geq_m(prime, *segment_min) - *segment_min;
16 | |                 for j in (tmp..*segment_len).step_by(prime) {
17 | |                     shared[j] = false;
18 | |                 }
19 | |             });
   | |______________- argument requires that `*true_block` is borrowed for `'1`

warning: unnecessary `unsafe` block
  --> src/lib.rs:12:26
   |
12 |             let shared = unsafe { &mut *true_block };
   |                          ^^^^^^ unnecessary `unsafe` block
   |
   = note: `#[warn(unused_unsafe)]` on by default

How I should write the unsafe in a way that Rust accepts?


Solution

  • Rust does not allow that in its model. You want AtomicBool with relaxed ordering.

    This is a somewhat common edge case in most concurrency models - if you have two non-atomic writes of the same value to one location, is that well-defined? In Rust (and C++) it is not, and you need to explicitly use atomic.

    On any platform you care about, the atomic bool store with relaxed ordering will have no impact.


    For a reason why this matters, consider:

    pub fn do_the_thing(x: &mut bool) {
        if !*x { return };
    
        // Do some stuff.
    
        *x = true;
    }
    

    At setting x to true, the compiler is free to assume (due to no shared mutable references) that x is still false. It may implement this assignment as, e.g., inc x in assembly, moving its value from 0 to 1.

    If two threads came through this and both reached *x = true, its value may become something other than 0 or 1, which could violate other assumed invariants.