Why Arc'ed and RwLock'ed type isn't Send and Sync?

Greetings!

I have construction like Arc<RwLock<Option<Channel<Msg>>>> and I can't understand why it isn't Send + Sync. RwLock must make it Sync and Arc must make it Send?

Whole example:

use std::sync::{
    mpsc::{Receiver, Sender},
    Arc,
};
use tokio::sync::RwLock;

struct Channel<Msg: Send + Sync + 'static> {
    tx: Sender<Msg>,
    rx: Receiver<Msg>,
}

struct Data<Msg: Send + Sync + 'static> {
    channel: Arc<RwLock<Option<Channel<Msg>>>>,
}

fn spawn<Msg: Send + Sync>(data: Data<Msg>) {
    tokio::task::spawn(async move {
        let data = data.channel.write().await.take().unwrap();
        while let Ok(msg) = data.rx.recv() {
            drop(msg);
        }
        println!("recv() not ok.")
    });
}

#[tokio::main]
async fn main() {}

Before I address your question, it is very important that you do not use the std channels in async code. Use the tokio::sync::mpsc channel instead. Read this article for more info on why.

Now, to answer your question, the Channel type is Send + !Sync because that's what std::sync::mpsc::Sender is. Now, the RwLock has the following impls:

impl<T: Send> Send for RwLock<T> {}
impl<T: Send + Sync> Sync for RwLock<T> {}

So since T is not Sync, neither is the RwLock. Then, the Arc has the following impl:

impl<T: Send + Sync> Send for Arc<T> {}
impl<T: Send + Sync> Sync for Arc<T> {}

But the RwLock<T> is not Sync, so the Arc becomes neither Send nor Sync.

Unrelated, but as a matter of code design, putting a channel sender or receiver in a mutex or rwlock is almost always a mistake.

4 Likes

This code is beyond of my authority, so I can't change it.
Same for these Sender / Receiver.

Using the std channel in async code is simply not possible. If the channels come from a library, then you're going to have to spawn a dedicated thread for managing the channels.

1 Like

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.