[mod.unix] Unix Technical Digest V1 #34

Ron Heiby (The Moderator) <unix-request@cbosgd.UUCP> (03/26/85)

Unix Technical Digest       Tue, 26 Mar 85       Volume  1 : Issue  34

Today's Topics:
                          [Signal Miscellany]
----------------------------------------------------------------------

Date: 15 Mar 85 13:53:36 GMT
From: jack@boring.UUCP
Subject: When does ( alarm(1) == alarm(INFINITY) ) ?

In article <179@encore.UUCP> ptw@encore.UUCP (P. Tucker Withington) writes:
>As I read the manual (I didn't read the code) pause will only return when a
>signal is "caught".  If the semantics were that it would also return
>(immediately) if there were no signal handlers active (since they revert when
>caught), the race condition would also disappear.  

This sounds nice at first, but gives rise to some even weirder
situations.
 For instance, I have a program that uses this feature, and then,
later on, decide to put an interrupt handler in it, in case the
user wishes to abort the program.
 Guess what? Suddenly my program stops working!! I wouldn't want
to look for *that* bug........
 An other approach would be to let pause() return if there is no
signal handler for SIGALRM, but I'm sure that there are people
who use pause() for things totally unrelated to alarm() calls.

 By the way, I *do* agree that it might not be a bad idea to
have pause() return when there are no signal handlers installed,
but I think it is not a complete solution to this race condition.
-- 
	Jack Jansen, {decvax|philabs|seismo}!mcvax!jack
It's wrong to wish on space hardware.

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

Date: 16 Mar 85 01:58:36 GMT
From: mike@whuxl.UUCP (BALDWIN)
Subject: sleep problems

System V doesn't have the sleep(1) == sleep(INFIN) problem because it
uses setjmp/longjmp to catch the alarm:

sleep(amt)
{
	...
	signal(SIGALRM, catch);
	if (setjmp(buf) == 0) {
		alarm(amt);
		pause();
	}
	...
}

catch()
{
	longjmp(buf, 1);
}

So if the alarm comes in before the pause, it will skip over the pause.
Unfortunately, the longjmp causes a particularly bad thing to happen: if
another signal is caught while the sleep is pausing, and the alarm goes
off before the signal routine has returned, that signal routine will be
cut off in the middle!
							Michael Baldwin
							AT&T Bell Labs
							harpo!whuxl!mike

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

Date: 19 Mar 85 19:21:11 GMT
From: ksbszabo@wateng.UUCP (Kevin Szabo)
Subject: Printing in signal handlers (more on ( alarm(1) == alarm(INFINITY))?

In article <312@calgary.UUCP> radford@calgary.UUCP (Radford Neal) writes:
>I think the moral of all this is:
>     - Don't use signal handlers if you can avoid it, especially for anything
>       other than printing an error message and exiting.
>
>     - If you have to use signal handlers, you better think REALLY carefully
>       about what could happen, or you may write a program which fails 
>       inexplicably once every few weeks.

Your second moral should be applied BEFORE you apply the first moral.
For instance, you can really confuse STDIO if you were to do the following
in a signal handler:

BadSignal( n )
{
	fprintf(stderr,"Huh? Caught unexpected signal %s!\n", list_of_sigs(n) );
}
Stdio could have been in the middle of a malloc for flsbuf() or something.
You could have gotten a core dump if you were unlucky enough...


The problem is that practically none of STDIO is re-entrant. The only
thing you can trust in a signal handler is a direct call to the kernel.

Thus any printing in a signal handler should probably look like:
(I might have the arguments in the wrong order to write()  )

BadSignal( n )
{
	extern	caught_signal;
	char	*msg;

	msg = "Huh? ...";
	while( *msg ) {
		if ( write( fileno(stderr), msg, 1 ) != 1 ) abort();
		msg++;
	}
	....spit out the rest of the message....

	/* tell the rest of the program that a signal occurred. We'll
	 * handle it outside of the signal handler because it is safer.
	 * Multiple signals will get lost, but they usually do under UNIX
	 * anyway */

	caught_signal = n;
}

Note: The abort() is to get a core dump in case you have done something
funny to stderr. You better not be catching SIGILL (or whatever abort 
causes) otherwise on some systems (4.2 BSD) you will just get an infinite
loop.

So maybe the first moral of the story is: don't do any more than set
an external in a signal handler, otherwise be prepared to look long
and hard at what you are doing.

						Kevin
-- 
Kevin Szabo  watmath!wateng!ksbszabo (U of Waterloo VLSI Group, Waterloo Ont.)

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

Date: 21 Mar 85 06:23:34 GMT
From: geoff@utcs.UUCP (Geoff Collyer)
Subject: Printing in signal handlers (more on ( alarm(1) == alarm(INFINITY))?

In article <2175@wateng.UUCP> ksbszabo@wateng.UUCP (Kevin Szabo) writes:
>The problem is that practically none of STDIO is re-entrant. The only
>thing you can trust in a signal handler is a direct call to the kernel.

I think the real problem is that UNIX allows one to catch signals at all.
Given that we don't have cheap processes a la Thoth, catching signals is a
necessary evil but signal handlers should be kept simple and in particular
should avoid using longjmp (if you have ever read and understood the source,
you won't want to use longjmp).  At most, setting a global flag often
suffices; this also may allow one to omit calls to signal to enable
and disable signal catching around critical sections by simply noting
the state of the flag at appropriate places.

I can't speak for all UNIXes, but the system call interface routines
in the PDP-11 v7 C library often aren't re-entrant either: they copy
their arguments into static storage, then issue the system call pointing
at the static storage.  If you interrupt such a routine (say signal(2))
while it is copying its arguments and in the signal catcher you invoke
the same system call, then return from the signal catcher without calling
longjmp, the first instance of signal will continue to execute, though
some of its static storage was trashed by the second call in the signal
handler.  So if you are going to make those direct calls to the kernel
in your signal handler, you'd better do it from assembler or verify
that your C library system call interfaces are re-entrant.

This is something that I believe Whitesmith's got right:
last time I looked they had a standard routine for issuing system calls
that left the arguments on the stack and made the system call point
at the stacked arguments.

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

Date: 23 Mar 85 01:40:09 GMT
From: wls@astrovax.UUCP (William L. Sebok)
Subject: don't do long jumps in signal handlers

> I think the real problem is that UNIX allows one to catch signals at all.
> Given that we don't have cheap processes a la Thoth, catching signals is a
> necessary evil but signal handlers should be kept simple and in particular
> should avoid using longjmp (if you have ever read and understood the source,
> you won't want to use longjmp).

Unfortunately if you wan't to interrupt a 4.2 read() from a terminal you
have to do longjmp (or worse).

In my opinion, if they didn't want signals to abort terminal reads they should
have provided an explicit i/o abort function that could be called from the
signal handler.
-- 
Bill Sebok			Princeton University, Astrophysics
{allegra,akgua,burl,cbosgd,decvax,ihnp4,noao,princeton,vax135}!astrovax!wls

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

Date: 22 Mar 85 22:23:46 GMT
From: gwyn@brl-tgr.ARPA (Doug Gwyn <gwyn>)
Subject: Printing in signal handlers (more on ( alarm(1) == alarm(INFINITY))?

My recommendation is to do little more than set a flag in the signal
catcher and return.  Test this flag at appropriate places in the
main program control loop.  This is much safer than trying to do
anything useful from inside a signal catcher.

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

End of Unix Technical Digest
******************************
-- 
Ronald W. Heiby / ihnp4!{wnuxa!heiby|wnuxb!netnews}
AT&T Information Systems, Inc.
Lisle, IL  (CU-D21)