Tokio's multithreaded runtime doesn't behave like I expected

62 points swip3798 39 comments

I have been trying to debug this issue for a while now, and I can't figure out, what the problem is.

Take this code:

    use std::time::Duration;
    
    use sqlx::PgPool;
    
    const DB_URL: &str = "postgres://devuser:devpassword@localhost:5432/devdb";
    
    async fn db_then_compute(pool: PgPool) {
        let num: (i32,) = sqlx::query_as("SELECT 1").fetch_one(&pool).await.unwrap();
        println!(
            "The num was returned: {}, but now, the CPU block will kill multithreading",
            num.0
        );
        println!("Start compute...");
        // This would "fix" the issue:
        // pool.close().await;
        loop {}
    }
    
    /// This doesn't cause the issue
    async fn http_then_compute() {
        let res = reqwest::get("https://google.de").await.unwrap();
        let status = res.status();
        println!(
            "The num was returned: {}, but the CPU block will not kill multithreading",
            status.as_u16()
        );
        loop {}
    }
    
    #[tokio::main]
    async fn main() {
        let pool = sqlx::PgPool::connect(DB_URL).await.unwrap();
    
        tokio::spawn(db_then_compute(pool));
        //tokio::spawn(http_then_compute());
        loop {
            println!("Observer thread, the sleep will never return");
            tokio::time::sleep(Duration::from_secs(1)).await;
        }
    }

I'd expect the Oberserver thread to continue. I know, CPU-bound tasks in async runtimes are not good, but tokio is multithreaded, so I'd assume, as long as I have enough workers, it should still function. For reference, the reqwest task works completely fine. Running one CPU bound task shouldn't starve a multithreaded runtime, right?

First, I thought this was an sqlx issue. It only happens with postgres, not with sqlite. However, I then tested the same thing with deadpool-postgres, and issue is still persistent. So it's not sqlx after all.

Can someone help me with my misunderstanding of tokio's runtime here? What is blocking other tasks from completing here?