rust-lang / rust-clippy

A bunch of lints to catch common mistakes and improve your Rust code. Book: https://doc.rust-lang.org/clippy/
https://rust-lang.github.io/rust-clippy/
Other
11.44k stars 1.54k forks source link

New lint suggestion: You're using a blocking operation when you could be using an async one #10794

Open omarandlorraine opened 1 year ago

omarandlorraine commented 1 year ago

What it does

A lint which checks for blocking functions inside an async block.

One I know of (and the cause of a bug I ran into today) is std::thread::sleep, but there are others. (file operations, networking, ...). If such a function has an async counterpart, then the user could be warned of this fact.

Lint Name

unwise-block

Category

suspicious

Advantage

Drawbacks

Example

use std::thread::sleep;
use std::time::Duration;
use tokio::signal::unix::signal;
use tokio::signal::unix::SignalKind;
use tokio::sync::mpsc;

async fn rx(rx: &mut mpsc::Receiver<()>) {
    loop {
        sleep(Duration::new(1, 0));
        if rx.try_recv().is_ok() {
            println!("got one splivet");
        }
    }
}

async fn tx(tx: mpsc::Sender<()>) {
    let mut stream = signal(SignalKind::user_defined1()).unwrap();

    loop {
        stream.recv().await;
        println!("sending one splivet");
        tx.send(()).await.unwrap();
    }
}

#[tokio::main(flavor = "current_thread")]
async fn main() {
    let (stx, mut srx) = mpsc::channel::<()>(5);

    tokio::join!(tx(stx), rx(&mut srx));
}

Could be written as:

use std::thread::sleep;
use std::time::Duration;
use tokio::signal::unix::signal;
use tokio::signal::unix::SignalKind;
use tokio::sync::mpsc;

async fn rx(rx: &mut mpsc::Receiver<()>) {
    loop {
        tokio::time::sleep(Duration::new(1, 0)).await;
        if rx.try_recv().is_ok() {
            println!("got one splivet");
        }
    }
}

async fn tx(tx: mpsc::Sender<()>) {
    let mut stream = signal(SignalKind::user_defined1()).unwrap();

    loop {
        stream.recv().await;
        println!("sending one splivet");
        tx.send(()).await.unwrap();
    }
}

#[tokio::main(flavor = "current_thread")]
async fn main() {
    let (stx, mut srx) = mpsc::channel::<()>(5);

    tokio::join!(tx(stx), rx(&mut srx));
}

Note the different way to sleep in the rx function.

y21 commented 1 year ago

I like this idea! Although there are some exceptions we should probably make, such as with Mutex. Using an async Mutex only makes sense when the guard is held across an await point. In most other cases, it's totally fine to use the Mutex from the standard library (or parking_lot), even in an async context

J-ZhengLi commented 1 year ago

Well since I have wrote a simple but similar one in the past for my own fork of clippy, I'll take the chance make it official, lol

@rustbot claim

tv42 commented 4 months ago

This looks like a duplicate (with refinements, suggesting what use instead) of #4377.