Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

This slightly modified Rc<()> is Sync, but not Send, right?

I've searched for types that are Sync, but not Send, because it often looks like one trait is a superset of the other one (“every type that implements Sync also implements Send”). I've found this question, but the only real answer is really complicated.

So I've come up with this code:

struct Foo(Rc<()>);  // <-- private field

impl Foo {
    fn my_clone(&mut self) -> Self {  // <-- mutable borrow
        Foo(self.0.clone())
    }
}

I know that the compiler won't automatically implement Send nor Sync for my type; but I'm interested in what I could safely implement manually. I think:

  • It should be able to implement Sync: having an immutable reference to Foo won't let us do anything with it (because we can only call my_clone() via mutable/exclusive reference). And without doing anything, nothing can go wrong, right?

  • It should not be able to implement Send: we can clone our Foo in the main thread (before starting another thread) to get a second object. Now both objects share some memory (the reference count, stored in a Cell<usize>). If I now could send one of those objects to another thread, both threads would have ownership of a Foo, referencing the same memory. Thus both objects could call my_clone() at the same time, leading to simultaneous, unsynchronized, mutable access to the reference count (a data race).

Is this reasoning correct or am I missing something?

like image 627
Lukas Kalbertodt Avatar asked Jan 25 '17 07:01

Lukas Kalbertodt


People also ask

Why is RC not send Rust?

Rc uses non-atomic reference counting. This means that overhead is very low, but an Rc cannot be sent between threads, and consequently Rc does not implement Send . As a result, the Rust compiler will check at compile time that you are not sending Rc s between threads.

What is send and sync?

A type is Send if it is safe to send it to another thread. A type is Sync if it is safe to share between threads (T is Sync if and only if &T is Send).

What is send in trait?

Send means that a type is safe to move from one thread to another. If the same type also implements Copy , this also means that it is safe to copy from one thread to another. Sync means that a type is safe to reference from multiple threads at the same time.


1 Answers

I know that the compiler won't automatically implement Send nor Sync for my type.

Indeed, the compiler automatically implements Send and Sync for you, only when it can determine it is safe to do so.

This little program:

use std::cell::Cell;
use std::sync::atomic::AtomicUsize;

fn ensure_sync<T: Sync>(_: T) {}

struct Automatic(AtomicUsize);

impl Automatic {
    fn new() -> Automatic { Automatic(AtomicUsize::new(0)) }
}

fn main() {
    ensure_sync(AtomicUsize::new(0));
    ensure_sync(Automatic::new());
    ensure_sync(Cell::new(0));
}

Only errors out on the Cell::new(0) line, Automatic is Sync because all its fields are Sync.


Regarding Foo, Rc is neither Sync nor Send, so indeed the compiler will not implement either for you.

Can Foo be Sync?

I believe1 so. As long as NO other operation is added to the module that operate on immutable references. Now or in the future.

Can Foo be Send?

I agree with your conclusion, but I think you missed another method that modifies the Cell: drop.

So, indeed, you seem to have come up with a type that is Sync and not Send, by using an underlying type that is Send and not Sync. It may be my nerd sense, I find it quite amusing :)

1When dealing with unsafe code, I am never sure of anything. It's very easy to fool oneself into thinking something is safe, simply because a tiny little detail escaped attention.

like image 187
Matthieu M. Avatar answered Sep 23 '22 06:09

Matthieu M.