problem with setjmp and longjmp to switch between 2 functions - c

I am trying to implement a code which continuously switched between functions fun() and main() which do nothing but to print on screen infinitely. I am trying to switch by setjmp and longjmp and using SIGALRM signal in C.
But when I run it, it just works once and then doesn't switch.
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <setjmp.h>
jmp_buf b1, b2;
int cur = 0;
void handlesig(int sig) {
if(!cur) {
cur = 1;
setjmp(b2);
longjmp(b1, 1);
}
else {
cur = 0;
setjmp(b1);
longjmp(b2, 1);
}
}
void fun() {
while(1) {
printf("I am in function fun()\n");
for(int x = 0; x < 100000000; x++);
}
}
int main() {
signal(SIGALRM, handlesig);
ualarm(900000, 900000); //send SIGALRM after each 900000 microseconds
if(!setjmp(b1))
fun(); //will be run when setjmp returns 0
while(1) {
printf("I am in function main()\n"); //will be run when setjmp returns 1
for(int x = 0; x < 100000000; x++);
}
return 0;
}
I am not getting what is the problem with this code.

Your program has undefined behavior because the lifetime of the block where setjmp was called on b1 or b2 in the signal handler ends as soon as longjmp is called (in the very next line). The next time you call longjmp trying to return to a jmp_buf that is no longer valid, the behavior is undefined, and this manifests as the state being utterly corrupted.
You can write a hack to work around this by using sigaltstack and SA_ONSTACK flag for the signal handler to have multiple stacks, so that even though the jmp_buf is formally invalid, it's in practice not clobbered. But this is not a valid program, just one which happens to work in practice on some systems (not all). Ultimately, there is no (valid/reliable) way to do what you're asking for with setjmp and longjmp; context switching requires a strictly stronger primitive than what they provide.

Related

Why does setjmp/longjmp

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.

Why the program not terminated on signal value change?

I have a simple program using signal with the user's handlers.
#include <signal.h>
#include <stdio.h>
#include <zconf.h>
int x = 0;
int i = 3;
void catcher3(int signum) {
i = 1;
}
void catcher2(int signum) {
// Stuck in infinity loop here.
// Happens even with i == 0
if (i != 0) {
x = 5;
}
}
void catcher1(int signum) {
printf("i = %d\n", i);
i--;
if (i == 0) {
signal(SIGFPE, catcher2);
signal(SIGTERM, catcher3);
}
}
int main() {
signal(SIGFPE, catcher1);
x = 10 / x;
printf("Goodbye");
}
While I expect it to print:
3
2
1
Goodbye
It actually prints:
3
2
1
# Infinity loop within catcher2
My questions are:
On running a user handler like catcher1, to which point the code returns after the handler's execution? I would expect it continue the execution but it re-runs the signal handler.
What causes the infinity loop?
How to fix it?
Why sending SIGTERM won't print "Goodbye"? (kill -s TERM <pid>)
As pointed out by AProgrammer, the program doesn't necessarily read x after returning from the handler, even if x is marked volatile (which it should be anyway). This is because the execution continues to the offending instruction. The read from memory and the actual division could be separate instructions.
To get around this you will have to continue the execution to a point before x was read from memory.
You can modify your program as follows -
#include <csetjmp>
jmp_buf fpe;
volatile int x = 0; // Notice the volatile
volatile int i = 3;
void catcher2(int signum) {
if (i != 0) {
x = 5;
longjump(fpe, 1);
}
}
int main() {
signal(SIGFPE, catcher1);
setjump(fpe);
x = 10 / x;
printf("Goodbye");
}
Rest of the functions can remain the same.
You should also not be using printf from the signal handler. Instead use write directly to print debug messages as -
write(1, "SIGNAL\n", sizeof("SIGNAL\n"));
The handling of signals is complex and full of implementation defined, unspecified and undefined behavior. If you want to be portable, there is in fact very few things that you can do. Mostly reading and writing volatile sig_atomic_t and calling _Exit. Depending on the signal number, it is often undefined if you leave the signal handler in another way than calling _Exit.
In your case, I think FPE is one of those signals for which leaving normally the signal handler is UB. The best I can see is restarting the machine instruction which triggered the signal. Few architectures, and last I looked x86 was not one of them, provide a way to do 10/x without loading x in a register; that means that restarting the instruction will always restart the signal, even if you modify x and x us a volatile sig_atomtic_t.
Usually longjmp is also able to leave signal handler. #Bodo confirmed that using setjmp and longjmp to restart the division, you can get the behavior you want.
Note: on Unix there is another set of functions, sigaction, siglongjump and others, which is better to use. In fact I don't recommend using something else in any serious program.

Raspberry Pi clean exit on CTRL+C in C

