SIGALRM can't be delivered after longjmp from handler?
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 voidinterrupt()
{
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/
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/
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_signaland 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/
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_bufenv;
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/
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/
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/
Did that; no effect.
Pete.
Quoted 1 line · David A. Gatwood
*ponders*.... Try sigsetjmp and siglongjmp
** Sent via the linuxppc-dev mail list. See http://lists.linuxppc.org/
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/
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/