0

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.

1 Answer 1

1

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
     },
     ...
}
Sign up to request clarification or add additional context in comments.

7 Comments

Thanks a lot for your answer! However, the problem is that recv_event is the thing that takes time, but I need to use that concurrently.
@LEGOlord208 I don't understand what you mean with that. The only safe way to call &mut self methods concurrently is with synchronization mechanisms such as Mutex. Consider improving your question to fit additional requirements.
Ooooooooohhhhhhhhhhhhhhhhhhhh uhh I think you just solved it right there. Maybe it doesn't have to be mutable! Lemme check. aww crap. If this is impossible I might as well make a pull request to discord-rs to make it not mutable if that's possible.
You don't think you're understanding the problem. But neither can I help if you do not update your question. In the mean time, check out the book on "interior mutability" and "concurrency".
Not understanding the problem? The problem is that recv_event blocks, and since it's a mutable reference, I have to lock it for that entire time, which means I can't do anything at the same time. Correct?
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.