Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Question about drop on the condition variable example of Chapter 1 #34

Open
francoisthire opened this issue Apr 8, 2023 · 1 comment
Labels

Comments

@francoisthire
Copy link

The content that the question is about

In the section of Chapter 1 related to condition variables (https://marabos.nl/atomics/basics.html#condvar), we can found the following example:

use std::sync::Condvar;

let queue = Mutex::new(VecDeque::new());
let not_empty = Condvar::new();

thread::scope(|s| {
    s.spawn(|| {
        loop {
            let mut q = queue.lock().unwrap();
            let item = loop {
                if let Some(item) = q.pop_front() {
                    break item;
                } else {
                    q = not_empty.wait(q).unwrap();
                }
            };
            drop(q);
            dbg!(item);
        }
    });

    for i in 0.. {
        queue.lock().unwrap().push_back(i);
        not_empty.notify_one();
        thread::sleep(Duration::from_secs(1));
    }
});

The question

This example is adapted from the one using park and unpark mentioned previously. However, in this example, we can see the line:

drop(q)

I guess this drop is about releasing the lock sooner, but it seems optional. Is there any particular reason to introduce it?

@mingmamma
Copy link

mingmamma commented Aug 10, 2024

I'd add some thoughts to this and hope someone can add more feedback. To the original poster's question, the short answer is that the explicit statement drop(q), where q is the mutex lock guard(or a value of MutexGuard<'_, VecDeque<_>>, to be precise in this example), is indeed to release the mutex lock sooner, as opposed to the variable being dropped at the end of its scope.
The short argument for this explicit drop is necessary is that this is a performance consideration. This pattern of can be viewed as some sort of basic implementation of a message queue, if we see the spawned thread that goes in the loop, and pop item from the front of the VecDeque as the receiving side, and the main thread that pushes one item into the back of the queue every 1 second as the sending side (c.f. we can see in the first half of this Rust educational video about channels that this pattern is used to give a basic implementation of a mpsc channel: youtu.be/b4mS5UPHh20). Within the video, the instructor gave a good explanation of the benefit of such explicit drop: noting that in such set up, both the receiver and sender needs to lock mutex to access the shared queue, and we wish to keep the contention of the lock as minimal as possible by keeping the critical section short (since we are dealing with fairly low-level constructs). In this example, practically that means to explicitly call drop before using the received item since the lock is not needed to use the received item. It seems not much different in this example since the use of the item is just dbg!() but that's a good demonstration of the consideration. Also noting that for the reason discussed, the implementation of such pattern involving lock is not high performant, but I haven't studied the real-world solutions that makes it more performant and how that would work. 😓 Hope someone can give better input to this issue.

Addition: Just wanted to add the book itself comments on this issue in a later chapter, which I read before and probably have influenced my view above (https://marabos.nl/atomics/building-channels.html#a-simple-mutex-based-channel)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants