An important application of nonlocal jumps is to permit an immediate return from a deeply nested function call, usually as a result of detecting some error condition. If an error condition is detected deep in a nested function call, we can use a nonlocal jump to return directly to a common localized error handler instead of laboriously unwinding the call stack. We present an example here.
#include "csapp.h"
#include <iostream>
using namespace std;
jmp_buf buf;
int error1 = 0;
int error2 = 1;
void foo(void), bar(void);
int main()
{
int rc;
rc = setjmp(buf);
if (rc == 0)
foo();
else if (rc == 1)
cout << "Detected an error1 condition in foo" << endl;
else if (rc == 2)
cout << "Detected an error2 condition in foo" << endl;
else
cout << "Unknown error condition in foo" << endl;
exit(0);
}
// Deeply nested function foo
void foo(void)
{
if (error1)
longjmp(buf, 1);
bar();
}
void bar(void)
{
if (error2)
longjmp(buf, 2);
}
In the above program, the main routine first calls setjmp to save the current calling environment, and then calls function foo, which in turn calls function bar.
If foo or bar encounter an error, they return immediately from the setjmp via a longjmp call. The nonzero return value of the setjmp indicates the error type.
Another important application of nonlocal jumps is to branch out of a signal handler to a specific code location, rather than returning to the instruction that was interrupted by the arrival of the signal. The following program uses signals and nonlocal jumps to do a soft restart whenever the user types ctrl-c at the keyboard. The sigsetjmp and siglongjmp functions are versions of setjmp and longjmp that can be used by signal handlers.
#include "csapp.h"
#include <iostream>
using namespace std;
sigjmp_buf buf;
void handler(int sig)
{
siglongjmp(buf, 1);
}
int main()
{
Signal(SIGINT, handler);
if (!sigsetjmp(buf, 1))
cout << "starting" << endl;
else
cout << "restarting" << endl;
while(1) {
Sleep(1);
cout << "processing..." << endl;
}
exit(0);
}
The initial call to the sigsetjmp function saves the stack and signal context when the program first starts. The main routine then enters an infinite processing loop. When the user types ctrl-c, the shell sends a SIGINT signal to the process, which catches it. Instead of returning from the signal handler, which would pass back control back to the interrupted processing loop, the handler performs a nonlocal jump back to the beginning of the main program. When we ran the program on our system, we got the following output:
starting
processing...
processing...
restarting User hits ctrl-c
processing...
restarting User hits ctrl-c
processing...
Source:
Randal E. Bryant, David R. O'Hallaron(2011). COMPUTER SYSTEMS A Programmer's Perspective (Second Edition).Beijing: China Machine Press.