Graceful Tokio Shutdown with Notify

Signal shutdown with Tokio Notify, let each worker finish its current cycle, and join every task before the process exits.

RustResilience

Code

snippet
use std::sync::Arc;
use std::time::Duration;
use tokio::sync::Notify;
use tokio::task::JoinSet;
use tokio::time::sleep;

#[tokio::main]
async fn main() {
    let shutdown = Arc::new(Notify::new());
    let mut tasks = JoinSet::new();

    for worker in 0..3 {
        let shutdown = Arc::clone(&shutdown);
        tasks.spawn(async move {
            loop {
                tokio::select! {
                    _ = shutdown.notified() => {
                        println!("worker {worker} draining");
                        break;
                    }
                    _ = sleep(Duration::from_millis(25)) => {
                        println!("worker {worker} tick");
                    }
                }
            }
            sleep(Duration::from_millis(20)).await;
            println!("worker {worker} stopped");
        });
    }

    sleep(Duration::from_millis(60)).await;
    shutdown.notify_waiters();
    while let Some(result) = tasks.join_next().await {
        result.expect("worker panicked");
    }
    println!("shutdown complete");
}