First of all, let me apologize as I can see that similar questions have been posted quite a few times in the past. However, as I am very unfamiliar with C, I need help confirming this.
I am trying to ensure that my program leaves a clean gpio if I interrupt it with CTRL+C. Easily done in python or java, but C proves to be a harder nut to crack for me, as I was led to believe that no try-catch-finally exists in C. Googling it, I found what I think may be the solution, but unexperienced as I am, I'm not sure it's done properly. Here is my code:
#include <stdio.h>
#include <wiringPi.h>
#include <signal.h>
void CleanGPIO() {
pinMode(1,INPUT);
}
int main()
{
wiringPiSetup();
signal(SIGINT, CleanGPIO);
pinMode(1, PWM_OUTPUT);
for (int i = 0; i < 1024; ++i) {
pwmWrite(1, i);
delay(1);
}
for (int i = 1023; i >= 0; --i) {
pwmWrite(1, i);
delay(1);
}
pinMode(1,INPUT);
return 0;
}
I have tested it and it works as intended (pin 1 is set as IN after I interrupt it with CTRL+C), but I'm concerned if this is the safe way to do it, and if there is a better solution available.
calling any function which is not speficied as signal-safe from a signal handler is undefined behaviour. I suppose there is no such guarantee about pinMode.
The proper way would be to set a volatile int flag that you periodically check in your main loop.
volatile int terminating = 0;
void terminate(int sign) {
signal(SIGINT, SIG_DFL);
terminating = 1;
}
int main() {
for (...) {
if (terminating) {
// cleanup
exit(1);
}
}
}
the call to signal inside the handler is to allow force terminating the program with a second ctrl+c in case proper clenup takes too long or is stuck for any reason.
Your solution is nearly right. You should also call exit in order to force the program to terminate (assuming you want to terminate immediately). The exit call takes a parameter which is the exit status to return to the caller (e.g., the shell). This should be non-zero for abnormal termination.
So, it should be:
void CleanGPIO() {
pinMode(1,INPUT);
exit(1);
}
If you don't want to exit from the handler but from main in a more controlled fashion you can set a flag instead and check the flag value inside the loops.

measuring time of signal

I need to measure the time it takes to handle an exception and invoke a signal handler 100,000 times. I need to use signal()system call to register a handler function for SIGFPE then i need to cause a divide by 0 error.
I only have a skeleton right now and am not sure how I should handle the signal. So far I plan on calling gettimeofday() then entering in a for loop 100k times to invoke the signal() then another gettimeofday() in order to end the time and then take the total elapsed time and average it out over those 100k invocations.
#include <signal.h>
#include <sys/time.h>
void handle_sigfe(int signum)
{
//unsure how to handle the signal to keep the loop running for 100k times
}
double time_in_milli (struct timeval t){ //for time conversion
return (((t.tv_sec*1000000+t.tv_usec)*1000)/1000000);
}
int main(int argv, char ** argv)
{
int x =5;
int y = 0;
int z = 0;
signal(SIGFPE, handle_sigfpe);
z = x/y;
return 0;
}
Anyone have any clue on how I need to handle this signal? I am completely lost on this
A divide-by-zero exception invokes the handler that you installed. When the handler returns, the processor goes back to the division instruction and tries again. The result is an infinite loop. To prevent that, you can use the sigsetjmp and siglongjmp routines.
When you call sigsetjmp it returns 0. However, when siglongjmp is called, the program behaves as if sigsetjmp returns the value supplied by siglongjmp. So you can use an if statement to execute the division or skip the division based on the return value from sigsetjmp.
If that's too confusing, hopefully the following example will clear things up.
#include <stdio.h>
#include <signal.h>
#include <setjmp.h>
static sig_atomic_t caught = 33;
static sigjmp_buf env;
void action( int unused )
{
caught = 42;
siglongjmp( env, 1 );
}
int main( void )
{
if ( signal( SIGFPE, action ) == SIG_ERR ) {
perror( "signal failed" );
return 1;
}
int x = 1;
int y = 0;
int z;
if ( sigsetjmp( env, 1 ) == 0 )
z = x / y;
printf( "%d\n", caught );
}
The global variable caught is used to indicate that the exception was caught. The initial value is 33. The signal handler sets it to 42. The printf at the end of the program displays the final value. It should print 42 to indicate the that signal was caught.
The global variable env is used by the sigsetjmp and siglongjmp functions to save a copy of the registers and stack.
The if (sigsetjmp(env,1) == 0) will initially be true, and the division will be attempted. But when the handler is invoked, siglongjmp will make the program behave as if sigsetjmp returned 1, and the division will be skipped.
This allows the program to move past the division, and execute the printf at the end.

reinversion of control using longjmp

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.

Resources