在 Rust 程序意外退出期间将函数注册到 运行 的最佳方法是什么?
What's the best way to register a function to run during an unexpected exit of a Rust program?
我正在用 Rust 创建一个终端文本编辑器。小编将终端设置为raw模式,禁用字符回显等,退出时恢复终端原有功能
然而,编辑器存在一些错误,并且由于无符号变量下溢等问题而时不时地意外崩溃。发生这种情况时,将终端恢复到其原始状态的清理代码永远不会 运行s.
我想要运行的清理函数如下:
fn restore_orig_mode(editor_config: &EditorConfig) -> io::Result<()> {
termios::tcsetattr(STDIN, termios::TCSAFLUSH, &editor_config.orig_termios)
}
尝试catch_unwind
。我没有用过,所以我不能保证它有效。
在最新稳定的Rust中,@for1096的回答是最好的。在您的情况下,应用起来可能非常简单,因为您的清理不需要使用与应用程序代码共享的状态:
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()
}
}
如果您的清理工作需要访问与您的应用程序共享的状态,那么您将需要一些额外的机制来让编译器相信它是安全的。例如,如果您的应用程序如下所示:
// 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 { }
}
}
然后,为了调用 clean_up
,您必须管理对数据的访问,如下所示:
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();
}
}
}
在 Rust 1.9 之前,您只能处理子线程中发生的恐慌。这没什么不同,除了你需要克隆 Arc
因为原始的需要 move
d 到线程闭包中。
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();
}
}
}
在 Unix 应用程序和使用其他语言(如 C)中解决此问题的常见方法是 fork()
并让您的 parent 等待 child。在 child 错误退出时,清理。
如果清理很重要,这确实是唯一可靠的清理方法。例如,您的程序可能会被 Linux OOM kill 杀死。它永远无法 运行 语言特定的恐慌、异常、at_exit 或类似的东西,因为操作系统会简单地破坏它。
通过一个单独的进程监视它,该进程可以处理任何特殊的文件或共享内存清理。
此解决方案实际上不需要使用 fork()
。 parent 可以是 shell 脚本或单独的可执行文件。
我正在用 Rust 创建一个终端文本编辑器。小编将终端设置为raw模式,禁用字符回显等,退出时恢复终端原有功能
然而,编辑器存在一些错误,并且由于无符号变量下溢等问题而时不时地意外崩溃。发生这种情况时,将终端恢复到其原始状态的清理代码永远不会 运行s.
我想要运行的清理函数如下:
fn restore_orig_mode(editor_config: &EditorConfig) -> io::Result<()> {
termios::tcsetattr(STDIN, termios::TCSAFLUSH, &editor_config.orig_termios)
}
尝试catch_unwind
。我没有用过,所以我不能保证它有效。
在最新稳定的Rust中,@for1096的回答是最好的。在您的情况下,应用起来可能非常简单,因为您的清理不需要使用与应用程序代码共享的状态:
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()
}
}
如果您的清理工作需要访问与您的应用程序共享的状态,那么您将需要一些额外的机制来让编译器相信它是安全的。例如,如果您的应用程序如下所示:
// 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 { }
}
}
然后,为了调用 clean_up
,您必须管理对数据的访问,如下所示:
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();
}
}
}
在 Rust 1.9 之前,您只能处理子线程中发生的恐慌。这没什么不同,除了你需要克隆 Arc
因为原始的需要 move
d 到线程闭包中。
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();
}
}
}
在 Unix 应用程序和使用其他语言(如 C)中解决此问题的常见方法是 fork()
并让您的 parent 等待 child。在 child 错误退出时,清理。
如果清理很重要,这确实是唯一可靠的清理方法。例如,您的程序可能会被 Linux OOM kill 杀死。它永远无法 运行 语言特定的恐慌、异常、at_exit 或类似的东西,因为操作系统会简单地破坏它。
通过一个单独的进程监视它,该进程可以处理任何特殊的文件或共享内存清理。
此解决方案实际上不需要使用 fork()
。 parent 可以是 shell 脚本或单独的可执行文件。