4 ms·
It looks like on the platform in this blog post, longjmp() simply discards the stack frames between the setjmp() and the longjmp(). That's perfectly correct, of
by jeff-davis 6y ago
It looks like on the platform in this blog post, longjmp() simply discards the stack frames between the setjmp() and the longjmp(). That's perfectly correct, of course, because C doesn't make any guarantees about longjmp()ing across language boundaries (e.g. if there are C++ stack frames in between that have destructors to call).
But that's unfortunate, because cross language exceptions can and should be supported to promote polyglot programming. Rust has some interesting work going on here (RFC already accepted):
https://rust-lang.github.io/rfcs/2945-c-unwind-abi.html https://rust-lang.github.io/rfcs/2945-c-unwind-abi.html
That will allow a way to call C++ code such that, if it throws an exception, will unwind over the rust frames in between (calling rust destructors as necessary) and keep unwinding back into C++ code that catches the exception.
Of course, C++ doesn't have a defined ABI, but I guess the rust team is comfortable that it can conform to whatever mechanism the target platform uses to implement C++ exceptions.
Setjmp/longjmp from C over rust code is trickier, because as I mentioned before, longjmp() can simply discard all the intervening stack frames. So there would still need to be some ceremony to call setjmp() first before calling from Rust to C, so that you can "catch" the longjmp(), turn it into a Rust unwind (called "panic"), and then again longjmp() back to C code (the prior setjmp() location).
A bit of standardization could help here a lot. The concept of a "Plain Old Frame" (with no pending destructors or other concepts) could be interesting to standardize in a way that exceptions or longjmp() could know what they need to do (if all frames are POFs, just discard them; else unwind).
I'm not really an expert on the subject, but it seems to me that we aren't that far away from a truly polyglot language environment (at least C/C++/Rust) that deals with exceptions across languages smoothly.
- hasheddan 6y agoThis is super cool. Thanks so much for sharing!
- wahern 6y agoYou can longjmp from signal handlers as it's async-signal-safe in POSIX. I've done this to implement sigtimedwait on macOS and OpenBSD. If you thought writing exception-safe C++ was difficult, imagine writing async-signal-safe C++ code. You can even longjmp across different stacks entirely. Many userspace threading libraries do this, but I've done it (legitimately, I might add) simply from a signal handler invoked on an alternate stack (see sigaltstack(2)). From the perspective of setjmp/longjmp, there isn't necessarily anything "above" the current frame. In general, longjmp is much more like a bare goto than a structured control flow construct. Notably, C++ doesn't try to make goto play nice with constructors and destructors. Rust can do w'ever it wants because 1) it has no standard, 2) refuses to commit to an ABI of its own, and 3) there's only a single implementation, which also coincidentally 4) shares most of its toolchain with one of the most widely used C++ compilers.