
Exceptions in C with Longjmp and Setjmp - AndreyKarpov
http://www.di.unipi.it/~nids/docs/longjump_try_trow_catch.html
======
rbehrends
A couple of caveats.

First, you rarely want to use setjmp() and longjmp(), but _setjmp() and
_longjmp(), or sigsetjmp(..., 0) and siglongjmp(..., 0) to avoid saving and
restoring the signal mask. You'll rarely be modifying the signal mask in
between, and the additional kernel calls to save/restore the signal mask are
pretty expensive.

Second, any function that uses setjmp() or friends should have its variables
declared volatile (or at least those that you rely on after executing a
longjmp()). Otherwise, you risk that they do not have the proper value after a
longjmp(). In particular, variables that are stored in registers and were
modified between setjmp() and longjmp() will typically be restored to the
value they had when setjmp() has called, not the value they had when you
called the function that resulted in a longjmp() (and optimizing compilers may
introduce further bugs). Unfortunately, declaring a variable as volatile will
typically prevent quite a few optimizations, but it's still necessary for
correctness.

~~~
rwmj
Thirdly: Any malloc'd pointers that are only tracked on the stack end up being
lost when you unwind the stack.

This third one makes using longjmp for exceptions almost untenable in real
life code. You can make it easier to track and free memory correctly by using
a pool allocator for the whole program, which may or may not be realistic, but
I once wrote a webserver this way (from scratch, hey it was the 90s).

~~~
norswap
Wrap malloc in a function that push malloc'ed pointer on a global stack,
insert NULL at the point where a try happens. When you throw an exception,
simply free everything on the stack until the previous NULL. Problem solved.

Better yet, simply free memory at the exception throw site, if possible.

~~~
rcfox
> Wrap malloc in a function that push malloc'ed pointer on a global stack,
> insert NULL at the point where a try happens. When you throw an exception,
> simply free everything on the stack until the previous NULL. Problem solved.

This frees everything, which isn't what you want. What if you've passed your
pointer off to somewhere else where it's still in use?

> Better yet, simply free memory at the exception throw site, if possible.

Most of the time, your exceptions are coming from low levels, like IO
libraries, that don't know anything about the memory that you've allocated.

~~~
daeken
> Most of the time, your exceptions are coming from low levels, like IO
> libraries, that don't know anything about the memory that you've allocated.

Yeah, but that's why you put exception handlers around anything that you
expect to throw an exception, IO being a big one. Dealing with memory cleanup
is definitely something that should be handled in the function allocating the
memory.

~~~
sausagefeet
So every function now needs a try/catch? Doesn't this kind of defeat the
purpose?

~~~
daeken
Every call to a function that can throw an exception should have a try/catch
block, so that things can be properly cleaned up. Global exception handlers
are seldom useful outside of reporting the existence of an error for future
usage.

~~~
norswap
Kind of defeat the point of exceptions in the first place. It becomes more
like an "enhance error return value" thing.

The thing about an exception is that you can't do about it in a number of
scopes. When you encounter the exception you just just jump to the scope where
you _can_ do something about it.

------
losvedir
If you like this article, the book C Interfaces and Implementations[1], goes
into this exact technique in great detail, along with many other useful ones.

I picked it up after tptacek recommended it here on HN, and it's definitely
worth every penny if you want to do any C programming on a reasonably sized
project.

[1] <http://www.amazon.com/gp/product/0201498413/>

------
hp
here is a great post from Owen Taylor in 1999 after this idea was repeatedly
raised for the "g" stack on Linux:

[http://mail.gnome.org/archives/gnome-
list/1999-December/msg0...](http://mail.gnome.org/archives/gnome-
list/1999-December/msg00609.html)

it kept coming up, too: [http://mail.gnome.org/archives/gtk-devel-
list/2001-February/...](http://mail.gnome.org/archives/gtk-devel-
list/2001-February/msg00107.html)

Here is what GLib 2.0 ended up with instead, which works well:
[http://developer.gnome.org/glib/2.31/glib-Error-
Reporting.ht...](http://developer.gnome.org/glib/2.31/glib-Error-
Reporting.html)

~~~
tptacek
It's worth mentioning here that exceptions are notoriously unsafe even in C++
programs, where automated ctors and dtors should mitigate these concerns (but
inevitably don't because of pointers).

------
malkia
If you have some C++ code linked, and use RAII
([http://en.wikipedia.org/wiki/Resource_Acquisition_Is_Initial...](http://en.wikipedia.org/wiki/Resource_Acquisition_Is_Initialization))
then you won't have the destructors of the objects in the stack getting
called. For example if you had a profiling kind of marker, that relies on C++
(instead of begin() end() kind of thing).

~~~
apaprocki
Similarly, jumping forward over C++ objects declared on the stack (also when
using goto) will skip their construction.

------
ma2rten
ReactOS actually makes heavy use of exception handling in C. The wiki page is
a bit messy, but here it is: <http://www.reactos.org/wiki/PSEH>

------
pagekalisedown
A practical example of this is libpng:

<http://www.libpng.org/pub/png/libpng-1.2.5-manual.html>

~~~
teraflop
I wouldn't go so far as to call that "practical." It's a cute trick, but it
becomes really annoying when trying to write libpng bindings for any
environment other than plain C.

------
norswap
It can even go much further than this. I have a version that \- supports
arbitrarily nested try/catch statement (even in the catch or try parts) \-
catching multiple exception in each clause \- supports exception hierarchy
(exception can "inherit" other exceptions)

I'll probably publish it one day, but it needs a little cleaning pass. If you
are interested, let me know.

------
obtino
If you're interested in similar implementations, Microsoft has a non-standard
try-except statement in their C library: [http://msdn.microsoft.com/en-
us/library/s58ftw19(v=vs.80).as...](http://msdn.microsoft.com/en-
us/library/s58ftw19\(v=vs.80\).aspx)

~~~
logancapaldo
That is not a "similar" implementation, and it's not in the "library". It
exposes the OS/ABI level exception functionality (which C++ exceptions are
also built atop of) to the compiler.

------
pmr_
Wasn't exception handling based on longjmp and setjmp abandoned in gcc in
favor of dwarf2 especially because the former implementation performs poorly?
Does the same approach yield different results for C instead of C++?

------
rcfox
I think the requirement of the ETRY macro solidifies this as a neat trick, and
not something you actually want to use in a project.

Forget ETRY once and you can kiss your afternoon goodbye while you debug.

~~~
daeken
Ever left a semicolon off of a struct definition? Forgetting end
statements/tokens will frequently lead to debugging pain, but that doesn't
make it any less valid. As noted elsewhere, ReactOS uses a very similar
structure to great effect.

~~~
rcfox
Missing a semicolon has a chance of giving a useful error message. Without
knowing what ETRY translates to, you have no hope of finding here it's
missing.

------
xvolQx
The current implementation can't be nested...

