Thursday, June 16, 2011

Non-local Jumps

10.  <setjmp.h>  Non-local Jumps

The declarations in <setjmp.h> provide a way to avoid the normal function call and return sequence, typically to permit an immediate return from a deeply nested function call.
int setjmp(jmp_buf env)
The macro setjmp saves state information in env of type jmp_buf for use by longjmp. The return is zero from a direct call of setjmp, and non-zero from a subsequent call of longjmp. A call to setjmp can only occur in certain contexts, basically the test of if, switch, and loops, and only in simple relational expressions.
      if (setjmp(env) == 0)
          /* get here on direct call */
      else
          /* get here by calling longjmp */
void longjmp(jmp_buf env, int val)
longjmp restores the state saved by the most recent call to setjmp, using the information saved in env, and execution resumes as if the setjmp function had just executed and returned the non-zero value val. The function containing the setjmp must not have terminated. Accessible objects have the values they had at the time longjmp was called, except that non-volatile automatic variables in the function calling setjmp become undefined if they were changed after the setjmp call.

EXAMPLE CODES

l jmp-1.c
#include <stdio.h>
#include <setjmp.h>

jmp_buf JmpBuf;

void f() {
    static int is_first = 1;
    if (is_first) {
        is_first = 0;
        printf("About to longjmp [1] ...\n");
        longjmp(JmpBuf, 1);
        printf("After longjmp\n");
    } else {
        printf("About to longjmp [2] ...\n");
        longjmp(JmpBuf, 2);
        printf("After longjmp\n");
    }
}

void main() {
    printf("About to setjmp ...\n");
    switch (setjmp(JmpBuf)) {
    case 0:
        printf("setjmp result = 0\n");
        f();
        printf("After f()\n");
        break;
    case 1:
        printf("Setjmp result = 1\n");
        f();
        printf("After f()\n");
        break;
    case 2:
        printf("setjmp result = 2\n");
        break;
    }
    printf("Bye.\n");
}
>   jmp-1
About to setjmp ...
setjmp result = 0
About to longjmp [1] ...
Setjmp result = 1
About to longjmp [2] ...
setjmp result = 2
Bye.
l jmp-2.c
#include <stdio.h>
#include <setjmp.h>
void main() {
    static jmp_buf JMP_ENV;    // **
    auto int a = 0; register int r = 0;
    volatile int v = 0; static int s = 0;

    if (setjmp(JMP_ENV) == 0) {
        a++; r++; v++; s++;
        printf("Before longjmp: a = %d, r = %d, v = %d, s = %d\n", a, r, v, s);
            // 1 1 1 1
        longjmp(JMP_ENV, -1);
    } else {
        printf("After longjmp:  a = %d, r = %d, v = %d, s = %d\n", a, r, v, s);
            // ? ? 1 1
    }
}
>   cl jmp-2.c && jmp-2
Before longjmp: a = 1, r = 1, v = 1, s = 1
After longjmp: a = 1, r = 1, v = 1, s = 1
>   cl /Ox jmp-2.c && jmp-2
Before longjmp: a = 1, r = 1, v = 1, s = 1
After longjmp: a = 1, r = 1, v = 1, s = 1
$ gcc jmp-2.c -o jmp-2; ./jmp-2
Before longjmp: a = 1, r = 1, v = 1, s = 1
After longjmp: a = 1, r = 1, v = 1, s = 1
$ gcc –O3 jmp-2.c -o jmp-2; ./jmp-2
Before longjmp: a = 1, r = 1, v = 1, s = 1
After longjmp: a = 0, r = 0, v = 1, s = 1
l jmp-3.c
#define _CRT_SECURE_NO_DEPRECATE
#include <stdio.h>
#include <stdlib.h>
#include <setjmp.h>

#define LIMIT 10
#define DEFAULT_VALUE 1
jmp_buf JENV1, JENV2;

void f1();
void f2(int size);

void main() {
    if (setjmp(JENV1))
        fprintf(stderr, "fatal error -- stop execution\n"), exit(1);
    f1();
    fprintf(stderr, "success.\n");
}

void f1() {
    int size;  // need not be volatile
    if (printf("size = "), scanf("%d", &size) != 1) longjmp(JENV1, 1);
    switch (setjmp(JENV2)) {
    case 0:
        f2(size); break;
    case 1:
        fprintf(stderr, "retry with default value\n");
        f2(DEFAULT_VALUE); break;
    default:
        fprintf(stderr, "too big size\n");
        longjmp(JENV1, 1); break;
    }
}

void f2(int size) {
    printf("[in f2] size = %d\n", size);
    if (size < 0) longjmp(JENV1, 1);
    if (size == 0) longjmp(JENV2, 1);
    if (size > LIMIT) longjmp(JENV2, 2);
    printf("do something ...\n");
}
>   jmp-3
size = -1
[in f2] size = -1
fatal error -- stop execution
>   jmp-3
size = 0
[in f2] size = 0
retry with default value
[in f2] size = 1
do something ...
success.
>   jmp-3
size = 99
[in f2] size = 99
too big size
fatal error -- stop execution
>   jmp-3
size = 3
[in f2] size = 3
do something ...
success.

No comments: