tokio-rs / tokio

A runtime for writing reliable asynchronous applications with Rust. Provides I/O, networking, scheduling, timers, ...
https://tokio.rs
MIT License
26.59k stars 2.45k forks source link

runtime.spawn doesn't run the task #6643

Closed tisonkun closed 3 months ago

tisonkun commented 3 months ago

Version List the versions of all tokio crates you are using. The easiest way to get this information is using cargo tree subcommand:

cargo tree | grep tokio

├── tokio v1.38.0
│   └── tokio-macros v2.3.0 (proc-macro)

Platform The output of uname -a (UNIX), or version and 32 or 64-bit (Windows)

Darwin tisondeMBP 22.5.0 Darwin Kernel Version 22.5.0: Mon Apr 24 20:53:19 PDT 2023; root:xnu-...5/RELEASE_ARM64_T6020 arm64

Description Enter your issue details here. One way to structure the description:

runtime.spawn sometimes not run the task passed.

I tried this code:

use std::future::Future;
use std::sync::Arc;
use tokio::runtime::Runtime;

#[tokio::main]
async fn main() {
    let runtime = Arc::new(tokio::runtime::Builder::new_multi_thread().build().unwrap());

    async fn spawn_executor<O>(
        runtime: &Runtime,
        fut: impl Future<Output=O> + Send + 'static,
    ) -> O
    where
        O: Send + 'static,
    {
        runtime.spawn(fut).await.unwrap()
    }

    spawn_executor(&runtime, async {
        println!("hello");
    }).await;

    // something time comsuming
}

This can print "hello", while you change the spawn_executor to:

    spawn_executor(&runtime, async {
        println!("hello");
    });

It doesn't print. Well. This is because I don't await. But runtime.spawn's docs says:

The provided future will start running in the background immediately when spawn is called, even if you don't await the returned JoinHandle.

And when I change to:

runtime.spawn(async {
        println!("hello");
    });

It can be executed again.

I expected to see this happen:

When calling spawn_executor, the future should be scheduled.

sfackler commented 3 months ago

Your spawn_executor function returns a future that does nothing until polled - i.e. the println future is not actually spawned unless you await the future returned from spawn_executor.

tisonkun commented 3 months ago

@sfackler I got it ... so as long as tokio's spawn called, it can schedule.

May I ask how tokio's spawn keep poll even if I don't await on the JoinHandle?

sfackler commented 3 months ago

That's the whole point of spawn - you're giving the future to the runtime to poll instead of doing it yourself. The only effect calling poll on the JoinHandle has is giving you the return value of the future once it completes.