77. The Longjmp And Setjmp
The C programming language does not let you nest functions. You cannot write a function definition inside another function definition, as in:
int fun1( )
{
int fun2() /* such nesting of functions is not allowed */
{
.....
}
}
Because of this restriction it is not possible to hide function names inside a hierarchy. As a result all the functions that you declare within a program are visible to each other. This of course is not a major drawback since one can limit visibility by grouping functions within separate C source files that belong to different logical units of the program. C does, however, suffer in another way because of this design decision. It provides no easy way to transfer control out of a function except by returning to the expression that called the function. For the vast majority of function calls, that is a desirable limitation. You want the discipline of nested function calls and returns to help you understand flow of control through a program. Nevertheless, on some occasions that discipline is too restrictive. The program is sometimes easier to write, and to understand, if you can jump out of one or more function invocations at a single stroke. You want to bypass the normal function returns and transfer control to somewhere in an earlier function invocation.
For example, you may want to return to execute some code for error recovery no matter where an error is detected in your application. The setjmp and the longjmp functions provide the tools to accomplish this. The setjmp function saves the "state" or the "context" of the process and the longjmp uses the saved context to revert to a previous point in the program. What is the context of the process? In general, the context of a process refers to information that enables you to reconstruct exactly the way the process is at a particular point in its flow of execution. In C program the relevant information includes quantities such as values of SP, SS, FLAGS, CS, IP, BP, DI, ES, SI and DS registers.
To save this information Turbo C uses the following structure, which is defined, in the header file 'setjmp.h'.
typedef struct
{
unsigned j_sp ;
unsigned j_ss ;
unsigned j_flag ;
unsigned j_cs ;
unsigned j_ip ;
unsigned j_bp ;
unsigned j_di ;
unsigned j_es ;
unsigned j_si ;
unsigned j_ds ;
} jmp_buf[1] ;
This is a system-dependent data type because different systems might require different amounts of information to capture the context of a process. In Turbo C, jmp_buf is simply an array of ten 2-byte integers. To understand the mechanics of setjmp and longjmp, look at the following code
fragment.
#include "setjmp.h"
jmp_buf buf ;
main( )
{
if ( setjmp ( buf ) == 0 )
process( ) ;
else
handle_error( ) ; /* executed when longjmp is called */
}
process( )
{
int flag = 0 ;
/* some processing is done here */
/* if an error occurs during processing flag is set up */
if ( flag )
longjmp ( buf, 1 ) ;
}
Upon entry to setjmp the stack contains the address of the buffer buf and the address of the if statement in the main function, to which setjmp will return. The setjmp function copies this return address as well as the current values of registers, SP, SS, FLAGS, BP, DI, ES, SI and DS, into the buffer buf. Then setjmp returns with a zero. In this case, the if statement is satisfied and the process( ) function is called. If something goes wrong in process( ) (indicated by the flag variable), we call longjmp with two arguments: the first is the buffer that contains the context to which we will return. When the stack reverts back to this saved state, and the return statement in longjmp is executed, it will be as if we were returning from the call to setjmp, which originally saved the buffer buf. The second argument to longjmp specifies the return value to be used during this return. It should be other than zero so that in the if statement we can tell whether the return is induced by a longjmp.
The setjmp/longjmp combination enables you to jump unconditionally from one C function to another without using the conventional return statements. Essentially, setjmp marks the destination of the jump and longjmp is a non-local goto that executes the jump.
No comments:
Post a Comment