How can I guarantee that a type that does not implement Sync can be safely split between threads?

I have code that creates RefCell and then wants to pass a reference to this RefCell to a single stream:

 extern crate crossbeam; use std::cell::RefCell; fn main() { let val = RefCell::new(1); crossbeam::scope(|scope| { scope.spawn(|| *val.borrow()); }); } 

In the full code, I use a type with RefCell embedded in it ( typed_arena::Arena ). I use crossbeam to ensure that the thread does not survive the required link.

This results in an error:

 error: the trait bound `std::cell::RefCell<i32>: std::marker::Sync` is not satisfied [E0277] scope.spawn(|| *val.borrow()); ^~~~~ 

I suppose I understand why this error occurs: RefCell not designed to be called from multiple threads at the same time, and since it uses internal variability, the normal mechanism for requesting one changed borrow will not prevent several parallel actions. This is even described on Sync :

Types that are not Sync are those that have "internal mutability" in an unsafe way, for example Cell and RefCell in std::cell .

This is good and good, but in this case, I know that only one thread has access to RefCell . How can I confirm to the compiler that I understand what I am doing and I guarantee that this is so? Of course, if my reasoning that this is actually safe is incorrect, I would be more than happy if they tell you why.

+5
source share
2 answers

Well, one way would be to use a wrapper with unsafe impl Sync :

 extern crate crossbeam; use std::cell::RefCell; fn main() { struct Wrap(RefCell<i32>); unsafe impl Sync for Wrap {}; let val = Wrap(RefCell::new(1)); crossbeam::scope(|scope| { scope.spawn(|| *val.0.borrow()); }); } 

So, as usual, with unsafe , now you need to ensure that the internal RefCell will never really be available from multiple threads at the same time. As far as I understand, this should be enough so as not to cause a data race.

+2
source

Another solution is to move the volatile element reference to the stream, although volatility is not required. Since there can only be one modified link, the compiler knows that it is safe to use in another thread.

 extern crate crossbeam; use std::cell::RefCell; fn main() { let mut val = RefCell::new(1); let val2 = &mut val; crossbeam::scope(|scope| { scope.spawn(move || *val2.borrow()); }); } 
+6
source

Source: https://habr.com/ru/post/1247220/


All Articles