Skip to contentSkip to conversation

SIGALRM can't be delivered after longjmp from handler?

DISCUSSION Peter M. Jansson 3 participants 9 messages Mar 15, 2000 – Mar 16, 2000

lore
Share & tools
lore
#0 lore raw

I wrote the example that follows to illustrate what I think is a problem with signal delivery. If this program works, then it should run in 5 cycles; the first 2 end with the "Timed out" message, while the last 3 end with the "Wait interrupted" message. On my PowerMac 7200 running 2.2.15pre3, I get 2 "Timed out" cycles, one "Wait interrupted" cycle, and then two more "Timed out" cycles. What I think is going on is that, once the SIGALRM handler executes the longjmp, no further SIGALRM signals are delivered to the process -- I don't know if this is because the signals aren't delivered, or because the setitimer call isn't working. I've observed this example to run correctly on BSD/OS, IRIX, and Solaris, and seen it fail on LinuxPPC and Linux x86.

Anyone seen this, and possibly know of a fix or workaround?

Pete.

-------------------------------

#include <signal.h>
#include <setjmp.h>
#include <sys/time.h>
#include <stdio.h>

static jmp_buf env;
static unsigned int count = 0;

static void
interrupt()

{
        printf ("Interrupt: %d\n", count);
        if (count > 2)
                longjmp(env, 1);
}

main ()

{
        struct timeval tv;
        struct itimerval it, oit;

        while (count++ < 5) {
                printf("Cycle %d\n", count);

                if (setjmp(env)) {
                        printf ("Wait interrupted\n");
                } else {
                        if (signal(SIGALRM, interrupt) == SIG_ERR)
                                perror("signal");
                        getitimer(ITIMER_REAL, &oit);
                        timerclear(&it.it_interval);
                        it.it_value.tv_sec = 2;
                        it.it_value.tv_usec = 16665;
                        if (setitimer(ITIMER_REAL, &it, &oit) < 0)
                        perror("tick");
                        printf ("Set timer; waiting...\n");
                        tv.tv_sec = 5;
                        tv.tv_usec = 0;
                        select(0, NULL, NULL, NULL, &tv);
                        printf ("Timed out\n");
                }
        }
}

** Sent via the linuxppc-dev mail list. See http://lists.linuxppc.org/

#1 lore raw

For other signals like SEGV and BUS and INTR, I had to re-enable the signal by calling signal() after longjmp as specified in the signal(2) manpage for Linux:

"Unlike on BSD systems, signals under Linux are reset to their default behavior when raised. However, if you include <bsd/signal.h> instead of <signal.h> then signal is redefined as __bsd_signal and signal has the BSD semantics. Both versions of signal are library routines built on top of sigaction(2)."

I changed your example program to restore the signal handler routine once the signal is raised and it worked as you described. I did not try using <bsd/signal.h>. You may wish to try that. I prefer just restoring the signal handler, since this avoid #ifdefs for different platorms.

----ranga <[email protected]>

** Sent via the linuxppc-dev mail list. See http://lists.linuxppc.org/

#2 lore raw

I'm sorry to be dense, but could you send me your modified version of my test program?

In an earlier run, I was calling signal inside the signal handler before the longjmp, and it had no effect, so I removed the code to make the example as simple as possible. It's possible I did it wrong, but if you could send me your changes, I'd be grateful.

Thanks,
Pete.

Quoted 11 lines · Sriranga Veeraraghavan

For other signals like SEGV and BUS and INTR, I had to re-enable the signal by calling signal() after longjmp as specified in the signal(2) manpage for Linux:

"Unlike on BSD systems, signals under Linux are reset to their default behavior when raised. However, if you include <bsd/signal.h> instead of <signal.h> then signal is redefined as __bsd_signal and signal has the BSD semantics. Both versions of signal are library routines built on top of sigaction(2)."

Quoted 8 lines

I changed your example program to restore the signal handler routine once the signal is raised and it worked as you described. I did not try using <bsd/signal.h>. You may wish to try that. I prefer just restoring the signal handler, since this avoid #ifdefs for different platorms.

----ranga <[email protected]>

** Sent via the linuxppc-dev mail list. See http://lists.linuxppc.org/

#3 lore raw
In reply to Peter M. Jansson
Quoted 12 lines · Peter M. Jansson

