From 714669bbb9c25586520572ecd94e6ceff269cb36 Mon Sep 17 00:00:00 2001 From: Sim-hu Date: Sun, 8 Mar 2026 13:59:27 +0900 Subject: [PATCH] examples: add graceful shutdown example Add an example demonstrating graceful shutdown using `tokio::signal`. A TCP echo server listens for connections and shuts down cleanly on Ctrl+C (SIGINT), notifying active connections via a broadcast channel before exiting. This is a common production pattern but was not covered by any existing example. --- examples/Cargo.toml | 4 ++ examples/graceful-shutdown.rs | 88 +++++++++++++++++++++++++++++++++++ 2 files changed, 92 insertions(+) create mode 100644 examples/graceful-shutdown.rs diff --git a/examples/Cargo.toml b/examples/Cargo.toml index 9a1d4d04ef7..37a43b51c15 100644 --- a/examples/Cargo.toml +++ b/examples/Cargo.toml @@ -98,6 +98,10 @@ path = "named-pipe-ready.rs" name = "named-pipe-multi-client" path = "named-pipe-multi-client.rs" +[[example]] +name = "graceful-shutdown" +path = "graceful-shutdown.rs" + [[example]] name = "dump" path = "dump.rs" diff --git a/examples/graceful-shutdown.rs b/examples/graceful-shutdown.rs new file mode 100644 index 00000000000..3148bead9e4 --- /dev/null +++ b/examples/graceful-shutdown.rs @@ -0,0 +1,88 @@ +//! Graceful shutdown example using `tokio::signal`. +//! +//! This starts a TCP echo server that shuts down cleanly when it receives +//! Ctrl+C (SIGINT). In-flight connections are allowed to finish before +//! the process exits. +//! +//! Start the server: +//! +//! cargo run --example graceful-shutdown +//! +//! Then connect with: +//! +//! nc 127.0.0.1 6142 +//! +//! Press Ctrl+C on the server to trigger a graceful shutdown. + +#![warn(rust_2018_idioms)] + +use tokio::io::{AsyncBufReadExt, AsyncWriteExt, BufReader}; +use tokio::net::TcpListener; +use tokio::sync::broadcast; + +use std::error::Error; + +#[tokio::main] +async fn main() -> Result<(), Box> { + let listener = TcpListener::bind("127.0.0.1:6142").await?; + println!("listening on 127.0.0.1:6142"); + + // A broadcast channel to notify all tasks when shutdown is requested. + let (shutdown_tx, _) = broadcast::channel::<()>(1); + + loop { + tokio::select! { + // Wait for a new connection. + result = listener.accept() => { + let (socket, addr) = result?; + println!("accepted connection from {addr}"); + + let mut shutdown_rx = shutdown_tx.subscribe(); + + tokio::spawn(async move { + let (reader, mut writer) = socket.into_split(); + let mut reader = BufReader::new(reader); + let mut line = String::new(); + + loop { + tokio::select! { + // Echo lines back to the client. + result = reader.read_line(&mut line) => { + match result { + Ok(0) | Err(_) => break, + Ok(_) => { + let _ = writer.write_all(line.as_bytes()).await; + line.clear(); + } + } + } + // Stop this connection when shutdown is signaled. + _ = shutdown_rx.recv() => { + let _ = writer.write_all(b"server shutting down\n").await; + break; + } + } + } + + println!("connection from {addr} closed"); + }); + } + + // Wait for Ctrl+C. + _ = tokio::signal::ctrl_c() => { + println!("\nshutdown signal received, closing listener"); + break; + } + } + } + + // Notify all active connections. + let _ = shutdown_tx.send(()); + + // Drop the sender and wait briefly for connections to finish. + drop(shutdown_tx); + tokio::time::sleep(std::time::Duration::from_millis(100)).await; + + println!("shutdown complete"); + Ok(()) +}