Crate elegant_departure
source ·Expand description
Runtime independent async graceful shutdowns.
Provides an easy meachnism to initiate a shutdown from anywhere and to wait for all workers and tasks to gracefully finish.
Why you would want to use elegant-departure
:
- Easy to use and minimal API
- Runtime independent (works with
tokio
,async-std
,smol
, …) - Additional integrations for
tokio
(shutdown onctrl-c
, signals etc.)
§Usage
This crate is on crates.io and can be
used by adding it to your dependencies in your project’s Cargo.toml
.
[dependencies]
elegant-departure = "0.3"
For a optional tokio integration, you need to enable the tokio feature:
[dependencies]
elegant-departure = { version = "0.3", features = "tokio" }
§Example: Axum
Axum is easily integrated through the tokio
integration.
use axum::{routing::get, Router};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let app = Router::new().route("/", get(|| async { "Hello, World!" }));
println!("Listening on port 3000!");
let listener = tokio::net::TcpListener::bind("127.0.0.1:3000").await?;
axum::serve(listener, app)
.with_graceful_shutdown(elegant_departure::tokio::depart().on_termination())
.await?;
Ok(())
}
§Example: Simple worker
A minimal example with multiple workers getting notified on shutdown. The workers need an additional second after notification to exit.
async fn worker(name: &'static str) {
// Creates a new shutdown guard, the shutdown will wait for all guards to either be dropped
// or explicitly cancelled.
let guard = elegant_departure::get_shutdown_guard();
println!("[{}] working", name);
// The future completes when a shutdown is initiated
guard.wait().await;
println!("[{}] shutting down", name);
tokio::time::sleep(std::time::Duration::from_secs(1)).await;
println!("[{}] done", name);
// Guard dropped here, signalling shutdown completion
}
#[tokio::main]
async fn main() {
tokio::spawn(worker("worker 1"));
tokio::spawn(worker("worker 2"));
// Could be any condition, e.g. waiting for a HTTP request
tokio::signal::ctrl_c().await.unwrap();
// Initiates the shutdown and waits for all tasks to complete
// Note: you could wrap this future using `tokio::time::timeout`.
elegant_departure::shutdown().await;
println!("Shutdown completed");
}
Example output:
[worker 1] working
[worker 2] working
^C
[worker 1] shutting down
[worker 2] shutting down
[worker 2] done
[worker 1] done
Shutdown completed
§Example: Tokio integration
The same example as before, but this time using the tokio integration to exit
on a termination signal (Ctrl+C
or SIGTERM
), SIGUSR1
or a custom future,
whichever happens first.
use tokio::{signal::unix::SignalKind, time::sleep};
/* ... */
#[tokio::main]
async fn main() {
tokio::spawn(worker("worker 1"));
tokio::spawn(worker("worker 2"));
elegant_departure::tokio::depart()
// Terminate on Ctrl+C and SIGTERM
.on_termination()
// Terminate on SIGUSR1
.on_signal(SignalKind::user_defined1())
// Automatically initiate a shutdown after 5 seconds
.on_completion(sleep(std::time::Duration::from_secs(5)))
.await
}
§More Examples
More examples can be found in the examples directory of the source code repository:
- Simple: the full simple example from above
- Axum: the full axum example from above
- Tokio: the full tokio example from above
- Hyper: a shutdown example using the Hyper webserver
- Worker: example implementation of a worker using
select!
- Smol: example using the smol runtime
- Async Std: example using the async_std runtime
§Things to consider
- Do not wait for a shutdown while holding a shutdown guard, this will wait forever:
let guard = elegant_departure::get_shutdown_guard();
// This will never exit, because `guard` is never dropped/cancelled!
elegant_departure::shutdown().await;
- Do not dynamically allocate shutdown guards for short living tasks. Currently every shutdown guard is stored globally and never freed. If you dynamically allocate shutdown guards, this essentially leaks memory.
Modules§
- tokio
tokio
Additional tokio integration, for builtin shutdown on signal, ctrl-c, etc.
Structs§
- A guard which delays shutdown until it is cancelled or dropped.
- Future returned by the
ShutdownGuard::wait
method.
Functions§
- Creates a new shutdown guard and returns it.
- Initiates the global shutdown.
- Creates a future which terminates when the shutdown is completed.