Locking
Locking
Posted Oct 2, 2024 12:56 UTC (Wed) by excors (subscriber, #95769)In reply to: Locking by taladar
Parent article: Resources for learning Rust for kernel development
For example, mutexes could be designed like:
let mut m = ScopedMutex::new(42);
m.with_lock(|i: &mut i32| { println!("{}", i); });
where with_lock guarantees it will always lock and unlock correctly around the accesses (unless the closure never returns).
The issue is, it would be nice to get similar guarantees *without* the closure, because closures create a potentially-inconvenient nested code structure and can introduce some extra lifetime challenges. Usually that's fine, but sometimes it's rather annoying. Better to have an API like:
let mut m = Mutex::new(42);
let i: MutexGuard<_> = m.lock().unwrap();
println!("{}", i);
std::mem::forget(i); // this should be an error - it should be required to properly drop i (by letting it go out of scope, or calling i.unlock() which transfers ownership of i, etc)
Currently the forget() is allowed and the mutex will never get unlocked, and some unrelated code may deadlock later (which is much harder to debug). It also means the Mutex might be dropped while it is still locked, so Mutex has to know how to clean up from that unusual state. Prohibiting the forget() (and any other code with similarly forgetful behaviour) will require new Rust language features.
(I think that's an easy trap to fall into when designing APIs: you design a MutexGuard whose lifetime is less than Mutex's lifetime, so you know the MutexGuard cannot be dropped after the Mutex is dropped (which is true, and a very helpful guarantee), and intuitively that means the MutexGuard will be dropped before the Mutex is dropped. And that's almost always true, but it's not guaranteed - the MutexGuard might have been forgotten instead of dropped - so you have to either handle that correctly (which can be difficult), or switch to a closure-based API.)
