What are some “good” ways to use longjmp/setjmp fo

2019-02-01 21:46发布

问题:

I have to use C for one project and I am thinking of using longjmp/setjmp for error handling as I think it will be much easier to handle error in one central place than return codes. I would appreciate if there are some leads on how to do this.

I am particularly concerned with resource cleanup being correctly done if any such error occurs.

Also how do I handle errors that result in multi-threaded programs using them?

Even better, is there some C library that already exists for error/exception handling?

回答1:

Have a look at this example/tutorial:
http://www.di.unipi.it/~nids/docs/longjump_try_trow_catch.html



回答2:

If you are worried about resource cleanup, you have to seriously wonder whether longjmp() and setjmp() are a good idea.

If you design your resource allocation system so that you can in fact clean up accurately, then it is OK - but that design tends to be tricky, and typically incomplete if, in fact, the standard libraries that your code uses themselves allocate resources that must be released. It requires extraordinary care, and because it is not wholly reliable, it is not suitable for long-running systems that might need to survive multiple uses of the setjmp()/longjmp() calls (they'll leak, expand, and eventually cause problems).



回答3:

Symbian implemented it's Leave mechanism in terms of longjmp() and this serves as a good walk through of all the things you need to do.

Symbian has a global 'cleanup stack' that you push and pop things you want cleaned up should an jump happened. This is the manual alternative to the automatic stack unwinding that a C++ compiler does when a C++ exception is thrown.

Symbian had 'trap harnesses' that it would jump out to; these could be nested.

(Symbian more recently reimplemented it in terms of C++ exceptions, but the interface remains unchanged).

All together, I think that proper C++ exceptions are less prone to coding errors and much faster than rolling your own C equivalent.

(Modern C++ compilers are very good at 'zero overhead' exceptions when they are not thrown, for example; longjmp() has to store the state of all the registers and such even when the jump is not later taken, so can fundamentally never be as fast as exceptions.)

Using C++ as a better C, where you only adopt exceptions and RAII, would be a good route should using longjmp() for exception emulation be tempting to you.



回答4:

I have only ever found one use for setjmp()/longjmp() and it wasn't to do with error handling.

There really is no need to use it for that since it can always be refactored into something easier to follow. The use of setjmp()/longjmp() is very similar to goto in that it can be easily abused. Anything that makes your code less readable is a bad idea in general. Note that I'm not saying they're inherently bad, just that they can lead to bad code easier than the alternatives.

FWIW, the one place they were invaluable was a project I did in the early days of the industry (MS-DOS 6 time frame). I managed to put together a co-operative multi-threading library using Turbo C which used those functions in a yield() function to switch tasks.

I'm pretty certain I haven't touched them (or had the need to) since those days.



回答5:

Exceptions are by far a better general mechanism, but in the deep dark days of C past, I wrote a processor emulator that included a command shell. The shell used to setjmp/longjmp for interrupt handling (ie, the processor is running and the user hits break/ctrl-c, the code traps SIGINT and longjmps back to the shell).



回答6:

I've used setjmp/longjmp reasonably tidily, to escape from within a callback, without having to negotiate my way up through various other library levels.

That case (if I recall correctly) was where a code within a yacc-generated parser could detect a (non-syntactical) problem, and wanted to abandon the parse but give a reasonably useful error report back to the caller on the other side of all the yacc-generated code. Another example was within a callback called from an Expat parser. In each case, there were other ways of doing this, but they seemed more cumbersome and obscure than simply bailing out in this way.

As other answers have pointed out, though, it's necessary to be careful about clean-up, and very thoughtful about making sure that the longjmp code is callable only within the scope of the region dynamically protected by the setjmp.

Doing it in the context of multi-threaded programming? I'm sure that's not impossible, but Oooh: get out your family-pack of aspirin now. It's probably wise to keep the setjmp/longjmp pairs as close together as possible. As long as a matching setjmp/longjmp pair are within the same thread, I expect you'll be OK, but ... be careful out there.