Mutant Bob
Mutant Bob

Reputation: 3538

how can I accomplish interior mutability(?) in a struct I am moving to another thread?

I have a structure that uses a limited form of dynamic programming. It maintains a cache of previously computed results to speed up later computations. I am currently storing it in a RefCell. I recently started experimenting with multiple threads. Unfortunately I can not seem to Send clone()s of this data structure into a thread::spawn()

error[E0277]: `RefCell<DualKeyHashMap<search_context::CostKey, PlanCostLog>>` cannot be shared between threads safely
   --> src/parallel_astar.rs:30:9
    |
30  |         std::thread::spawn(move || {
    |         ^^^^^^^^^^^^^^^^^^ `RefCell<DualKeyHashMap<search_context::CostKey, PlanCostLog>>` cannot be shared between threads safely
    | 
   ::: /home/thoth/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/thread/mod.rs:624:8
    |
624 |     F: Send + 'static,
    |        ---- required by this bound in `spawn`
    |
    = help: within `SearchContext`, the trait `Sync` is not implemented for `RefCell<DualKeyHashMap<search_context::CostKey, PlanCostLog>>`
    = note: required because it appears within the type `SearchContext`
    = note: required because of the requirements on the impl of `Send` for `&SearchContext`
    = note: required because it appears within the type `[closure@src/parallel_astar.rs:30:28: 42:10]`

Jmb's fix worked fine for my minimal example, but not on the full code, so I had to complicate the example:

use std::cell::RefCell;
use std::thread;

//#[derive(Clone)]
struct Inner {
    pub val: i32,
}

//#[derive(Clone)]
struct Bacon {
    count: RefCell<Inner>,
}

impl Bacon {
    fn incr(&self) {
        self.count.borrow_mut().val += 1;
    }

    fn get(&self) -> i32 {
        self.count.borrow().val
    }
}

fn go(bacon: &Bacon) {
    let handles = (0..10)
        .map(move |_| {
            let b2 = bacon.clone();
            thread::spawn(move || {
                b2.incr();
                println!("{}", b2.get())
            })
        })
        .collect::<Vec<_>>();

    for handle in handles {
        handle.join().unwrap();
    }
}

fn main() {
    let bacon = Bacon {
        count: RefCell::new(Inner{val:0}),
    };
    go(&bacon);
}

I can work around it by cloning the pieces of the SearchContext and building a new one inside the thread. One oddity is that I can clone() the field that is a RefCell, and move that into the thread to construct a new object (even though it is not necessary in my context).

Since some of the comments mention accessing the RefCell from multiple threads: I do not want to access the RefCell from multiple threads. That is why I am cloning and moving the data structure. Every thread should get its own indepent copy of Bacon (or SearchContext)

Upvotes: 1

Views: 337

Answers (1)

Mutant Bob
Mutant Bob

Reputation: 3538

It turns out the real problem is that the Bacon does not implement Clone.

The error message the compiler gives does not point at this problem, so it took me a while to check for it. I'm not sure why it didn't give an error about the call to bacon.clone() . Maybe the one error short-circuited error checking of the rest of the closure.

Upvotes: 1

Related Questions