tags:

views:

164

answers:

2

For some reason when I return from the signal handler for SIGFPE, the variable i is one less than I expect it to be, to get the correct results from my program (iterate through all of the pairs in the pairs array) I must check the return value of sigsetjmp and increment i if I am returning from a signal "catch". Why is this? Why is the incrementation that happens during the iteration of the floating point exception lost?

#include <stdio.h>
#include <signal.h>
#include <stdlib.h>
#include <setjmp.h>                                                               

jmp_buf e;

int i;
void float_exception ();
int main ()
{
    int pairs[][2] = {
        {10, -5}, {10, -3}, {-10, -3}, {-10, -5}, {-10, 3}, {-10, 5}, {-10, 0},
        {10, 0}, {0, 10}, {2, 3}, {3, 3}, {5, 10}
    };
    int npairs;
    /* handle sigfpe so /0 doesn't interrupt the rest of the program */
    signal (SIGFPE, float_exception);
    printf ("Seeing what my C implementation does with negative or 0 modulo"
            "\narithmetic.\n");
    npairs =  sizeof (pairs) / sizeof (int) / 2;
    i = 0;

    if (sigsetjmp (e, 1) != 0) {
        i++;    /* without this line, i is one less than I expect it to be */
    }
    for (; i < npairs - 1; i++) {
        printf ("%d:   %d %% %d\t= ", i, pairs[i][0], pairs[i][1]);
        fflush (stdout);
        printf ("%d\n", pairs[i][0] %pairs[i][1]);
        fflush (stdout);
    }
    return 0;
}



void float_exception ()
{
    printf ("fpe\n");
    fflush (stdout);
    longjmp (e, 1);
}
+1  A: 

That looks right to me. The "normal" i++ happens at the end of the for() loop - and if you call longjmp(e,1) you skip that.

caf
I didn't understand this until now :( doh.
Tom Dignan
+1  A: 

caf's answer seems to be right.

What I actually expected initially, was, that i needs to be declared volatile. At least that's what the author of NSBlog claims.

One other thing to note: it might be dangerous to call printf and fflush inside a signal handler. Given the fact that SIGFPE is a synchronous signal, it's not completely out of the question, but you should know what you are doing.

And finally, from the Linux man-page: "longjmp() and siglongjmp() make programs hard to understand and maintain. If possible an alternative should be used."

Ringding
I had tried declaring i volatile to no avail, I am thinking caf's answer is right too.long/sigset/jmp were the only thing I could find that would let me jump out of the callback function and back to the loop. otherwise the exception just keeps emitting the signal over and over (it's division by zero, so I'm not really surprised), and to jump out of that loop, I need to jump out of the signal handler into another block of code. I guess I can use a mutex around the stdio in float_execption().
Tom Dignan
Calling *any* library function except signal() from within a signal handler is dangerous. I've been bitten by this personally; we called printf() from within a signal handler, but instead of writing the message to the console, it overwrote part of an Access .mdb file we were updating, hosing the database beyond repair. Frankly, C does not provide very good tools for handling exceptions like this; you're far better off doing a sanity check on your arguments before attempting the operation.
John Bode
@Ringding, well dDividing by zero is undefined behavior, so Tom has no guarantee at all (given by the C Standard. The POSIX Standard may give guarantees, i don't know) that a signal happens. Another thing is that of course the value of i isn't guaranteed to be stored across the jump at all. The value of i is unspecified. That's why volatile is needed. His program relies on both undefined behavior and unspecified values. But in the end of the day, the program runs in an implementation, not on the Standard's PDF, so if the guy knows what he does he's fine.
Johannes Schaub - litb
@JohnI changed the code to check the value of sigsetjmp to see if I was jumping back to it, and printf from the main program's thread of execution instead. That sounds like a real horror story. Thanks!
Tom Dignan
@John Bode: that's not true. There's a list of functions which are explicitly allowed in signal handlers. Of course, printf is not in this list. It is reproduced in the second link of my answer above ("Asynchronous—signal-safe functions").
Ringding
@Ringding longjmp() isn't in the that list either.
sigjuice