webrtc-rs/webrtc

[All] Mutex performance improvement: asynchronous mutex vs synchronous mutex

Opened this issue · 4 comments

tokio::sync::Mutex vs std::sync::Mutex vs parking_lot::Mutex

change tokio::sync::Mutex to std::sync::Mutex or parking_lot::Mutex when it is not across .await

For example, don't use std::sync::Mutex or parking_lot::Mutex on the following case, which hold the lock across the .await point.

async fn work(mtx: &Mutex<i32>) {
    println!("lock");
    {
        let mut v = mtx.lock().unwrap();
        println!("locked");
        // synchronous Mutex lock across .await point will cause deadlock,
        // for this case, use asynchronous Mutex
        delay_for(Duration::from_millis(100)).await;
        *v += 1;
    }
    println!("unlock")
}

it is ok to use std::sync::Mutex or parking_lot::Mutex when it is not across the .await point, for example:

async fn work(mtx: &Mutex<i32>) {
    println!("lock");
    {
        let mut v = mtx.lock().unwrap();
        println!("locked");        
        *v += 1;
    }
    println!("unlock")
}

it is ok to use std::sync::Mutex or parking_lot::Mutex when it is not across the .await point, for example:

It also kinda depends on what thread is being locked. Locking runtime's (tokio/async-std) threads might result in whole system performance degradation, since locking runtime's threads would prevent them from driving other tasks (like i/o) or even disrupt the whole cooperative scheduling. Blocking operations are usually offloaded on a separate threads (spawn_blocking)

@alexlapa

Tokio's docs recommend using std::sync::Mutex or parking_lot::Mutex

Note, std::sync::Mutex and not tokio::sync::Mutex is used to guard the HashMap. A common error is to unconditionally use tokio::sync::Mutex from within async code. An async mutex is a mutex that is locked across calls to .await.

A synchronous mutex will block the current thread when waiting to acquire the lock. This, in turn, will block other tasks from processing. However, switching to tokio::sync::Mutex usually does not help as the asynchronous mutex uses a synchronous mutex internally.

As a rule of thumb, using a synchronous mutex from within asynchronous code is fine as long as contention remains low and the lock is not held across calls to .await. Additionally, consider using parking_lot::Mutex as a faster alternative to std::sync::Mutex.