khanhhh89
khanhhh89

Reputation: 317

Restoring the environment by sigsetjmp and longsetjmp

I'm using the couple sigsetjmp and singlongjmp with SIGALARM for interrupting a system call, which is illustrated in the following code

//data of Alarm_interrupter
void (TClass::*fpt)(const char*);   // pointer to member function
TClass* pt2Object;                  // pointer to object
===================================================
//for timeout processing
static sigjmp_buf jmpbuf;
static void recvfrom_alarm(int) {
    siglongjmp(jmpbuf, 1);
}
======================================================
void Alarm_interrupter::start_timeout() {
    signal(SIGALRM, recvfrom_alarm);
    alarm(timeout);
    (*pt2Object.*fpt)("timeouted before sigsetjmp"); //this call works OK
    if (sigsetjmp(jmpbuf,1) != 0) {
        //at this point, pt2Object is still OK,
        //but fpt seems to point to nothign.
        (*pt2Object.*fpt)("timeouted after sigsetjmp");
    }
    return;
}
==============================================================

Before sigsetjmp returnn 1, the call using object and the method pointer: *pt2Object.*fpt("timeouted before sigsetjmp") is OK, but after sigsetjmp return 1, this call failed. After examining the state of variables, I noticed that the object pointer "pt2Object" is still Ok, but the method pointer "fpt" seems to different.

I think that one possible reasons for this is that sigsetjmp cannot restore the whole earlier environment, which includes the method pointer "fpt".

Could you guys help me fix this problem. Thanks so much!

Upvotes: 2

Views: 1053

Answers (1)

luser droog
luser droog

Reputation: 19504

As Potatoswatter points out, using the alarm to delay the longjmp is too clever to rely upon. You have to call 'sigsetjmp' first. It has to happen before you try to return there.

The only way sigsetjmp or setjmp will work is following this pseudocode.

if (sigsetjmp(...) != 0) {
    //  Error handling code
}
// code that might call siglongjmp to bail out to Error handling code

You see, it has to be executed once to perform the save of the context. This initializes the jmpbuf. If you call longjmp without having called setjmp earlier in the execution, the behavior cannot be predicted.

Also, longjmp will tend to obliterate any local variables you might try to use.

int var = 3;
var = 2;
if (sigsetjmp(...) != 0) {
    //  Error handling code
    printf("%d", var); // could print 2, 3 or even "potato". Local vars get trashed.
}
// code that might call siglongjmp to bail out to Error handling code

So you really want to do everything interesting after the *setjmp.

int var = 3;
if (sigsetjmp(...) != 0) {
    //  Error handling code
    var = 2;
    printf("%d", var); // now you know it's 2
}
// code that might call siglongjmp to bail out to Error handling code

For any hope of it surviving across the *longjmp, it needs to be marked volatile.

volatile int var = 3;
var = 2;
if (sigsetjmp(...) != 0) {
    //  Error handling code
    printf("%d", var); // probably 2
}
// code that might call siglongjmp to bail out to Error handling code

And even this may not be enough. It may need to be something called a sigatomic_t or something similar. But try not to need crazy stuff like that.

int var = 3;
memcpy(var, (int *){2}); //memcpy is pretty reliable (C99ism: immediate pointer))
if (sigsetjmp(...) != 0) {
    //  Error handling code
    printf("%d", var); // probably 2
}
// code that might call siglongjmp to bail out to Error handling code

Upvotes: 1

Related Questions