I'm going through the graceful shutdown chapter of Tokio's guide and have a few questions. Consider the simple async program below.
use futures::StreamExt;
use std::io;
use tempfile::TempDir;
use tokio::net::TcpListener;
use tokio_util::codec::Decoder;
use tokio_util::codec::LinesCodec;
async fn run_server(listener: TcpListener) -> io::Result<()> {
let tempdir = TempDir::new().unwrap();
eprintln!("tempdir at: {:?}", tempdir.path());
loop {
let (socket, _) = listener.accept().await?;
tokio::spawn(async move {
let mut framed = LinesCodec::new().framed(socket);
while let Some(message) = framed.next().await {
match message {
Ok(bytes) => {
eprintln!("got line: {}", bytes);
}
Err(err) => println!("Socket closed with error: {:?}", err),
}
}
println!("Socket received FIN packet and closed connection");
});
}
}
#[tokio::main]
async fn main() -> io::Result<()> {
// Bind the listener to the address
let listener = TcpListener::bind("127.0.0.1:2345").await.unwrap();
tokio::select! {
_ = run_server(listener) => {}
// _ = tokio::signal::ctrl_c() => {
// // The shutdown signal has been received.
// eprintln!("shutting down");
// }
}
Ok(())
}
With this Cargo.toml:
[package]
name = "async-graceful-shutdown"
version = "0.1.0"
edition = "2021"
[dependencies]
futures = "0.3.24"
tempfile = "3.3.0"
tokio = { version = "1", features = ["full"] }
tokio-util = { version = "0.7.3", features = ["full"] }
As written, killing the program with Ctrl+C leaks the tempdir
. But just uncommenting the code that handles Ctrl+C in the select!
block is enough to get the tempdir
cleaned up on Ctrl+C. Can someone please explain why this is the case?
I'm assuming it has to do with the default SIGTERM handler installed by Rust? I guess it doesn't unwind stacks and call drop()
?
Taking it a step further: given that the simple no-op Ctrl+C handler seems to be enough to unwind stacks and cleanup things with drop
, how much of the machinery described in Telling things to shut down is really necessary? As the article mentions, I understand that it's necessary if I want things to happen before a socket closes down, e.g. send a "closing" message to clients. But it seems like the framed.next().await
is automatically cancelled, for example.