2017-04-16 43 views
2

我在Rust中创建终端文本编辑器。编辑器将终端置于原始模式,禁用字符回显等,然后在退出时恢复原始终端功能。在Rust程序意外退出期间注册函数以运行的最佳方式是什么?

但是,由于诸如无符号变量下溢等问题,编辑器有一些错误,并且意外崩溃。发生这种情况时,将终端恢复到其原始状态的清除代码将不会运行。

的清理功能,我想运行如下:

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

您可以创建一个新线程来运行您的应用程序。主应用程序线程几乎没有任何可能出错的地方,并且可以在发生混乱时清理并重新启动子线程。 –

回答

0

尝试catch_unwind。我没有使用它,所以我不能保证它的工作。

+0

不在OP所要求的同一平流层。 – Qix

+1

嗯,我认为它实际上可以解决op的问题。你认为@isaacg – for1096

+2

@Qix由于“无符号变量下溢”导致的崩溃是由于panic,catch_unwind *处理。 (该文件警告说,这不会让中止这一过程的恐慌,但这些也不会被线程方法所捕获。) – user4815162342

4

在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(); 
     } 
    } 
} 

此前生锈1.9 ,你只能处理发生在子线程中的恐慌。除了你需要克隆Arc之外,这并没有什么不同,因为原来的那个需要在线程关闭中变成moved。

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(); 
     } 
    } 
} 
+1

我没有想过为这种事情使用线程,而不是并发。我不会接受一点,以防万一有更多的想法进来,但我尝试了这一点,而且它似乎工作得很好。 – isaacg

+0

正如另一个答案所说的,['catch_unwind'](https://doc.rust-lang.org/std/panic/fn.catch_unwind.html)允许避开一个线程。 – Shepmaster

+0

@Shepmaster'catch_unwind'是否有缺点? [文档](https://doc.rust-lang.org/std/panic/fn.catch_unwind。html#notes)提到它不适用于流氓恐慌。我不知道如何检查我的解决方案是否可以在那里工作,因为我不知道什么会导致流产恐慌而不是放松。 –

0

一个共同的解决了这个问题,在Unix应用程序和使用其他语言如C是fork()和您的父母等候孩子。在孩子出错时,清理。

+0

downvote?我猜我不应该说“在C中”。因为使用子进程确实是只有*可靠的清理方式。 –

+2

不幸的是,我们确实得到了偶尔的答案,即海报完全错过了语言。这不是这种情况,所以我编辑了一下,以澄清你的解决方案是语言不可知的。但是,[像其他答案](http://stackoverflow.com/questions/43441047/whats-the-best-way-to-register-a-function-to-run-during-an-unexpected-exit-of -a/43441583?noredirect = 1#comment73942311_43441099),这个答案也很稀疏。指向惯用的Rust代码来实现解决方案(直接调用'fork'不是惯用的)并解释为什么它是“唯一可靠的方法”将会有很大的帮助。 – Shepmaster

相关问题