Subject: Re: Sane exit from a program on receipt of a signal
To: None <tech-userlevel@NetBSD.org>
From: ITOH Yasufumi <itohy@NetBSD.org>
List: tech-userlevel
Date: 08/02/2007 19:50:41
Hello,

summary:

			[A]			[B]
			use kill(0, SIGTSTP)	properly use kill(0, SIGTSTP)
			everywhere		or kill(getpid(), SIGTSTP)

(a) code is		simple			complex

(b) checking code is	easy			difficult

(c) process wants to	kill(0, SIGTSTP)	kill(0, SIGTSTP)
    suspend w/o signal

(d) tty suspend 	kill(0, SIGTSTP)	kill(getpid(), SIGTSTP)
			signal may duplicate

(e) SIGTSTP is sent	kill(0, SIGTSTP)	kill(getpid(), SIGTSTP)
    to the process	job suspends		job hangs


[A](d) is problematic if
 - There's another process P in the process group
 - P is not a process group leader (in which case the terminal hangs
   and both [A] and [B] are equally useless),
 - P does not suspend with SIGTSTP,
 - P behaves incorrect if P received SIGTSTP twice.

I don't think any real application of such P exists.

[A] is simple, easy to verify, and no real problem;
[B] is complex, difficult to verify the usage, and have problem
(for normal users, not for pedants) that easily reproduces.

That's why I recommend to use kill(0, SIGTSTP) everywhere.


mouse@Rodents.Montreal.QC.CA writes:
> >>>     (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.

Telling which case has happen is sometimes difficult.

> > 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.

In order to trap misuse of proposed function (kill(getpid(), SIGTSTP)
for this purpose, the function must return an error unless
the signal is sent to all other processes of the process group.
This is, however, almost impossible to implement, so I think we
should not use the proposed function for SIGTSTP.

> > 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.

That does no more than something like the user types ^Z twice.
A UNIX process should not depend on the number of arrived signals.
If two signal may arrive, yes, the process may do the work twice,
but this should not harm the process.

I wonder there ever exists such a program that invokes tty-oriented
programs and that also handles SIGTSTP.
Handling SIGTSTP is used for changing tty mode, and I don't think
multiple processes out of a job handles SIGTSTP.
Do you know a real application?  If none, this argument doesn't
make any meanings.

> > 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!

The right right behavior has no benefit for normal users, though.

> You are trying to protect na$B}W(Be 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.

We didn't to intend to do any clever tricks.
As a result of taking the simplest way, we have the side effect
that accidentally protects users.
Since there's no reason to avoid the not-so-bad side effect (for
normal users), we just leave it.

Spending more code sequences and having more possibility of making bugs
to avoid ``clever'' thing seems rather silly.

We still can avoid the side effect with SIGSTOP.

> > 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.

If we don't handle SIGTSTP in the simplest way, the side effect don't appear.

Regards,
-- 
ITOH Yasufumi