longjmp and volatile variables

Discussion in 'C Programming' started by jacob navia, Apr 29, 2006.

  1. jacob navia

    jacob navia Guest

    Clausfor a écrit :
    > The solution?
    > maybe this one: "volatile int x;" ?
    > Is this to be put in front of any local variable of the calling
    > function?
    >


    That will work, yes.

    Another, less portable solution is to read your compiler's documentation
    and see what happens when optimizations are NOT enabled. Maybe in that
    case the compiler does not use register variables and the problem does
    not exist.
     
    jacob navia, Apr 29, 2006
    #1
    1. Advertising

  2. jacob navia

    Clausfor Guest

    Hello,
    I have a problem with restoring variables in the setjmp/longjmp
    functions:

    K&R2 for longjmp says:
    "Accessible objects have the same value they had when longjmp was
    called, except for automatic non volatile variables of the function
    invoking setjmp, these will be undefined if modified after the setjmp
    call"

    MSDN for longjmp says:
    "The values of all variables (except register variables) that are
    accessible to the routine receiving control contain the values they had
    when longjmp was called. The values of register variables are
    unpredictable."
    [...]
    "Do not assume that the values of the register variables will remain
    the same. The values of register variables in the routine calling
    setjmp may not be restored to the proper values after longjmp is
    executed."

    I think, but I'm not sure, that both these mean
    "the variables accessible from both the caller of setjmp and the caller
    of longjmp
    are not assured to keep the value if the compiler stores them in
    registers"

    Correct?

    But... when a variable is stored in registers?

    The solution?
    maybe this one: "volatile int x;" ?
    Is this to be put in front of any local variable of the calling
    function?

    Thank you in advance!
    Claus
     
    Clausfor, Apr 29, 2006
    #2
    1. Advertising

  3. In article <>,
    Clausfor <> wrote:

    >I think, but I'm not sure, that both these mean
    >"the variables accessible from both the caller of setjmp and the caller
    >of longjmp
    >are not assured to keep the value if the compiler stores them in
    >registers"


    The local variables in the function where longjmp() is called are
    irrelevant: you're returning from that function so they can't be
    accessed anyway (except in the trivial case where longjmp() is called
    from the same function invocation as setjmp()). The variables at risk
    are those in the function invocation where setjmp() was called.

    It may be useful to know the reason for this. A common way to implement
    setjmp() is to store the current values of all registers, and restore
    them when longjmp() is called. This will restore the stack pointer to
    point to the frame that was current when setjmp() was called, so that
    variables stored on the stack will be correct. But variables stored
    in registers will be restored to their values when setjmp() was called,
    losing any changes to them between the setjmp() and longjmp().

    The standard, as usual, does not say that this particular "incorrect"
    behaviour will occur; it just says that such any variables that
    *might* be in registers are undefined.

    >But... when a variable is stored in registers?


    Potentially, any time when it isn't declared volatile. So any local
    variables whose value must be right after the longjmp() should be declared
    volatile.

    -- Richard
     
    Richard Tobin, Apr 29, 2006
    #3
  4. jacob navia

    Chris Torek Guest

    >In article <>,
    >Clausfor <> wrote:
    >>I think, but I'm not sure, that both these mean
    >>"the variables accessible from both the caller of setjmp and the caller
    >>of longjmp
    >>are not assured to keep the value if the compiler stores them in
    >>registers"


    In article <e30f2g$2ou0$>
    Richard Tobin <> wrote:
    >The local variables in the function where longjmp() is called are
    >irrelevant: you're returning from that function so they can't be
    >accessed anyway (except in the trivial case where longjmp() is called
    >from the same function invocation as setjmp()). The variables at risk
    >are those in the function invocation where setjmp() was called.


    Indeed.

    >It may be useful to know the reason for this. A common way to implement
    >setjmp() is to store the current values of all registers, and restore
    >them when longjmp() is called. This will restore the stack pointer to
    >point to the frame that was current when setjmp() was called, so that
    >variables stored on the stack will be correct. But variables stored
    >in registers will be restored to their values when setjmp() was called,
    >losing any changes to them between the setjmp() and longjmp().


    Or, equivalently, the implementation might save and restore *only*
    those registers critical to locating the correct stack frame (really,
    "overall execution context"). On most machines, excluding the x86
    that nobody uses :) , this avoids a whole lot of often-useless
    work, making setjmp() execute noticeably faster.

    In either case, to elaborate a bit, consider the following C source
    fragment (which I do not claim is any good or even any use; it is
    just for illustration, especially the "block1" and "block2" labels):

    #include <setjmp.h>

    jmp_buf label;

    void f(T1 arg1, T2 arg2) {
    while (someloop) {

    if (somecond) {
    register int x;

    block1:
    x = 42;
    ... some code here ...
    if (setjmp(label)) {
    printf("jumped back, in block 1, x = %d\n", x);
    goto block2;
    }
    x += 3;
    ... more code here, calling g() ...
    }

    if (othercond) {
    register int y;

    block2:
    ... still more code here, calling h() ...
    }

    }
    }

    Suppose that the compiler uses the *same* machine register for both
    x and y. This is a perfectly reasonable thing to do, since the
    two variables have separate scopes. When "x" disappears, "y"
    appears, and vice versa: perfect conditions for sharing.

    Now, when -- in your function that uses longjmp(), say inside g()
    -- you execute a "goto label" (via longjmp(label), which is just
    a particularly powerful kind of "go to"), this will either restore
    x to the saved value, if the implementation saves all the registers;
    or it will fail to restore x.

    Suppose it chooses to restore x to the saved value. In this case,
    if you were inside block1, all is somewhat well, except that x has
    "magically" (via the restore) had the 3 subtracted back out. That
    is, the flow of control was as follows:

    - enter block 1
    - set x to 42
    - call setjmp
    . save the point of the call
    . save the register holding x (42)
    . return 0
    - add 3 to x (now it is 45)
    - execute "more code":
    . call some function g()
    . longjmp(label):
    * restore x (to 42)
    * terminate function g() and resume f() at the "if (setjmp(label))"
    (making setjmp() return some nonzero value this time)
    - print x (42), which apparently has not had 3 added.

    On the other hand, if the implementation chooses *not* to restore
    x, it *may* still have 45 in it. This would be even better, since
    this would make x act like a regular variable. Also, it is "faster",
    because the setjmp and longjmp do not have to modify the register.
    But what if "g" had a "register int z" that used the same register?
    Then x would quite likely "magically" acquire z's value.

    Now, if you were to get out of the first block in f() safely, and
    get into block2, and call h() and have h() longjmp back to f(),
    and print x there, this would either restore the saved value (42),
    or leave the register alone. Of course, such code is technically
    undefined -- you left the block that defined x and then re-entered
    it (via the goto that longjmp() does) -- so you should not expect
    any particular value anyway. But now let me modify the original
    code a bit, to remove the sub-block for x. I will also remove the
    "register" keyword, since it is just a hint anyway.

    void f(T1 arg1, T2 arg2) {
    int x;

    while (someloop) {
    if (somecond) {
    block1:
    x = 42;
    ... some code here ...
    if (setjmp(label)) {
    printf("jumped back, in block 1, x = %d\n", x);
    goto block2;
    }
    x += 3;
    ... more code here, calling g() ...
    }
    if (othercond) {
    int y;
    block2:
    ... still more code here, calling h() ...
    }
    }
    }

    Now if h() calls longjmp(label), the effect is "almost defined"
    (with the word "almost" inserted because of the "volatile" issue
    being described here), because the variable x is defined way up
    at the top of f().

    Suppose, however, our compiler is a decent one, that optimizes
    well. It notices that x is only used in block1; and of course
    y is only used in block2. It therefore realizes that it can use
    a single machine register to hold *both* variables, just as we did
    "manually" in the first version of the code.

    This time, if we longjmp() from h(), and setjmp() and longjmp() do
    *not* save and restore the machine register, we can get the value
    that was in y printed when we reach the printf(). Of course, if
    the implementation's setjmp()/longjmp() save and restore the
    register, we get 42 -- the "x += 3" has again "magically vanished".

    >The standard, as usual, does not say that this particular "incorrect"
    >behaviour will occur; it just says that such any variables that
    >*might* be in registers are undefined.


    And of course:

    >>But... when a variable is stored in registers?


    >Potentially, any time when it isn't declared volatile. ...


    So this means that you have to write f() above as:

    void f(T1 arg1, T2 arg2) {
    volatile int x;

    while (someloop) {
    if (somecond) {
    block1:
    x = 42;
    ... some code here ...
    if (setjmp(label)) {
    printf("jumped back, in block 1, x = %d\n", x);
    goto block2;
    }
    x += 3;
    ... more code here, calling g() ...
    }
    if (othercond) {
    int y;
    block2:
    ... still more code here, calling h() ...
    }
    }
    }

    That way, whether g() or h() (or anything they call) use longjmp
    to go back to the "label", x will have a predictable value -- 45
    -- and everything will be guaranteed to work.

    Note that, in this *particular* case, "y" need not be declared
    volatile, because block2 cannot use it without first setting it
    to some value. It is of course safe to use "volatile" on y too,
    but it is not required (and may inhibit some optimization).

    Your best bet is usually to avoid longjmp() entirely, because it
    is a very powerful "goto", and "goto" is best used sparingly, if
    at all. There are restrictions on where you can use setjmp() and
    longjmp(), but compilers rarely enforce them, and if you accidentally
    misuse them, not only does your program usually crash, many debuggers
    get confused in the process and cannot help you pinpoint the problem.

    (The reason is that longjmp() often just "sets" the machine's stack
    and/or frame pointer registers, and if the values it sets are
    garbage, the debugger has no clue where to look for the problem.
    Worse, after setting these registers, longjmp just "sets" the
    program-counter / instruction-pointer register, eradicating all
    traces of its own handiwork. So you get a mysterious failure at
    the jumped-to point, with no record of *why* you got to that point.
    All the debugger can tell you is "the stack frames are all trashed,
    and the program was executing in this apparently-randomly-chosen
    function or data area, at the time the crash occurred.")
    --
    In-Real-Life: Chris Torek, Wind River Systems
    Salt Lake City, UT, USA (40°39.22'N, 111°50.29'W) +1 801 277 2603
    email: forget about it http://web.torek.net/torek/index.html
    Reading email is like searching for food in the garbage, thanks to spammers.
     
    Chris Torek, Apr 29, 2006
    #4
  5. jacob navia

    SM Ryan Guest

    "Clausfor" <> wrote:
    # Hello,
    # I have a problem with restoring variables in the setjmp/longjmp
    # functions:

    setjmp() is usually implemented by taking a snapshot of all
    the registers and storing that in the jump buffer. longjmp
    then restores that snapshot except the for the register that
    holds the return value. This means any variable in a register
    at the setjmp will have its value restored by a longjmp.

    ANSI doesn't know which or if any variables can be in a
    register: that's up to the implementor. But it does know that
    it's possible, hence the vaguely ominous warning. Microsoft
    is referring to their particular implementation, of which
    they might be more certain (though it is, after all,
    Microsoft).

    volatile variables are stored in way that they can be
    asynchronously modified, and the compiler must guarentee
    their current values are always accessed. On current and
    foreseeable architecures, that means the variables are
    stored in memory not registers, and they are not cached
    in registers either. This means volatile variables are
    not affected by setjmp/longjmp. Many other variables would
    also be unaffected, but less predictably so.

    --
    SM Ryan http://www.rawbw.com/~wyrmwif/
    This is one wacky game show.
     
    SM Ryan, Apr 29, 2006
    #5
  6. jacob navia

    Richard Bos Guest

    jacob navia <> wrote:

    > Clausfor a écrit :
    > > The solution?
    > > maybe this one: "volatile int x;" ?
    > > Is this to be put in front of any local variable of the calling
    > > function?

    >
    > That will work, yes.


    It is the only correct option.

    > Another, less portable solution is to read your compiler's documentation
    > and see what happens when optimizations are NOT enabled. Maybe in that
    > case the compiler does not use register variables and the problem does
    > not exist.


    Do _not_ do this. It does not solve anything, will make your program
    unportable, and is likely to make that particular function less
    efficient, to boot. Why apply a "solution" that only has downsides?

    Richard
     
    Richard Bos, May 1, 2006
    #6
  7. jacob navia

    Ben Pfaff Guest

    (Richard Bos) writes:

    > jacob navia <> wrote:
    >
    >> Clausfor a icrit :
    >> > The solution?
    >> > maybe this one: "volatile int x;" ?
    >> > Is this to be put in front of any local variable of the calling
    >> > function?

    >>
    >> That will work, yes.

    >
    > It is the only correct option.


    Another would be to not modify any local variables of the
    function that calls setjmp() before longjmp() (may be) called.
    This is not necessarily difficult.
    --
    "The way I see it, an intelligent person who disagrees with me is
    probably the most important person I'll interact with on any given
    day."
    --Billy Chambless
     
    Ben Pfaff, May 1, 2006
    #7
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. ben
    Replies:
    5
    Views:
    606
    Ulrich Eckhardt
    Jan 11, 2005
  2. Zheng Da
    Replies:
    8
    Views:
    506
    Christian Bau
    Nov 7, 2005
  3. Replies:
    3
    Views:
    419
    Fred Kleinschmidt
    Feb 23, 2007
  4. jacob navia

    The stack and longjmp/setjmp

    jacob navia, Mar 4, 2008, in forum: C Programming
    Replies:
    34
    Views:
    1,466
    David Thompson
    Mar 31, 2008
  5. aleksa

    setjmp and longjmp problem

    aleksa, Sep 5, 2010, in forum: C Programming
    Replies:
    5
    Views:
    718
    Francois Grieu
    Sep 7, 2010
Loading...

Share This Page