Struct ShutdownHandler

Source
pub struct ShutdownHandler { /* private fields */ }
Expand description

A graceful shutdown handler that allows all parts of an application to trigger a shutdown.

§Example

use std::pin::pin;
use std::sync::Arc;
use shutdown_handler::{ShutdownHandler, SignalOrComplete};

// Create the shutdown handler
let shutdown = Arc::new(ShutdownHandler::new());

// Shutdown on SIGTERM
shutdown.spawn_sigterm_handler().unwrap();

// Spawn a few service workers
let mut workers = tokio::task::JoinSet::new();
for port in 0..4 {
    workers.spawn(service(Arc::clone(&shutdown), port));
}

// await all workers and collect the errors
let mut errors = vec![];
while let Some(result) = workers.join_next().await {
    // unwrap any JoinErrors that happen if the tokio task panicked
    let result = result.unwrap();

    // did our service error?
    if let Err(e) = result {
        errors.push(e);
    }
}

assert_eq!(errors, ["port closed"]);

// Define our services to loop on work and shutdown gracefully

async fn service(shutdown: Arc<ShutdownHandler>, port: u16) -> Result<(), &'static str> {
    // a work loop that handles events
    for request in 0.. {
        let handle = pin!(handle_request(port, request));

        match shutdown.wait_for_signal_or_future(handle).await {
            // We finished handling the request without any interuptions. Continue
            SignalOrComplete::Completed(Ok(_)) => {}

            // There was an error handling the request, let's shutdown
            SignalOrComplete::Completed(Err(e)) => {
                shutdown.shutdown();
                return Err(e);
            }

            // There was a shutdown signal raised while handling this request
            SignalOrComplete::ShutdownSignal(handle) => {
                // We will finish handling the request but then exit
                return handle.await;
            }
        }
    }
    Ok(())
}

async fn handle_request(port: u16, request: usize) -> Result<(), &'static str> {
    // simulate some work being done
    tokio::time::sleep(std::time::Duration::from_millis(10)).await;
     
    // simulate an error
    if port == 3 && request > 12 {
        Err("port closed")
    } else {
        Ok(())
    }
}

Implementations§

Source§

impl ShutdownHandler

Source

pub fn new() -> Self

Source

pub fn sigterm() -> Result<Arc<Self>>

Creates a new ShutdownHandler and registers the sigterm handler

Source

pub fn spawn_sigterm_handler(self: &Arc<Self>) -> Result<()>

Registers the signal event SIGTERM to trigger an application shutdown

Source

pub fn spawn_signal_handler( self: &Arc<Self>, signal_kind: SignalKind, ) -> Result<()>

Registers a signal event to trigger an application shutdown

Source

pub fn shutdown(&self)

Sends the shutdown signal to all the current and future waiters

Source

pub fn wait_for_signal(&self) -> ShutdownSignal<'_>

Returns a future that waits for the shutdown signal.

You can use this like an async function.

Source

pub async fn wait_for_signal_or_future<F: Future + Unpin>( &self, f: F, ) -> SignalOrComplete<F>

This method will try to complete the given future, but will give up if the shutdown signal is raised. The unfinished future is returned in case it is not cancel safe and you need to complete it

use std::sync::Arc;
use std::pin::pin;
use shutdown_handler::{ShutdownHandler, SignalOrComplete};

async fn important_work() -> i32 {
    tokio::time::sleep(std::time::Duration::from_secs(2)).await;
    42
}

let shutdown = Arc::new(ShutdownHandler::new());

// another part of the application signals a shutdown
let shutdown2 = Arc::clone(&shutdown);
let handle = tokio::spawn(async move {
    tokio::time::sleep(std::time::Duration::from_secs(1)).await;
    shutdown2.shutdown();
});

let work = pin!(important_work());

match shutdown.wait_for_signal_or_future(work).await {
    SignalOrComplete::Completed(res) => println!("important work completed without interuption: {res}"),
    SignalOrComplete::ShutdownSignal(work) => {
        println!("shutdown signal recieved");
        let res = work.await;
        println!("important work completed: {res}");
    },
}

Trait Implementations§

Source§

impl Debug for ShutdownHandler

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Default for ShutdownHandler

Source§

fn default() -> ShutdownHandler

Returns the “default value” for a type. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.