5 ms·
Exceptions in C with Longjmp and Setjmp
- pagekalisedown 15y agoA practical example of this is libpng: http://www.libpng.org/pub/png/libpng-1.2.5-manual.html http://www.libpng.org/pub/png/libpng-1.2.5-manual.html
- wulczer 15y agoPostgreSQL also uses longjmp to implement TRY/CATCH macros: https://github.com/postgres/postgres/blob/master/src/include/utils/elog.h#L231 https://github.com/postgres/postgres/blob/master/src/include...
- teraflop 15y agoI 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.
- hp 15y agoas someone who has used libpng I'd describe it as an example of why this is a terrible idea.
- rbehrends 15y agoA 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 15y agoThirdly: 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 15y agoWrap 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 15y ago> 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 15y ago> 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.
- 15y ago
- malkia 15y agoIf you have some C++ code linked, and use RAII (http://en.wikipedia.org/wiki/Resource_Acquisition_Is_Initialization http://en.wikipedia.org/wiki/Resource_Acquisition_Is_Initial...) 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 15y agoSimilarly, jumping forward over C++ objects declared on the stack (also when using goto) will skip their construction.
- rcfox 15y agoI 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 15y agoEver 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 15y agoMissing 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.
- JoachimSchipper 15y agoOn one hand, I agree. On the other, POSIX already has EINTR... (and yes, this does cause lots of bugs.)
- norswap 15y agoIt 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.
- losethos 15y agoI made my own C/C++ compiler and my try/catch is not a kludge. God says... C:\LoseThos\www.losethos.com\text\WEALTH.TXT er. Whatever part of his succession might come to such children, would be a real addition to their fortune, and might, therefore, perhaps, without more inconveniency than what attends all duties of this kind, be liable to some tax. The casualties of the feudal law were taxes upon the transference of land, both from the dead to the living, and from the living to the living. In ancient times, they constituted, in every part of Europe, one of the principal branches of the revenue of the crown. The h
- ma2rten 15y agoReactOS 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 http://www.reactos.org/wiki/PSEH
- losvedir 15y agoIf 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/ http://www.amazon.com/gp/product/0201498413/
- xvolQx 15y agoThe current implementation can't be nested...
- obtino 15y agoIf 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).aspx http://msdn.microsoft.com/en-us/library/s58ftw19(v=vs.80).as...
- logancapaldo 15y agoThat 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_ 15y agoWasn'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++?
- hp 15y agohere 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/msg00609.html http://mail.gnome.org/archives/gnome-list/1999-December/msg0... it kept coming up, too: http://mail.gnome.org/archives/gtk-devel-list/2001-February/msg00107.html http://mail.gnome.org/archives/gtk-devel-list/2001-February/... Here is what GLib 2.0 ended up with instead, which works well: http://developer.gnome.org/glib/2.31/glib-Error-Reporting.html http://developer.gnome.org/glib/2.31/glib-Error-Reporting.ht...
- tptacek 15y agoIt'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).