jaemk / cached

Rust cache structures and easy function memoization

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Once with sync_writes and async causes deadlocks in tokio.

Lesny opened this issue · comments

the following code will freeze:

use cached::proc_macro::once;
use std::time::Duration;
use tokio::time::sleep;

#[once(time = 1, sync_writes = true)]
async fn once_writes_per_second() -> String {
    "results".to_string()
}

async fn sleep_secs(secs: u64) {
    sleep(Duration::from_secs(secs)).await;
}

#[tokio::main]
async fn main() {
    let _ = once_writes_per_second().await;
    println!("sleeping for 2 seconds");
    sleep_secs(2).await;
    let _ = once_writes_per_second().await; // will freeze
    println!("executed");
}

it freezes because it will expand to (I redacted the unimportant parts with ...):

async fn once_writes_per_second() -> String {
    ...
    let mut cached = ONCE_WRITES_PER_SECOND.write().await;
    if let Some(result) = &*cached {
        {
            let mut cached = ONCE_WRITES_PER_SECOND.read().await;
           ...
        }
    }
   ...
    *cached = Some((now, result.clone()));
    result
}

and ONCE_WRITES_PER_SECOND is tokio::sync::RwLock which is not re-entry, and will not allow for read lock to be obtained while keeping write lock. Besides, if we have write lock there is no need for read lock anyway.