Home > database >  Rust match operator makes one thread monopolising mpsc channel
Rust match operator makes one thread monopolising mpsc channel

Time:03-21

I'm doing the final project of the rust book (ch 20 - Building a Multithreaded Web Server) and the final working code for a thread worker is :

impl Worker {
    fn new(id: usize, receiver: Arc<Mutex<mpsc::Receiver<Message>>>) -> Worker {
        let thread = thread::spawn(move || loop {
            let message = receiver.lock().unwrap().recv().unwrap();

            match message {
                Message::NewJob(job) => {
                    println!("Worker {} got a job; executing.", id);

                    job();
                }
                Message::Terminate => {
                    println!("Worker {} was told to terminate.", id);

                    break;
                }
            }
        });

        // ...
    }
}

Then I decided to shorten the code by removing the message variable and apply the match operator directly on receiver.lock().unwrap().recv().unwrap()

impl Worker {
    fn new(id: usize, receiver: Arc<Mutex<mpsc::Receiver<Message>>>) -> Worker {
        let thread = thread::spawn(move || loop {
            match receiver.lock().unwrap().recv().unwrap() {
                Message::NewJob(job) => {
                    println!("Worker {} got a job; executing.", id);

                    job();
                }
                Message::Terminate => {
                    println!("Worker {} was told to terminate.", id);

                    break;
                }
            }
        });

        // ...
    }
}

but now there is only one worker (the first one) responding to the job queue.

What is happening here ?

CodePudding user response:

The two code snippets are not equivalent. The difference lies in the way temporaries are borrowed and dropped.

In the first example, the temporary mutex guard is dropped at the end of the statement:

   let message = receiver.lock().unwrap().recv().unwrap();
   // the mutex guard is dropped here, and other threads can acquire the lock

while in the second snipped, the mutex guard remains alive and thus holds the lock until the end of the match statement:

            match receiver.lock().unwrap().recv().unwrap() { // the mutex is locked here and will remain locked until the channel yields a result, thus blocking other threads
                Message::NewJob(job) => {
                    println!("Worker {} got a job; executing.", id);

                    job();
                }
                Message::Terminate => {
                    println!("Worker {} was told to terminate.", id);

                    break;
                }
            }

            // the mutex guard is dropped here

So in the second example other threads are locked out from acquiring access to the receiving end of the channel.

To quote the answer from this thread:

when a temporary is created inside an expression, it is (only) dropped at the end of the enscoping statement:

  • A let binding ends its statement before the binding ("variable") is usable;
  • A match and derivatives, end the statement after the scope in which the binding ("variable") is usable.
  • Related