a piece of code here
jmp_buf mark;
int Sub_Func()
{
int be_modify, jmpret;
be_modify = 0;
jmpret = setjmp( mark );
if( jmpret == 0 )
{
// sth else here
}
else
{
// error handle
switch (jmpret)
{
case 1:
printf( "Error 1\n");
break;
case 2:
printf( "Error 2\n");
break;
case 3:
printf( "Error 3\n");
break;
default :
printf( "Unknown Error");
break;
}
printf("after switch\n");
}
return jmpret;
}
void main( void )
{
Sub_Func();
// the longjmp after setjmp
longjmp(mark, 1);
}
the result is:
Error 1
after switch
Segmentation fault
I know the reason maybe longjmp jump back to the previous stack. but I'm not sure about the detail, and what kind of value has been stored in 'mark', can anyone explain that?
setjmp() and longjmp() work by recording a stack frame position. If you record the stack frame in Sub_Func() but return from the function before calling longjmp(), the stack frame is no more valid.
longjmp() is meant to be called in the same function than setjmp() (subfunction is ok).
You are trying to longjmp back to a deeper function. You can only longjmp back to a shallower function.
So if A calls setjmp, then calls B, then B can longjmp back into A.
But if A calls b, B calls setjmp, B returns to A, A cannot longjmp back to B.
You have invoked undefined behavior by violating the following (7.13.2.1):
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 function containing the invocation of the setjmp macro has terminated execution217) 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.
217) 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.
In short, longjmp cannot be used to jump to a setjmp point in a function which has already returned.
Related
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:
The main() function is going to remember where the 'body' part is and skip it using if (setjmp(jmp_body) == 1).
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)
The body is going to execute and jump back to the func() call using longjmp(jmp_ret, 1)
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?
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.
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.
I do not understand why in the function middleFunc(), a segmentation fault is raisen when entry_point(arg) is invoked inside the if ( setjmp(middle) ) statement.
#include <stdio.h>
#include <setjmp.h>
jmp_buf start,middle,end;
void finalFunc(void *v)
{
printf("hello\n");
return ;
}
void middleFunc(void (*entry_point)(void *), void *arg)
{
//just debug : this does not cause segmentation fault
entry_point(arg);
if ( setjmp(middle) ){
//this casues the segmentation fault
entry_point(arg);
//once the entry point (finalFunc) is executed go to jmp_buffer end
longjmp(end,1);
}
else {
longjmp(start,1);
}
}
int main(){
if (setjmp(end)){
//exit since finalFunc has been executed
return 0;
}
if (setjmp(start)){
//the middleFunc has previously set the jmp_buffer middle
longjmp(middle,1);
}
else{
int x = 1;
middleFunc(finalFunc,(void*)&x);
}
}
In your code the behavior is undefined. You are not allowed to long-jump to middle after middleFunc finished execution (either by normal completion or by another longjmp).
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 function containing the invocation of the setjmp macro has terminated execution248) 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.
In your code middleFunc sets up middle and after that immediately exits to main by doing longjmp(start,1). After that jump middle is no longer valid. You are no longer allowed to jump to middle from anywhere. setjmp/longjmp mechanism only supports jumps up the call stack. You cannot do side-jumps or down-jumps. Only up-jumps are supported.
From the practical point of view, you are attempting to jump into a "dead" function invocation and somehow expecting that function parameter values are still valid (like, preserved from the previous invocation or something). But they are not. setjmp/longjmp do not preserve/restore parameter values. Value of entry_point in that "dead" invocation is probably some garbage. When you attempt to make a call through entry_point, the code coredumps.
P.S. It is true that side-jumping with setjmp/longjmp is sometimes used to implement co-routines. However, such usage falls outside the boundaries of standard library specification. And in any case such usage will never expect preservation of parameter values.
I'm trying to implement reinversion of control in c using longjmp, currently i have this code:
#include <stdio.h>
#include <setjmp.h>
jmp_buf env;
int arg;
typedef void (*fptr)(int);
fptr callback;
void cb(int a)
{
arg = a;
longjmp(env, 1);
}
#define cont1(f, x) do { if(!setjmp(env)) { f(x, &cb); return; } } while(0)
void callback_func(int num, fptr cb)
{
printf("in a func, num = %d\n", num);
callback = cb;
}
void task1()
{
printf("before continuation\n");
cont1(callback_func, 7);
printf("after continuation\n");
}
void task2()
{
printf("in thread 2\n");
(*callback)(5);
}
int main()
{
task1();
task2();
printf("arg = %d\n", arg);
return 0;
}
And my question is: doesn't this invoke undefined behavior or could cause any problem in real world use and if yes, then is there any better way to do this?
This program has undefined behavior. See C11 7.13.2.1 The longjmp function (emphasis mine):
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.
At the time you cb calls longjmp, the function which invoked setjmp to fill env, task1, has returned. Thus calling longjmp is undefined.
The C language has no means to do what you're trying to do except for (now with C11) threads, using condition variables to control which thread runs.
I am trying to understand setjmp in the following code:
http://androidxref.com/4.2.2_r1/xref/frameworks/base/core/jni/android/graphics/YuvToJpegEncoder.cpp#24
What does this line setjpg() mean?
32 if (setjmp(sk_err.fJmpBuf)) {
33 return false;
34 }
setjmp establishes a return-point that you can return to from deep in the call stack using longjmp. It's usually considered bad style (akin to goto).
The first time it is called, it returns 0. Then if a longjmp happens back to the jump-point, it will be as if setjmp returned with 1. longjmp can also send a different return code, but if you try to send 0, the result will be 1.
#include <setjmp.h>
jmp_buf j;
main(){
if(setjmp(j)){
printf("boo!\n");
return 0;
}
myfunc();
printf("5");
}
myfunc(){
printf("1");
myotherfunc();
}
myotherfunc(){
printf("2");
myfriendsfunc();
}
myfriendsfunc(){
printf("3");
longjmp(j, 0);
printf("4");
}
output:
$ ./jmp
123boo!
setjmp saves the context for a future call to longjmp. It returns zero when it is directly called (so, here, execution continues with jpeg_create_compress). When longjmp is called later (it must be before the current function returns, but it can be at any arbitrary call depth), execution will directly return to the setjmp call, and resume as if setjmp returned whatever you gave to longjmp. Presumably, in this case, longjmp will be called with a non-zero value, hence the method will directly return false.
Looking at the context, it looks like it is used for error handling. I assume longjmp can be called during the following compress. Think of it like low-level exceptions.
This question already has answers here:
What is the difference between exit and return?
(5 answers)
Closed 8 years ago.
Is there any difference between return 0 and exit (0) when using in a function?
If yes, When should I use return 0 or exit (0) in a function?
return exits from the function while exit exits from the program.
In main function executing return 0; statement or calling exit(0) function will call the registered atexit handlers and will cause program termination.
exit 0 is a syntax error in C. You can have exit(0) that is instead a call to a standard library function.
The function exit will quit the whole program, returning the provided exit code to the OS. The return statement instead only quits the current function giving the caller the specified result.
They are the same only when used in main (because quitting the main function will terminate the program).
Normally exit is only used in emergency cases where you want to terminate the program because there's no sensible way to continue execution. For example:
//
// Ensure allocation of `size` bytes (will never return
// a NULL pointer to the caller).
//
// Too good to be true? Here's the catch: in case of memory
// exhaustion the function will not return **at all** :-)
//
void *safe_malloc(int size) {
void *p = malloc(size);
if (!p) {
fprintf(stderr, "Out of memory: quitting\n");
exit(1);
}
return p;
}
In this case if function a calls function b that calls function c that calls safe_malloc you may want to quit the program on the spot instead of returning to c an error code (e.g. a NULL pointer) if the code is not written to handle allocation failures.
Yes there is, since there is no statement called exit. I guess you mean the function exit?
In that case, there is a big difference: The exit function exits the process, in other words the program is terminated. The return statement simply return from the current function.
They are only similar if used in the main function.
return is a statement that returns control back to the calling
function.
exit is a system call which terminates the current process i.e the
currently executing program.
In main() the return 0; and exit(0); perform the same thing.
NOTE: you have to include #include<stdlib.h>.