Sharing std::mpsc::Receiver safely

I'm trying to send a message from one thread to another but the situation is a bit different than what I'm used to. Most of the times I share the producer of the while this time I need to share the consumer. Boiling down to the minimal:

use std::thread;
use std::sync::mpsc::{channel, Receiver};

fn start_thread(rx: Receiver<()>) {
    let t = thread::spawn(move || {
        match rx.try_recv() {
            Ok(()) => {},
            Err(_) => {}
        }

        // Other blocking call       
    });

    t.join().unwrap();
}

fn main() {
    let (tx, rx) = channel();
    start_thread(rx);

    match tx.send(()) {
        Ok(_) => {},
        Err(err) => println!("{}", err)
    }; 

In the case above I get the error Sending on a closed channel at runtime, which I guess is due to passing the ownership of the receiver to another function.

If I change the signature of the function to receive a reference of the receiver (fn start_thread(rx: &Receiver<()>) ), I get the compile time error std::sync::mpsc::Receiver<()>` cannot be shared between threads safely.

Is there a way of sharing a single reference for the consumer between threads safely?

No, this is due to the fact that rx.try_recv returns immediately, without waiting for message. You should just use recv instead.

You're correct about try_recv (and I guess this still needs to be fixed), but the error here is about sending on a closed channel. And the channel is closed, because the receiver thread is finished when start_thread is over due to the t.join() call.

So the receiver thread is created, try_recv sees there's nothing to receive, thread finishes (and channel is closed), then join finishes, and only then attempt to send is made on a closed channel.

1 Like

Actually, I can't use a blocking call because I have another blocking call right after the receiver call.

PS: Updated the code to indicate the blocking call

It’s not clear what you’re trying to accomplish with the second blocking call. Do you need the result from rx for the second call? If you just call try_recv once and the data hasn’t been sent yet, you’ll never receive the data from the other thread. What’s the issue with having two blocking calls one after the other in your problem? As far as rust is concerned that’s fine. A little more detail of your problem would help us find a better solution for you.

Sorry I wasn't clear. The real situation is the following:

I'm using Tauri to build an interface to a Rust back-end.
I have two endpoints, one that starts the server and the other that should terminate the execution of it.
So what the hypothetical function start_thread is trying to accomplish is to spin up a server(aka the other blocking call) while waits for a command to shut it down through the receiver being passed as parameters.

I really don't know if this is possible by this approach. I'm well open for suggestions :slight_smile:

This kind of code where you are waiting for one of two things to happen in a single thread generally cannot be done without the use of async code, unless you are ok with a loop that repeatedly checks both, which is very expensive CPU-usage wise.

How do you start and stop Tauri? Maybe there is a better solution that works in this specific use-case.

Tauri accepts commands from JavaScript through a method called promisified that maps to a method in Rust. All the commands are user defined, so the start and stop server commands are defined by me.