views:

382

answers:

3

I have a few questions relating to setjmp/longjmp usage -

  1. What is the use of setjmp(jmp___buf stackVariables) returning 0. It is a default, which we cannot influence.

  2. Is the only significance of setjmp(stackVariables) is to push the stack in stackVariables. And basically 0 tells us if the stack was pushed on stack_variables successfully.

  3. Their is one occasion when the value is non-zero (any non-zero) when you return from a longjmp. What is returning from a lomgjmp, when do you return from longjmp, when your exception is handled. This is setup is really confusing.

  4. Can some please relate it to try/throw and catch. And would be really great, if some good examples of setjmp/longjmp could be provided.

  5. Is longJmp like throw, and it is called just after the place where exception can be raised.

Thanks.

+2  A: 

setjmp is used to place a marker to where the call of longjump should return, it returns 0 if it is called directly, it returns 1 if it's called because a longjmp to that setjmp is invoked.

You have to think about setjmp like something that can be normally called and does not do anything (returning 0) in normal operation while returns 1 and it's indirectly called (and returns from there) when a long jump is called. I know what you mean about confusing because it's actually confusing..

This is the example given by wikipedia:

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

static jmp_buf buf;

void second(void)
{
    printf("second\n");         // prints
    longjmp(buf,1);             // jumps back to where setjmp was called - making setjmp now return 1
}

void first(void)
{
    second();
    printf("first\n");          // does not print
}

int main()
{   
    if ( ! setjmp(buf) )
    {
        first();                // when executed, setjmp returns 0
    } 
    else
    {                    // when longjmp jumps back, setjmp returns 1
        printf("main");         // prints
    }

    return 0;
}

Are you able to understand it? When program is launched setjmp is executed in main and returns 0 (because it is directly called), so first is called, that calls second and then it arrives longjmp that switches context going back to where setjmp was used, but this time, since it goes back from a jump and it is indirectly called the function returns 1.

The useful thing of setjmp/longjmp approach is that you can handle error situations without caring to keep a flag between function calls (especially when you have many, think about a recursive procedure for typechecking in a compiler). If something goes wrong in typechecking deep in call stack normally you have to return a flag and keep returning it to warn the caller that typechecking failed. With longjmp you just go out and handle error without caring about passing flags back. The only problem is that this forces a context switch that doesn't care about standard deallocation of stack/heap memory so you should handle it by yourself.

Jack
thanks for the example
Vivek Sharma
The problem you point out is quite difficult to handle because you have to know everything that was allocated before the longjmp and how to deallocate it. Also, IIRC, any non volatile local variables in the function that issues the setjmp may be corrupted.
David G
+2  A: 

The first part is almost simple: When you do a longjmp, you end up exactly after the setjmp. If the return value is 0, it means you just did the setjmp; if it is nonzero, you know you got there from a longjmp from elsewhere. That information is often useful in controlling what your code does after that.

setjmp/longjmp are the old ancestors of throw/catch. setjmp/longjmp are defined in C, whereas throw/catch is the more "modern" mechanism for doing error recovery in more object-oriented languages like C++.

calling longjmp says: "I think there's something wrong here, help, get me out of here - I'm too confused to clean up after myself and return through a bunch of function calls and if's; just get me right back to where the world was OK again, right after the last setjmp."

throw says pretty much the same thing, except it's more clearly and cleanly supported in syntax. Also, while longjmp can take you to practically ANY place in the program (wherever you did the setjmp), throw ends up directly upward in the call hierarchy of where the throw is.

Carl Smotricz
thanks fort the last, 2 paras.
Vivek Sharma
+4  A: 

The C99 spec gives:

If the return is from a direct invocation, the setjmp macro returns the value zero. If the return is from a call to the longjmp function, the setjmp macro returns a nonzero value.

So the answer to 1 is that a zero indicates you have called setjmp the first time, and non-zero indicates it is returning from a longjmp.

  1. It pushes the current program state. After a longjmp, the state is restored, control returns to the point it was called, and the return value is non-zero.

  2. There are no exceptions in C. It's sort-of similar to fork returning different values depending whether you're in the original process, or a second process which has inherited the environment, if you're familiar with that.

  3. try/catch in C++ will call destructors on all automatic objects between the throw and the catch. setjmp/longjmp will not call destructors, as they don't exist in C. So you are on your own as far as calling free on anything you've malloced in the mean time.

With that proviso, this:

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

void foo ( char** data ) ;
void handle ( char* data ) ;
jmp_buf env;

int main ()
{
    char* data = 0;

    int res = setjmp ( env ); 
    // stored for demo purposes. 
    // in portable code do not store 
    // the result, but test it directly.

    printf ( "setjmp returned %d\n", res );

    if ( res == 0 )
        foo ( &data );
    else
        handle ( data );

    return 0;
}


void foo ( char** data )
{
    *data = malloc ( 32 );

    printf ( "in foo\n" );

    strcpy ( *data, "Hello World" );

    printf ( "data = %s\n", *data );

    longjmp ( env, 42 );
}

void handle ( char* data )
{
    printf ( "in handler\n" );

    if ( data ) {
        free ( data );
        printf ( "data freed\n" );
    }
}

is roughly equivalent to

#include <iostream>

void foo ( ) ;
void handle ( ) ;

int main ()
{
    try {
        foo ();
    } catch (int x) {
        std::cout << "caught " << x << "\n";
        handle ();
    }

    return 0;
}

void foo ( )
{
    printf ( "in foo\n" );

    std::string data = "Hello World";

    std::cout << "data = " << data << "\n";

    throw 42;
}

void handle ( )
{
    std::cout << "in handler\n";
}

In the C case, you have to do explicit memory management (though normally you'd free it in the function which malloc'd it before calling longjmp as it makes life simpler)

Pete Kirkham
awesome man, thanks
Vivek Sharma
Is longJmp like throw, and it is called just after the place where exception can be raised. And also why are you using the number 42, you can do the same job 1, or is 42 any non-zero positive int.
Vivek Sharma
The key point is that the setjmp() function may return once, or it may return several times. It will return once from the 'direct invocation' (which will return 0); any subsequent returns are the result of a longjmp() and will return a non-zero value.
Jonathan Leffler
You cannot portably store the result of setjmp() into a variable. The C standard says: "_An invocation of the setjmp macro shall appear only in one of the following contexts: the entire controlling expression of a selection or iteration statement; one operand of a relational or equality operator with the other operand an integer constant expression, with the resulting expression being the entire controlling expression of a selection or iteration statement; the operand of a unary ! operator with the resulting expression being [...]; or the entire expression of an expression statement [...]."
Jonathan Leffler
@Jonathan Leffler You're right, but it's hard to demonstrate the return value in demo code if you don't.
Pete Kirkham