1

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?

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Delfin
  • 157
  • 7
  • 2
    *multiple mutable references are acceptable* — no, multiple mutable references to the same value are **never** allowed, period. – Shepmaster May 26 '21 at 00:42
  • It looks like your question might be answered by the answers of [How to get mutable references to two array elements at the same time?](https://stackoverflow.com/q/30073684/155423); [How do I pass disjoint slices from a vector to different threads?](https://stackoverflow.com/q/33818141/155423); If not, please **[edit]** your question to explain the differences. Otherwise, we can mark this question as already answered. – Shepmaster May 26 '21 at 00:44
  • Perhaps [Simultaneous mutable access to arbitrary indices of a large vector that are guaranteed to be disjoint](https://stackoverflow.com/q/55939552/155423) is more obviously applied to your case? – Shepmaster May 26 '21 at 00:49
  • What I need is not mutable references to strictly diferrent parts of the Array, but several mutable references to potencially several times the same element of the array. – Delfin May 26 '21 at 00:53
  • 2
    I'd suggest you change `true_block: &mut Vec` to `true_block: &[AtomicBool]`. (see [Why is it discouraged to accept a reference to a String (&String), Vec (&Vec), or Box (&Box) as a function argument?](https://stackoverflow.com/q/40006219/155423)) – Shepmaster May 26 '21 at 01:10

1 Answers1

4

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.

GManNickG
  • 494,350
  • 52
  • 494
  • 543
  • Using `shared[j].store(false, Ordering::Relaxed)` It will block the thread in some way like Mutex? – Delfin May 26 '21 at 01:28
  • 1
    @Delfin: No, this is an atomic variable. I suggest brushing up on concurrency and threading primitives when you have a chance, as you dive into such code. An atomic acts like a regular variable wrapped in a mutex, so only one thread can modify it at a time - except implemented in hardware so its much faster. If you observe the assembly output, it produces identical output as your regular boolean store, due to relaxed ordering constraints. Generally, you want to leave the ordering constraint as `SeqCst` if you don't know what to do, but in this specific case `Relaxed` is fine. – GManNickG May 26 '21 at 01:33
  • And how i convert my Vec to &[AtomicBool]? – Delfin May 26 '21 at 01:33
  • 1
    @Delfin Well, frankly that's a very basic question. I _highly_ suggest you get some more practice with Rust (via [the Rust book](https://doc.rust-lang.org/book/)) before diving into such complicated code. You can search around for "vec slices" to learn more. – GManNickG May 26 '21 at 01:36
  • 2
    @Delfin The suggestion is to use `Vec` to begin with, and then you get `&[AtomicBool]` by calling `as_slice()` or just applying `&` to the vec. – user4815162342 May 26 '21 at 08:29
  • I just generated a AtomicBool vector. and you & not works. (I just check) – Delfin May 27 '21 at 05:43
  • @Delfin Works for me: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=c311e868fbea4adcbc83febcd0c2a024. Again, you need to take some time to really understand the language more deeply before you try to use it. – GManNickG May 27 '21 at 17:59
  • @GManNickG I have a Vec no a Vec. – Delfin May 28 '21 at 07:19