Reputation: 10499
I need to have a mutable collection of Mutex
that needs to be shared between multiple threads. The purpose of this collection is to return for a given key a list of MutexGuard
(to be able to synchronize the threads depending on the key). Please note that when initialized, the collection has no Mutex
in it, these need to be created at runtime depending on the key.
My code is the following:
use std::collections::HashMap;
use std::sync::{Arc, Mutex, MutexGuard};
struct Bucket {
locks: HashMap<String, Mutex<()>>,
}
impl Bucket {
// This method will create and add one or multiple Mutex to the
// collection (therefore it needs to take self as mutable), according
// to the give key (if the Mutex already exist it will just return
// its MutexGuard).
fn get_guards(
&mut self,
key: impl Into<String>,
) -> Vec<MutexGuard<'_, ()>> {
let lock = self.locks.entry(key.into()).or_default();
vec![lock.lock().unwrap()]
}
}
impl Default for Bucket {
fn default() -> Self {
Self {
locks: HashMap::new(),
}
}
}
fn main() {
// I need to wrap the bucket in a Arc<Mutex> since it's going to be shared
// between multiple threads
let mut bucket = Arc::new(Mutex::new(Bucket::default()));
// Here I need to get a list of guards, so that until they are dropped
// I can synchronize multiple threads with the same key (or to be more
// precise with the same MutexGuards, as different keys may yields the
// same MutexGuards).
let guards = {
// IMPORTANT: I need to unlock the Mutex used for the `bucket` (with
// write access) asap, otherwise it will nullify the purpose of the
// bucket, since the write lock would be hold for the whole `guards`
// scope.
let mut b = bucket.lock().unwrap();
b.get_guards("key")
};
}
The error I am getting is the following:
error[E0597]: `b` does not live long enough
--> src/main.rs:29:9
|
27 | let _guards = {
| ------- borrow later stored here
28 | let mut b = bucket.lock().unwrap();
29 | b.get_guards("key")
| ^ borrowed value does not live long enough
30 | };
| - `b` dropped here while still borrowed
error: aborting due to previous error
Is there a way to design my Bucket
collections of Mutex
so that I can achieve my goal?
Upvotes: 3
Views: 424
Reputation: 8833
Basically you want to borrow an object from a locked one and keep it after it's enclosing object is unlocked.
Non-Static borrows are not possible in this case because this is not safe, e.g., any other thread may drop the owner of the object that you previously borrowed.
According to your logic, inner locks
needs to be shared between other threads safely you need to wrap your mutexes
with Arc
.
struct Bucket {
locks: HashMap<String, Arc<Mutex<()>>>,
}
And this will return the atomic reference of inner mutexes.
//previously get_guards
fn get_mutexes(&mut self, key: impl Into<String>) -> Vec<Arc<Mutex<()>>> {
let lock = self.locks.entry(key.into()).or_default();
vec![lock.clone()]
}
You can simply lock all the mutexes you need like this:
let mutexes = bucket.lock().unwrap().get_mutexes("key"); // locks(borrows bucket's guard) temporarily in here
let guards: Vec<MutexGuard<'_, ()>> =
mutexes.iter().map(|guard| guard.lock().unwrap()).collect();
Please see full code on Playground
Upvotes: 1