-
Notifications
You must be signed in to change notification settings - Fork 0
7962: examples: add graceful shutdown example #73
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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<dyn Error>> { | ||
| 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 { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The Severity: medium Other Locations
🤖 Was this useful? React with 👍 or 👎, or 🚀 if it prevented an incident/outage.
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. value:useful; category:bug; feedback: The Augment AI reviewer is correct! The example does not allow the clients to gracefully finish. It just waits for 100ms before dropping the Tokio runtime. The example should either collect all JoinResults and await them or use shutdown_tx.closed() to be notified when the subscribers are all doppped. |
||
| 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) => { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The use of
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. value:good-to-have; category:bug; feedback: The Gemini Ai reviewer is correct! A bad client could cause denial of service attack by sending a huge line. But since this is an example of a graceful shutdown it is OK to leave it as is. |
||
| match result { | ||
| Ok(0) | Err(_) => break, | ||
| Ok(_) => { | ||
| let _ = writer.write_all(line.as_bytes()).await; | ||
| line.clear(); | ||
| } | ||
|
Comment on lines
+53
to
+56
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The result of Ok(_) => {
if writer.write_all(line.as_bytes()).await.is_err() {
// Client disconnected.
break;
}
line.clear();
}
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. value:good-to-have; category:bug; feedback: The Gemini Ai reviewer is correct! A failed write indicates that the client is gone and the server should stop trying to write to it. |
||
| } | ||
| } | ||
| // 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; | ||
|
Comment on lines
+82
to
+84
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Using a fixed-duration // Wait for all active connections to finish processing.
// The `closed()` future will complete when all receivers have been dropped.
shutdown_tx.closed().await;
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. value:useful; category:bug; feedback: The Gemini AI reviewer is correct! The example does not allow the clients to gracefully finish. It just waits for 100ms before dropping the Tokio runtime. The example should either collect all JoinResults and await them or use shutdown_tx.closed() to be notified when the subscribers are all doppped. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Spawned tasks not awaited, shutdown not actually gracefulMedium Severity The documentation promises "in-flight connections are allowed to finish before the process exits," but the Additional Locations (2)
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. value:useful; category:bug; feedback: The Bugbot AI reviewer is correct! The example does not allow the clients to gracefully finish. It just waits for 100ms before dropping the Tokio runtime. The example should either collect all JoinResults and await them or use shutdown_tx.closed() to be notified when the subscribers are all doppped. |
||
|
|
||
| println!("shutdown complete"); | ||
| Ok(()) | ||
| } | ||


There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The docs say “In-flight connections are allowed to finish”, but on shutdown each connection breaks as soon as
shutdown_rx.recv()completes, which interrupts active sessions rather than letting them complete naturally.Severity: low
Other Locations
examples/graceful-shutdown.rs:60🤖 Was this useful? React with 👍 or 👎, or 🚀 if it prevented an incident/outage.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
value:useful; category:bug; feedback: The Augment AI reviewer is correct! The example does not allow the clients to gracefully finish. It just waits for 100ms before dropping the Tokio runtime. The example should either collect all JoinResults and await them or use shutdown_tx.closed() to be notified when the subscribers are all doppped.