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);
}