Reputation: 35
I'm using the discord crate, which uses event loops. I need to check for events concurrently, while I am taking user input:
let clone = context.clone();
thread::spawn(
move || loop {
match clone.lock().unwrap().gateway.recv_event() {
Ok(event) => {
// println!("Updating state: {:?}", event);
clone.lock().unwrap().state.update(&event)
},
Err(err) => {
stderr!("Error receiving: {}", err);
},
}
}
);
This doesn't work because it stays locked... So what about
println!("Locking");
let mut gateway = {
&mut clone.lock().unwrap().gateway
};
println!("Unlocked? {:?}", clone);
match gateway.recv_event() {
Ok(event) => {
This also doesn't seem to work:
Locking
Unlocked? Mutex { <locked> }
How would this be solved?
It occurred to me it might just be best if there was a way to access to mutex's contents without locking.
The original question is here on reddit.
Upvotes: 0
Views: 436
Reputation: 30052
I would recommend you to read again on Mutex
and what a lock guard is (see also Borrow data out of a mutex "borrowed value does not live long enough", which has a similar problem). The problem is that clone.lock().unwrap().gateway.recv_event()
creates a Mutex lock guard that is retained until the full match
statement. For instance, let us redefine Something
:
#[derive(Debug)]
struct Something(i32);
impl Something {
pub fn get_some_event(&self) -> i32 {
thread::sleep(Duration::from_secs(1));
self.0
}
}
This code works:
let variable = Mutex::new(Something(4));
match variable.lock().unwrap().get_some_event()
{
5 => {
variable.lock().unwrap();
}
_ => {
println!("{:?}", variable); // Still locked
}
}
println!("{:?}", variable); // no longer locked!
But this one leads to a deadlock (since the standard mutexes in Rust are not reentrant):
let variable = Mutex::new(Something(5));
match variable.lock().unwrap().get_some_event()
{
5 => {
variable.lock().unwrap(); // deadlock!
}
_ => {
println!("{:?}", variable);
}
}
println!("{:?}", variable);
The solution lies in the fact that you only need the lock while fetching the event (or while performing other actions). While not doing so, make sure that the respective lock is dropped.
let r = clone.lock().unwrap().gateway.recv_event(); // get result + drop lock
match event {
Ok(event) => {
clone.lock().unwrap().state.update(&event) // also ok
},
...
}
Upvotes: 1