如何解决如何在 Rust 子线程中捕获堆栈溢出?
我有一个可以嵌套很深的递归算法(它是反编译器的一部分)。我知道通常你不会只增加堆栈大小,因为堆栈溢出通常表示无限递归,但在这种情况下,算法有时可能只需要更大的堆栈,所以我在带有堆栈的子线程中运行该算法可以通过 CLI 标志增加的大小:
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
传递给应用程序,运行线程将获得 20MB 的堆栈,只要这足够了,它就会愉快地运行。
除非,就是第一次运行它,只有默认的 8MB 堆栈,你会得到这个错误:
thread 'run' has overflowed its stack
fatal runtime error: stack overflow
我想捕获此错误并打印一条消息,提醒用户他们可以通过 --stack-size=X
为反编译器提供更大的堆栈。
如何捕获 Rust 子线程的堆栈溢出?
解决方法
“中止恐慌”:
堆栈溢出属于我所看到的“中止恐慌”。无法通过使用 catch_unwind()
来捕获和恢复它们。 OP 建议使用子进程将故障与应用程序的其余部分隔离,这似乎是一种合理的解决方法。
这是 Reddit 上关于“堆栈探测”(以及其他内容)的精彩 long thread。这可能是一种防止线程溢出的方法。如果您想了解更多信息,请查看 Module compiler_builtins::probestack 的文档。
此参考文献的一些摘录:
堆栈探测的目的是提供一个静态保证,如果一个线程有一个保护页,那么堆栈溢出一定会命中那个保护页。
最后值得注意的是,在撰写本文时,LLVM 仅支持 x86 和 x86_64 上的堆栈探测。
需要注意。我看到有人提到堆栈探测功能并不完全安全。这对于大多数应用程序来说可能无关紧要,但对于通过网站自动化提供的编译器之类的东西来说可能无关紧要。
避免递归
递归算法更容易编码,但在许多情况下效率低于循环迭代树等数据结构。循环方法更难编码,但速度更快并且使用更少的内存。如果树遍历是要解决的问题,那么网上有很多示例可供参考。避免递归的 Some algorithms 使用自己的编程声明的堆栈,例如向量、列表或其他类似堆栈的结构。其他算法如 Morris Traversal 不需要维护堆栈数据结构。重新处理有问题的递归逻辑是减少堆栈溢出机会的一种方法。
对于非致命的可恢复恐慌:
如果恐慌不是致命的/不可恢复的,则可以使用 std::panic
crate 捕获它们并获取诊断信息。
有关控制恐慌的更多信息可以在 Rust 版指南的 Controlling panics with std::panic 部分找到。这是关于 std::panic
的文档的 link。
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(())
}
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。