Search code examples
rustthreadpoolmutex

Is this because the mutex was not released?


I've read this Turning Our Single-Threaded Server into a Multithreaded Server. And tried to implement it.

I wrote this:

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

use std::sync::{Arc, Mutex};

use std::thread;

type task = dyn FnOnce() + Send + 'static;

pub struct Threadpool {
    threads: Vec<thread::JoinHandle<()>>,

    rx: Arc<Mutex<Receiver<Box<task>>>>,

    tx: Sender<Box<task>>,
}

impl Threadpool {
    pub fn new(size: usize) -> Threadpool {
        let mut tasks = Vec::with_capacity(size);

        let (tx, rx): (Sender<Box<task>>, Receiver<Box<task>>) = channel();

        let rx = Arc::new(Mutex::new(rx));

        for _ in 0..size {
            let rx = rx.clone();

            let task = thread::spawn(move || {
                loop {
                   let job= rx.lock().unwrap().recv().unwrap();
                   job();
                }
            });

            tasks.push(task);
        }

        Threadpool {
            threads: tasks,
            rx,
            tx,
        }
    }

    pub fn execute<F>(&self, f: F)
    where
        F: FnOnce() + Send + 'static,
    {
        self.tx.send(Box::new(f)).unwrap();
    }
}

It works.

But when I change

let job= rx.lock().unwrap().recv().unwrap();
job();

to

rx.lock().unwrap().recv().unwrap()();

When I open localhost:port/sleep, and then open localhost:port, it will takes 5 seconds.

I set this in main

"GET /sleep HTTP/1.1" => {
            thread::sleep(Duration::from_secs(5));
            ("HTTP/1.1 200 OK", "hello.html")
        }

I already knew that while let will cause that.

But I can't figure out why my code above will also lead to that.

Can anybody give me the answer.


Solution

  • In Rust temporary objects are dropped at the end of the expression that contains them (with a few caveats not relevant here).

    And the temporary we are interested in is the guard of the mutex, whose drop is responsible of releasing the mutex lock.

    So, writingh the drop explicitly, your first code:

    let job = rx.lock().unwrap().recv().unwrap();
    job();
    

    Is equivalent to:

    let mut guard = rx.lock().unwrap();
    let job = guard.recv().unwrap();
    drop(guard);
    job();
    

    And your second code:

    rx.lock().unwrap().recv().unwrap()();
    

    Is equivalent to:

    let mut guard = rx.lock().unwrap();
    let job = guard.recv().unwrap()
    job();
    drop(guard);
    

    As you can see, now you are calling the job() function with the mutex still locked.