4

I want to use setjmp/longjmp to reuse some code inside the main function (NOTE: this is only an exercise and not something I ever seriously plan on doing in the real world). The following code is what I've came up with:

#include <stdio.h>
#include <setjmp.h>
jmp_buf jmp_body, jmp_ret;
    
void func(void)
{
    if (setjmp(jmp_ret) == 0)
        longjmp(jmp_body, 1);
}

int main()
{
    int x = 0;
    if (setjmp(jmp_body) == 1) {
        printf("Body %d\n", ++x);
        longjmp(jmp_ret, 1);
    }
    func();
    func();
    func();
    return 0;
}

The way I expected this code to work is the following:

  1. The main() function is going to remember where the 'body' part is and skip it using if (setjmp(jmp_body) == 1).
  2. The func() call is going to temporarily jump to the body using longjmp(jmp_body) after remembering where the body is supposed to return using if (setjmp(jmp_ret) == 0)
  3. The body is going to execute and jump back to the func() call using longjmp(jmp_ret, 1)
  4. The func() is just going to return to main() as expected.

Therefore, what I expected the code to print is the following:

Body 1
Body 2
Body 3

Instead, it loops forever continually executing the body which indicates to me the func() call isn't returning where it's supposed to and instead might be returning above itself executing itself over and over again.

In comparison, the following code prints just what I expected:

#include <stdio.h>
#include <setjmp.h>
jmp_buf jmp_body, jmp_ret;

int main()
{
    int x = 0;
    if (setjmp(jmp_body) == 1) {
        printf("Body %d\n", ++x);
        longjmp(jmp_ret, 1);
    }
    if (setjmp(jmp_ret) == 0)
        longjmp(jmp_body, 1);
    if (setjmp(jmp_ret) == 0)
        longjmp(jmp_body, 1);
    if (setjmp(jmp_ret) == 0)
        longjmp(jmp_body, 1);
    return 0;
}

What is it about putting if (setjmp(jmp_ret) == 0) longjmp(jmp_body, 1) inside a function call that makes the original approach invalid?

Ihato
  • 55
  • 3
  • 4
    "temporarily jump" is an interesting expression. There's nothing temporary about it. When you jump out of `func`, `jump_rec` is no longer valid. So the subsequent attempt to long jump to it is undefined. – William Pursell Jul 15 '21 at 14:27
  • 1
    Yeah, I think you are misunderstanding what `longjmp` is and does. You can only jump up the call stack, into a function which hasn't yet returned. And when you do so, you implicitly return from the current function and all the ones in between; you can't then longjmp back to them. It's not for jumping to random blocks of code throughout the program; it's instead for forcing several functions on the stack to return at once, like throwing an exception in languages which support that. – Nate Eldredge Jul 15 '21 at 15:13
  • `longjmp()` does unwind the stack, everything that is in the unwinded part is no longer valid, you may inform yourself about stack unwinding and how `setjmp()` and `longjmp()` actually works. And please, use a consistent intention style. – 12431234123412341234123 Jul 15 '21 at 16:39

2 Answers2

5

TL/DR - you can't jump back into a function you jumped out of.

7.13.2.1 The longjmp function
...
2     The longjmp function restores the environment saved by the most recent invocation of the setjmp macro in the same invocation of the program with the corresponding jmp_buf argument. If there has been no such invocation, or if the invocation was from another thread of execution, or if the function containing the invocation of the setjmp macro has terminated execution248) in the interim, or if the invocation of the setjmp macro was within the scope of an identifier with variably modified type and execution has left that scope in the interim, the behavior is undefined.
248) For example, by executing a return statement or because another longjmp call has caused a transfer to a setjmp invocation in a function earlier in the set of nested calls.
C 2011 Online Draft

When you execute longjmp(jump_body, 1); in func, you invalidate jump_ret.

longjmp isn't bidirectional - it unwinds the stack as though any of the function calls between the setjmp and longjmp never happened.

John Bode
  • 119,563
  • 19
  • 122
  • 198
1

You tried to longjmp back down the stack from main() to func(). This is not defined. Since longjmp is itself a function, you most likely ended up longjmping into func with the return address being the longjmp call itself thus an infinite loop.

Joshua
  • 40,822
  • 8
  • 72
  • 132