Can the mpsc sender be cloned concurrently?

Hi everyone,

I'm aware that the Sender of std::sync::mpsc::channel() should not be used be two threads in parallel, but be cloned so each thread has its own copy.

However, how is the implementation of "clone" under the hood. Could I (with the help of some unsafe code) call clone from two threads concurrently without race conditions?

I'm asking this because I have a rocket app and want the handler threads to get a common state injected, the sender, and clone this one to then use it thread safe.

I'm aware of other, more proper solutions, but I'm interested if that would, in theory, be possible.

Best regards

It's implementation details, so you can't rely on it.Even if it was callable from two threads concurrently without race conditions, Sender being !Sync means that it could be changed at any time in a way that would introduce such race conditions.

Don't do it. Use a crossbeam channel.

1 Like

I don't see why not. No unsafe required. Or I don't understand the question:

use std::sync::mpsc::sync_channel;

fn main() {
    let (sender1, receiver) = sync_channel(1);
    let sender2 = sender1.clone();

    let t1 = thread::spawn(move || {
        sender1.send(1).unwrap();
        let sender = sender1.clone();
        sender.send(10).unwrap();

    });
   
    let t2 = thread::spawn(move || {
        sender2.send(2).unwrap();
        let sender = sender2.clone();
        sender.send(20).unwrap();
        
    });
    
    println!("{}", receiver.recv().unwrap());
    println!("{}", receiver.recv().unwrap());
    println!("{}", receiver.recv().unwrap());
    println!("{}", receiver.recv().unwrap());
    
    let res = t1.join();
    let res = t2.join();
}

https://play.rust-lang.org/

op specified the Sender returned from std::sync::mpsc::channel(), you used std::sync::mpsc::sync-channel() instead

That does not change the situation re: cloning in multiple threads:

use std::thread;
use std::sync::mpsc::channel;

fn main() {
    let (sender1, receiver) = channel();
    let sender2 = sender1.clone();

    let t1 = thread::spawn(move || {
        sender1.send(1).unwrap();
        let sender = sender1.clone();
        sender.send(10).unwrap();

    });
    
    let t2 = thread::spawn(move || {
        sender2.send(2).unwrap();
        let sender = sender2.clone();
        sender.send(20).unwrap();
        
    });
    
    println!("{}", receiver.recv().unwrap());
    println!("{}", receiver.recv().unwrap());
    println!("{}", receiver.recv().unwrap());
    println!("{}", receiver.recv().unwrap());
    
    let res = t1.join();
    let res = t2.join();
}

https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=36faa55273a99230a7390a3dfab91be9

1 Like

Ok, to be more precise, concurrently calling clone on two different clones of the sender is fine. Concurrently calling clone on the same sender twice is not.

3 Likes

Yes. Seems to be a rather strange and unnecessary thing to want to do anyway.

The std Sender is not Sync, so it's not safe to simultaneously access it from multiple threads, under any circumstances.

You should always use crossbeam-channel instead of std::mpsc. It's always significantly faster. It's more flexible (including Sender being Sync and safe to clone from any thread), and has more polished API.

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.