首页 > 解决方案 > 如何在 Rust 程序中添加关闭挂钩?

问题描述

我正在编写一个 Rust 程序,它需要在执行结束时保存一些数据,无论发生什么。

在 Java 世界中,我会使用shutdown hook来做到这一点。有一个板条箱,恰当地称为shutdown_hooks,但它似乎只能注册extern "C"函数(我不完全确定它会在 上运行panic!(...))。

如何实现在正常退出和恐慌时触发的关闭挂钩?

标签: rustshutdown-hook

解决方案


在一般情况下,这是不可能的。即使忽略程序外部的影响(如mcarton 所提到的),编译最终二进制文件的人也可以选择恐慌是否真的触发堆栈展开,或者它是否只是中止程序。在后一种情况下,您无能为力。

在展开恐慌或正常退出的情况下,您可以实现和使用RAIIDrop的常规方面:

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

Java 的关闭钩子似乎允许在线程中并行运行多段代码。您可以通过一些小的修改来做类似的事情:

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!");
}

也可以看看:


推荐阅读