WARNING: this crate is experimental and even careful use is likely undefined behavior.
This crate exposes four C standard library functions to Rust:
pub fn setjmp(env: *mut jmp_buf) -> c_int;
pub fn sigsetjmp(env: *mut sigjmp_buf, savesigs: c_int) -> c_int;
pub fn longjmp(env: *mut jmp_buf, val: c_int) -> c_void;
pub fn siglongjmp(env: *mut sigjmp_buf, val: c_int) -> c_void;
as well as the jmp_buf
and sigjmp_buf
types needed to use them.
See
setjmp(3)
for details and caveats.
Also see RFC #2625.
To interact better with C code that may use
setjmp()
/longjmp()
:
longjmp()
happens, you may want the rust code to catch, the
longjmp()
, transform it into a panic (to safely unwind), then
catch_unwind()
,
then turn it back into a longjmp()
to return to someplace in the
C code (the last place that called setjmp()
).longjmp()
from the C code and handle it somehow.longjmp()
to return control to C code.It is possible to use setjmp()
/longjmp()
just for managing
control flow in rust (without interacting with C), but that would be
quite dangerous and has no clear use case.
Ordinarily, using a C function from rust is easy: you just declare it. Why go to the trouble of making a special crate?
jmp_buf
and sigjmp_buf
types are not trivial and are
best defined using bindgen on the system's <setjmp.h>
header.sigsetjmp
the actual
libc symbol might be __sigsetjmp
, and there may be a macro
to rewrite the sigsetjmp()
call into __sigsetjmp()
.The invocation of setjmp can appear only in the following contexts (see this comment):
match
, e.g. match setjmp(env) { ... }
.if setjmp(env) $integer_relational_operator $integer_constant_expression { ... }
setjmp(env);
See tests for examples.
Beyond the many challenges using setjmp/longjmp
in C, there are
additional challenges using them from rust.
longjmp()
must be
careful to not jump over any stack frame that owns references to
variables that have destructors.fork()
or setjmp()
, so it's easy to imagine
that rust might generate incorrect code around such a function.returns_twice
attribute; but rust has no way to propagate that attribute to
LLVM. Without this attribute, it's possible that LLVM itself will
generate incorrect code (See
this
comment).Given these problems, you should seriously consider alternatives.
One alternative is to use C wrappers when entering a rust stack frame
from C or a C stack frame from rust. The wrappers could turn special
return values from rust into a C longjmp()
if necessary, or catch
a longjmp()
from C and turn it into a rust panic!()
,
respectively. This is not always practical, however, so sometimes
calling setjmp()
/longjmp()
from rust is still the best
solution.
setjmp()
with #[inline(never)]
to
reduce the chances for misoptimizations.setjmp()
returns 0
and possible longjmp()
should be as minimal as possible. Typically, this might just be
saving/setting global variables and calling a C FFI function (which
might longjmp()
). This code should avoid allocating memory on
the heap, using types that implement the Drop
trait, or code
that is complex enough that it might trigger misoptimizations.longjmp()
or any parent stack frames should also
be minimal. Typically, this would be just enough code to retrieve a
return value from a callee, or catch a panic with
catch_unwind()
.
This code should avoid allocating memory on the heap, using types
that implement the Drop
trait, or code that is complex enough
that it might trigger misoptimizations.