I'm testing how tasks created with tokio::spawn behave when the main thread terminates. According to tokio docs, all tasks are dropped when the runtime is shutdown.
There is no guarantee that a spawned task will execute to completion. When a runtime is shutdown, all outstanding tasks are dropped, regardless of the lifecycle of that task.
However, when the test is run as follows, the task does not seem to be terminated even if the runtime is terminated.
[package]
name = "tokio_test"
version = "0.1.0"
edition = "2021"
[dependencies]
tokio = { version = "1", features = ["rt", "test-util", "macros", "rt-multi-thread"]}
use std::{thread, time};
#[tokio::main]
async fn main() {
println!("tokio test - main thread...");
tokio::spawn(async move {
let mut i = 0;
loop {
println!(
"tokio test - tokio spawn(worker) thread... sleep(1): {}/10",
i
);
thread::sleep(time::Duration::from_millis(1000));
i += 1;
if i > 3 {
break;
}
}
});
thread::sleep(time::Duration::from_millis(1000));
println!("tokio test - main thread... closed ");
}
tokio test - main thread...
tokio test - tokio spawn(worker) thread... sleep(1): 0/10
tokio test - main thread... closed
tokio test - tokio spawn(worker) thread... sleep(1): 1/10
tokio test - tokio spawn(worker) thread... sleep(1): 2/10
tokio test - tokio spawn(worker) thread... sleep(1): 3/10
Q1: If I want the tasks to terminate automatically when the runtime terminates, do I have to terminate them directly via the JoinHandle for the task?
Q2: Nested tasks do not work if I call tokio::spawn inside tokio::spawn. Can't I nest tokio::spawn like this? (It shows the same result as the Output)
tokio::spawn(async move {
let mut i = 0;
loop {
println!(
"tokio test - tokio spawn(worker) thread... sleep(1): {}/10",
i
);
thread::sleep(time::Duration::from_millis(1000));
i += 1;
/* nested spawn */
tokio::spawn(async move {
println!("worker thread in worker, sleep(1): 10");
thread::sleep(time::Duration::from_millis(1000));
});
if i > 3 {
break;
}
}
});
This is because you're using std::thread::sleep()
. Tokio has no control over blocking functions, and it cannot stop the task while sleep()
is executing. It can only stop the task on await
points.
If you replace std::thread::sleep()
with tokio::time::sleep().await
, it stops after main()
is finished. Here's a playground proving the task is indeed dropped.