jD91mZM2
jD91mZM2

Reputation: 35

Accessing event loop via Mutex

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.

Example in Playground.

The original question is here on reddit.

Upvotes: 0

Views: 436

Answers (1)

E_net4
E_net4

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

Related Questions