简体   繁体   中英

How to add a shutdown hook to a Rust program?

I'm writing a Rust program which needs to save some data at the end of its execution, whatever happens.

In the Java world, I would do that with a shutdown hook . There is a crate, aptly called shutdown_hooks , but it seems only able to register extern "C" functions (and I'm not totally sure it will run on panic!(...) ).

How can I implement a shutdown hook that triggers on normal exit as well on panic?

In the general case, it's impossible. Even ignoring effects from outside of the program (as mentioned by mcarton ), whoever compiles the final binary can choose if a panic actually triggers stack unwinding or if it simply aborts the program. In the latter case, there's nothing you can do.

In the case of unwinding panic or normal exit, you can implement Drop and use the conventional aspects of RAII :

struct Cleanup;

impl Drop for Cleanup {
    fn drop(&mut self) {
        eprintln!("Doing some final cleanup");
    }
}

fn main() {
    let _cleanup = Cleanup;

    panic!("Oh no!");
}
thread 'main' panicked at 'Oh no!', src/main.rs:12:5
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace.
Doing some final cleanup

It appears that Java's shutdown hooks allow running multiple pieces of code in parallel in threads. You could do something similar with some small modifications:

use std::{
    sync::{Arc, Condvar, Mutex},
    thread,
};

#[derive(Debug, Default)]
struct Cleanup {
    hooks: Vec<thread::JoinHandle<()>>,
    run: Arc<Mutex<bool>>,
    go: Arc<Condvar>,
}

impl Cleanup {
    fn add(&mut self, f: impl FnOnce() + Send + 'static) {
        let run = self.run.clone();
        let go = self.go.clone();

        let t = thread::spawn(move || {
            let mut run = run.lock().unwrap();

            while !*run {
                run = go.wait(run).unwrap();
            }

            f();
        });
        self.hooks.push(t);
    }
}

impl Drop for Cleanup {
    fn drop(&mut self) {
        eprintln!("Starting final cleanup");

        *self.run.lock().unwrap() = true;
        self.go.notify_all();

        for h in self.hooks.drain(..) {
            h.join().unwrap();
        }

        eprintln!("Final cleanup complete");
    }
}

fn main() {
    let mut cleanup = Cleanup::default();

    cleanup.add(|| {
        eprintln!("Cleanup #1");
    });

    cleanup.add(|| {
        eprintln!("Cleanup #2");
    });

    panic!("Oh no!");
}

See also:

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM