抓狂!当 Rust 从 C FFI 调用时,没有产生线程

Posted

技术标签:

【中文标题】抓狂!当 Rust 从 C FFI 调用时,没有产生线程【英文标题】:Catching panic! when Rust called from C FFI, without spawning threads 【发布时间】:2015-02-07 16:36:50 【问题描述】:

我正在处理Rust wrapper for the Duktape javascript interpreter。在正常用例中,调用堆栈将如下所示:

    Rust:任意应用代码。 Rust:我的库包装器。 C:Duktape 解释器。 Rust:我的 Rust 代码。 Rust:应用程序代码中的任意回调。

如果 (5) 调用 panic! 会发生什么?根据 IRC 上的各种 Rust 开发人员的说法,尝试从像 (3) 这样的非 Rust 调用框架内部 panic! 可能会导致未定义的行为。

但根据 Rust 文档,捕获panic! 的唯一方法是使用std::task::try,它会产生一个额外的线程。还有rustrt::unwind::try,它不能在一个线程中嵌套两次,还有其他限制。

Benjamin Herr 提出的一种解决方案是,如果 (5) 中的代码出现恐慌,则中止该过程。我已经将他的解决方案打包为abort_on_panic,它似乎可以工作,对于“工作”的价值,包括“使整个程序崩溃,但至少不会巧妙地破坏事物”:

abort_on_panic!("cannot panic inside this block", 
    panic!("something went wrong!");
);

但是有没有一种方法可以模拟 std::task::try 而无需创建线程/任务的开销?

【问题讨论】:

【参考方案1】:

编者注:此答案早于 Rust 1.0,不再一定准确。其他答案仍然包含有价值的信息。

您无法“捕获”panic!。它终止当前线程的执行。因此,如果不启动一个新的来隔离,它将终止您所在的线程。

【讨论】:

谢谢! std::task::try 真的有必要产生一个新的操作系统线程吗?似乎可以实现它来保存一些运行时状态,在同一个 OS 线程中调用“任务”,并在完成时恢复运行时状态。这将保持与当前版本相同的公共 API,但它可能会节省调用 pthread_new 的所有 JavaScript 调用 Rust 的成本。 这比 Rust 高一点。【参考方案2】:

从 Rust 1.9.0 开始,您可以使用 panic::catch_unwind 来恢复错误:

use std::panic;

fn main() 
    let result = panic::catch_unwind(|| 
        panic!("oh no!");
    );
    assert!(result.is_err());

使用panic::resume_unwind 将其传递到下一层同样简单:

use std::panic;

fn main() 
    let result = panic::catch_unwind(|| 
        panic!("oh no!");
    );

    if let Err(e) = result 
        panic::resume_unwind(e);
    

【讨论】:

以上是关于抓狂!当 Rust 从 C FFI 调用时,没有产生线程的主要内容,如果未能解决你的问题,请参考以下文章

在 WSL 中学习 Rust ffi

如何在 Rust 的 FFI 中使用 C 预处理器宏?

如何将 C 字符串转换为 Rust 字符串并通过 FFI 转换回来?

如何将 Rust `Vec<T>` 暴露给 FFI?

为啥 Rust 函数和 FFI C++ 函数以相反的顺序执行?

如何从 NodeJS 中的 Rust FFI 函数返回字符串值?