Poor Man's Take on Rust's Sync and Send

Today I refactored a rust project, which involves using a type for rocket's managed state(https://rocket.rs/v0.5-rc/guide/state/#managed-state). The underlying database behind the type is sqlite via rusqlite crate. Before the refactoring, a database file path was used as the managed state to share database access between rocket handlers. Use `String` as a managed state is simple and straightforward - `String` is both `Send` and `Sync`, and does not need to consider lifetime issue. For the refactoring, I want to use a single object of the type as the shared state. It took me some time to solve all the complaints from rust compiler, mainly due to the type I want to use is not `Sync`.

The type is like this:

pub trait BusinessModel {
  // ...omitted
}

struct BackendStore {
    conn: rusqlite::Connection,
}


impl BusinessModel for BackendStore {
  // ...omitted
}

// The type I initially try to put in rocket::State:
type Store = Box<dyn BusinessModel>;

This does not compile - `rusqlite::Connection` is not `Sync` because it has a field of type `RefCell`. So the root of `!Sync` is in the rusqlite crate. I'm not familiar with sqlite's concurrency model and my project does not use any concurrent ability of sqlite, but by common sense it should be **totally reasonable** to implement database connection as `!Sync`. So instead of replacing rusqlite with some other crates like `sqlx`(which provides a `Sync+Send` pool implementation), what firstly came to my head is to see if there is a way to make the `!Sync` type sync. There actually is one - `Mutex`.

`Mutex` has the following implementation regards `Sync` and `Send`:

unsafe impl<T: ?Sized + Send> Send for Mutex<T> {}

unsafe impl<T: ?Sized + Send> Sync for Mutex<T> {}

Which means that it wraps any type of `Send` into `Send + Sync` - which is just what I need. Change the type for rocket state to `Box<Mutex<dyn BusinessModel + Send>>`, everything works.

But this makes me wondering for a while: why `Mutex` **can** wrap `!Sync` type into `Sync`? I went back to read the chapter of concurrency of The Book, and also the document of `Sync` trait - then I understood.

Quote from the document of `Sync` trait:

The precise definition is: a type T is Sync if and only if &T is Send. In other words, if there is no possibility of undefined behavior (including data races) when passing &T references between threads.

Maybe it's just me, but I often feel that formal definition is usually harder to really understand - like 'The precise definition...' of the above quoted document. The **"In other words"** part is essential: it does matter whether the type provides interior mutability or not, as long as it's impossible to cause issues when the immutable references are used in multiple threads, it is `Sync`.

For `Mutex`, the only way the inside owned data can be accessed, is via the `lock` interface. Which means regardless of `Sync` or not the owned type is (whether or not the owned inner type like `RefCell` can be modified via a shared reference), there is always only one thread can access the inner data. This behavior is **exactly** what `Sync` trait means.

But why `Mutex` requires inner type `T: Send` to make itself `Sync`? Let's *assume* the following code compiles (which doesn't):

let rc = Rc::new(1);

let m = Arc::new(Mutex::new(Rc::clone(&rc)));

thread::spawn(move || {
    let guard = m.lock().unwrap();
    let rc_inside = Rc::clone(&guard); // clone
});

let rc2 = Rc::clone(&rc); //clone

The reason of why `Rc` is `!Send` is well documented, makes it a suitable type for the demo here. These two `clone`s try to increase the same underlying reference counter which is non-atomic(which is why `Rc` itself is not`Send`). Now if `Mutex` wraps `!Send` type like `Rc` into a `Sync` type (which then the above code **will** compile), *bad* behavior happened via **shared** reference of `Mutex`, which violates the definition of `Sync` trait.

Anyway, this is what I get from today's refactoring, even maybe it's too obvious for more experienced rust programmers.

---------------------

Published on 2023-02-22

The content for this site is licensed under:

CC-BY-SA