Repository navigation
Description
This issue has been brought up before in #972 by @baumanj.
Now chapter 20.2, at the end, talks about why using loop is better than while so that the MutexGuard gets unlocked before the expensive job.call_box() call. Specifically: By using loop instead and acquiring the lock and a job within the block rather than outside it, the MutexGuard returned from the lock method is dropped as soon as the let job statement ends.
Acquiring the lock inside the loop block does not guarantee that the MutexGuard is dropped after the let job statement! It's dropped because the MutexGuard doesn't get assigned a variable name, i.e. it's a temporary variable.
loop {
// We chain call recv() immediately on the temporary MutexGuard
let job = receiver.lock().unwrap().recv().unwrap();
// MutexGuard is dropped, because it didn't get assigned to any variable
println!("Worker {} got a job; executing.", id);
job.call_box();
}loop {
// We assign MutexGuard to a variable
let mg = receiver.lock().unwrap();
let job = mg.recv().unwrap();
// Although the MutexGuard is never referenced again,
// it won't be dropped until the end of the scope
println!("Worker {} got a job; executing.", id);
job.call_box();
} // MutexGuard finally droppedI think it's important to add a note to the book that the MutexGuard is dropped due to not being assigned to a variable. I can also see value in rewriting it with an explict drop call.
Because frankly all of this seems pretty unsafe. How stable & strict are these compiler contracts of when things get dropped? Can the drop calls be lazily executed sometimes? There's some info in the reference and there's a nice playground example.
Always explicitly calling drop on any synchronization objects seems to be a smart move, because otherwise subtle issues & inefficiencies can start creeping in where locks are held longer than expected. When someone changes code by introducing a named variable of a mutex, I doubt it's going to be obvious to most that it'll introduce extended lock holding. This seems like something only a minority of Rustaceans will know, much less catch in a code review. Add on that the risks of the compiler optimizations being changed on exact drop call timings. Being explicit seems worth it.
Activity
whycatdothis commentedon Apr 5, 2019
Cant agree more. I was quite confused at there.
After seeing this, all I think is that MutexGuard is dropped at the end of the loop, where it go out of scope. This makes me doubt on what Rust advertises it can do. A slightly (but quite common) change makes the multi thread example fall back to single thread. That's not RUST at all.
UnHumbleBen commentedon Jul 31, 2019
Thanks for bringing this issue up, but I have trouble understanding your explanation that
MutexGuardis dropped due to not being assigned to any variables. In your example, it makes sense sinceMutexGuardis explicitly assigned tomg.So how come using
while letdoesn't work then? As shown in the book,There is no variable assigned to
MutexGuard. Shouldn'tMutexGuardalso be dropped?xStrom commentedon Jul 31, 2019
@UnHumbleBen great question! I did some testing to get more info and you can check out the playground code.
It seems that in the
whileloop case temporary variables aren't dropped either until the end of the loop scope, if there's a named variable containing a result from one of its methods.I'm more and more convinced that locks (or anything else where drop timing matters) should always have explicit drop calls, because the implicit drop mechanics seem to be pretty varied and aren't properly documented anywhere.
baumanj commentedon Sep 3, 2019
Hey, @steveklabnik this is the issue I mentioned to you at RustConf (at the end of the morning training).
I posted https://users.rust-lang.org/t/drop-semantics-and-temporary-value-lifetimes-in-while-let/30903, which led to some interesting discussion and ultimately the conclusion that
letandif letbindings have surprisingly different behavior. As someone who's been involved with the community for a long time, I'm interested in your take on this. Do you think it could ever be changed (perhaps in an edition), or are we stuck with unfortunately inconsistent behavior we have now?Fix ambiguous explanation of MutexGuard unlocking in ch20-02