Reputation: 1902
From what I understood, setjmp
saves the current context and it's supposed to restore it when calling longjmp
. However the next piece of code prints 15 (I compiled with -g and without any optimization). Did I misunderstand this construct or am I missing anything else?
#include <iostream>
#include <csetjmp>
std::jmp_buf jump_buffer;
int main()
{
int a = 0;
if (setjmp(jump_buffer) == 0) {
a = 15;
std::longjmp(jump_buffer, 42);
}
std::cerr << a << std::endl;
}
Disclaimer: only trying to use it for curiosity. I never heard about this construct until I recently read some paper about NASA coding guidelines that mentioned it's forbidden to use this flow of control construct
Using both c and c++ tags because the code is mixed and I would assume the actual relevant functions are more relevant to c heavy users rather than c++... :/
Upvotes: 5
Views: 2798
Reputation: 2551
I'd just like to answer the other part of the question, by speculating why NASA would forbid these functions (basically linking relevant answers from SO). The use of setjmp
and longjmp
are discouraged in C++ more so than in C code, due to undefined behavior regarding automatic object destruction, see this SO thread, particularly the comments to the accepted answer:
Generally, whenever there's some way to exit a scope in C++ (return, throw, or whatever), the compiler will place instructions to call the dtors for any automatic variables that need to be destroyed as a result of leaving that block.
longjmp()
just jumps to a new location in the code, so it will not provide any chance for the dtors to be called. The standard is actually less specific than that - the standard doesn't say that dtors won't be called - it says that all bets are off. You can't depend on any particular behavior in this case.[...]
Since smart pointers depend on being destroyed, you will get undefined behavior. It's likely that that undefined behavior would include a refcount not getting decremented. You're 'safe' using
longjmp()
as long as you don't longjmp out of code that should cause dtors to be invoked. However, as David Thornley noted in a comment,setjmp()
/longjmp()
can be tricky to use right even in straight C - in C++ they're downright dangerous. Avoid them if at all possible.
So what makes the setjmp()
/longjmp()
tricky in C? Look at possible use cases we can see that one of them is implementation of coroutines. The answer was already given here in the comments @StoryTeler, but could you use goto
across different functions?
You can't in Standard C; labels are local to a single function.
The nearest standard equivalent is the setjmp() and longjmp() pair of functions.
However, you're quite limited with setjmp
and longjmp
as well, and you might quickly run into a segfault. The treasure can again be found in the comments:
You can think of a
longjmp()
as an "extended return". A successfullongjmp()
works like a series of successive returns, unwinding the call stack until it reaches the correspondingsetjmp()
. Once the call stack frames are unwound, they are no longer valid. This is in contrast to implementations of coroutines (eg. Modula-2) or continuations (eg. Scheme) where the call stack remains valid after jumping somewhere else. C and C++ only support a single linear call stack, unless you use threads where you create multiple independent call stacks.
Upvotes: 1
Reputation: 170055
That's the expected behavior:
Upon return to the scope of setjmp, all accessible objects, floating-point status flags, and other components of the abstract machine have the same values as they had when
std::longjmp
was executed, except for the non-volatile local variables insetjmp
's scope, whose values are indeterminate if they have been changed since thesetjmp
invocation.
The value of a
when executing longjmp
is 15, so that is a value one could expect to see (it's indeterminate in general). The jmp_buf
only stores the point of execution. Not the state of every variable in the program.
Upvotes: 8
Reputation: 29
“Setjump” and “Longjump” are defined in setjmp.h, a header file in C standard library.
setjump(jmp_buf buf) : uses buf to remember current position and returns 0.
longjump(jmp_buf buf, i) : Go back to place buf is pointing to and return i .
Simple Example
#include <stdio.h>
#include <setjmp.h>
static jmp_buf buf;
void second() {
printf("second\n"); // prints
longjmp(buf,1); // jumps back to where setjmp was called - making setjmp now return 1
}
void first() {
second();
printf("first\n"); // does not print
}
int main() {
if (!setjmp(buf))
first(); // when executed, setjmp returned 0
else // when longjmp jumps back, setjmp returns 1
printf("main\n"); // prints
return 0;
}
This is the reason why setjump() returns 0 for you and as you check for condition,it assigns a=15 and once the process is done,next step it would give 42.
The main feature of these function is to provide a way that deviates from standard call and return sequence. This is mainly used to implement exception handling in C. setjmp can be used like try (in languages like C++ and Java). The call to longjmp can be used like throw (Note that longjmp() transfers control to the point set by setjmp()).
Upvotes: 0
Reputation: 52334
The except for the non-volatile local variables in setjmp's scope, whose values are indeterminate if they have been changed since the setjmp invocation. part of the description is really important, because the value you're seeing falls into that indeterminate category.
Consider a slight modification to your program:
#include <iostream>
#include <csetjmp>
std::jmp_buf jump_buffer;
void func() {
std::longjmp(jump_buffer, 42);
}
int main()
{
int a = 0;
volatile int b = 0;
if (std::setjmp(jump_buffer) == 0) {
a = 15;
b = 1;
func();
}
std::cout << a << ' ' << b << '\n';
}
When I compile and run this version (With -O
), I get 0 1
as output, not 15 1
(Because a
is indeterminate, your results may vary).
If you want a local variable that's changed between the initial setjmp()
call and calling longjmp()
to reliably keep that change, it needs to be volatile
.
Upvotes: 2
Reputation: 99
I think you might be seeing older codebase. Where exception was not quite popular or not available in some compilers.
You should not use setjmp & longjmp until you are working more close to system software.
setjmp & longjmp stores & restore the CPU SFRs(i.e. context registers).
Here’s a little control flow example:
#include <stdio.h>
#include <setjmp.h>
jmp_buf env;
void foo()
{
longjmp(&env, 10); +---->----+
} | |
| |
int main() (entry)---+ ^ V
{ | | |
if(setjmp(&env) == 0) | (= 0) | | (= 10)
{ | ^ |
foo(); +---->----+ |
} +---->----+
else |
{ |
return 0; +--- (end)
}
}
Upvotes: 0
Reputation: 409166
For setjmp
and longjmp
the "context" is the execution context, not the actual contents of the stack (where local variables are commonly stored).
Using setjmp
and longjmp
you can't "roll back" changes made to local variables.
Upvotes: 0