setjmp / longjmp

In einer Zeit, als die alten Götter herrschten, schrie das Land, das Kriegsherrn in Aufruhr versetzten, nach …

Exceptions, weil weder Hercules noch Xena ein Konzept zur strukturierten Fehlerbehandlung vorweisen konnten.

C++ wurde erst 1998 vollständig standardisiert und die anderen Sprachen … gab es die eigentlich schon?

Doch einer der ältesten Titanen, nämlich C, zeigte sich unbeeindruckt, da er schon lange über ein Technik verfügte, in “Ausnahmefällen” quer durch den Code zu springen.


Die Rede ist von den oft vergessenen Funktionen setjmp()/longjmp() und die beiden stellen aus meiner bescheidenen Sicht die Krönung der Hardware- Abstraktion in einer Hochsprache dar.

setjmp() sichert den aktuellen CPU Status in einem Puffer und arbeitet dann wie jede andere Funktion linear im Code weiter.
Wird jedoch später longjmp aktiviert, kommt es zur Wiederherstellung des gespeicherten Zustandes und egal wo in welcher Unterfunktion man gerade Code abarbeitete, man landet wieder dort, wo man zuvor setjmp ausgeführt hatte.

Syntaktisch Funktionen auch Exceptions so. Man umklammert einen kritischen Block mit try { ... } und landet bei einem Fehler (Exception) im nachfolgenden catch(...) { ... } Block.

setjmp teilt über seinen Rückgabewert mit, ob es sich um ein “Setzen” des Sprungpunktes handelte oder eine Rücksprung zu diesem.
Man könnte also fast glauben, C hätte einen Exception Mechanismus und man könnte C++ Exceptions mit setjmp()/longjmp() umsetzen.

Ganz so einfach ist es aber nicht, denn es handelt sich eben nur um einen gezielten Sprung. In anderen Funktion geöffnete Ressourcen bleiben weiter offen, da sie niemand schließt und folglich ist der Zustand des Programms nach einem longjmp() in vielen Fällen korrumpiert.

Wenn, dann müsste man (am besten automatisiert) um jeden Funktionsaufruf einen setjmp()/longjmp() mit einem eigenen Kontextpuffer basteln, der lokale Spezialitäten freigibt, so wie es in C++ durch Destruktoren erledigt wird.

Denn C++ Exceptions garantieren, dass im Falle von throw ... jede laufende Funktion “rückabgewickelt” wird, bis man den try-catch Block erreicht hat. Eine solche Garantie kennen setjmp/longjmp leider nicht.

Einsatzort

Tja, wo setzt man also solche Konstrukte heute noch ein?
Nun es gibt C-Bibliotheken, die in Fehlerfällen abort() oder exit() aufrufen, was einem ziemlich den Tag versauen kann.

Die libpng ist so ein Teufelchen. Sie lässt einen nur eine Notfall-Funktion registrieren, die aufgerufen wird um nach Hilfe zu rufen, beendet aber nach deren Rückkehr den Prozess, zumindest war dies in früheren Versionen so.
Einzige Lösung ist der libpng mit einem longjmp die Kontrolle zu entziehen.

Man kann longjmp() aber auch in Signalhandlern einsetzen, um sich vor einem tödlichen [SEGFAULT](http://de.wikipedia.org/wiki/Schutzverletzung) zu schützen.
Signale werden ähnlich wie normale Funktionsaufrufe in den laufenden Code injiziert. Unterbricht der Prozessor die Ausführung wegen eines solchen schweren Fehlers, findet die Ausführung über (genauer genommen ‘unter’) dem Context der zuletzt ausgeführten Funktion statt.
Und von hier aus kann man zum letzten “Fail-Safe” Punkt zurückspringen. Andernfalls würde ebenfalls nach dem Signalcode der Prozess beendet werden.

Fazit

Jeder C Programmierer, die noch nie von der Existenz von setjmp()/longjmp() gehört hat, sollte sich wieder mal die Zeit nehmen und im Handbuch nachschlagen.

Diese Funktionen sind besonders in der Systemprogrammierung hilfreich um Kollateralschäden durch defekte Komponenten zu bekämpfen. Null-Pointer und andere tödliche Programmierfehler können so zur Laufzeit abgefangen werden und das Programm in einen (einigermaßen) stabilen Zustand überführen.