What's the best way to register a function to run during an unexpected exit of a Rust program?

不问归期 提交于 2020-01-15 07:28:07

问题


I'm creating a terminal text editor in Rust. The editor puts the terminal into raw mode, disabling character echoing and the like, and then restores the original terminal function upon exit.

However, the editor has some bugs, and crashes unexpectedly every now and again due to issues like unsigned variable underflow. When this happens, the cleanup code which would restore the terminal to its original state never runs.

The cleanup function I'd like to run is the following:

fn restore_orig_mode(editor_config: &EditorConfig) -> io::Result<()> {
    termios::tcsetattr(STDIN, termios::TCSAFLUSH, &editor_config.orig_termios)
}

回答1:


Try catch_unwind. I haven't used it, so I cannot guarantee it works.




回答2:


In the latest stable Rust, @for1096's answer is the best. In your case, it might be quite simple to apply because your clean-up does not need to use state that is shared with the application code:

use std::panic::catch_unwind;

fn run_editor(){
    panic!("Error!");
    println!("Running!");
}

fn clean_up(){
    println!("Cleaning up!");
}

fn main(){
    match catch_unwind(|| run_editor()) {
        Ok(_) => println!("Exited successfully"),
        Err(_) =>  clean_up()
    }
}

If your clean-up requires accessing shared state with your application, then you will need some additional machinery to convince the compiler that it is safe. For example, if your application looks like this:

// The shared state of your application
struct Editor { /* ... */ }

impl Editor {
    fn run(&mut self){
        println!("running!");
        // panic!("Error!");
    }

    fn clean_up(&mut self){
        println!("cleaning up!");
    }

    fn new() -> Editor {
        Editor { }
    }
}

Then, in order to call clean_up, you would have to manage access to the data, something like this:

use std::panic::catch_unwind;
use std::sync::{Arc, Mutex};

fn main() {
    let editor = Arc::new(Mutex::new(Editor::new()));

    match catch_unwind(|| editor.lock().unwrap().run()) {
         Ok(_) => println!("Exited successfully"),
         Err(_) => {
             println!("Application panicked.");
             let mut editor = match editor.lock() {
                Ok(guard) => guard,
                Err(poisoned) => poisoned.into_inner(),
             };
             editor.clean_up();
         }
    }
}

Prior to Rust 1.9, you can only handle panics that occur in a child thread. This isn't much different except that you need to clone the Arc because the original one will need to be moved into the thread closure.

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

fn main() {
    let editor = Arc::new(Mutex::new(Editor::new()));
    // clone before the original is moved into the thread closure
    let editor_recovery = editor.clone();

    let child = thread::spawn(move || {
         editor.lock().unwrap().run();
    });

    match child.join() {
        Ok(_) => println!("Exited successfully"),
        Err(_) => {
            println!("Application panicked.");
            let mut editor = match editor_recovery.lock() {
                Ok(guard) => guard,
                Err(poisoned) => poisoned.into_inner(),
            };
            editor.clean_up();
        }
    }
}



回答3:


A common solution to this problem in Unix applications and using other languages such as C is to fork() and have your parent wait for the child. On an error exit by the child, clean up.

This is really the only reliable way to clean up if the clean up is important. For example, your program might be killed by the Linux OOM kill. It will never be able to run a language specific panic, exception, at_exit or anything like that because the operating system simply destroys it.

By having a separate process watching it, that process can handle any special cleanup of files or shared memory.

This solution does not really require using fork(). The parent could be a shell script or a separate executable.



来源:https://stackoverflow.com/questions/43441047/whats-the-best-way-to-register-a-function-to-run-during-an-unexpected-exit-of-a

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!