首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何在Rust子线程中捕获堆栈溢出?

如何在Rust子线程中捕获堆栈溢出?
EN

Stack Overflow用户
提问于 2021-06-18 12:33:42
回答 1查看 164关注 0票数 1

我有一个可以嵌套得很深的递归算法(它是反编译器的一部分)。我知道通常情况下您不会仅仅增加堆栈大小,因为堆栈溢出通常表示无限递归,但在这种情况下,算法有时可能只需要更大的堆栈,因此我在一个子线程中运行该算法,其堆栈大小可以通过CLI标志来增加:

代码语言:javascript
复制
fn main() -> Result<(), Box<std::io::Error>> {
    // Process arguments
    let args: Cli = Cli::from_args();

    let child = thread::Builder::new()
        .name("run".into())
        .stack_size(args.stack_size * 1024 * 1024)
        .spawn(move || -> Result<(), Box<std::io::Error>> { run(args)?; Ok(()) })
        .unwrap();

    child.join().unwrap()?;

    Ok(())
}

fn run(args: Cli) -> Result<(), Box<std::io::Error>> {
    ...
}

这很有效,将--stack-size=20传递给应用程序,run线程将获得一个20MB的堆栈,只要足够,它就会愉快地运行。

除非是第一次运行它,只使用8MB的默认堆栈,并且您会得到以下错误:

代码语言:javascript
复制
thread 'run' has overflowed its stack
fatal runtime error: stack overflow

我想捕捉这个错误,而不是打印一条消息,提醒用户他们可以传递--stack-size=X来为反编译器提供更大的堆栈。

如何捕获Rust子线程的堆栈溢出?

EN

回答 1

Stack Overflow用户

发布于 2021-06-18 13:25:28

“中止死机”:

堆栈溢出属于我见过的被称为“中止死机”的类别。它们不能被捕获,也不能通过使用catch_unwind()恢复。OP建议使用子进程将故障与应用程序的其余部分隔离,这似乎是一种合理的解决方法。

Reddit上有一个很好的long thread,它谈到了“堆栈探测”(还有其他东西)。这可能是防止线程溢出的一种方法。如果您想了解更多信息,这里有Module compiler_builtins::probestack的文档。

下面是该参考文献的几个摘录:

堆栈探测的目的是提供一种静态保证,即如果线程具有保护页,则保证堆栈溢出将命中该保护页。最后,值得注意的是,在撰写本文时,LLVM只支持x86和x86_64上的堆栈探测。

有一个警告是必要的。我已经提到堆栈探测功能并不是完全安全的。对于大多数应用程序来说,这可能无关紧要,但对于通过网站自动化提供的编译器来说,这可能是无关紧要的。

避免递归

递归算法可以更容易编码,但在许多情况下比循环遍历树等数据结构的效率要低。循环方法更难编码,但速度更快,使用的内存更少。如果树遍历是正在解决的问题,那么在线上有很多示例可供参考。避免递归Some algorithms使用它们自己的编程声明的堆栈,例如向量、列表或其他类似堆栈的结构。其他算法,如Morris Traversal,不需要维护堆栈数据结构。返工有问题的递归逻辑是减少堆栈溢出机会的一种方法。

实现您自己的堆栈

有关如何在Python语言中将递归函数转换为迭代函数的语言不可知的示例,请参阅here's a generic approach。我在遇到递归多键快速排序代码的堆栈问题后写下了这个答案,我将其转换为Rust。我用它来对后缀数组进行排序,这需要成千上万的深度递归调用。在我按照所描述的方法进行转换后,应用程序能够毫无问题地处理非常大的文本块。

用于非致命可恢复死机的

如果死机不是致命的/不可恢复的,则可以使用std::panic机箱捕获它们并获取诊断信息。

有关控制恐慌的更多信息可以在Rust Edition Guide的Controlling panics with std::panic部分找到。这里有一个指向std::panic文档的link

代码语言:javascript
复制
use std::env;
use std::thread;
use std::panic::catch_unwind;
use std::panic;

fn main() -> Result<(), Box<std::io::Error>> {
    let args = env::args().collect::<Vec<String>>();

    panic::set_hook(Box::new(move |panic_info| {
        match panic_info.location() {
            Some(loc) => {
                println!("Panic in file {} line {}.", loc.file(), loc.line());
            },
            None => { println!("No panic info provided..."); },
        }
    }));

    let child = thread::spawn(move || {
        catch_unwind(|| {
            run(args);
        });
    });

    child.join();
    
    Ok(())
}

fn run(args: Vec<String>) -> Result<(), Box<std::io::Error>> {
    println!("Args from command line: {:?}", args);
    panic!("uh oh!");
    Ok(())
}
票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/68029361

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档