Passing callbacks to C: panic!

Now I wonder what's the right thing to do if I can ensure that my Rust function does not panic and I want to have as minimum overhead as possible (e.g. because the function is called in a hot loop).

I see two options there:

  • Use extern "C". This would now insert an unnecessary panic-handling shim if I'm right, thus introduce runtime overhead.
  • Use extern "C-unwind". According to this comment (see "Mixed panic modes"), this would unnecessarily prohibit linking a crate with a panic=abort runtime if it was compiled with panic=unwind and contains a call to this never-unwinding "C-unwind" function (which is guaranteed to never unwind in this hypothetic use-case, but the compiler doesn't know that).

I would assume using "C-unwind" is still the best choice in that scenario?

Cargo either compile all crates with panic=unwind or all crates with panic=abort. This restriction is only relevant for the standard library (as it is precompiled with panic=unwind) and for non-cargo build systems that don't have this logic.

I figured it's not a practical problem when using cargo. Still, it seems like an unnecessary constraint imposed. But I guess it's impact is neglectible.

It seems that to resolve this, you would want to unsafely assert that your program does not panic. I can think of a few ways to do this right now. The one which works with no-std is creating a type whose drop implementation calls unreachable_unchecked(). Make an instance of the type, call a function that you don't think can panic, and then forget the instance, ensuring it's only dropped if an unwind occurs. This tells the compiler it's allowed to assume no unwinding occurs. On Rust 1.69 with optimizations, this works (see godbolt code here). The compiler is able to optimize out the branch where the function panics entirely.

4 Likes

I think my problem is that the function which can't panic is an extern "C" function that get's called from C, not from Rust.

It's this function that gets passed here to LMDB.

So not sure how I could apply your solution to my code.


Not sure if I understand the Godbolt example correctly, but when I modified it to create the UnsafeToDrop in the extern "C" function, it doesn't seem to do the optimization.

Curiously, it looks like the working combination to eliminate the abort-on-panic shim with #![feature(c_unwind)] is to use both an UnsafeToDrop guard and a redundant catch_unwind() (Compiler Explorer).

1 Like

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.