cshu
cshu

Reputation: 5944

Safe usage of `setjmp` and `longjmp`

I know people always say don't use longjmp, it's evil, it's dangerous.

But I think it can be useful for exiting deep recursions/nested function calls.

Is a single longjmp faster than a lot of repeated checks and returns like if(returnVal != SUCCESS) return returnVal;?

As for safety, as long as dynamic memory and other resources are released properly, there shouldn't be a problem, right?

So far it seems using longjmp isn't difficult and it even makes my code terser. I'm tempted to use it a lot.

(IMHO in many cases there is no dynamic memory/resources allocated within a deep recursion in the first place. Deep function call seems more common for data parsing/manipulation/validation. Dynamic allocation often happens at a higher level, before invoking the function where setjmp appears.)

Upvotes: 6

Views: 2836

Answers (2)

setjmp and longjmp can be seen as a poor man's exception mechanism. BTW, Ocaml exceptions are as quick as setjmp but have a much clearer semantics.

Of course a longjmp is much faster than repeatedly returning error codes in intermediate functions, since it pops up a perhaps significant call stack portion.

(I am implicitly focusing on Linux)

They are valid and useful as long as no resources are allocated between them, including:

  • heap memory (malloc)
  • fopen-ing FILE* handles
  • opening operating system file descriptors (e.g. for sockets)
  • other operating system resources, such as timers or signal handlers
  • getting some external resource managed by some server, e.g. X11 windows (hence using any widget toolkit like GTK), or database handle or connection...
  • etc...

The main issue is that that property of not leaking resources is a global whole-program property (or at least global to all functions possibly called between setjmp and longjmp), so it prohibits modular software development : any other colleague having to improve some code in any function between setjmp and longjmp has to be aware of that limitation and follow that discipline.

Hence, if you use setjmp document that very clearly.

BTW, if you only care about malloc, using systematically Boehm's conservative garbage collector would help a lot; you'll use GC_malloc instead of malloc everywhere and you won't care about free, and practically that is enough; then you can use setjmp without fears (since you could call GC_malloc between setjmp and longjmp).

(notice that the concepts and the terminology around garbage collector are quite related to exception handling and setjmp, but many people don't know them enough. Reading the Garbage Collection Handbook should be worthwhile)

Read also about RAII and learn about C++11 exceptions (and their relation to destructors). Learn a bit about continuations and CPS.

Read setjmp(3), longjmp(3) (and also about sigsetjmp, siglongjmp, and setcontext(3)) and be aware that the compiler has to know about setjmp

Upvotes: 8

Mabus
Mabus

Reputation: 1493

You should note that calling setjmp in some contexts is not guaranteed to be safe (for example, you can't portably store the return value of setjmp).

Also, if you want to access local variables after calling setjmp, in the same function, that could have been changed you should mark that variables as volatile.

Using setjmp and longjmp is also useful because if the recursion causes a Stack Overflow, you can recover with a longjmp from the signal handler (don't forget to set an alternate stack) and return an error instead. If you want to do that you should consider to use sigsetjmp and siglongjmp for preserving signal dispositions.

Upvotes: 3

Related Questions