al8n / stretto

Stretto is a Rust implementation for Dgraph's ristretto (https://github.com/dgraph-io/ristretto). A high performance memory-bound Rust cache.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Possible deadlock in `wait` in `async_std` runtime

stepantubanov opened this issue · comments

Code below may freeze on wait() call. It happens most of the time for me (macOS).

use async_std::task;
use stretto::AsyncCache;

fn main() {
    let max_cost = 10_000;
    let lru = AsyncCache::new(max_cost * 10, max_cost as i64, task::spawn)
        .expect("failed to create cache");

    task::block_on(task::spawn(async move {
        for i in 0..10_000 {
            println!("i = {i}, len before insert = {}", lru.len());

            let value = 123;
            let cost = 1;
            lru.insert(i, value, cost).await;
            lru.wait().await.unwrap(); // <-- freezes here
        }

        println!("done");
    }));
}

I also have a question. Is there an easy way to predict how many items a cache will fit (with parameters above) if we always use cost = 1? Seems like it's capping out at ~175 items in an example above (max_cost = 10_000, with each item cost = 1).

Hi, the freeze situation happens on async-std runtime. I will add some test cases for async-std and try to fix it.

There is an ignore_internal_cost configuration, when you build cache you can set it to true, If you want to store the exact max_cost items to the cache, you should build cache like this.

use async_std::task;
use stretto::AsyncCache;

fn main() {
        let max_cost = 10_000;
        let lru = AsyncCacheBuilder::new(max_cost * 10, max_cost as i64)
        .set_ignore_internal_cost(true)
        .finalize(tokio::spawn)
        .expect("failed to create cache");
        
        // your code here
}

Maybe it is caused by blow code:

    let waker;
    cfg_std_expr!(
        waker = self.inner.waker.lock().unwrap().take();
    );
    cfg_not_std_expr!(
        waker = self.inner.waker.lock().take();
    );
    if let Some(waker) = waker {
        waker.wake();
    }
    Some(0)
} else if val == 0 {
    None
} else {
    Some(val - 1)

the task has been woken and polled before updating count. So we should update count firstly and then wake up the Future.
I have created a PR in wg repository.