Go to the first, previous, next, last section, table of contents.


20 Non-Local Exits

Sometimes when your program detects an unusual situation inside a deeply nested set of function calls, you would like to be able to immediately return to an outer level of control. This section describes how you can do such non-local exits using the setjmp and longjmp functions.

20.1 Introduction to Non-Local Exits

As an example of a situation where a non-local exit can be useful, suppose you have an interactive program that has a "main loop" that prompts for and executes commands. Suppose the "read" command reads input from a file, doing some lexical analysis and parsing of the input while processing it. If a low-level input error is detected, it would be useful to be able to return immediately to the "main loop" instead of having to make each of the lexical analysis, parsing, and processing phases all have to explicitly deal with error situations initially detected by nested calls.

(On the other hand, if each of these phases has to do a substantial amount of cleanup when it exits--such as closing files, deallocating buffers or other data structures, and the like--then it can be more appropriate to do a normal return and have each phase do its own cleanup, because a non-local exit would bypass the intervening phases and their associated cleanup code entirely. Alternatively, you could use a non-local exit but do the cleanup explicitly either before or after returning to the "main loop".)

In some ways, a non-local exit is similar to using the `return' statement to return from a function. But while `return' abandons only a single function call, transferring control back to the point at which it was called, a non-local exit can potentially abandon many levels of nested function calls.

You identify return points for non-local exits calling the function setjmp. This function saves information about the execution environment in which the call to setjmp appears in an object of type jmp_buf. Execution of the program continues normally after the call to setjmp, but if a exit is later made to this return point by calling longjmp with the corresponding jmp_buf object, control is transferred back to the point where setjmp was called. The return value from setjmp is used to distinguish between an ordinary return and a return made by a call to longjmp, so calls to setjmp usually appear in an `if' statement.

Here is how the example program described above might be set up:

#include <setjmp.h>
#include <stdlib.h>
#include <stdio.h>

jmp_buf main_loop;

void 
abort_to_main_loop (int status)
{
  longjmp (main_loop, status);
}

int
main (void)
{
  while (1)
    if (setjmp (main_loop))
      puts ("Back at main loop....");
    else
      do_command ();
}

void 
do_command (void)
{
  char buffer[128];
  if (fgets (buffer, 128, stdin) == NULL)
    abort_to_main_loop (-1);
  else
    exit (EXIT_SUCCESS);
}

The function abort_to_main_loop causes an immediate transfer of control back to the main loop of the program, no matter where it is called from.

The flow of control inside the main function may appear a little mysterious at first, but it is actually a common idiom with setjmp. A normal call to setjmp returns zero, so the "else" clause of the conditional is executed. If abort_to_main_loop is called somewhere within the execution of do_command, then it actually appears as if the same call to setjmp in main were returning a second time with a value of -1.

So, the general pattern for using setjmp looks something like:

if (setjmp (buffer))
  /* Code to clean up after premature return. */
  ...
else
  /* Code to be executed normally after setting up the return point. */
  ...

20.2 Details of Non-Local Exits

Here are the details on the functions and data structures used for performing non-local exits. These facilities are declared in `setjmp.h'.

Data Type: jmp_buf
Objects of type jmp_buf hold the state information to be restored by a non-local exit. The contents of a jmp_buf identify a specific place to return to.

Macro: int setjmp (jmp_buf state)
When called normally, setjmp stores information about the execution state of the program in state and returns zero. If longjmp is later used to perform a non-local exit to this state, setjmp returns a nonzero value.

Function: void longjmp (jmp_buf state, int value)
This function restores current execution to the state saved in state, and continues execution from the call to setjmp that established that return point. Returning from setjmp by means of longjmp returns the value argument that was passed to longjmp, rather than 0. (But if value is given as 0, setjmp returns 1).

There are a lot of obscure but important restrictions on the use of setjmp and longjmp. Most of these restrictions are present because non-local exits require a fair amount of magic on the part of the C compiler and can interact with other parts of the language in strange ways.

The setjmp function is actually a macro without an actual function definition, so you shouldn't try to `#undef' it or take its address. In addition, calls to setjmp are safe in only the following contexts:

Return points are valid only during the dynamic extent of the function that called setjmp to establish them. If you longjmp to a return point that was established in a function that has already returned, unpredictable and disastrous things are likely to happen.

You should use a nonzero value argument to longjmp. While longjmp refuses to pass back a zero argument as the return value from setjmp, this is intended as a safety net against accidental misuse and is not really good programming style.

When you perform a non-local exit, accessible objects generally retain whatever values they had at the time longjmp was called. The exception is that the values of automatic variables local to the function containing the setjmp call that have been changed since the call to setjmp are indeterminate, unless you have declared them volatile.

20.3 Non-Local Exits and Signals

In BSD Unix systems, setjmp and longjmp also save and restore the set of blocked signals; see section 21.7 Blocking Signals. However, the POSIX.1 standard requires setjmp and longjmp not to change the set of blocked signals, and provides an additional pair of functions (sigsetjmp and sigsetjmp) to get the BSD behavior.

The behavior of setjmp and longjmp in the GNU library is controlled by feature test macros; see section 1.3.4 Feature Test Macros. The default in the GNU system is the POSIX.1 behavior rather than the BSD behavior.

The facilities in this section are declared in the header file `setjmp.h'.

Data Type: sigjmp_buf
This is similar to jmp_buf, except that it can also store state information about the set of blocked signals.

Function: int sigsetjmp (sigjmp_buf state, int savesigs)
This is similar to setjmp. If savesigs is nonzero, the set of blocked signals is saved in state and will be restored if a siglongjmp is later performed with this state.

Function: void siglongjmp (sigjmp_buf state, int value)
This is similar to longjmp except for the type of its state argument. If the sigsetjmp call that set this state used a nonzero savesigs flag, siglongjmp also restores the set of blocked signals.


Go to the first, previous, next, last section, table of contents.