Subject: Re: Sane exit from a program on receipt of a signal
To: None <tech-userlevel@NetBSD.org>
From: der Mouse <mouse@Rodents.Montreal.QC.CA>
List: tech-userlevel
Date: 07/30/2007 12:08:17
>>>     (such as kill(0, SIGTSTP))
>> This is correct if, and only if, no SIGTSTP has already been
>> generated to the process group.  An example might be vi's behaviour
>> on receipt of ^Z, which vi uses as a suspend command regardless of
>> the user's suspc.
> OK, so kill(0, SIGTSTP) code is required anyway.

Required when no SIGTSTP has already been generated to the rest of the
process group.

Wrong when SIGTSTP has already been sent to them.

> What we can do in a signal hander is very limited, most tty-based
> text editors take 1., as far is I know.  Such programs must not use
> the proposed function that does kill(getpid(), SIGTSTP) (or
> raise(SIGTSTP)).

Right.

And (almost) everything else must not use the proposed function that
does kill(0,...) - the only programs it is correct for are programs
that, like vi or the other editors you mention, change the tty modes
enough that SIGTSTP is not generated from the tty, and want to simulate
its generation.

> A UNIX process should not count signals of the same type raised in
> succession, since it is unreliable.  Even if two signals of the same
> type is sent to a proceess, the process may receive only one.

Only when the second is sent while the first is still pending.  A
system that dropped a second SIGTSTP sent ten minutes later, after the
job had been suspended and resumed, on the grounds that "signals are
unreliable" would, quite correctly, be called broken.

And this is my point: generating a second SIGTSTP is wrong because it
produces a race between process A, generating the second SIGTSTP, and
process B, handling the first SIGTSTP.  If process A wins, B receives
only one SIGTSTP.  If process B wins, process B receives two SIGTSTPs.
That's why I call that paradigm wrong.

>> The only case where it is a good thing is when someone specifically
>> sends a SIGTSTP to just one process but expects it to affect the
>> entire process group.  This isn't the way default SIGTSTP handling
>> works, so any such attempt is broken in general; I prefer this
>> routine behave the same way as default SIGTSTP handling to the
>> extent possible, which means *not* resending to other processes.
> The way you propose is not easy to implement and has no benefit.

It is trivial to implement; simply use raise(), or kill(getpid(),...),
when invoking the default action while handling the signal.

It has the benefit that it works just like the default SIGTSTP handling
that a non-TSTP-aware process gets.

> Here's an example of "sh -c rogue", the processes are

> 	#process	sleeping at	comment
> 	base shell	wait		with job control
> 	sh		wait		no job control (process group leader)
> 	rogue				the terminal manipulating process

> If only the rogue process is suspended, the processes will be

> 	base shell	wait		waiting for status change of sh
> 	sh		wait		waiting for child to exit
> 	rogue				suspended

> and the terminal will not respond to user's input.
> This is the hang I'm saying.

Right.

This is what happens if you "sh -c cat" and kill -TSTP the cat.  That's
why believe it is the correct behaviour for rogue as well: I believe
that behaviour should be easily predictable and, in particular, should
not depend on whether the process happens to be TSTP-aware.

> This hang can be easily avoided if the rogue process (re)send SIGTSTP
> to the process group (sh and rogue).

Right.  But *it shouldn't be avoided*.  It is the right behaviour under
the circumstances!

You are trying to protect naïve users from getting wedged by doing
stupid things.  But it doesn't work reliably - it won't "fix" the
"problem" if the process to which the signal is sent doesn't happen to
be TSTP-aware, or doesn't use your "impose resending to the whole pg"
call when handling the signal - and confuses the issue by appearing to
"work" sometimes, under relatively unpredictable circumstances.  Not to
mention, it breaks any possible clever tricks that look to your routine
like this case.

> FYI, the rogue(6) uses curses(3) and does the latter behavior.  Try
> "pkill -TSTP rogue" and you can see the job suspends properly.

Actually, no, it suspends the job improperly.  It suspends the whole
job, which is wrong; it should suspend just the rogue process, like
TSTPing the cat process in the "sh -c cat" case.

/~\ The ASCII				der Mouse
\ / Ribbon Campaign
 X  Against HTML	       mouse@rodents.montreal.qc.ca
/ \ Email!	     7D C8 61 52 5D E7 2D 39  4E F1 31 3E E8 B3 27 4B