SetJmp/LongJmp: Why is this throwing a segfault?
You can only longjmp()
back up the call stack. The call to longjmp(b_buf, 1)
is where things start to go wrong, because the stack frame referenced by b_buf
no longer exists after the longjmp(a_buf)
.
From the documentation for longjmp
:
The longjmp() routines may not be called after the routine which called the setjmp() routines returns.
This includes "returning" through a longjmp()
out of the function.
Different behavior of setjmp/longjmp in release & debug
The code in question causes undefined behaviour. The program is incorrect, there is no expected behaviour. You should not expect nor be surprised by any particular output or other behaviour.
setjmp longjmp with Stack
I can't see anything obviously wrong with your code, but it is not an MVCE, so it is tough to tell -- there may be something off in your scheduler or your push and pop functions.
One thing that seems questionable is the tests in ult_yield
and ult_read
:
if(runqueue->start == NULL && blockedqueue->start == NULL) ...
These should both be:
if (runqueue->start == NULL) {
printf("Scheduler queue corrupted");
abort(); }
since when these functions are called, runqueue->start
MUST point at the current thread's tcb/queue node.
Your valgrind error looks like it is trying to longjmp
through an invalid jmp_buf
, so try to backtrack to see where it came from and how it got into that state.
You should also probably unset the signal handler at the end of ult_spawn
(or use SA_RESETHAND
), lest a spurious SIGUSR1
from somewhere cause corruption of things.
Is longjmp supposed to restore the stack?
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 whenstd::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.
memory leak between ljmp and setjmp
There isn't really a way to do this with stock malloc
. There is no portable facility that allows you to trace what allocations have been done since a certain point in time.
What you could do is something like this: Create a function that wraps malloc
and adds each newly allocated memory region into a linked-list. When you need to clear memory leaks, traverse this list and free
all the chunks you find there. Of course, this only works if you can guarantee that no pointers into the allocated regions remain.
It might be a good idea to rework your control flow so no memory allocations happen during the time where you want to abort control.
C++: Safe to use longjmp and setjmp?
setjmp()
/longjmp()
completely subvert stack unwinding and therefore exception handling as well as RAII (destructors in general).
From 18.7/4 "Other runtime support" in the standard:
If any automatic objects would be destroyed by a thrown exception transferring
control to another (destination) point in the program, then a call tolongjmp(jbuf, val)
at the throw point that transfers control to the same (destination) point has undefined behavior.
So the bottom line is that setjmp()
/longjmp()
do not play well in C++.
setjmp / longjmp does not jump where I think it should
First, the behavior of your calls to setjmp
is not defined by the C standard because they violate the constraints in C 2018 7.13.1.1 4 and 5:
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 the entire controlling expression of a selection or iteration statement; or— the entire expression of an expression statement (possibly cast to
void
).If the invocation appears in any other context, the behavior is undefined.
For example, in if( ( sjr = setjmp( *s_pEnvA ) ) == 0 )
, the setjmp
invocation is not the entire controlling expression, it is not an operand of a relational or equality operator (<
, <=
, >
, >=
, ==
, or !=
), it is not the operand of !
, and it is not the entire expression of an expression statement.
Second, longjmp
can only jump up the call stack, to functions that are still executing. Once a call to a function stops executing (as when it returns), you cannot jump back into that call. Your code saves the context in routineB
, then jumps to routineA
(which ends execution of routineB
), then attempts to jump to the saved context. But C 2018 7.13.2.1 2, about longjmp
says:
… if the function containing the invocation of the
setjmp
macro has terminated execution in the interim,… the behavior is undefined.
Can addresses of unmodified locals wind up corrupted in setjmp/longjmp?
Is there a reason the helper
call is "embedded" into the controlling expression of if
through ?:
operator? This is actually a violation of the language requirements that says
7.13.1.1 The setjmp macro
4 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 the entire controlling
expression of a selection or iteration statement; or— the entire
expression of an expression statement (possibly cast to void).5 If
the invocation appears in any other context, the behavior is
undefined.
The whole point of that requirement is to make sure the "unpredictable" return from setjmp
, triggered by longjmp
, should not land in the middle of an expression evaluation, i.e. in an unsequenced context. In your specific example it is rather obvious that from the point of view of abstract C language, variable error
cannot possibly be changed by setjmp
call, which opens the door for many optimizations.
It is hard to say what happened here, since helper
receives a pointer &error
, not error
's direct value. At the surface everything seems fine from practical point of view. But formally the behavior is undefined.
In your case, you should not try to fix thing by making variables volatile
, but rather should simplify the context the setjmp
is used in to conform with the above requirements. Something along the lines of
if (setjmp(buf) != 0) {
helper(&error);
...
return;
}
Related Topics
Pass by Reference and Value with Pointers
How to Ensure That the Template Parameter Is a Subtype of a Desired Type
Stl Containers Element Destruction Order
Write a File in a Specific Path in C++
Can Sfinae Detect Private Access Violations
How to Make Gcc Warn on Passing Too-Wide Types to Functions
Are Class Members Guaranteed to Be Contiguous in Memory
Embedded C++:To Use Stl or Not
Finding "Dead Code" in a Large C++ Legacy Application
C++ Priority_Queue with Lambda Comparator Error
C++ Gdb Python Pretty Printing Tutorial
C++ Error: Undefined Reference to 'Clock_Gettime' and 'Clock_Settime'
C++ Terminate Called Without an Active Exception
Where Are the Man Pages for C++