I wrote the example that follows to illustrate what I think is a problem with signal delivery. If this program works, then it should run in 5 cycles; the first 2 end with the "Timed out" message, while the last 3 end with the "Wait interrupted" message. On my PowerMac 7200 running 2.2.15pre3, I get 2 "Timed out" cycles, one "Wait interrupted" cycle, and then two more "Timed out" cycles. What I think is going on is that, once the SIGALRM handler executes the longjmp, no further SIGALRM signals are delivered to the process -- I don't know if this is because the signals aren't delivered, or because the setitimer call isn't working. I've observed this example to run correctly on BSD/OS, IRIX, and Solaris, and seen it fail on LinuxPPC and Linux x86.

Some OSes automatically reset signal handlers to SIG_DFL (which is SIG_IGN for alarm) after they receive a signal and some leave the signal hndler in place. The BSDs to the latter, and Linux, IIRC, does the former. You can fix this by re-installing the handler during the signal handler function, but the better way is to use sigaction instead of signal, which will give consistent behaviour on all platforms.

Later,
David

Quoted 7 lines

-------------------------------

#include <signal.h>
#include <setjmp.h>
#include <sys/time.h>
#include <stdio.h>

Quoted 6 lines

static jmp_buf env;
static unsigned int count = 0;

static void
interrupt()
{

	signal(SIGALRM, interrupt);
Quoted 24 lines
        printf ("Interrupt: %d\n", count);
        if (count > 2)
                longjmp(env, 1);
}

main ()

{
        struct timeval tv;
        struct itimerval it, oit;

        while (count++ < 5) {
                printf("Cycle %d\n", count);

                if (setjmp(env)) {
                        printf ("Wait interrupted\n");
                } else {
                        if (signal(SIGALRM, interrupt) == SIG_ERR)
                                perror("signal");
                        getitimer(ITIMER_REAL, &oit);
                        timerclear(&it.it_interval);
                        it.it_value.tv_sec = 2;
                        it.it_value.tv_usec = 16665;
                        if (setitimer(ITIMER_REAL, &it, &oit) < 0)
                        perror("tick");
Quoted 8 lines
                        printf ("Set timer; waiting...\n");
                        tv.tv_sec = 5;
                        tv.tv_usec = 0;
                        select(0, NULL, NULL, NULL, &tv);
                        printf ("Timed out\n");
                }
        }
}

** Sent via the linuxppc-dev mail list. See http://lists.linuxppc.org/

#4 lore raw

I made this exact change, and the program's behavior is unchanged. Did you compile and run the program with your change and observe different behavior? I still get only one "Wait interrupted" instead of three.

The signal is being delivered multiple times, it just isn't delivered anymore after the longjmp, so I'm not sure that resetting the signal handler is involved in this problem.

Pete.

Quoted 8 lines · David A. Gatwood
Quoted 3 lines

static void
interrupt()
{

	signal(SIGALRM, interrupt);
Quoted 2 lines
        printf ("Interrupt: %d\n", count);
        if (count > 2)

** Sent via the linuxppc-dev mail list. See http://lists.linuxppc.org/

#5 lore raw
Quoted 7 lines · Peter M. Jansson

I made this exact change, and the program's behavior is unchanged. Did you compile and run the program with your change and observe different behavior? I still get only one "Wait interrupted" instead of three.

The signal is being delivered multiple times, it just isn't delivered anymore after the longjmp, so I'm not sure that resetting the signal handler is involved in this problem.

*ponders*.... Try sigsetjmp and siglongjmp

David

** Sent via the linuxppc-dev mail list. See http://lists.linuxppc.org/

#7 lore raw
Quoted 1 line · Peter M. Jansson

Did that; no effect.

Well, it did for me, but I had to change the location of the signal() line. The installation of the signal handler has to occur before the sigsetjmp or else the longjmp() restores an empty set of handlers.

1. Move the if (signal(...)) line to right after the printf("Cycle...") line

2. Change setjmp(jmp_buf) to sigsetjmp(jmp_buf,1)

3. Change longjmp to siglongjmp

4. add a signal(...) line to the beginning of the signal handler

The program then works as it did on *BSD.

David

** Sent via the linuxppc-dev mail list. See http://lists.linuxppc.org/

#8 lore raw
In reply to David A. Gatwood

OK...I just tried this with setting the second parameter of sigsetjmp to 1, instead of leaving it at 0. I need to RTFM more to understand why it made a difference, but at least it works.

Thanks all!

Pete.

Quoted 1 line · David A. Gatwood

*ponders*.... Try sigsetjmp and siglongjmp

** Sent via the linuxppc-dev mail list. See http://lists.linuxppc.org/