0

We are trying to switch between multiple functions in our C program after saving the state using setjmp and longjmp but for only one function we are able to save the context not for other two functions. What could be the possible solution to it. Please suggest if necessary changes are required in the code. In the o/p the state of fun1() is saved successfully in env1 and we are able to resume it back, but in case of fun2() and fun3() the state is not saved in the env2, env3.

Sample o/p:

   i=0,j=999
   i=1,j=998
   i=2,j=997
   i=3,j=996
   ch=a
   ch=b
   ch=c
   ch=d
   a=0
   a=-1
   a=-2
   a=-3
   i=4,j=995/*The previous value of i is preserved*/
   i=5,j=996
   i=6,j=994
   i=7,j=993
   ch=<garbagevalue> /*The previous value of ch is lost*/
   ch=<garbagevalue>
   ch=<garbagevalue>
   ch=<garbagevalue>
   a=<garbagevalue>  /*The previous value of ch is lost*/
   a=<garbagevalue>
   a=<garbagevalue>
   a=<garbagevalue>
   i=8,j=992
  and so on..

Code follows:

#include <signal.h>
#include <stdio.h>
#include <string.h>
#include <sys/time.h>
#include<setjmp.h>
#define INTERVAL 5000
void fun1();
void fun2();
void fun3();
void timer_handler(int );
struct itimerval it_val;
jmp_buf env1,env2,env3;
int count=0;
void timer_handler (int signum)
{

  if(count==0){
    count++;
    fun1();
  }
  if(count==1){
    count++;
    fun2();
    }

  if(count==2){
    count++;
    fun3();
  }


L1:   
  if(count==3){
    count++;
    siglongjmp(env1,7);
    }
  if(count==4){
     count++;
     siglongjmp(env2,7);
    }

  if(count==5){
    count++;
    siglongjmp(env3,7);
    }
  count=3;
  goto L1;
}

void fun1()
{

  struct sigaction sa;
  int i=0,j=999;
  /* Install timer_handler as the signal handler for SIGVTALRM. */
  memset (&sa, 0, sizeof (sa));
  sa.sa_flags=SA_NODEFER;
  sa.sa_handler = &timer_handler;

  /* Configure the timer to expire after 250 msec... */
  it_val.it_value.tv_sec = INTERVAL/1000;
  it_val.it_value.tv_usec = (INTERVAL*1000) % 1000000;   
  it_val.it_interval.tv_sec = INTERVAL/1000;;
  it_val.it_interval.tv_usec=(INTERVAL*1000) % 1000000;


  sigaction (SIGALRM, &sa, NULL);
  setitimer (ITIMER_REAL, &it_val, NULL);
  while (1) {
    while(sigsetjmp(env1,3)==0){
      sleep(1);
       printf("i=%d,j=%d\n",i,j);
      i++;
      j--;
    }
  }
}

void fun2()
{
  struct sigaction sa;
  char ch='a';
  /* Install timer_handler as the signal handler for SIGVTALRM. */
  memset (&sa, 0, sizeof (sa));
  sa.sa_handler = &timer_handler;
  sa.sa_flags=SA_NODEFER;
  sigaction (SIGALRM, &sa, NULL);

  /* Configure the timer to expire after 250 msec... */
  it_val.it_value.tv_sec = INTERVAL/1000;
  it_val.it_value.tv_usec = (INTERVAL*1000) % 1000000;   
  /* ... and every 250 msec after that. */
  it_val.it_interval.tv_sec = INTERVAL/1000;;
  it_val.it_interval.tv_usec=(INTERVAL*1000) % 1000000;
  /* Start a virtual timer. It counts down whenever this process is*/

  setitimer (ITIMER_REAL, &it_val, NULL);

  /* Do busy work. */
  while (1) {
    while(sigsetjmp(env2,2)==0) {
      sleep(1);
      printf("ch=%c\n",ch);
      if(ch=='z')
        ch='a';
      ch++;
    }
  }
}

void fun3()
{
  struct sigaction sa;
  int a=0;
  /* Install timer_handler as the signal handler for SIGVTALRM. */
  memset (&sa, 0, sizeof (sa));
  sa.sa_handler = &timer_handler;
  sa.sa_flags=SA_NODEFER;
  sigaction (SIGALRM, &sa, NULL);

  /* Configure the timer to expire after 250 msec... */
  it_val.it_value.tv_sec = INTERVAL/1000;
  it_val.it_value.tv_usec = (INTERVAL*1000) % 1000000;   
  /* ... and every 250 msec after that. */
  it_val.it_interval.tv_sec = INTERVAL/1000;;
  it_val.it_interval.tv_usec=(INTERVAL*1000) % 1000000;
  /* Start a virtual timer. It counts down whenever this process is*/

  setitimer (ITIMER_REAL, &it_val, NULL);

  /* Do busy work. */
  while (1){
    while(sigsetjmp(env3,1)==0){
      sleep(1);
      printf("a=%d\n",a);
      a--;
    }
  }
}

int main ()
{
  timer_handler(1);
  return 0;
}
artless noise
  • 21,212
  • 6
  • 68
  • 105
Kush
  • 1

2 Answers2

3

You seem to be trying to implement some kind of threading without managing your stacks. This will not work. Ignoring the fact that pretty much everything in your code is undefined behavior, let's see what's actually going on.

You first call timer_handler(). This calls fun1. This sets a real time timer that will call timer_handler(). Then fun1 calls setjmp and sleeps. At this point your stack looks something like this:

timer_handler, fun1 (env1 points here), sleep

Now you get a SIGALRM. You execute timer_handler, this time count is 1, so you call fun2. It does more or less the same thing as fun1 and your stack looks like this:

timer_handler, fun1 (&env1), sleep, timer_handler, fun2 (&env2), sleep

One more SIGALRM. Stack looks like this:

timer_handler, fun1(&env1), sleep, timer_handler, fun2 (&env2), sleep, timer_handler, fun3 (&env3), sleep

One more SIGALRM, this is where the fun happens. You siglongjmp to env1. At this point your stack looks like this:

timer_handler, fun1

That's it. There might be stuff on the stack for fun2 and fun3 that might even work when you siglongjmp to them, but those parts are completely invalid now that you've rewound the stack pointer back to fun1. The call to printf seals the deal. Even if there might have been surviving parts on the stack that you could longjmp to, printf will definitely overwrite those parts.

Please suggest if necessary changes are required in the code.

The necessary changes are to delete the code, forget that setjmp and longjmp exist and never speak of this again. You can't do this. I've seen successful implementations of poor mans threads using setjmp and longjmp, but they also set up special stacks for the threads with sigaltstack, had proper locking and critical sections implemented and restricted the allowed functions calls to a very limited set of functions. Using printf when running such code is as far as you can get from having defined behavior. And all the successful implementations of this were of the "Look at this, isn't it horrible?" kind, not something anyone would ever use in real code.

peterh
  • 11,875
  • 18
  • 85
  • 108
Art
  • 19,807
  • 1
  • 34
  • 60
1

Good Grief !!

I see you added the SA_NODEFER following your earlier question on this piece of code.

Mr POSIX says:

The interaction between setitimer() and alarm() or sleep() is unspecified.

That may be enough to stop this from working... but, frankly, it is so absolutely frightful that it does not deserve to work.

To fix this you need to take all the logic out of the signal action function. Signals are very, very nasty "software interrupts" and need to be handled very carefully, preferably with gloves, eye protection and steady hands. Unless this is homework for setjmp etc., I would treat those as even more nasty, and avoid touching them at all at all.