![]() |
|
|
|
|
|||||||
| High Level Programming Post questions about C, C++, Java, SQL, and other programming languages here. |
|
|
||||
| Thread | Thread Starter | Forum | Replies | Last Post |
| signal handler problems | blowtorch | High Level Programming | 4 | 08-26-2007 05:14 AM |
| signal handler for SIGCHLD | jens | High Level Programming | 3 | 07-02-2005 09:05 AM |
| Runaway processes killed (Really need help) | Micz | UNIX for Dummies Questions & Answers | 2 | 10-28-2003 10:45 AM |
| shell script signal handler | jalburger | Shell Programming and Scripting | 2 | 12-04-2002 01:10 PM |
| SIGALRM Help | skannan | High Level Programming | 1 | 06-16-2002 07:17 AM |
|
|
Submit Tools | LinkBack | Thread Tools | Display Modes |
|
|||
|
Runaway SIGALRM signal handler
I have written a program to demonstrate a problem I have encountered when using BSD style asynchronous input using the O_ASYNC flag in conjunction with a real time interval timer sending regular SIGALRM signals to the program. The SIGIO handler obeys all safe practices, using only an atomic update to a volatile sig_atomic_t variable.
The program is below. The idea is that when the user enters a 'q', the program is supposed to stop. It does not. If you uncomment the printf statement in teh main program's loop you will see that the loop is exited, but the return statement is never reached because the SIGALRMS seem to keep the program alive. Be prepared to kill it with a Ctrl-C. If I use the AIO style asynchronous I/O there is no problem. It is related to the O_ASYNC flag. (In fact if you simply call fcntl() and tell the kernel to send signals to the process, without setting the flag, the same problem will occur! ) Can anyone explain what is wrong? Code:
#include <stdio.h>
#include <signal.h>
#include <sys/time.h>
#include <fcntl.h>
void on_alarm(int); /* handler for alarm */
void on_input(int); /* handler for keybd */
int set_timer( int which, long initial, long repeat );
volatile sig_atomic_t finished = 0;
int main( int argc, char * argv[])
{
int fd_flags;
int k = 0;
struct sigaction newhandler;
sigset_t blocked;
newhandler.sa_handler = on_input; /* handler function */
newhandler.sa_flags = 0;
sigemptyset(&blocked);
newhandler.sa_mask = blocked;
if ( sigaction(SIGIO, &newhandler, NULL) == -1 )
perror("sigaction");
newhandler.sa_handler = on_alarm; /* handler function */
if ( sigaction(SIGALRM, &newhandler, NULL) == -1 )
perror("sigaction");
fcntl(0, F_SETOWN, getpid());
fd_flags = fcntl(0, F_GETFL);
fcntl(0, F_SETFL, (fd_flags|O_ASYNC));
set_timer(ITIMER_REAL, 500, 500);
while( !finished ) {
pause();
printf("still in loop and finished = %d\n", finished);
}
return 0;
}
void on_input(int signum)
{
int c = getchar();
if ( c == 'q' )
finished = 1;
}
void on_alarm(int signum)
{
static int k = 0;
printf("call number %d\n", k++);
}
int set_timer( int which, long initial, long repeat )
{
struct itimerval itimer;
long secs;
// initialize initial delay
secs = initial / 1000 ;
itimer.it_value.tv_sec = secs;
itimer.it_value.tv_usec = (initial - secs*1000 ) * 1000 ;
// initialize repeat inveral
secs = repeat / 1000 ;
itimer.it_interval.tv_sec = secs;
itimer.it_interval.tv_usec = (repeat - secs*1000 ) * 1000 ;
return setitimer(which, &itimer, NULL);
}
Last edited by Perderabo; 04-10-2008 at 07:30 PM. Reason: Adding code tags... it's just 13 little keystrokes to readability. |
| Forum Sponsor | ||
|
|
|
|||
|
Not that simple
The lines:
secs = initial / 1000 ; itimer.it_value.tv_sec = secs; itimer.it_value.tv_usec = (initial - secs*1000 ) * 1000 ; set the seconds to 0 and the microseconds to 500,000. That is intentional integer division. It si the standard way to convert milliseconds to (secs,microsecs). Stewart |
|
|||
|
When you run it, the 'q' causes the program to quit? I have run this program on Linux 2.6..x (from 9 to 24) and on Solaris 9, and it does not terminate on 'q'. And I have run it on at least 6 different machines. You used the exact code?
Stewartw |
|
|||
|
Yep. I did the intuitive interactive thing. I fear your problem is pretty standard for stdio buffered programs looking for a newline.
Code:
@skbsd::~/src>./progname call number 0 still in loop and finished = 0 call number 1 still in loop and finished = 0 call number 2 still in loop and finished = 0 call number 3 still in loop and finished = 0 call number 4 still in loop and finished = 0 call number 5 still in loop and finished = 0 qcall number 6 qcall number 7 qcall number 8 qcall number 9 qcall number 10 <\r here> still in loop and finished = 1 |
|
||||
|
You are doing something extremely non-standard and you aren't bothering to test the return codes. Solaris does not support O_ASYNC. Here is the Solaris 9 fcntl man page. I don't see any O_ASYNC flag mentioned. You actually got this to compile on Solaris??? I got to try that tomorrow.... I would expect it to run on FreeBSD. I'm not sure about Linux.... I'm don't have time right now to look up the man pages. Anyway this BSD async stuff is pretty much BSD only and not part of Posix.
Please test those return codes. If not all the time, at least break down and test them when you don't understand the behavior of the system calls. |
|
|||
|
Ramen Noodle,
You are correct. When I supply the return it terminates. But this is the real story. Below is the original program that had this problem. It uses the NCurses library. I simplified it for the post. This program puts the terminal into cbreak mode and does not require the newline character -- it is using non-blocking I/O. And you will discover that typign newline after the 'q' does not solve it. But I would be grateful to know what does. Here it is: #include <stdio.h> #include <string.h> #include <stdlib.h> #include <curses.h> #include <signal.h> #include <sys/time.h> #include <fcntl.h> #include <time.h> #include "timers.h" /*****************************************************************************/ /* Global Constants */ /*****************************************************************************/ #define INITIAL_SPEED 30 #define RIGHT 1 #define LEFT -1 #define ROW 12 #define MESSAGE "ooooooo=>" #define REVMSSGE "<=ooooooo" #define BLANK " " int row; // current row int col; // current column int dir; // Global variable to store direction of movement int speed; // Current speed in chars/second volatile sig_atomic_t finished; /*****************************************************************************/ /* Signal Handler Prototypes */ /*****************************************************************************/ void on_alarm(int); /* handler for alarm */ void on_input(int); /* handler for keybd */ /*****************************************************************************/ /* Utility Function Prototype */ /*****************************************************************************/ void enable_kbd_signals(); /*****************************************************************************/ /* Main */ /*****************************************************************************/ int main( int argc, char * argv[]) { struct sigaction newhandler; // for installing handlers sigset_t blocked; // to set mask for handler // Set up signal handling newhandler.sa_handler = on_input; // name of handler newhandler.sa_flags = SA_RESTART; // flag is just RESTART sigemptyset(&blocked); // clear all bits of blocked set //sigaddset(&blocked, SIGALRM); newhandler.sa_mask = blocked; // set this empty set to be the mask if ( sigaction(SIGIO, &newhandler, NULL) == -1 ) { perror("sigaction"); return (1); } sigemptyset(&blocked); // clear all bits of blocked set //sigaddset(&blocked, SIGIO); newhandler.sa_mask = blocked; // set this empty set to be the mask newhandler.sa_handler = on_alarm; // SIGALRM handler function if ( sigaction(SIGALRM, &newhandler, NULL) == -1 ){ // try to install perror("sigaction"); return (1); } // Prepare the terminal for the animation initscr(); // initialize the library and screen cbreak(); // put terminal into non-blocking input mode noecho(); // turn off echo clear(); // clear the screen curs_set(0); // hide the cursor // Initialize the parameters of the program row = ROW; col = 0; dir = RIGHT; finished = 0; speed = INITIAL_SPEED ; // Turn on keyboard signals enable_kbd_signals(); // Start the real time interval timer with delay interval size set_timer( ITIMER_REAL, 1000/speed, 1000/speed ); // mvaddstr(LINES-1, 0, "Current speed:"); //refresh(); // Put the message into the first position and start mvaddstr(row, col, MESSAGE); while( !finished ) { pause(); } endwin(); return 0; } /*****************************************************************************/ void on_input(int signum) { int c; c = getch(); switch (c) { case 'Q': case 'q': finished = 1; } } /*****************************************************************************/ void on_alarm(int signum) { mvaddstr( row, col, BLANK ); // erase old string col += dir; // advance one column move( row, col ); // move to new locataion if ( RIGHT == dir ) { addstr( MESSAGE ); // add forward string if ( col+strlen(MESSAGE) >= COLS-1 ) dir = LEFT; // reverse if hitting edge } else { addstr( REVMSSGE ); // add reverse string if ( col <= 0 ) dir = RIGHT; // reverse if hitting edge } refresh(); } /*****************************************************************************/ /* * install a handler, tell kernel who to notify on input, enable signals */ void enable_kbd_signals() { int fd_flags; fcntl(0, F_SETOWN, getpid()); fd_flags = fcntl(0, F_GETFL); fcntl(0, F_SETFL, (fd_flags|O_ASYNC)); } |
| Tags |
| linux, solaris |
| Thread Tools | |
| Display Modes | |
|
|