Hacker News new | past | comments | ask | show | jobs | submit login

While some programs/libs (most notably libjpeg) use setjmp/longjmp, I tend to avoid them:

  * Does setjmp/longjmp save/restore all registers (eg simd) of modern CPU variations? Can incosistiences happen here?
  * As a goto, it restores some context (registers) but not others (memory), which can be counterintuitive. Does it work with volatile variables
  * Does it play well with less standard C features like attribute(cleanup) or with C++ features like exception handling or class destructors? To avoid issues, it may be best to stick to very procedural and basic C when using setjmp/longjmp?
  * Except registers and memory, there are also IO, FS, Net (at least) contexts, and these are not restored (cannot be really), so this notion of "restore certain vars to their original states" might not work well with certain types of code
Bc some programming gods recommend using setjmp/longjmp, my hesitance is likely unfounded.



"setjmp/longjmp" must save/restore only those registers that are defined by the C ABI as being preserved across function calls.

The program point to where "longjmp" jumps is viewed by the compiler as a function return point, so the compiler assumes that all the other registers hold undefined values.

For most CPUs, the C ABI defines the SIMD registers as being not preserved across function calls, so "setjmp" does not need to save them. Had some of them been defined as preserved registers, "setjmp" would have also saved those.

"setjmp/longjmp" cannot be used in any C++ program, unless all the nested functions that occur between a "setjmp" and a "longjmp" are C functions. This may happen in C libraries which are linked into C++ programs and which may use "setjmp/longjmp" internally, without interfering with C++ objects.

As you say, "attribute(cleanup)" is not something specified in the C language standard, so you cannot expect that "longjmp" will invoke the cleanup function, unless you use a specific libc implementation, whose documentation explicitly says that its "longjmp" will invoke the cleanup functions, when the program is compiled with a certain C compiler. I am not aware of any such "longjmp" implementation.

If you want to use exceptions in a C program, you must use "setjmp/longjmp". If you do not want to use exceptions in a C program, you have no need for them.

The same happens in any other programming language. If you do not want to use exceptions in a C++ program, you have no need to use keywords like "try" and "throw".


> I am not aware of any such "longjmp" implementation.

Clang on Windows behaves this way! That is, when targeting the Visual C++ runtime, as opposed to MinGW.

With the Visual C++ toolchain and runtime, longjmp behaves like a C++ exception throw and goes through a whole stack unwinding routine, unlike on Unix (and MinGW) where it just sets a few registers and branches. This has the downside of being much slower, but the benefit that longjmp will run destructors of C++ local variables as it unwinds (as well as SEH `__finally` blocks), making it less of a footgun in C++ code.

Visual C++ itself doesn't support GCC extensions like `__attribute__((cleanup))`, but these days Clang is highly compatible with Visual C++ while also supporting GCC extensions.

Edit: Windows also has the distinction of, on x86-64, treating the SIMD registers xmm6-xmm15 as callee-saved (preserved across function calls). As you mention, this means that setjmp has to save them, which is fine except that it bloats the size of jmp_buf.


One of the largest C++ projects I worked on was compiled with exceptions disabled (-fno-exceptions).

Still in production, for 20 years now.


Google famously doesn't use exceptions for C++, and Google's C++ code is not exception-safe (https://google.github.io/styleguide/cppguide.html#Exceptions.)


Specifically, The C++ Programming Language standard ISO/IEC 14882:2011 18.10/4 [support.runtime] says this.

> A setjmp/longjmp call pair has undefined behavior if replacing the setjmp and longjmp by catch and throw would invoke any non-trivial destructors for any automatic objects.

Of course, the C++ language runtime is usually written in pure C, and at least one naive implementation of the C++ try/catch mechanism uses setjmp/longjmp under the hood.


I wonder though if setjmp/longjmp was designed with exceptions in mind, or just as a general escape hatch for the 'tamed' structured goto in C (my guess would be the latter).


> While some programs/libs (most notably libjpeg) use setjmp/longjmp

Also libpng. What is it with these graphics guys that they can't just return an error code like a normal person?


libjpeg is a fairly old library (1991), so it isn't terribly surprising that it doesn't follow modern programming conventions.

libpng is only slightly newer (1995), and may have adopted the pattern from libjpeg.


The only local variables you can count on being preserved are "volatile" variables initialised before the setjmp() call, and then not changed.

That's all the guarantee the C standard gives. Anything else is specific to compiler/OS.


As you have phrased them, the standard guarantees seem weaker than they are actually specified in the C standard.

Where a "longjmp" returns after a "setjmp", everything is preserved exactly like after a normal function call (including the registers specified by the ABI),

"except that the values of objects of automatic storage duration that are local to the function containing the invocation of the corresponding setjmp macro that do not have volatile-qualified type and have been changed between the setjmp invocation and longjmp call are indeterminate."

For the most frequent use case, when "longjmp" is used to throw exceptions, the only local variables that can become indeterminate, according to the standard, are those that have been passed as arguments to functions invoked inside the C equivalent of a "try" block, or which have been explicitly modified in another way there. Any local variable that is not assigned to or passed as an argument there is preserved.

The standard behavior is normal, because where a "longjmp" returns it is not known where the execution of the nested functions has been aborted and whether any of their output parameters already store their expected final values or only some unpredictable temporary values.




Join us for AI Startup School this June 16-17 in San Francisco!

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: