I'm getting a deadlock on the following example:
use tokio::net::TcpListener;
use tokio::io::{AsyncReadExt, AsyncWriteExt};
use futures::lock::Mutex;
use std::sync::Arc;
struct A{
}
impl A {
pub async fn do_something(&self) -> std::result::Result<(), ()>{
Err(())
}
}
async fn lock_and_use(a: Arc<Mutex<A>>) {
match a.clone().lock().await.do_something().await {
Ok(()) => {
},
Err(()) => {
//try again on error
println!("trying again");
a.clone().lock().await.do_something().await.unwrap();
}
}
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
println!("begin");
let a = Arc::new(Mutex::new(A{}));
lock_and_use(a.clone()).await;
println!("end");
Ok(())
}
If I did this:
a.clone().lock().await.do_something().await;
a.clone().lock().await.do_something().await;
there would be no problem, the lock()
dies on the same line it's created. I thought this principle would be the same for match
. If you think about match, it locks the value, calls do_something
, await
s on it, and then compares the value. It's true that do_something
returns a future which capture the self
, but when we await
on it, it should discard the self
. Why it still holds self
? How can I solve this without cloning the result?
Yes:
Temporaries live for the entire statement, never shorter.
Cause code could be:
{
match self.cache.read() { // <-- direct pattern matching
Ok(ref data) => Some(data)
_ => None,
}
}.map(|data| {
// use `data` - the lock better be held
})
Read this issue for more detail.
So you need to lock outside your match statement:
let x = a.clone().lock().await.do_something().await;
match x {
Ok(()) => {}
Err(()) => {
a.clone().lock().await.do_something().await.unwrap();
}
}