[comp.unix.internals] BSD vs. Sys V alarm and SIGALRM PROBLEMS!

cats@lamont.ldgo.columbia.edu (catherine susch) (02/05/91)

	We believe that we've discovered a problem/bug with 
	the alarm(3) function and signal(2). Give us your 
	feedback if you	think otherwise, because we think 
	that this is a major problem that needs fixing. 

HERE IT IS:

	We've been trying  to write a timeout  procedure so  that
	if the application  is  waiting  for input  and  the user 
	decides to  take a nap,  beep at him to  type  something,
	and if he doesn't, then exit.

	The system we're using is SunOS 4.0.3  and  the application
	is written using AT&T system 5 curses.
	
	In SunOS 3.5 we used the following test code fragment which 
	will give the desired results:



------------------------------------------------------------------------
	#include <stdio.h>
	#include <signal.h>
	#include <errno.h>
	#include <sys/time.h>

	void	new_alarm();
	void	last_alarm();
	void	bad();

	main()
	{
		int inp;

	/* start alarm and wait for input	*/
		alarm(10); 
		signal(SIGALRM, new_alarm);   
		printf("waiting for input\n");
		inp = getchar();

	/* If input was recieved you should get here	*/

		printf("got input\n bye\n");
		signal(SIGALRM, SIG_IGN);
		exit(0);
}

void	new_alarm()
{
	/* start a new alarm and set a new handler for SIGALRM to 
	   quit if still no input received			*/

	alarm(20); 
	signal(SIGALRM, last_alarm);
	printf("alarm has sounded and new alarm is set\n");
}

void	last_alarm()
{
	printf("no input recieved, exiting program\n");
	exit(-1);
}
------------------------------------------------------------------


   Under all BSD versions it also works the same as SunOS 3.5.
   Under SunOS 4.0.3 and 4.1.1, however, this routine has very different
   results:
	
	The signal call

		signal(SIGALRM, last_alarm),

	inside the handler new_alarm(), never gets set and thus
	the program never gets to last_alarm().  It acts  as if 
	input was received when it wasn't.

	When the routine is incorporated  into an  application  with
	cursesV  calls, once an alarm sounds, it goes into new_alarm() and
	returns without resetting either signal or alarm. This causes it
	to call new_alarm over and over again until input is received.
	
   So here's our question:

		Is this a bug? 

		Has anyone ever encountered this?

		WHAT IS REALLY SUPPOSED TO HAPPEN UNDER SYSTEM 5???????

		Furthermore, is anyone aware of a workaround?

	Please email responses, since we are so preoccupied with
	this problem we may have trouble reading this newsgroup.
	I will post a summary if there is interest.


Cat


________________________________________________________________________________
 	 ______     ___      _________     | 
 	/          /   \         /         |	Catherine Ann Teresa Susch
       /	  /     \       /	   |	Lamont-Doherty Geo. Observatory
      /		 /_______\     /	   |	of Columbia University
     /_______   /         \   /    __	   |	Palisades,N.Y. 10964  USA
				  /__	   |    
				  __/	   |	cats@lamont.ldgo.columbia.edu
___________________________________________|____________________________________

rbj@uunet.UU.NET (Root Boy Jim) (02/06/91)

In article <3313@lamont.ldgo.columbia.edu> cats@lamont.ldgo.columbia.edu (catherine susch) writes:
>
>
>	We believe that we've discovered a problem/bug with 
>	the alarm(3) function and signal(2). Give us your 
>	feedback if you	think otherwise, because we think 
>	that this is a major problem that needs fixing. 

What you have discovered is an incompatibility between systems.
There are two kinds of system calls. Ones that are interruptible,
and ones that aren't. Most are not. Wait and read or write on a
slow device (such as a tty, where input may never occur) are
interruptible. When interrupted by a signal handler, they
return -1 and set errno to EINTR, or EAGAIN or some such.

Berkeley changed these calls to automaticly restart in 4.2BSD.
Purists screamed long and hard and a bit was put into the sigvec
to allow a choice to be made for each type of signal.

I don't know which versions you have. Another complication is
that you are using two timeouts, not one. Probably the most
portable solution is to use setjmp with the getchar and
longjmp from the alarm routine.

You are right. It is a major problem. It has been fixed, in the
POSIX specs, but it'll take awhile until everyone is fully
compliant. Also, POSIX doesn't specify the behavior of "signal",
only that of "sigaction". Such is life.
-- 

	Root Boy Jim Cottrell <rbj@uunet.uu.net>
	I got a head full of ideas
	They're driving me insane

hue@island.COM (Pond Scum) (02/07/91)

In article <121464@uunet.UU.NET> rbj@uunet.UU.NET (Root Boy Jim) writes:
>There are two kinds of system calls. Ones that are interruptible,
>and ones that aren't. Most are not. Wait and read or write on a
>slow device (such as a tty, where input may never occur) are
>interruptible. When interrupted by a signal handler, they
>return -1 and set errno to EINTR, or EAGAIN or some such.

Writes to a file appear to be interruptable and not restartable under SunOS
4.1.1.  Suprised the sh*t out of me when someone started having problems with
it.  In 4.0.3 and previous versions writes to "fast" devices could not
be interrupted.  I'll post the test program in a followup.


>Berkeley changed these calls to automaticly restart in 4.2BSD.
>Purists screamed long and hard and a bit was put into the sigvec
>to allow a choice to be made for each type of signal.

For the original poster, in order to prevent the auto restart, use sigvec()
or sigaction() (only available in SunOS 4.1.1), and specify SV_INTERRUPT
in the sv_flags field (SA_INTERRUPT and sa_flags for sigaction()).

>I don't know which versions you have. Another complication is
>that you are using two timeouts, not one. Probably the most
>portable solution is to use setjmp with the getchar and
>longjmp from the alarm routine.

If all you want is a timeout on tty input, I'd just use VTIME and VMIN, seems
simpler to me (though perhaps less portable):

#include <sys/termios.h>
#include <stdio.h>

main()
{
    struct termios oldt, newt;
    int c;

    ioctl(fileno(stdin), TCGETS, &oldt);
    newt = oldt;
    newt.c_cc[VMIN] = 0;
    newt.c_cc[VTIME] = 50;
    newt.c_lflag &= ~ICANON;
    ioctl(fileno(stdin), TCSETS, &newt);
    if ((c = getchar()) == EOF)
	fputs("No input\n", stderr);
    ioctl(fileno(stdin), TCSETS, &oldt);
    return(0);
}

(I know this isn't internals, but this is where to followups were directed)

----
"I'll tell you what "MTV" stands for.  It stands for "Music To Vomit by"."
-Bobby "The Brain" Heenan

Jonathan Hue, Senior Coding Pig	   Island Grapics Corp.  Graphic Arts Division
4000 Civic Center Drive San Rafael, CA  {uunet,sun}!island!hue  hue@island.com