Thread parking behavior anomaly

v 1.67.1

I'm pretty new to rust and am trying to navigate my way through async features.

I was going through an example of learning thread parking and ran into a behavior which I'm unable to understand

I'm trying to use thread park to make the loop thread go to sleep which is observing a queue and goes to sleep when the queue is empty. Otherwise, it prints out the element dequeued from the queue

Following is the code snippet:

pub fn thread_parking() {
    let mutex: Mutex<VecDeque<i8>> = Mutex::new(VecDeque::new());

    thread::scope(|s| {
        let scoped_handle = s.spawn(|| loop {
            let mut guard = mutex.lock().unwrap();
            let item = guard.pop_front();

            if let Some(item) = item {
                if item == 10 { break; }
                dbg!(item);
            } else {
                thread::park();
            }
        });

        for num in 0..11 {
            println!("current iteration {:#?}", num);
            let mut guard = mutex.lock().unwrap();
            guard.push_back(num);
            scoped_handle.thread().unpark();
            thread::sleep(Duration::from_millis(200));
        }
    });

    println!("completed");
}

The code above gets stuck after the first or the second iteration indefinitely (I think?; the max time I waited for is about 30 seconds) and does not complete

Though if I don't use the variables to store guards, the code is working as expected and prints out from 0 to 9

Expected result achieved by the following code snippet

pub fn thread_parking() {
    let mutex: Mutex<VecDeque<i8>> = Mutex::new(VecDeque::new());

    thread::scope(|s| {
        let scoped_handle = s.spawn(|| loop {
            let item = mutex.lock().unwrap().pop_front();

            if let Some(item) = item {
                if item == 10 { break; }
                dbg!(item);
            } else {
                thread::park();
            }
        });

        for num in 0..11 {
            println!("current iteration {:#?}", num);
            let guard = mutex.lock().unwrap().push_back(num);
            scoped_handle.thread().unpark();
            thread::sleep(Duration::from_millis(200));
        }
    });

    println!("completed");
}

Could someone let me know why I'm seeing this behavior?

Your first version keeps the Mutex locked while the thread is parked. This happens because you assign the Mutex guard to a variable at the top level of the loop, so the lock won't be released until the loop iteration is exited (either due to a break, or preparing to start the next loop iteration).

Then when the for loop tries to lock the Mutex it effectively deadlocks since nothing can wake the thread while the Mutex lock is held.


Your second version never assigns the guard to a variable. It only exists as a temporary which will be dropped at the end of the expression, instead of at the end of the loop iteration. That means the lock is released before the thread is parked.

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.